From 724c37fb41c6f9dcc0ad768bb2b336517c5ca5a6 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 3 Mar 2020 17:46:03 -0800 Subject: [PATCH] wgengine/magicsock: start tracking nearest DERP node --- wgengine/magicsock/derpmap.go | 18 ++++++++++++----- wgengine/magicsock/magicsock.go | 30 +++++++++++++++++++++------- wgengine/magicsock/magicsock_test.go | 7 +++++-- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/wgengine/magicsock/derpmap.go b/wgengine/magicsock/derpmap.go index a28635f40..c45659b4e 100644 --- a/wgengine/magicsock/derpmap.go +++ b/wgengine/magicsock/derpmap.go @@ -9,20 +9,28 @@ import ( "net" ) -// derpFakeIPStr is a fake WireGuard endpoint IP address that means +// DerpMagicIP is a fake WireGuard endpoint IP address that means // to use DERP. When used, the port number of the WireGuard endpoint // is the DERP server number to use. -const derpMagicIPStr = "127.3.3.40" // 3340 are above the keys DERP on the keyboard -var derpMagicIP = net.IPv4(127, 3, 3, 40) // net.IP version of above +// +// Mnemonic: 3.3.40 are numbers above the keys D, E, R, P. +const DerpMagicIP = "127.3.3.40" + +var derpMagicIP = net.ParseIP(DerpMagicIP).To4() var ( derpHostOfIndex = map[int]string{} // index (fake port number) -> hostname derpIndexOfHost = map[string]int{} // derpHostOfIndex reversed ) +const ( + derpNYC = 1 + derpSF = 2 +) + func init() { - // Just one zone for now: - addDerper(1, "derp.tailscale.com") + addDerper(derpNYC, "derp.tailscale.com") + addDerper(derpSF, "derp2.tailscale.com") } func addDerper(i int, host string) { diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 2358b0d5e..b70f19133 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -71,6 +71,7 @@ type Conn struct { derpMu sync.Mutex privateKey key.Private + myDerp int // nearest DERP server; 0 means none/unknown derpConn map[int]*derphttp.Client // magic derp port (see derpmap.go) to its client derpCancel map[int]context.CancelFunc // to close derp goroutines derpWriteCh map[int]chan<- derpWriteRequest @@ -203,25 +204,40 @@ func (c *Conn) epUpdate(ctx context.Context) { go func() { defer close(lastDone) - endpoints, err := c.determineEndpoints(epCtx) + nearestDerp, endpoints, err := c.determineEndpoints(epCtx) if err != nil { c.logf("magicsock.Conn: endpoint update failed: %v", err) // TODO(crawshaw): are there any conditions under which // we should trigger a retry based on the error here? return } - if stringsEqual(endpoints, lastEndpoints) { + derpChanged := c.setNearestDerp(nearestDerp) + if stringsEqual(endpoints, lastEndpoints) && !derpChanged { return } lastEndpoints = endpoints + // TODO(bradfiz): get nearestDerp back to ipn for a HostInfo update c.epFunc(endpoints) }() } } +func (c *Conn) setNearestDerp(derpNum int) (changed bool) { + c.derpMu.Lock() + defer c.derpMu.Unlock() + changed = c.myDerp != derpNum + if changed && derpNum != 0 { + // On change, start connecting to it: + go c.derpWriteChanOfAddr(&net.UDPAddr{IP: derpMagicIP, Port: derpNum}) + } + c.myDerp = derpNum + return changed +} + // determineEndpoints returns the machine's endpoint addresses. It // does a STUN lookup to determine its public address. -func (c *Conn) determineEndpoints(ctx context.Context) ([]string, error) { +func (c *Conn) determineEndpoints(ctx context.Context) (nearestDerp int, ipPorts []string, err error) { + nearestDerp = derpNYC // for now var ( alreadyMu sync.Mutex already = make(map[string]bool) // endpoint -> true @@ -249,7 +265,7 @@ func (c *Conn) determineEndpoints(ctx context.Context) ([]string, error) { c.stunReceiveFunc.Store(s.Receive) if err := s.Run(ctx); err != nil { - return nil, err + return 0, nil, err } c.ignoreSTUNPackets() @@ -257,7 +273,7 @@ func (c *Conn) determineEndpoints(ctx context.Context) ([]string, error) { if localAddr := c.pconn.LocalAddr(); localAddr.IP.IsUnspecified() { ips, loopback, err := interfaces.LocalAddresses() if err != nil { - return nil, err + return 0, nil, err } reason := "localAddresses" if len(ips) == 0 { @@ -287,7 +303,7 @@ func (c *Conn) determineEndpoints(ctx context.Context) ([]string, error) { // The STUN address(es) are always first so that legacy wireguard // can use eps[0] as its only known endpoint address (although that's // obviously non-ideal). - return eps, nil + return nearestDerp, eps, nil } func stringsEqual(x, y []string) bool { @@ -496,7 +512,7 @@ func (c *Conn) derpWriteChanOfAddr(addr *net.UDPAddr) chan<- derpWriteRequest { } ctx, cancel := context.WithCancel(context.Background()) - + // TODO: close derp channels (if addr.Port != myDerp) on inactivity timer bidiCh := make(chan derpWriteRequest, bufferedDerpWritesBeforeDrop) ch = bidiCh c.derpConn[addr.Port] = dc diff --git a/wgengine/magicsock/magicsock_test.go b/wgengine/magicsock/magicsock_test.go index 364041d98..cd596837d 100644 --- a/wgengine/magicsock/magicsock_test.go +++ b/wgengine/magicsock/magicsock_test.go @@ -73,7 +73,10 @@ func pickPort(t *testing.T) uint16 { } func TestDerpIPConstant(t *testing.T) { - if derpMagicIPStr != derpMagicIP.String() { - t.Errorf("str %q != IP %v", derpMagicIPStr, derpMagicIP) + if DerpMagicIP != derpMagicIP.String() { + t.Errorf("str %q != IP %v", DerpMagicIP, derpMagicIP) + } + if len(derpMagicIP) != 4 { + t.Errorf("derpMagicIP is len %d; want 4", len(derpMagicIP)) } }