From 87cb2a8d1e65d2519046c69bcffba8ff667d1a66 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 24 Jun 2026 20:38:25 +0000 Subject: [PATCH] wgengine: replace Engine.SetNetworkMap with SetSelfNode The engine only used the netmap to look up self addresses and the self node's primary routes, so pass it the self node directly rather than the whole netmap. Updates #12542 Change-Id: I13c0028eed65d2177baf4cf6c449f5e441845a18 Signed-off-by: Brad Fitzpatrick --- ipn/ipnlocal/local.go | 36 ++++++++++++++++++++++-------------- ipn/ipnlocal/state_test.go | 2 +- wgengine/bench/wg.go | 32 ++------------------------------ wgengine/userspace.go | 29 ++++++++++++++--------------- wgengine/userspace_test.go | 8 ++++---- wgengine/wgengine.go | 9 +++------ 6 files changed, 46 insertions(+), 70 deletions(-) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index d5283d97a..283248060 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -2103,7 +2103,7 @@ func (b *LocalBackend) setControlClientStatusLocked(c controlclient.Client, st c } } - b.e.SetNetworkMap(st.NetMap) + b.e.SetSelfNode(st.NetMap.SelfNode) var cachedHome int if c == nil && st.NetMap.Cached && st.NetMap.SelfNode.Valid() { @@ -2514,21 +2514,25 @@ func (b *LocalBackend) UpdateNetmapDelta(muts []netmap.NodeMutation) (handled bo } ms.UpdateNetmapDelta(muts) - // Temporary for 1.100.x: force a full authReconfig + SetNetworkMap - // on any peer add or remove. netmapDeltaNeedsAuthReconfig only - // considered NodeMutationUpsert of already-known NodeIDs whose + // Force a full authReconfig + SetSelfNode on any peer add or + // remove. netmapDeltaNeedsAuthReconfig only considered + // NodeMutationUpsert of already-known NodeIDs whose // peerRouteConfigChanged, so brand-new peers and removes left - // e.lastCfgFull / the engine BART / wgdev's PeerLookupFunc closure - // / e.netMap all stale, and Engine.PeerForIP, lookupPeerByIP, and - // outbound wgdev encryption all missed those peers. authReconfig - // fixes the wireguard side; SetNetworkMap fixes the e.netMap that - // PeerForIP reads. The proper per-peer fix lands in the next dev - // cycle. See tailscale/corp#43394. + // e.lastCfgFull and wgdev's PeerLookupFunc closure stale, and + // outbound wgdev encryption missed those peers. authReconfig + // fixes the wireguard side; SetSelfNode refreshes the engine's + // cached self node. PeerForIP / lookupPeerByIP staleness was + // addressed separately in d4f2917c1b, which routes those lookups + // through nodeBackend's live data, and as part of the broader + // netmap.NetworkMap removal effort the engine no longer caches + // the netmap at all (see tailscale/corp#43394). As of 2026-06-24 + // the only remaining staleness this guards against is + // e.lastCfgFull and the wgdev peer set. needsAuthReconfig = needsAuthReconfig || peersUpsertedOrRemoved if needsAuthReconfig { if peersUpsertedOrRemoved { if nm := cn.netMapWithPeers(); nm != nil { - b.e.SetNetworkMap(nm) + b.e.SetSelfNode(nm.SelfNode) } } b.authReconfigLocked() @@ -3953,7 +3957,11 @@ func (b *LocalBackend) DebugForceNetmapUpdate() { defer b.mu.Unlock() // TODO(nickkhyl): this all should be done in [LocalBackend.setNetMapLocked]. nm := b.currentNode().NetMap() - b.e.SetNetworkMap(nm) + var self tailcfg.NodeView + if nm != nil { + self = nm.SelfNode + } + b.e.SetSelfNode(self) if nm != nil { b.MagicConn().SetDERPMap(nm.DERPMap) } @@ -8375,8 +8383,8 @@ func (b *LocalBackend) resetForProfileChangeLocked() error { defer newNode.ready() b.setNetMapLocked(nil) // Reset netmap. b.updateFilterLocked(ipn.PrefsView{}) - // Reset the NetworkMap in the engine - b.e.SetNetworkMap(new(netmap.NetworkMap)) + // Reset the self node in the engine. + b.e.SetSelfNode(tailcfg.NodeView{}) if prevCC := b.resetControlClientLocked(); prevCC != nil { // Shutdown outside of b.mu to avoid deadlocks. b.goTracker.Go(prevCC.Shutdown) diff --git a/ipn/ipnlocal/state_test.go b/ipn/ipnlocal/state_test.go index 6ca96e4bd..0e6adfa1f 100644 --- a/ipn/ipnlocal/state_test.go +++ b/ipn/ipnlocal/state_test.go @@ -1972,7 +1972,7 @@ func (e *mockEngine) PeerByKey(key.NodePublic) (_ wgint.Peer, ok bool) { return wgint.Peer{}, false } -func (e *mockEngine) SetNetworkMap(*netmap.NetworkMap) {} +func (e *mockEngine) SetSelfNode(tailcfg.NodeView) {} func (e *mockEngine) UpdateStatus(*ipnstate.StatusBuilder) {} diff --git a/wgengine/bench/wg.go b/wgengine/bench/wg.go index 7b35a089a..4264ae448 100644 --- a/wgengine/bench/wg.go +++ b/wgengine/bench/wg.go @@ -19,20 +19,12 @@ "tailscale.com/tsd" "tailscale.com/types/key" "tailscale.com/types/logger" - "tailscale.com/types/netmap" "tailscale.com/wgengine" "tailscale.com/wgengine/filter" "tailscale.com/wgengine/router" "tailscale.com/wgengine/wgcfg" ) -func epFromTyped(eps []tailcfg.Endpoint) (ret []netip.AddrPort) { - for _, ep := range eps { - ret = append(ret, ep.Addr) - } - return -} - func setupWGTest(b *testing.B, logf logger.Logf, traf *TrafficGen, a1, a2 netip.Prefix) { l1 := logger.WithPrefix(logf, "e1: ") k1 := key.NewNode() @@ -103,17 +95,7 @@ func setupWGTest(b *testing.B, logf logger.Logf, traf *TrafficGen, a1, a2 netip. } logf("e1 status: %v", *st) - n := &tailcfg.Node{ - ID: tailcfg.NodeID(0), - Name: "n1", - Addresses: []netip.Prefix{a1}, - AllowedIPs: []netip.Prefix{a1}, - Endpoints: epFromTyped(st.LocalAddrs), - } - e2.SetNetworkMap(&netmap.NetworkMap{ - NodeKey: k2.Public(), - Peers: []tailcfg.NodeView{n.View()}, - }) + e2.SetSelfNode(tailcfg.NodeView{}) p := wgcfg.Peer{ PublicKey: c1.PrivateKey.Public(), @@ -134,17 +116,7 @@ func setupWGTest(b *testing.B, logf logger.Logf, traf *TrafficGen, a1, a2 netip. } logf("e2 status: %v", *st) - n := &tailcfg.Node{ - ID: tailcfg.NodeID(0), - Name: "n2", - Addresses: []netip.Prefix{a2}, - AllowedIPs: []netip.Prefix{a2}, - Endpoints: epFromTyped(st.LocalAddrs), - } - e1.SetNetworkMap(&netmap.NetworkMap{ - NodeKey: k1.Public(), - Peers: []tailcfg.NodeView{n.View()}, - }) + e1.SetSelfNode(tailcfg.NodeView{}) p := wgcfg.Peer{ PublicKey: c2.PrivateKey.Public(), diff --git a/wgengine/userspace.go b/wgengine/userspace.go index aa9ce053c..e471f2554 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -45,7 +45,6 @@ "tailscale.com/types/ipproto" "tailscale.com/types/key" "tailscale.com/types/logger" - "tailscale.com/types/netmap" "tailscale.com/types/views" "tailscale.com/util/checkchange" "tailscale.com/util/clientmetric" @@ -142,9 +141,9 @@ type userspaceEngine struct { lastAppliedDisableTUNUDPGRO bool lastAppliedDisableTUNTCPGRO bool - mu sync.Mutex // guards following; see lock order comment below - netMap *netmap.NetworkMap // or nil - closing bool // Close was called (even if we're still closing) + mu sync.Mutex // guards following; see lock order comment below + selfNode tailcfg.NodeView // or invalid if none + closing bool // Close was called (even if we're still closing) statusCallback StatusCallback endpoints []tailcfg.Endpoint pendOpen map[flowtrackTuple]*pendingOpenFlow // see pendopen.go @@ -837,7 +836,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, } e.mu.Lock() - nm := e.netMap + self := e.selfNode e.mu.Unlock() listenPort := e.confListenPort @@ -848,10 +847,10 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, peerMTUEnable := e.magicConn.ShouldPMTUD() isSubnetRouter := false - if buildfeatures.HasBird && e.birdClient != nil && nm != nil && nm.SelfNode.Valid() { - isSubnetRouter = hasOverlap(nm.SelfNode.PrimaryRoutes(), nm.SelfNode.Hostinfo().RoutableIPs()) + if buildfeatures.HasBird && e.birdClient != nil && self.Valid() { + isSubnetRouter = hasOverlap(self.PrimaryRoutes(), self.Hostinfo().RoutableIPs()) e.logf("[v1] Reconfig: hasOverlap(%v, %v) = %v; isSubnetRouter=%v lastIsSubnetRouter=%v", - nm.SelfNode.PrimaryRoutes(), nm.SelfNode.Hostinfo().RoutableIPs(), + self.PrimaryRoutes(), self.Hostinfo().RoutableIPs(), isSubnetRouter, isSubnetRouter, e.lastIsSubnetRouter) } isSubnetRouterChanged := buildfeatures.HasAdvertiseRoutes && isSubnetRouter != e.lastIsSubnetRouter @@ -1331,9 +1330,9 @@ func (e *userspaceEngine) linkChange(delta *netmon.ChangeDelta) { } } -func (e *userspaceEngine) SetNetworkMap(nm *netmap.NetworkMap) { +func (e *userspaceEngine) SetSelfNode(self tailcfg.NodeView) { e.mu.Lock() - e.netMap = nm + e.selfNode = self tunGROKnobsChanged := false var curUDP, curTCP bool if buildfeatures.HasGRO && runtime.GOOS == "linux" && e.controlKnobs != nil { @@ -1409,19 +1408,19 @@ func (e *userspaceEngine) mySelfIPMatchingFamily(dst netip.Addr) (src netip.Addr var zero netip.Addr e.mu.Lock() defer e.mu.Unlock() - if e.netMap == nil { - return zero, errors.New("no netmap") + if !e.selfNode.Valid() { + return zero, errors.New("no self node") } - addrs := e.netMap.GetAddresses() + addrs := e.selfNode.Addresses() if addrs.Len() == 0 { - return zero, errors.New("no self address in netmap") + return zero, errors.New("no self address") } for _, p := range addrs.All() { if p.IsSingleIP() && p.Addr().BitLen() == dst.BitLen() { return p.Addr(), nil } } - return zero, errors.New("no self address in netmap matching address family") + return zero, errors.New("no self address matching address family") } func (e *userspaceEngine) sendICMPEchoRequest(destIP netip.Addr, peer tailcfg.NodeView, res *ipnstate.PingResult, cb func(*ipnstate.PingResult)) { diff --git a/wgengine/userspace_test.go b/wgengine/userspace_test.go index 4cadbcb69..809190bfa 100644 --- a/wgengine/userspace_test.go +++ b/wgengine/userspace_test.go @@ -116,7 +116,7 @@ func TestUserspaceEngineReconfig(t *testing.T) { }, } - e.SetNetworkMap(nm) + e.SetSelfNode(nm.SelfNode) err = e.Reconfig(cfg, routerCfg, &dns.Config{}) if err != nil { t.Fatal(err) @@ -168,7 +168,7 @@ func TestUserspaceEngineTSMPLearned(t *testing.T) { if err != nil { t.Fatal(err) } - e.SetNetworkMap(nm) + e.SetSelfNode(nm.SelfNode) newDisco := key.NewDisco() cfg := &wgcfg.Config{ @@ -244,7 +244,7 @@ func TestUserspaceEngineTSMPLearnedMismatch(t *testing.T) { if err != nil { t.Fatal(err) } - e.SetNetworkMap(nm) + e.SetSelfNode(nm.SelfNode) newDisco := key.NewDisco() cfg := &wgcfg.Config{ @@ -485,7 +485,7 @@ func TestTSMPKeyAdvertisement(t *testing.T) { }, } - ue.SetNetworkMap(nm) + ue.SetSelfNode(nm.SelfNode) err = ue.Reconfig(cfg, routerCfg, &dns.Config{}) if err != nil { t.Fatal(err) diff --git a/wgengine/wgengine.go b/wgengine/wgengine.go index 8ca37a65b..6b996fe81 100644 --- a/wgengine/wgengine.go +++ b/wgengine/wgengine.go @@ -175,12 +175,9 @@ type Engine interface { // You don't have to call this. Done() <-chan struct{} - // SetNetworkMap informs the engine of the latest network map - // from the server. The network map's DERPMap field should be - // ignored as as it might be disabled; get it from SetDERPMap - // instead. - // The network map should only be read from. - SetNetworkMap(*netmap.NetworkMap) + // SetSelfNode informs the engine of the current self node. + // The zero (invalid) NodeView indicates no self node. + SetSelfNode(tailcfg.NodeView) // UpdateStatus populates the network state using the provided // status builder.