From eb06ec172f1d984bb87c589da1dd2d3f15dc6d82 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 5 May 2021 12:32:46 -0700 Subject: [PATCH] wgengine/netstack: don't pass non-subnet traffic to netstack in hybrid mode Fixes tailscale/corp#1725 Signed-off-by: Brad Fitzpatrick --- types/netmap/netmap.go | 4 ++-- wgengine/netstack/netstack.go | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/types/netmap/netmap.go b/types/netmap/netmap.go index a97d87b73..b69779c69 100644 --- a/types/netmap/netmap.go +++ b/types/netmap/netmap.go @@ -31,8 +31,8 @@ type NetworkMap struct { Expiry time.Time // Name is the DNS name assigned to this node. Name string - Addresses []netaddr.IPPrefix - LocalPort uint16 // used for debugging + Addresses []netaddr.IPPrefix // same as tailcfg.Node.Addresses (IP addresses of this Node directly) + LocalPort uint16 // used for debugging MachineStatus tailcfg.MachineStatus MachineKey tailcfg.MachineKey Peers []*tailcfg.Node // sorted by Node.ID diff --git a/wgengine/netstack/netstack.go b/wgengine/netstack/netstack.go index 1c051f5d2..acc32bb56 100644 --- a/wgengine/netstack/netstack.go +++ b/wgengine/netstack/netstack.go @@ -15,6 +15,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" "inet.af/netaddr" @@ -55,6 +56,12 @@ type Impl struct { logf logger.Logf onlySubnets bool // whether we only want to handle subnet relaying + // atomicIsLocalIPFunc holds a func that reports whether an IP + // is a local (non-subnet) Tailscale IP address of this + // machine. It's always a non-nil func. It's changed on netmap + // updates. + atomicIsLocalIPFunc atomic.Value // of func(netaddr.IP) bool + mu sync.Mutex dns DNSMap // connsOpenBySubnetIP keeps track of number of connections open @@ -119,6 +126,7 @@ func Create(logf logger.Logf, tundev *tstun.Wrapper, e wgengine.Engine, mc *magi connsOpenBySubnetIP: make(map[netaddr.IP]int), onlySubnets: onlySubnets, } + ns.atomicIsLocalIPFunc.Store(tsaddr.NewContainsIPFunc(nil)) return ns, nil } @@ -145,7 +153,7 @@ func (ns *Impl) Start() error { ns.logf("netstack: could not parse local address %s for incoming TCP connection", ip) return false } - if !tsaddr.IsTailscaleIP(ip) { + if !ns.isLocalIP(ip) { ns.addSubnetAddress(pn, ip) } return tcpFwd.HandlePacket(tei, pb) @@ -219,6 +227,7 @@ func ipPrefixToAddressWithPrefix(ipp netaddr.IPPrefix) tcpip.AddressWithPrefix { } func (ns *Impl) updateIPs(nm *netmap.NetworkMap) { + ns.atomicIsLocalIPFunc.Store(tsaddr.NewContainsIPFunc(nm.Addresses)) ns.updateDNS(nm) oldIPs := make(map[tcpip.AddressWithPrefix]bool) @@ -373,7 +382,19 @@ func (ns *Impl) injectOutbound() { } } +// isLocalIP reports whether ip is a Tailscale IP assigned to this +// node directly (but not a subnet-routed IP). +func (ns *Impl) isLocalIP(ip netaddr.IP) bool { + return ns.atomicIsLocalIPFunc.Load().(func(netaddr.IP) bool)(ip) +} + func (ns *Impl) injectInbound(p *packet.Parsed, t *tstun.Wrapper) filter.Response { + if ns.onlySubnets && ns.isLocalIP(p.Dst.IP) { + // In hybrid ("only subnets") mode, bail out early if + // the traffic is destined for an actual Tailscale + // address. The real host OS interface will handle it. + return filter.Accept + } var pn tcpip.NetworkProtocolNumber switch p.IPVersion { case 4: