wgengine, ipn/ipnlocal: route PeerForIP through LocalBackend's live data
userspaceEngine.PeerForIP read from e.netMap.Peers and e.lastCfgFull.Peers, both of which go stale when peers arrive via netmap deltas (which skip Engine.SetNetworkMap and Engine.Reconfig). Every PeerForIP caller (Engine.Ping, the TSMP disco-key handler, pendopen diagnostics, tsdial.Dialer.UseNetstackForIP, and LocalBackend.GetPeerEndpointChanges) would report "no matching peer" for freshly-added peers. Fix it the same way SetPeerByIPPacketFunc fixed the outbound packet hot path: have LocalBackend install a callback that reads the live nodeBackend. nb.NodeByAddr is built from both SelfNode and Peers (updateNodeByAddrLocked), so a single lookup covers the common case with IsSelf set when the matched node ID is SelfNode's. The subnet- route / exit-node-default-route slow path goes through a new Engine.PeerKeyForIP that exposes the engine's AllowedIPs BART table (the same table the outbound packet hot path already consults, with exit-node selection honored), and resolves the matched key back to a NodeView via the live nodeBackend. Updates #12542 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: I0d4b0d8997c8e796b7367c46b49b61d4fdc717b0
This commit is contained in:
committed by
Brad Fitzpatrick
parent
e9ae398199
commit
d4f2917c1b
+37
-1
@@ -100,9 +100,45 @@ type Engine interface {
|
||||
ResetAndStop() (*Status, error)
|
||||
|
||||
// PeerForIP returns the node to which the provided IP routes,
|
||||
// if any. If none is found, (nil, false) is returned.
|
||||
// if any. If none is found, (zero, false) is returned.
|
||||
//
|
||||
// Despite the name, it can return the self node (with
|
||||
// PeerForIP.IsSelf set). It handles Tailscale IPs, subnet-routed
|
||||
// IPs, and exit-node global internet IPs, returning whichever
|
||||
// node would handle that traffic.
|
||||
//
|
||||
// This is the cold path used by Ping, TSMP, pendopen diagnostics,
|
||||
// and debug endpoints. It uses the same underlying data structures
|
||||
// as the wireguard-go outbound packet path
|
||||
// ([Engine.SetPeerByIPPacketFunc]), but is slower because it
|
||||
// returns richer data (a full NodeView, the matched route prefix,
|
||||
// and the IsSelf flag) requiring extra lookups.
|
||||
//
|
||||
// In production, the lookup is implemented by LocalBackend and
|
||||
// plumbed in via [Engine.SetPeerForIPFunc]; the engine itself holds
|
||||
// no peer-lookup state on this path.
|
||||
PeerForIP(netip.Addr) (_ PeerForIP, ok bool)
|
||||
|
||||
// SetPeerForIPFunc installs a callback used by [Engine.PeerForIP].
|
||||
// It parallels [Engine.SetPeerByIPPacketFunc] but serves the
|
||||
// cold-path control lookups (Ping, TSMP, pendopen diagnostics,
|
||||
// [tsdial.Dialer.UseNetstackForIP], debug endpoints).
|
||||
//
|
||||
// If fn is nil, PeerForIP returns (zero, false) for every IP.
|
||||
//
|
||||
// LocalBackend installs a func backed by the live nodeBackend for
|
||||
// exact-match and self addresses, with [Engine.PeerKeyForIP]
|
||||
// supplying the subnet-route / exit-node fallback.
|
||||
SetPeerForIPFunc(fn func(netip.Addr) (_ PeerForIP, ok bool))
|
||||
|
||||
// PeerKeyForIP returns the peer's NodePublic and the matched prefix
|
||||
// for the longest-prefix match of ip in the engine's AllowedIPs
|
||||
// table (the wireguard config most recently installed via
|
||||
// [Engine.Reconfig]). Exit-node selection is honored: an unselected
|
||||
// exit node's 0.0.0.0/0 is not matched. It is the same table the
|
||||
// outbound packet hot path consults via [Engine.SetPeerByIPPacketFunc].
|
||||
PeerKeyForIP(netip.Addr) (_ key.NodePublic, _ netip.Prefix, ok bool)
|
||||
|
||||
// GetFilter returns the current packet filter, if any.
|
||||
GetFilter() *filter.Filter
|
||||
|
||||
|
||||
Reference in New Issue
Block a user