WIP: rebase fork onto upstream/main (v1.103.0) #15

Closed
codinget wants to merge 670 commits from webnet into save/webnet-2026-07-29
10 changed files with 43 additions and 62 deletions
Showing only changes of commit 9cb1147805 - Show all commits
-5
View File
@@ -44,11 +44,6 @@ func newNetstack(logf logger.Logf, sys *tsd.System, onlyNetstack bool) (tsd.Nets
dialer := sys.Dialer.Get() // must be set by caller already
if onlyNetstack {
e := sys.Engine.Get()
dialer.UseNetstackForIP = func(ip netip.Addr) bool {
_, ok := e.PeerForIP(ip)
return ok
}
dialer.NetstackDialTCP = func(ctx context.Context, dst netip.AddrPort) (net.Conn, error) {
// Note: don't just return ns.DialContextTCP or we'll return
// *gonet.TCPConn(nil) instead of a nil interface which trips up
+7
View File
@@ -18,6 +18,7 @@ import (
"log"
"net"
"net/http"
"net/netip"
"os"
"os/signal"
"path/filepath"
@@ -694,6 +695,12 @@ func getLocalBackend(ctx context.Context, logf logger.Logf, logID logid.PublicID
if err != nil {
return nil, fmt.Errorf("ipnlocal.NewLocalBackend: %w", err)
}
if onlyNetstack {
dialer.UseNetstackForIP = func(ip netip.Addr) bool {
_, ok := lb.PeerForIP(ip)
return ok
}
}
lb.SetVarRoot(opts.VarRoot)
if logPol != nil {
lb.SetLogFlusher(logPol.Logtail.StartFlush)
+2 -2
View File
@@ -634,7 +634,7 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
e.SetPeerByIPPacketFunc(b.lookupPeerByIP)
e.SetPeerConfigFunc(b.peerAllowedIPs)
e.SetPeerForIPFunc(b.peerForIP)
e.SetPeerForIPFunc(b.PeerForIP)
e.SetPeerSessionStateFunc(b.onPeerWireGuardState)
e.SetNetLogSource(netLogNodeSource{b})
e.SetWGPeerLookup(b.lookupPeerWireGuardString)
@@ -8492,7 +8492,7 @@ func (b *LocalBackend) ResetAuth() error {
}
func (b *LocalBackend) GetPeerEndpointChanges(ctx context.Context, ip netip.Addr) ([]magicsock.EndpointChange, error) {
pip, ok := b.e.PeerForIP(ip)
pip, ok := b.PeerForIP(ip)
if !ok {
return nil, fmt.Errorf("no matching peer")
}
+3 -5
View File
@@ -9488,14 +9488,13 @@ func TestEnginePeerForIPAdjustsForPrefs(t *testing.T) {
nm := buildNetmapWithPeers(selfNode, exitA, exitB, subnetBig, subnetSmall)
var eng wgengine.Engine
var curLB *LocalBackend
var curT *testing.T // active subtest, for test helpers
wantPeer := func(ip string, n tailcfg.NodeView) {
t := curT
t.Helper()
pip, ok := eng.PeerForIP(netip.MustParseAddr(ip))
pip, ok := curLB.PeerForIP(netip.MustParseAddr(ip))
if !ok {
t.Fatalf("PeerForIP(%s): ok=false, want true", ip)
}
@@ -9509,7 +9508,7 @@ func TestEnginePeerForIPAdjustsForPrefs(t *testing.T) {
wantNotPeer := func(ip string) {
t := curT
t.Helper()
if _, ok := eng.PeerForIP(netip.MustParseAddr(ip)); ok {
if _, ok := curLB.PeerForIP(netip.MustParseAddr(ip)); ok {
t.Fatalf("PeerForIP(%s): ok=true, want false", ip)
}
}
@@ -9534,7 +9533,7 @@ func TestEnginePeerForIPAdjustsForPrefs(t *testing.T) {
wantSelf := func(ip string) {
t := curT
t.Helper()
pip, ok := eng.PeerForIP(netip.MustParseAddr(ip))
pip, ok := curLB.PeerForIP(netip.MustParseAddr(ip))
if !ok {
t.Fatalf("PeerForIP(%s): ok=false, want true", ip)
}
@@ -9663,7 +9662,6 @@ func TestEnginePeerForIPAdjustsForPrefs(t *testing.T) {
LoggedIn: true,
})
eng = lb.sys.Engine.Get()
curLB = lb
curT = t
tt.check()
+5 -3
View File
@@ -118,13 +118,15 @@ func addrFamilyMatch(ip netip.Addr, network string) bool {
return true
}
// peerForIP returns which peer is responsible for a given IP address.
// PeerForIP returns which peer is responsible for a given IP address.
// Despite the name, it can also return the self node (with IsSelf set).
// It handles both Tailscale IPs (returning the owning peer or self) and
// non-Tailscale addresses like subnet-routed IPs or exit-node global
// internet IPs (returning whichever peer would route that traffic).
// It is installed as the [wgengine.Engine.SetPeerForIPFunc] callback.
func (b *LocalBackend) peerForIP(ip netip.Addr) (_ wgengine.PeerForIP, ok bool) {
// It is installed as the [wgengine.Engine.SetPeerForIPFunc] callback,
// serving the engine's internal cold-path lookups (Ping, TSMP, pendopen
// diagnostics).
func (b *LocalBackend) PeerForIP(ip netip.Addr) (_ wgengine.PeerForIP, ok bool) {
nb := b.currentNode()
if tsaddr.IsTailscaleIP(ip) {
-4
View File
@@ -1949,10 +1949,6 @@ func (e *mockEngine) DNSConfig() *dns.Config {
return e.dnsCfg
}
func (e *mockEngine) PeerForIP(netip.Addr) (_ wgengine.PeerForIP, ok bool) {
return wgengine.PeerForIP{}, false
}
func (e *mockEngine) GetFilter() *filter.Filter {
e.mu.Lock()
defer e.mu.Unlock()
+2 -1
View File
@@ -886,7 +886,8 @@ func (s *Server) start() (reterr error) {
ns.GetUDPHandlerForFlow = s.getUDPHandlerForFlow
s.netstack = ns
s.dialer.UseNetstackForIP = func(ip netip.Addr) bool {
_, ok := eng.PeerForIP(ip)
// s.lb is assigned below, before any dials can happen.
_, ok := s.lb.PeerForIP(ip)
return ok
}
s.dialer.NetstackDialTCP = func(ctx context.Context, dst netip.AddrPort) (net.Conn, error) {
+3 -3
View File
@@ -138,14 +138,14 @@ func (e *userspaceEngine) isOSNetworkProbe(dst netip.AddrPort) bool {
// iOS had log spam like:
// open-conn-track: timeout opening (100.115.73.60:52501 => 17.125.252.5:443); no associated peer node
if runtime.GOOS == "ios" && dst.Port() == 443 && appleIPRange.Contains(dst.Addr()) {
if _, ok := e.PeerForIP(dst.Addr()); !ok {
if _, ok := e.peerForIP(dst.Addr()); !ok {
return true
}
}
// NetworkManager; https://github.com/tailscale/tailscale/issues/13687
// open-conn-track: timeout opening (TCP 100.96.229.119:42798 => 185.125.190.49:80); no associated peer node
if runtime.GOOS == "linux" && dst.Port() == 80 && canonicalIPs()(dst.Addr()) {
if _, ok := e.PeerForIP(dst.Addr()); !ok {
if _, ok := e.peerForIP(dst.Addr()); !ok {
return true
}
}
@@ -199,7 +199,7 @@ func (e *userspaceEngine) onOpenTimeout(flow flowtrack.Tuple) {
}
// Diagnose why it might've timed out.
pip, ok := e.PeerForIP(flow.DstAddr())
pip, ok := e.peerForIP(flow.DstAddr())
if !ok {
e.logf("open-conn-track: timeout opening %v; no associated peer node", flow)
return
+13 -13
View File
@@ -103,10 +103,10 @@ type userspaceEngine struct {
wgLock sync.Mutex // serializes all wgdev operations; see lock order comment below
// peerForIP, if non-nil, is the callback installed via
// [userspaceEngine.SetPeerForIPFunc]. PeerForIP delegates to it
// peerForIPFn, if non-nil, is the callback installed via
// [userspaceEngine.SetPeerForIPFunc]. peerForIP delegates to it
// for the cold-path control lookups (Ping, TSMP, pendopen, etc).
peerForIP atomic.Pointer[func(netip.Addr) (_ PeerForIP, ok bool)]
peerForIPFn atomic.Pointer[func(netip.Addr) (_ PeerForIP, ok bool)]
// peerConfigFn, if non-nil, is the live per-peer allowed-IPs
// source installed via [userspaceEngine.SetPeerConfigFunc]. When
@@ -605,7 +605,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
pkt := packet.TSMPDiscoKeyAdvertisement{
Key: update.Key,
}
peer, ok := e.PeerForIP(update.Src)
peer, ok := e.peerForIP(update.Src)
if !ok {
e.logf("wgengine: no peer found for %v", update.Src)
return
@@ -1388,7 +1388,7 @@ func (e *userspaceEngine) UpdateStatus(sb *ipnstate.StatusBuilder) {
func (e *userspaceEngine) Ping(ip netip.Addr, pingType tailcfg.PingType, size int, cb func(*ipnstate.PingResult)) {
res := &ipnstate.PingResult{IP: ip.String()}
pip, ok := e.PeerForIP(ip)
pip, ok := e.peerForIP(ip)
if !ok {
e.logf("ping(%v): no matching peer", ip)
res.Err = "no matching peer"
@@ -1585,24 +1585,24 @@ func (e *userspaceEngine) ProbeLocks() {
e.wgLock.Unlock()
}
// SetPeerForIPFunc installs the callback used by [userspaceEngine.PeerForIP].
// SetPeerForIPFunc installs the callback used by [userspaceEngine.peerForIP].
// See [Engine.SetPeerForIPFunc].
func (e *userspaceEngine) SetPeerForIPFunc(fn func(netip.Addr) (PeerForIP, bool)) {
if fn == nil {
e.peerForIP.Store(nil)
e.peerForIPFn.Store(nil)
return
}
e.peerForIP.Store(&fn)
e.peerForIPFn.Store(&fn)
}
// PeerForIP returns the node responsible for handling the given IP.
// It delegates to the callback installed via [SetPeerForIPFunc]; engines
// without an installed callback return (zero, false).
func (e *userspaceEngine) PeerForIP(ip netip.Addr) (ret PeerForIP, ok bool) {
// peerForIP returns the node responsible for handling the given IP.
// It delegates to the callback installed via [Engine.SetPeerForIPFunc];
// engines without an installed callback return (zero, false).
func (e *userspaceEngine) peerForIP(ip netip.Addr) (ret PeerForIP, ok bool) {
if !ip.IsValid() {
return ret, false
}
if fn := e.peerForIP.Load(); fn != nil {
if fn := e.peerForIPFn.Load(); fn != nil {
return (*fn)(ip)
}
return ret, false
+8 -26
View File
@@ -98,36 +98,18 @@ type Engine interface {
// Unlike Reconfig, it does not return ErrNoChanges.
ResetAndStop() (*Status, error)
// PeerForIP returns the node to which the provided IP routes,
// if any. If none is found, (zero, false) is returned.
// SetPeerForIPFunc installs the IP-to-node lookup used by the
// engine's internal cold paths (Ping, TSMP, pendopen diagnostics).
// It parallels [Engine.SetPeerByIPPacketFunc] but returns richer
// data (a full NodeView, the matched route prefix, and the IsSelf
// flag).
//
// 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.
// If fn is nil, those lookups fail for every IP.
//
// LocalBackend installs a func backed by the live nodeBackend for
// exact-match and self addresses, with the RouteManager's outbound
// table supplying the subnet-route / exit-node fallback.
// table supplying the subnet-route / exit-node fallback; the engine
// itself holds no peer-lookup state on this path.
SetPeerForIPFunc(fn func(netip.Addr) (_ PeerForIP, ok bool))
// GetFilter returns the current packet filter, if any.