func()

in cmd/ziffy/node/sender.go [173:241]


func (s *Sender) traceRoute(destinationIP string, sendingPort int, sweep bool) ([]SwitchTrafficInfo, error) {
	var route []SwitchTrafficInfo
	ptpUDPAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(destinationIP, fmt.Sprint(s.Config.DestinationPort)))
	if err != nil {
		return nil, fmt.Errorf("traceRoute unable to resolve UDPAddr: %w", err)
	}
	ptpAddr := timestamp.IPToSockaddr(ptpUDPAddr.IP, s.Config.DestinationPort)
	domain := unix.AF_INET6
	if ptpUDPAddr.IP.To4() != nil {
		domain = unix.AF_INET
	}
	connFd, err := unix.Socket(domain, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
	if err != nil {
		return nil, fmt.Errorf("traceRoute unable to create connection: %w", err)
	}
	defer unix.Close(connFd)
	// set SO_REUSEPORT so we can trace network path from same source port that ptp4u uses
	if err = unix.SetsockoptInt(connFd, unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
		return nil, fmt.Errorf("setting SO_REUSEPORT on sender socket: %s", err)
	}
	localAddr := timestamp.IPToSockaddr(net.IPv6zero, sendingPort)
	if err := unix.Bind(connFd, localAddr); err != nil {
		return nil, fmt.Errorf("traceRoute unable to bind %v connection: %w", localAddr, err)
	}

	destReached := false
	hopMax := s.Config.HopMax

	// if sweep is activated and the destination was found
	if sweep && s.destHop > 0 {
		hopMax = s.destHop - 1
	}
	// Stop incrementing hops when either the max hop count is reached or
	// the destination has responded unless continue is specified
	for hop := s.Config.HopMin; hop <= hopMax && (!destReached || s.Config.ContReached); hop++ {
		if err := unix.SetsockoptInt(connFd, unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, hop); err != nil {
			return route, err
		}
		// First 2 bits from Traffic Class are unused, so we shift the value 2 bits
		if err := unix.SetsockoptInt(connFd, unix.IPPROTO_IPV6, unix.IPV6_TCLASS, s.Config.DSCP<<2); err != nil {
			return route, err
		}
		var p ptp.Packet
		switch s.Config.MessageType {
		case ptp.MessageSync, ptp.MessageDelayReq:
			p = formSyncPacket(s.Config.MessageType, hop, s.currentRoute)
		case ptp.MessageSignaling:
			p = formSignalingPacket(hop, s.currentRoute)
		default:
			return route, fmt.Errorf("unsupported packet type %v", s.Config.MessageType)
		}

		if err := s.sendEventMsg(p, connFd, ptpAddr); err != nil {
			return route, err
		}

		select {
		case sw := <-s.inputQueue:
			s.routes[sw.routeIdx].switches = append(s.routes[sw.routeIdx].switches, *sw)
			if net.ParseIP(sw.ip).Equal(ptpUDPAddr.IP) {
				destReached = true
				s.destHop = hop
			}
		case <-time.After(s.Config.IcmpTimeout):
			continue
		}
	}
	return route, nil
}