in ptp/simpleclient/client.go [190:316]
func (c *Client) setup(ctx context.Context, eg *errgroup.Group) error {
iface, err := net.InterfaceByName(c.cfg.Iface)
if err != nil {
return err
}
cid, err := ptp.NewClockIdentity(iface.HardwareAddr)
if err != nil {
return err
}
log.Infof("using ClockIdentity %s, talking to %v using Two-Step Unicast PTPv2 protocol", cid, c.cfg.Address)
c.clockID = cid
// addresses
// where to send to
genAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(c.cfg.Address, fmt.Sprintf("%d", ptp.PortGeneral)))
if err != nil {
return err
}
eventAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(c.cfg.Address, fmt.Sprintf("%d", ptp.PortEvent)))
if err != nil {
return err
}
// bind to general port
genConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("::"), Port: ptp.PortGeneral})
if err != nil {
return err
}
c.genConn = genConn
c.genAddr = genAddr
// bind to event port
eventConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("::"), Port: ptp.PortEvent})
if err != nil {
return err
}
// get FD of the connection. Can be optimized by doing this when connection is created
connFd, err := timestamp.ConnFd(eventConn)
if err != nil {
return err
}
// we need to enable HW or SW timestamps on event port
switch c.cfg.Timestamping {
case "": // auto-detection
if err := timestamp.EnableHWTimestamps(connFd, c.cfg.Iface); err != nil {
if err := timestamp.EnableSWTimestamps(connFd); err != nil {
return fmt.Errorf("failed to enable timestamps on port %d: %v", ptp.PortEvent, err)
}
log.Warningf("Failed to enable hardware timestamps on port %d, falling back to software timestamps", ptp.PortEvent)
} else {
log.Infof("Using hardware timestamps")
}
case HWTIMESTAMP:
if err := timestamp.EnableHWTimestamps(connFd, c.cfg.Iface); err != nil {
return fmt.Errorf("failed to enable hardware timestamps on port %d: %v", ptp.PortEvent, err)
}
case SWTIMESTAMP:
if err := timestamp.EnableSWTimestamps(connFd); err != nil {
return fmt.Errorf("failed to enable software timestamps on port %d: %v", ptp.PortEvent, err)
}
default:
return fmt.Errorf("unknown type of typestamping: %q", c.cfg.Timestamping)
}
// set it to blocking mode, otherwise recvmsg will just return with nothing most of the time
if err := unix.SetNonblock(connFd, false); err != nil {
return fmt.Errorf("failed to set event socket to blocking: %w", err)
}
c.eventConn = &udpConnTS{eventConn}
c.eventAddr = eventAddr
// get packets from general port
eg.Go(func() error {
// it's done in non-blocking way, so if context is cancelled we exit correctly
doneChan := make(chan error, 1)
go func() {
for {
response := make([]uint8, 1024)
n, addr, err := genConn.ReadFromUDP(response)
if err != nil {
doneChan <- err
return
}
log.Debugf("got packet on port 320, n = %v, addr = %v", n, addr)
if !addr.IP.Equal(genAddr.IP) {
log.Warningf("ignoring packets from server %v", addr)
}
c.inChan <- &inPacket{data: response[:n]}
}
}()
select {
case <-ctx.Done():
log.Debugf("cancelled general port receiver")
return ctx.Err()
case err = <-doneChan:
return err
}
})
// get packets from event port
eg.Go(func() error {
// it's done in non-blocking way, so if context is cancelled we exit correctly
doneChan := make(chan error, 1)
go func() {
for {
response, addr, rxtx, err := timestamp.ReadPacketWithRXTimestamp(connFd)
if err != nil {
doneChan <- err
return
}
log.Debugf("got packet on port 319, addr = %v", addr)
if !timestamp.SockaddrToIP(addr).Equal(eventAddr.IP) {
log.Warningf("ignoring packets from server %v", addr)
}
c.inChan <- &inPacket{data: response, ts: rxtx}
}
}()
select {
case <-ctx.Done():
log.Debugf("cancelled event port receiver")
return ctx.Err()
case err = <-doneChan:
return err
}
})
return nil
}