timestamp/timestamp.go (77 lines of code) (raw):

/* Copyright (c) Facebook, Inc. and its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package timestamp // Here we have basic HW and SW timestamping support import ( "fmt" "net" "time" "golang.org/x/sys/unix" ) // from include/uapi/linux/net_tstamp.h const ( // HWTSTAMP_TX_ON int 1 hwtstampTXON int32 = 0x00000001 // HWTSTAMP_FILTER_ALL int 1 hwtstampFilterAll int32 = 0x00000001 // HWTSTAMP_FILTER_PTP_V2_EVENT int 12 hwtstampFilterPTPv2Event int32 = 0x0000000c ) const ( // ControlSizeBytes is a socket control message containing TX/RX timestamp // If the read fails we may endup with multiple timestamps in the buffer // which is best to read right away ControlSizeBytes = 128 // PayloadSizeBytes is a size of maximum ptp packet which is usually up to 66 bytes PayloadSizeBytes = 128 // look only for X sequential TS maxTXTS = 100 // Socket Control Message Header Offset on Linux ) const ( // HWTIMESTAMP is a hardware timestamp HWTIMESTAMP = "hardware" // SWTIMESTAMP is a software timestamp SWTIMESTAMP = "software" ) // Ifreq is a struct for ioctl ethernet manipulation syscalls. type ifreq struct { name [unix.IFNAMSIZ]byte data uintptr } // from include/uapi/linux/net_tstamp.h type hwtstampСonfig struct { flags int32 txType int32 rxFilter int32 } // ConnFd returns file descriptor of a connection func ConnFd(conn *net.UDPConn) (int, error) { sc, err := conn.SyscallConn() if err != nil { return -1, err } var intfd int err = sc.Control(func(fd uintptr) { intfd = int(fd) }) if err != nil { return -1, err } return intfd, nil } // ReadPacketWithRXTimestamp returns byte packet and HW RX timestamp func ReadPacketWithRXTimestamp(connFd int) ([]byte, unix.Sockaddr, time.Time, error) { // Accessing hw timestamp buf := make([]byte, PayloadSizeBytes) oob := make([]byte, ControlSizeBytes) bbuf, sa, t, err := ReadPacketWithRXTimestampBuf(connFd, buf, oob) return buf[:bbuf], sa, t, err } // ReadPacketWithRXTimestampBuf writes byte packet into provide buffer buf, and returns number of bytes copied to the buffer, client ip and HW RX timestamp. // oob buffer can be reaused after ReadPacketWithRXTimestampBuf call. func ReadPacketWithRXTimestampBuf(connFd int, buf, oob []byte) (int, unix.Sockaddr, time.Time, error) { bbuf, boob, _, saddr, err := unix.Recvmsg(connFd, buf, oob, 0) if err != nil { return 0, nil, time.Time{}, fmt.Errorf("failed to read timestamp: %v", err) } timestamp, err := socketControlMessageTimestamp(oob[:boob]) return bbuf, saddr, timestamp, err } // IPToSockaddr converts IP + port into a socket address // Somewhat copy from https://github.com/golang/go/blob/16cd770e0668a410a511680b2ac1412e554bd27b/src/net/ipsock_posix.go#L145 func IPToSockaddr(ip net.IP, port int) unix.Sockaddr { if ip.To4() != nil { sa := &unix.SockaddrInet4{Port: port} copy(sa.Addr[:], ip.To4()) return sa } sa := &unix.SockaddrInet6{Port: port} copy(sa.Addr[:], ip.To16()) return sa } // SockaddrToIP converts socket address to an IP // Somewhat copy from https://github.com/golang/go/blob/658b5e66ecbc41a49e6fb5aa63c5d9c804cf305f/src/net/udpsock_posix.go#L15 func SockaddrToIP(sa unix.Sockaddr) net.IP { switch sa := sa.(type) { case *unix.SockaddrInet4: return sa.Addr[0:] case *unix.SockaddrInet6: return sa.Addr[0:] } return nil }