diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 9f8aba14a..128f5ef5d 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -378,18 +378,13 @@ func (c *Conn) pickDERPFallback() int { c.mu.Lock() defer c.mu.Unlock() - if c.myDerp != 0 { - // If we already had one in the past, stay on it. - return c.myDerp - } - ids := c.derps.IDs() if len(ids) == 0 { // No DERP nodes registered. return 0 } - // See where our peers are. Pick wherever the most nodes are. + // See where our peers are. var ( peersOnDerp = map[int]int{} best int @@ -404,6 +399,15 @@ func (c *Conn) pickDERPFallback() int { } } } + + // If we already had selected something in the past and it has + // any peers, stay on it. If there are no peers, though, also + // stay where we are. + if c.myDerp != 0 && (best == 0 || peersOnDerp[c.myDerp] != 0) { + return c.myDerp + } + + // Otherwise pick wherever the most peers are. if best != 0 { return best } diff --git a/wgengine/magicsock/magicsock_test.go b/wgengine/magicsock/magicsock_test.go index ef183faea..25dff1d24 100644 --- a/wgengine/magicsock/magicsock_test.go +++ b/wgengine/magicsock/magicsock_test.go @@ -135,6 +135,15 @@ func TestPickDERPFallback(t *testing.T) { if got := c.pickDERPFallback(); got != someNode { t.Errorf("not sticky: got %v; want %v", got, someNode) } + + // But move if peers are elsewhere. + const otherNode = 789 + c.addrsByKey = map[key.Public]*AddrSet{ + key.Public{1}: &AddrSet{addrs: []net.UDPAddr{{IP: derpMagicIP, Port: otherNode}}}, + } + if got := c.pickDERPFallback(); got != otherNode { + t.Errorf("didn't join peers: got %v; want %v", got, someNode) + } } func makeConfigs(t *testing.T, ports []uint16) []wgcfg.Config {