func()

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
}