wgengine/magicsock,ipn/ipnlocal: store and load homeDERP from cache (#19491)
With netmap caching, the home DERP of the self node was neither saved to
the cache or loaded from it, making nodes not stick to a DERP when
starting without a connection to control.
Instead, make sure that when a cache is available, load that cache,
before looking for DERP servers. This is implemented by allowing a skip
of ReSTUN in setting the DERP map (we must have a DERP map before
setting the home DERP), so the DERP from cache will set itself and be
sticky until a connection to control is established.
Making DERP only change when connected to control is handled by existing
code from f072d017bd.
Updates #19490
Signed-off-by: Claus Lensbøl <claus@tailscale.com>
This commit is contained in:
@@ -4,9 +4,15 @@
|
||||
package magicsock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/net/netcheck"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/util/eventbus"
|
||||
"tailscale.com/util/eventbus/eventbustest"
|
||||
)
|
||||
|
||||
func CheckDERPHeuristicTimes(t *testing.T) {
|
||||
@@ -14,3 +20,111 @@ func CheckDERPHeuristicTimes(t *testing.T) {
|
||||
t.Errorf("PreferredDERPFrameTime too low; should be at least frameReceiveRecordRate")
|
||||
}
|
||||
}
|
||||
|
||||
func TestForceSetNearestDERP(t *testing.T) {
|
||||
derpMap := &tailcfg.DERPMap{
|
||||
Regions: map[int]*tailcfg.DERPRegion{
|
||||
7: {
|
||||
RegionID: 7,
|
||||
RegionCode: "test",
|
||||
Nodes: []*tailcfg.DERPNode{
|
||||
{
|
||||
Name: "7a",
|
||||
RegionID: 7,
|
||||
HostName: "derp7.test.unused",
|
||||
IPv4: "127.0.0.1",
|
||||
IPv6: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Force the real control health check so we can verify force=true bypasses it.
|
||||
tstest.Replace(t, &checkControlHealthDuringNearestDERPInTests, true)
|
||||
|
||||
bus := eventbustest.NewBus(t)
|
||||
ht := health.NewTracker(bus)
|
||||
c := newConn(t.Logf)
|
||||
ec := bus.Client("magicsock.Conn.Test")
|
||||
c.eventClient = ec
|
||||
c.homeDERPChangedPub = eventbus.Publish[HomeDERPChanged](ec)
|
||||
c.eventBus = bus
|
||||
c.derpMap = derpMap
|
||||
c.health = ht
|
||||
|
||||
ht.SetOutOfPollNetMap()
|
||||
|
||||
tw := eventbustest.NewWatcher(t, bus)
|
||||
|
||||
got := c.ForceSetNearestDERP(7)
|
||||
if got != 7 {
|
||||
t.Fatalf("ForceSetNearestDERP(7) = %d, want 7", got)
|
||||
}
|
||||
if c.myDerp != 7 {
|
||||
t.Errorf("c.myDerp = %d after ForceSetNearestDERP, want 7", c.myDerp)
|
||||
}
|
||||
|
||||
if err := eventbustest.Expect(tw, func(e HomeDERPChanged) error {
|
||||
if e.Old != 0 || e.New != 7 {
|
||||
return fmt.Errorf("got HomeDERPChanged{Old:%d, New:%d}, want {Old:0, New:7}", e.Old, e.New)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Errorf("expected HomeDERPChanged event: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDERPMapDoReStun(t *testing.T) {
|
||||
derpMap1 := &tailcfg.DERPMap{
|
||||
Regions: map[int]*tailcfg.DERPRegion{
|
||||
1: {
|
||||
RegionID: 1,
|
||||
RegionCode: "cph",
|
||||
Nodes: []*tailcfg.DERPNode{
|
||||
{Name: "1a", RegionID: 1, HostName: "cph.test.unused", IPv4: "127.0.0.1", IPv6: "none"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
derpMap2 := &tailcfg.DERPMap{
|
||||
Regions: map[int]*tailcfg.DERPRegion{
|
||||
2: {
|
||||
RegionID: 2,
|
||||
RegionCode: "inc",
|
||||
Nodes: []*tailcfg.DERPNode{
|
||||
{Name: "2a", RegionID: 2, HostName: "inc.test.unused", IPv4: "127.0.0.1", IPv6: "none"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var reSTUNCalls int
|
||||
tstest.Replace(t, &reSTUNHookForTests, func(_ string) {
|
||||
reSTUNCalls++
|
||||
})
|
||||
|
||||
bus := eventbustest.NewBus(t)
|
||||
ht := health.NewTracker(bus)
|
||||
c := newConn(t.Logf)
|
||||
ec := bus.Client("magicsock.Conn.Test")
|
||||
c.eventClient = ec
|
||||
c.homeDERPChangedPub = eventbus.Publish[HomeDERPChanged](ec)
|
||||
c.eventBus = bus
|
||||
c.health = ht
|
||||
// With a zero private key and everHadKey=true, ReSTUN returns early without
|
||||
// spawning updateEndpoints.
|
||||
c.everHadKey = true
|
||||
|
||||
// Should not trigger a ReSTUN.
|
||||
c.SetDERPMap(derpMap1, false)
|
||||
if reSTUNCalls != 0 {
|
||||
t.Errorf("SetDERPMap(dm, doReStun=false): got %d ReSTUN calls, want 0", reSTUNCalls)
|
||||
}
|
||||
|
||||
// doReStun=true: should trigger a ReSTUN.
|
||||
c.SetDERPMap(derpMap2, true)
|
||||
if reSTUNCalls != 1 {
|
||||
t.Errorf("SetDERPMap(dm, doReStun=true): got %d ReSTUN calls, want 1", reSTUNCalls)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user