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
3 changed files with 82 additions and 0 deletions
Showing only changes of commit 6a709216b9 - Show all commits
+6
View File
@@ -6696,6 +6696,12 @@ func (b *LocalBackend) setControlClientLocked(cc controlclient.Client) {
b.cc = cc
b.ccAuto, _ = cc.(*controlclient.Auto)
b.ignoreControlClientUpdates.Store(cc == nil)
// magicsock de-dupes NetInfo against a cache that outlives the control
// client; clear it on (re)install so the next netcheck re-reports our
// NetInfo (notably PreferredDERP, our home DERP) to the new client rather
// than suppressing it as unchanged.
b.MagicConn().ResetNetInfoLast()
}
// resetControlClientLocked sets b.cc to nil and returns the old value. If the
+20
View File
@@ -1104,6 +1104,26 @@ func (c *Conn) callNetInfoCallbackLocked(ni *tailcfg.NetInfo) {
}
}
// ResetNetInfoLast clears the cached NetInfo used to de-duplicate NetInfo
// callbacks, so that the next NetInfo is delivered to the registered callback
// (see [Conn.SetNetInfoCallback]) even if it's structurally identical to the
// previously delivered one.
//
// It must be called whenever the downstream consumer of NetInfo updates is
// replaced, notably when [ipnlocal.LocalBackend] installs a new control client
// after an interactive login or a profile switch.
//
// TODO(tailscale/tailscale#17887): remove once NetInfo updates move to the
// eventbus, where a newly-installed consumer can fetch current state on
// subscribe instead of magicsock exposing this de-dup-cache reset hook.
//
// c.mu must NOT be held.
func (c *Conn) ResetNetInfoLast() {
c.mu.Lock()
defer c.mu.Unlock()
c.netInfoLast = nil
}
// addValidDiscoPathForTest makes addr a validated disco address for
// discoKey. It's used in tests to enable receiving of packets from
// addr without having to spin up the entire active discovery
+56
View File
@@ -489,6 +489,62 @@ collectEndpoints:
}
}
// TestResetNetInfoLast verifies that ResetNetInfoLast clears the NetInfo
// de-duplication cache, so the next NetInfo is delivered to the callback even
// when it's structurally unchanged (which callNetInfoCallback would normally
// suppress). This is what lets a node re-advertise its home DERP
// (NetInfo.PreferredDERP) to a freshly-installed control client after an
// interactive login or profile switch; without it, peers can't reach the node
// over DERP until some unrelated NetInfo field changes.
func TestResetNetInfoLast(t *testing.T) {
tstest.PanicOnLog()
c := newConn(t.Logf)
got := make(chan *tailcfg.NetInfo, 8)
c.SetNetInfoCallback(func(ni *tailcfg.NetInfo) {
got <- ni
})
wantCall := func(why string, wantDERP int) {
t.Helper()
select {
case ni := <-got:
if ni.PreferredDERP != wantDERP {
t.Fatalf("%s: got PreferredDERP=%d, want %d", why, ni.PreferredDERP, wantDERP)
}
case <-time.After(5 * time.Second):
t.Fatalf("%s: timed out waiting for NetInfo callback", why)
}
}
wantNoCall := func(why string) {
t.Helper()
select {
case ni := <-got:
t.Fatalf("%s: unexpected NetInfo callback: %+v", why, ni)
case <-time.After(200 * time.Millisecond):
}
}
ni := &tailcfg.NetInfo{PreferredDERP: 7, WorkingUDP: "true", LinkType: "wired"}
// First delivery fires the callback.
c.callNetInfoCallback(ni.Clone())
wantCall("initial", 7)
// An identical NetInfo is de-duplicated: no callback. This is exactly the
// behavior that, on a control-client swap, drops the home DERP.
c.callNetInfoCallback(ni.Clone())
wantNoCall("duplicate suppressed")
// Simulate a new control client being installed: clearing the de-dup cache
// must let the next (otherwise-identical) NetInfo through, modeling the
// post-login netcheck that re-reports derp-7 to the new session.
c.ResetNetInfoLast()
c.callNetInfoCallback(ni.Clone())
wantCall("after ResetNetInfoLast", 7)
}
func TestPickDERPFallback(t *testing.T) {
tstest.PanicOnLog()
tstest.ResourceCheck(t)