From bc78993f8dea105a3587224e1e9bb115472217cf Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sun, 12 Jan 2025 17:00:27 -0800 Subject: [PATCH] lanscaping: remove TSMP -rwxr-xr-x@ 1 bradfitz staff 9752834 Jan 12 16:59 /Users/bradfitz/bin/tailscaled.min -rwxr-xr-x@ 1 bradfitz staff 9633944 Jan 12 16:59 /Users/bradfitz/bin/tailscaled.minlinux Change-Id: I12db5d0f2b90aae55709eed4751cc342d59b43cd Signed-off-by: Brad Fitzpatrick --- cmd/tailscale/depaware-minlinux.txt | 1 - cmd/tailscaled/depaware-minlinux.txt | 2 +- net/packet/packet.go | 8 - net/packet/tsmp.go | 264 --------------------------- net/tstun/wrap.go | 26 --- types/ipproto/ipproto.go | 14 -- wgengine/filter/filter.go | 4 - wgengine/pendopen.go | 228 ----------------------- wgengine/userspace.go | 93 +--------- 9 files changed, 6 insertions(+), 634 deletions(-) delete mode 100644 net/packet/tsmp.go delete mode 100644 wgengine/pendopen.go diff --git a/cmd/tailscale/depaware-minlinux.txt b/cmd/tailscale/depaware-minlinux.txt index a8f62babe..00c8048c7 100644 --- a/cmd/tailscale/depaware-minlinux.txt +++ b/cmd/tailscale/depaware-minlinux.txt @@ -51,7 +51,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep tailscale.com/ipn from tailscale.com/client/tailscale+ tailscale.com/ipn/ipnstate from tailscale.com/client/tailscale+ tailscale.com/licenses from tailscale.com/client/web+ - tailscale.com/net/flowtrack from tailscale.com/net/packet tailscale.com/net/netaddr from tailscale.com/ipn+ tailscale.com/net/netcheck from tailscale.com/cmd/tailscale/cli tailscale.com/net/neterror from tailscale.com/net/netcheck+ diff --git a/cmd/tailscaled/depaware-minlinux.txt b/cmd/tailscaled/depaware-minlinux.txt index 2f30e4f01..19bbb06d3 100644 --- a/cmd/tailscaled/depaware-minlinux.txt +++ b/cmd/tailscaled/depaware-minlinux.txt @@ -44,7 +44,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/ipn/store from tailscale.com/cmd/tailscaled tailscale.com/ipn/store/mem from tailscale.com/ipn/store tailscale.com/logtail/backoff from tailscale.com/control/controlclient+ - tailscale.com/net/flowtrack from tailscale.com/net/packet+ + tailscale.com/net/flowtrack from tailscale.com/wgengine/filter tailscale.com/net/ipset from tailscale.com/ipn/ipnlocal+ tailscale.com/net/netaddr from tailscale.com/ipn+ tailscale.com/net/netcheck from tailscale.com/wgengine/magicsock diff --git a/net/packet/packet.go b/net/packet/packet.go index c9521ad46..76557cd0d 100644 --- a/net/packet/packet.go +++ b/net/packet/packet.go @@ -218,10 +218,6 @@ func (q *Parsed) decode4(b []byte) { q.Src = withPort(q.Src, binary.BigEndian.Uint16(sub[0:2])) q.Dst = withPort(q.Dst, binary.BigEndian.Uint16(sub[2:4])) return - case ipproto.TSMP: - // Inter-tailscale messages. - q.dataofs = q.subofs - return case ipproto.Fragment: // An IPProto value of 0xff (our Fragment constant for internal use) // should never actually be used in the wild; if we see it, @@ -321,10 +317,6 @@ func (q *Parsed) decode6(b []byte) { q.Src = withPort(q.Src, binary.BigEndian.Uint16(sub[0:2])) q.Dst = withPort(q.Dst, binary.BigEndian.Uint16(sub[2:4])) return - case ipproto.TSMP: - // Inter-tailscale messages. - q.dataofs = q.subofs - return case ipproto.Fragment: // An IPProto value of 0xff (our Fragment constant for internal use) // should never actually be used in the wild; if we see it, diff --git a/net/packet/tsmp.go b/net/packet/tsmp.go deleted file mode 100644 index 4e004cca2..000000000 --- a/net/packet/tsmp.go +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) Tailscale Inc & AUTHORS -// SPDX-License-Identifier: BSD-3-Clause - -// TSMP is our ICMP-like "Tailscale Message Protocol" for signaling -// Tailscale-specific messages between nodes. It uses IP protocol 99 -// (reserved for "any private encryption scheme") within -// WireGuard's normal encryption between peers and never hits the host -// network stack. - -package packet - -import ( - "encoding/binary" - "errors" - "fmt" - "net/netip" - - "tailscale.com/net/flowtrack" - "tailscale.com/types/ipproto" -) - -// TailscaleRejectedHeader is a TSMP message that says that one -// Tailscale node has rejected the connection from another. Unlike a -// TCP RST, this includes a reason. -// -// On the wire, after the IP header, it's currently 7 or 8 bytes: -// - '!' -// - IPProto byte (IANA protocol number: TCP or UDP) -// - 'A' or 'S' (RejectedDueToACLs, RejectedDueToShieldsUp) -// - srcPort big endian uint16 -// - dstPort big endian uint16 -// - [optional] byte of flag bits: -// lowest bit (0x1): MaybeBroken -// -// In the future it might also accept 16 byte IP flow src/dst IPs -// after the header, if they're different than the IP-level ones. -type TailscaleRejectedHeader struct { - IPSrc netip.Addr // IPv4 or IPv6 header's src IP - IPDst netip.Addr // IPv4 or IPv6 header's dst IP - Src netip.AddrPort // rejected flow's src - Dst netip.AddrPort // rejected flow's dst - Proto ipproto.Proto // proto that was rejected (TCP or UDP) - Reason TailscaleRejectReason // why the connection was rejected - - // MaybeBroken is whether the rejection is non-terminal (the - // client should not fail immediately). This is sent by a - // target when it's not sure whether it's totally broken, but - // it might be. For example, the target tailscaled might think - // its host firewall or IP forwarding aren't configured - // properly, but tailscaled might be wrong (not having enough - // visibility into what the OS is doing). When true, the - // message is simply an FYI as a potential reason to use for - // later when the pendopen connection tracking timer expires. - MaybeBroken bool -} - -const rejectFlagBitMaybeBroken = 0x1 - -func (rh TailscaleRejectedHeader) Flow() flowtrack.Tuple { - return flowtrack.MakeTuple(rh.Proto, rh.Src, rh.Dst) -} - -func (rh TailscaleRejectedHeader) String() string { - return fmt.Sprintf("TSMP-reject-flow{%s %s > %s}: %s", rh.Proto, rh.Src, rh.Dst, rh.Reason) -} - -type TSMPType uint8 - -const ( - // TSMPTypeRejectedConn is the type byte for a TailscaleRejectedHeader. - TSMPTypeRejectedConn TSMPType = '!' - - // TSMPTypePing is the type byte for a TailscalePingRequest. - TSMPTypePing TSMPType = 'p' - - // TSMPTypePong is the type byte for a TailscalePongResponse. - TSMPTypePong TSMPType = 'o' -) - -type TailscaleRejectReason byte - -// IsZero reports whether r is the zero value, representing no rejection. -func (r TailscaleRejectReason) IsZero() bool { return r == TailscaleRejectReasonNone } - -const ( - // TailscaleRejectReasonNone is the TailscaleRejectReason zero value. - TailscaleRejectReasonNone TailscaleRejectReason = 0 - - // RejectedDueToACLs means that the host rejected the connection due to ACLs. - RejectedDueToACLs TailscaleRejectReason = 'A' - - // RejectedDueToShieldsUp means that the host rejected the connection due to shields being up. - RejectedDueToShieldsUp TailscaleRejectReason = 'S' - - // RejectedDueToIPForwarding means that the relay node's IP - // forwarding is disabled. - RejectedDueToIPForwarding TailscaleRejectReason = 'F' - - // RejectedDueToHostFirewall means that the target host's - // firewall is blocking the traffic. - RejectedDueToHostFirewall TailscaleRejectReason = 'W' -) - -func (r TailscaleRejectReason) String() string { - switch r { - case RejectedDueToACLs: - return "acl" - case RejectedDueToShieldsUp: - return "shields" - case RejectedDueToIPForwarding: - return "host-ip-forwarding-unavailable" - case RejectedDueToHostFirewall: - return "host-firewall" - } - return fmt.Sprintf("0x%02x", byte(r)) -} - -func (h TailscaleRejectedHeader) hasFlags() bool { - return h.MaybeBroken // the only one currently -} - -func (h TailscaleRejectedHeader) Len() int { - v := 1 + // TSMPType byte - 1 + // IPProto byte - 1 + // TailscaleRejectReason byte - 2*2 // 2 uint16 ports - if h.IPSrc.Is4() { - v += ip4HeaderLength - } else if h.IPSrc.Is6() { - v += ip6HeaderLength - } - if h.hasFlags() { - v++ - } - return v -} - -func (h TailscaleRejectedHeader) Marshal(buf []byte) error { - if len(buf) < h.Len() { - return errSmallBuffer - } - if len(buf) > maxPacketLength { - return errLargePacket - } - if h.Src.Addr().Is4() { - iph := IP4Header{ - IPProto: ipproto.TSMP, - Src: h.IPSrc, - Dst: h.IPDst, - } - iph.Marshal(buf) - buf = buf[ip4HeaderLength:] - } else if h.Src.Addr().Is6() { - iph := IP6Header{ - IPProto: ipproto.TSMP, - Src: h.IPSrc, - Dst: h.IPDst, - } - iph.Marshal(buf) - buf = buf[ip6HeaderLength:] - } else { - return errors.New("bogus src IP") - } - buf[0] = byte(TSMPTypeRejectedConn) - buf[1] = byte(h.Proto) - buf[2] = byte(h.Reason) - binary.BigEndian.PutUint16(buf[3:5], h.Src.Port()) - binary.BigEndian.PutUint16(buf[5:7], h.Dst.Port()) - - if h.hasFlags() { - var flags byte - if h.MaybeBroken { - flags |= rejectFlagBitMaybeBroken - } - buf[7] = flags - } - return nil -} - -// AsTailscaleRejectedHeader parses pp as an incoming rejection -// connection TSMP message. -// -// ok reports whether pp was a valid TSMP rejection packet. -func (pp *Parsed) AsTailscaleRejectedHeader() (h TailscaleRejectedHeader, ok bool) { - p := pp.Payload() - if len(p) < 7 || p[0] != byte(TSMPTypeRejectedConn) { - return - } - h = TailscaleRejectedHeader{ - Proto: ipproto.Proto(p[1]), - Reason: TailscaleRejectReason(p[2]), - IPSrc: pp.Src.Addr(), - IPDst: pp.Dst.Addr(), - Src: netip.AddrPortFrom(pp.Dst.Addr(), binary.BigEndian.Uint16(p[3:5])), - Dst: netip.AddrPortFrom(pp.Src.Addr(), binary.BigEndian.Uint16(p[5:7])), - } - if len(p) > 7 { - flags := p[7] - h.MaybeBroken = (flags & rejectFlagBitMaybeBroken) != 0 - } - return h, true -} - -// TSMPPingRequest is a TSMP message that's like an ICMP ping request. -// -// On the wire, after the IP header, it's currently 9 bytes: -// - 'p' (TSMPTypePing) -// - 8 opaque ping bytes to copy back in the response -type TSMPPingRequest struct { - Data [8]byte -} - -func (pp *Parsed) AsTSMPPing() (h TSMPPingRequest, ok bool) { - if pp.IPProto != ipproto.TSMP { - return - } - p := pp.Payload() - if len(p) < 9 || p[0] != byte(TSMPTypePing) { - return - } - copy(h.Data[:], p[1:]) - return h, true -} - -type TSMPPongReply struct { - IPHeader Header - Data [8]byte - PeerAPIPort uint16 -} - -// AsTSMPPong returns pp as a TSMPPongReply and whether it is one. -// The pong.IPHeader field is not populated. -func (pp *Parsed) AsTSMPPong() (pong TSMPPongReply, ok bool) { - if pp.IPProto != ipproto.TSMP { - return - } - p := pp.Payload() - if len(p) < 9 || p[0] != byte(TSMPTypePong) { - return - } - copy(pong.Data[:], p[1:]) - if len(p) >= 11 { - pong.PeerAPIPort = binary.BigEndian.Uint16(p[9:]) - } - return pong, true -} - -func (h TSMPPongReply) Len() int { - return h.IPHeader.Len() + 11 -} - -func (h TSMPPongReply) Marshal(buf []byte) error { - if len(buf) < h.Len() { - return errSmallBuffer - } - if err := h.IPHeader.Marshal(buf); err != nil { - return err - } - buf = buf[h.IPHeader.Len():] - buf[0] = byte(TSMPTypePong) - copy(buf[1:], h.Data[:]) - binary.BigEndian.PutUint16(buf[9:11], h.PeerAPIPort) - return nil -} diff --git a/net/tstun/wrap.go b/net/tstun/wrap.go index b1b0b07b0..d65d0641c 100644 --- a/net/tstun/wrap.go +++ b/net/tstun/wrap.go @@ -164,9 +164,6 @@ type Wrapper struct { // PostFilterPacketOutboundToWireGuard is the outbound filter function that runs after the main filter. PostFilterPacketOutboundToWireGuard FilterFunc - // OnTSMPPongReceived, if non-nil, is called whenever a TSMP pong arrives. - OnTSMPPongReceived func(packet.TSMPPongReply) - // OnICMPEchoResponseReceived, if non-nil, is called whenever a ICMP echo response // arrives. If the packet is to be handled internally this returns true, // false otherwise. @@ -916,29 +913,6 @@ func (t *Wrapper) InjectInboundDirect(buf []byte, offset int) error { return err } -func (t *Wrapper) injectOutboundPong(pp *packet.Parsed, req packet.TSMPPingRequest) { - pong := packet.TSMPPongReply{ - Data: req.Data, - } - if t.PeerAPIPort != nil { - pong.PeerAPIPort, _ = t.PeerAPIPort(pp.Dst.Addr()) - } - switch pp.IPVersion { - case 4: - h4 := pp.IP4Header() - h4.ToResponse() - pong.IPHeader = h4 - case 6: - h6 := pp.IP6Header() - h6.ToResponse() - pong.IPHeader = h6 - default: - return - } - - t.InjectOutbound(packet.Generate(pong, nil)) -} - // InjectOutbound makes the Wrapper device behave as if a packet // with the given contents was sent to the network. // It does not block, but takes ownership of the packet. diff --git a/types/ipproto/ipproto.go b/types/ipproto/ipproto.go index cfb41dcd5..3858cb1b7 100644 --- a/types/ipproto/ipproto.go +++ b/types/ipproto/ipproto.go @@ -54,17 +54,6 @@ const ( GRE Proto = 0x2f SCTP Proto = 0x84 - // TSMP is the Tailscale Message Protocol (our ICMP-ish - // thing), an IP protocol used only between Tailscale nodes - // (still encrypted by WireGuard) that communicates why things - // failed, etc. - // - // Proto number 99 is reserved for "any private encryption - // scheme". We never accept these from the host OS stack nor - // send them to the host network stack. It's only used between - // nodes. - TSMP Proto = 99 - // Fragment represents any non-first IP fragment, for which we // don't have the sub-protocol header (and therefore can't // figure out what the sub-protocol is). @@ -93,8 +82,6 @@ func (p Proto) String() string { return "TCP" case SCTP: return "SCTP" - case TSMP: - return "TSMP" case GRE: return "GRE" case DCCP: @@ -144,7 +131,6 @@ var ( "ipv6-icmp": ICMPv6, "sctp": SCTP, "tcp": TCP, - "tsmp": TSMP, "udp": UDP, } ) diff --git a/wgengine/filter/filter.go b/wgengine/filter/filter.go index 9e5d8a37f..36cee12cd 100644 --- a/wgengine/filter/filter.go +++ b/wgengine/filter/filter.go @@ -503,8 +503,6 @@ func (f *Filter) runIn4(q *packet.Parsed) (r Response, why string) { if f.matches4.match(q, f.srcIPHasCap) { return Accept, "ok" } - case ipproto.TSMP: - return Accept, "tsmp ok" default: if f.matches4.matchProtoAndIPsOnlyIfAllPorts(q) { return Accept, "other-portless ok" @@ -563,8 +561,6 @@ func (f *Filter) runIn6(q *packet.Parsed) (r Response, why string) { if f.matches6.match(q, f.srcIPHasCap) { return Accept, "ok" } - case ipproto.TSMP: - return Accept, "tsmp ok" default: if f.matches6.matchProtoAndIPsOnlyIfAllPorts(q) { return Accept, "other-portless ok" diff --git a/wgengine/pendopen.go b/wgengine/pendopen.go deleted file mode 100644 index c7c028d06..000000000 --- a/wgengine/pendopen.go +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright (c) Tailscale Inc & AUTHORS -// SPDX-License-Identifier: BSD-3-Clause - -package wgengine - -import ( - "fmt" - "net/netip" - "time" - - "tailscale.com/net/flowtrack" - "tailscale.com/net/packet" - "tailscale.com/net/tstun" - "tailscale.com/types/ipproto" - "tailscale.com/util/mak" - "tailscale.com/wgengine/filter" -) - -const tcpTimeoutBeforeDebug = 5 * time.Second - -type pendingOpenFlow struct { - timer *time.Timer // until giving up on the flow - - // guarded by userspaceEngine.mu: - - // problem is non-zero if we got a MaybeBroken (non-terminal) - // TSMP "reject" header. - problem packet.TailscaleRejectReason -} - -func (e *userspaceEngine) removeFlow(f flowtrack.Tuple) (removed bool) { - e.mu.Lock() - defer e.mu.Unlock() - of, ok := e.pendOpen[f] - if !ok { - // Not a tracked flow (likely already removed) - return false - } - of.timer.Stop() - delete(e.pendOpen, f) - return true -} - -func (e *userspaceEngine) noteFlowProblemFromPeer(f flowtrack.Tuple, problem packet.TailscaleRejectReason) { - e.mu.Lock() - defer e.mu.Unlock() - of, ok := e.pendOpen[f] - if !ok { - // Not a tracked flow (likely already removed) - return - } - of.problem = problem -} - -func (e *userspaceEngine) trackOpenPreFilterIn(pp *packet.Parsed, t *tstun.Wrapper) (res filter.Response) { - res = filter.Accept // always - - if pp.IPProto == ipproto.TSMP { - res = filter.DropSilently - rh, ok := pp.AsTailscaleRejectedHeader() - if !ok { - return - } - if rh.MaybeBroken { - e.noteFlowProblemFromPeer(rh.Flow(), rh.Reason) - } else if f := rh.Flow(); e.removeFlow(f) { - e.logf("open-conn-track: flow %v %v > %v rejected due to %v", rh.Proto, rh.Src, rh.Dst, rh.Reason) - } - return - } - - if pp.IPVersion == 0 || - pp.IPProto != ipproto.TCP || - pp.TCPFlags&(packet.TCPSyn|packet.TCPRst) == 0 { - return - } - - // Either a SYN or a RST came back. Remove it in either case. - - f := flowtrack.MakeTuple(pp.IPProto, pp.Dst, pp.Src) // src/dst reversed - removed := e.removeFlow(f) - if removed && pp.TCPFlags&packet.TCPRst != 0 { - e.logf("open-conn-track: flow TCP %v got RST by peer", f) - } - return -} - -// isOSNetworkProbe reports whether the target is likely a network -// connectivity probe target from e.g. iOS or Ubuntu network-manager. -// -// iOS likes to probe Apple IPs on all interfaces to check for connectivity. -// Don't start timers tracking those. They won't succeed anyway. Avoids log -// spam like: -func (e *userspaceEngine) isOSNetworkProbe(dst netip.AddrPort) bool { - - return false -} - -func (e *userspaceEngine) trackOpenPostFilterOut(pp *packet.Parsed, t *tstun.Wrapper) (res filter.Response) { - res = filter.Accept // always - - if pp.IPVersion == 0 || - pp.IPProto != ipproto.TCP || - pp.TCPFlags&packet.TCPAck != 0 || - pp.TCPFlags&packet.TCPSyn == 0 { - return - } - if e.isOSNetworkProbe(pp.Dst) { - return - } - - flow := flowtrack.MakeTuple(pp.IPProto, pp.Src, pp.Dst) - - e.mu.Lock() - defer e.mu.Unlock() - if _, dup := e.pendOpen[flow]; dup { - // Duplicates are expected when the OS retransmits. Ignore. - return - } - - timer := time.AfterFunc(tcpTimeoutBeforeDebug, func() { - e.onOpenTimeout(flow) - }) - mak.Set(&e.pendOpen, flow, &pendingOpenFlow{timer: timer}) - - return filter.Accept -} - -func (e *userspaceEngine) onOpenTimeout(flow flowtrack.Tuple) { - e.mu.Lock() - of, ok := e.pendOpen[flow] - if !ok { - // Not a tracked flow, or already handled & deleted. - e.mu.Unlock() - return - } - delete(e.pendOpen, flow) - problem := of.problem - e.mu.Unlock() - - if !problem.IsZero() { - e.logf("open-conn-track: timeout opening %v; peer reported problem: %v", flow, problem) - } - - // Diagnose why it might've timed out. - pip, ok := e.PeerForIP(flow.DstAddr()) - if !ok { - e.logf("open-conn-track: timeout opening %v; no associated peer node", flow) - return - } - n := pip.Node - if !n.IsWireGuardOnly() { - if n.DiscoKey().IsZero() { - e.logf("open-conn-track: timeout opening %v; peer node %v running pre-0.100", flow, n.Key().ShortString()) - return - } - if n.DERP() == "" { - e.logf("open-conn-track: timeout opening %v; peer node %v not connected to any DERP relay", flow, n.Key().ShortString()) - return - } - } - - ps, found := e.getPeerStatusLite(n.Key()) - if !found { - onlyZeroRoute := true // whether peerForIP returned n only because its /0 route matched - for _, r := range n.AllowedIPs().All() { - if r.Bits() != 0 && r.Contains(flow.DstAddr()) { - onlyZeroRoute = false - break - } - } - if onlyZeroRoute { - // This node was returned by peerForIP because - // its exit node /0 route(s) matched, but this - // might not be the exit node that's currently - // selected. Rather than log misleading - // errors, just don't log at all for now. - // TODO(bradfitz): update this code to be - // exit-node-aware and make peerForIP return - // the node of the currently selected exit - // node. - return - } - e.logf("open-conn-track: timeout opening %v; target node %v in netmap but unknown to WireGuard", flow, n.Key().ShortString()) - return - } - - // TODO(bradfitz): figure out what PeerStatus.LastHandshake - // is. It appears to be the last time we sent a handshake, - // which isn't what I expected. I thought it was when a - // handshake completed, which is what I want. - _ = ps.LastHandshake - - online := "?" - if n.IsWireGuardOnly() { - online = "wg" - } else { - if v := n.Online(); v != nil { - if *v { - online = "yes" - } else { - online = "no" - } - } - if n.LastSeen() != nil && online != "yes" { - online += fmt.Sprintf(", lastseen=%v", durFmt(*n.LastSeen())) - } - } - e.logf("open-conn-track: timeout opening %v to node %v; online=%v, lastRecv=%v", - flow, n.Key().ShortString(), - online, - e.magicConn.LastRecvActivityOfNodeKey(n.Key())) -} - -func durFmt(t time.Time) string { - if t.IsZero() { - return "never" - } - d := time.Since(t).Round(time.Second) - if d < 10*time.Minute { - // node.LastSeen times are rounded very coarsely, and - // we compare times from different clocks (server vs - // local), so negative is common when close. Format as - // "recent" if negative or actually recent. - return "recent" - } - return d.String() -} diff --git a/wgengine/userspace.go b/wgengine/userspace.go index cc37f5b0c..5d2896b86 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -5,7 +5,6 @@ package wgengine import ( "bufio" - crand "crypto/rand" "errors" "fmt" "io" @@ -22,7 +21,6 @@ import ( "tailscale.com/envknob" "tailscale.com/health" "tailscale.com/ipn/ipnstate" - "tailscale.com/net/flowtrack" "tailscale.com/net/ipset" "tailscale.com/net/netmon" "tailscale.com/net/packet" @@ -123,11 +121,7 @@ type userspaceEngine struct { statusCallback StatusCallback peerSequence []key.NodePublic endpoints []tailcfg.Endpoint - pendOpen map[flowtrack.Tuple]*pendingOpenFlow // see pendopen.go - // pongCallback is the map of response handlers waiting for disco or TSMP - // pong callbacks. The map key is a random slice of bytes. - pongCallback map[[8]byte]func(packet.TSMPPongReply) // icmpEchoResponseCallback is the map of response handlers waiting for ICMP // echo responses. The map key is a random uint32 that is the little endian // value of the ICMP identifier and sequence number concatenated. @@ -366,27 +360,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error) } e.tundev.PreFilterPacketOutboundToWireGuardEngineIntercept = e.handleLocalPackets - if envknob.BoolDefaultTrue("TS_DEBUG_CONNECT_FAILURES") { - if e.tundev.PreFilterPacketInboundFromWireGuard != nil { - return nil, errors.New("unexpected PreFilterIn already set") - } - e.tundev.PreFilterPacketInboundFromWireGuard = e.trackOpenPreFilterIn - if e.tundev.PostFilterPacketOutboundToWireGuard != nil { - return nil, errors.New("unexpected PostFilterOut already set") - } - e.tundev.PostFilterPacketOutboundToWireGuard = e.trackOpenPostFilterOut - } - e.wgLogger = wglog.NewLogger(logf) - e.tundev.OnTSMPPongReceived = func(pong packet.TSMPPongReply) { - e.mu.Lock() - defer e.mu.Unlock() - cb := e.pongCallback[pong.Data] - e.logf("wgengine: got TSMP pong %02x, peerAPIPort=%v; cb=%v", pong.Data, pong.PeerAPIPort, cb != nil) - if cb != nil { - go cb(pong) - } - } e.tundev.OnICMPEchoResponseReceived = func(p *packet.Parsed) bool { idSeq := p.EchoIDSeq() @@ -1111,14 +1085,17 @@ func (e *userspaceEngine) Ping(ip netip.Addr, pingType tailcfg.PingType, size in cb(res) return } + if pingType == "TSMP" { + res.Err = "TSMP ping not supported" + cb(res) + return + } peer := pip.Node e.logf("ping(%v): sending %v ping to %v %v ...", ip, pingType, peer.Key().ShortString(), peer.ComputedName()) switch pingType { case "disco": e.magicConn.Ping(peer, res, size, cb) - case "TSMP": - e.sendTSMPPing(ip, peer, res, cb) case "ICMP": e.sendICMPEchoRequest(ip, peer, res, cb) } @@ -1192,66 +1169,6 @@ func (e *userspaceEngine) sendICMPEchoRequest(destIP netip.Addr, peer tailcfg.No e.tundev.InjectOutbound(icmpPing) } -func (e *userspaceEngine) sendTSMPPing(ip netip.Addr, peer tailcfg.NodeView, res *ipnstate.PingResult, cb func(*ipnstate.PingResult)) { - srcIP, err := e.mySelfIPMatchingFamily(ip) - if err != nil { - res.Err = err.Error() - cb(res) - return - } - var iph packet.Header - if srcIP.Is4() { - iph = packet.IP4Header{ - IPProto: ipproto.TSMP, - Src: srcIP, - Dst: ip, - } - } else { - iph = packet.IP6Header{ - IPProto: ipproto.TSMP, - Src: srcIP, - Dst: ip, - } - } - - var data [8]byte - crand.Read(data[:]) - - expireTimer := time.AfterFunc(10*time.Second, func() { - e.setTSMPPongCallback(data, nil) - }) - t0 := time.Now() - e.setTSMPPongCallback(data, func(pong packet.TSMPPongReply) { - expireTimer.Stop() - d := time.Since(t0) - res.LatencySeconds = d.Seconds() - res.NodeIP = ip.String() - res.NodeName = peer.ComputedName() - res.PeerAPIPort = pong.PeerAPIPort - cb(res) - }) - - var tsmpPayload [9]byte - tsmpPayload[0] = byte(packet.TSMPTypePing) - copy(tsmpPayload[1:], data[:]) - - tsmpPing := packet.Generate(iph, tsmpPayload[:]) - e.tundev.InjectOutbound(tsmpPing) -} - -func (e *userspaceEngine) setTSMPPongCallback(data [8]byte, cb func(packet.TSMPPongReply)) { - e.mu.Lock() - defer e.mu.Unlock() - if e.pongCallback == nil { - e.pongCallback = map[[8]byte]func(packet.TSMPPongReply){} - } - if cb == nil { - delete(e.pongCallback, data) - } else { - e.pongCallback[data] = cb - } -} - func (e *userspaceEngine) setICMPEchoResponseCallback(idSeq uint32, cb func()) { e.mu.Lock() defer e.mu.Unlock()