wgengine/magicsock: start tracking nearest DERP node
parent
dbc99dc0d2
commit
724c37fb41
|
@ -9,20 +9,28 @@ import (
|
||||||
"net"
|
"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
|
// to use DERP. When used, the port number of the WireGuard endpoint
|
||||||
// is the DERP server number to use.
|
// 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 (
|
var (
|
||||||
derpHostOfIndex = map[int]string{} // index (fake port number) -> hostname
|
derpHostOfIndex = map[int]string{} // index (fake port number) -> hostname
|
||||||
derpIndexOfHost = map[string]int{} // derpHostOfIndex reversed
|
derpIndexOfHost = map[string]int{} // derpHostOfIndex reversed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
derpNYC = 1
|
||||||
|
derpSF = 2
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Just one zone for now:
|
addDerper(derpNYC, "derp.tailscale.com")
|
||||||
addDerper(1, "derp.tailscale.com")
|
addDerper(derpSF, "derp2.tailscale.com")
|
||||||
}
|
}
|
||||||
|
|
||||||
func addDerper(i int, host string) {
|
func addDerper(i int, host string) {
|
||||||
|
|
|
@ -71,6 +71,7 @@ type Conn struct {
|
||||||
|
|
||||||
derpMu sync.Mutex
|
derpMu sync.Mutex
|
||||||
privateKey key.Private
|
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
|
derpConn map[int]*derphttp.Client // magic derp port (see derpmap.go) to its client
|
||||||
derpCancel map[int]context.CancelFunc // to close derp goroutines
|
derpCancel map[int]context.CancelFunc // to close derp goroutines
|
||||||
derpWriteCh map[int]chan<- derpWriteRequest
|
derpWriteCh map[int]chan<- derpWriteRequest
|
||||||
|
@ -203,25 +204,40 @@ func (c *Conn) epUpdate(ctx context.Context) {
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(lastDone)
|
defer close(lastDone)
|
||||||
endpoints, err := c.determineEndpoints(epCtx)
|
nearestDerp, endpoints, err := c.determineEndpoints(epCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logf("magicsock.Conn: endpoint update failed: %v", err)
|
c.logf("magicsock.Conn: endpoint update failed: %v", err)
|
||||||
// TODO(crawshaw): are there any conditions under which
|
// TODO(crawshaw): are there any conditions under which
|
||||||
// we should trigger a retry based on the error here?
|
// we should trigger a retry based on the error here?
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if stringsEqual(endpoints, lastEndpoints) {
|
derpChanged := c.setNearestDerp(nearestDerp)
|
||||||
|
if stringsEqual(endpoints, lastEndpoints) && !derpChanged {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lastEndpoints = endpoints
|
lastEndpoints = endpoints
|
||||||
|
// TODO(bradfiz): get nearestDerp back to ipn for a HostInfo update
|
||||||
c.epFunc(endpoints)
|
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
|
// determineEndpoints returns the machine's endpoint addresses. It
|
||||||
// does a STUN lookup to determine its public address.
|
// 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 (
|
var (
|
||||||
alreadyMu sync.Mutex
|
alreadyMu sync.Mutex
|
||||||
already = make(map[string]bool) // endpoint -> true
|
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)
|
c.stunReceiveFunc.Store(s.Receive)
|
||||||
|
|
||||||
if err := s.Run(ctx); err != nil {
|
if err := s.Run(ctx); err != nil {
|
||||||
return nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ignoreSTUNPackets()
|
c.ignoreSTUNPackets()
|
||||||
|
@ -257,7 +273,7 @@ func (c *Conn) determineEndpoints(ctx context.Context) ([]string, error) {
|
||||||
if localAddr := c.pconn.LocalAddr(); localAddr.IP.IsUnspecified() {
|
if localAddr := c.pconn.LocalAddr(); localAddr.IP.IsUnspecified() {
|
||||||
ips, loopback, err := interfaces.LocalAddresses()
|
ips, loopback, err := interfaces.LocalAddresses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
reason := "localAddresses"
|
reason := "localAddresses"
|
||||||
if len(ips) == 0 {
|
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
|
// The STUN address(es) are always first so that legacy wireguard
|
||||||
// can use eps[0] as its only known endpoint address (although that's
|
// can use eps[0] as its only known endpoint address (although that's
|
||||||
// obviously non-ideal).
|
// obviously non-ideal).
|
||||||
return eps, nil
|
return nearestDerp, eps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringsEqual(x, y []string) bool {
|
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())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
// TODO: close derp channels (if addr.Port != myDerp) on inactivity timer
|
||||||
bidiCh := make(chan derpWriteRequest, bufferedDerpWritesBeforeDrop)
|
bidiCh := make(chan derpWriteRequest, bufferedDerpWritesBeforeDrop)
|
||||||
ch = bidiCh
|
ch = bidiCh
|
||||||
c.derpConn[addr.Port] = dc
|
c.derpConn[addr.Port] = dc
|
||||||
|
|
|
@ -73,7 +73,10 @@ func pickPort(t *testing.T) uint16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDerpIPConstant(t *testing.T) {
|
func TestDerpIPConstant(t *testing.T) {
|
||||||
if derpMagicIPStr != derpMagicIP.String() {
|
if DerpMagicIP != derpMagicIP.String() {
|
||||||
t.Errorf("str %q != IP %v", derpMagicIPStr, derpMagicIP)
|
t.Errorf("str %q != IP %v", DerpMagicIP, derpMagicIP)
|
||||||
|
}
|
||||||
|
if len(derpMagicIP) != 4 {
|
||||||
|
t.Errorf("derpMagicIP is len %d; want 4", len(derpMagicIP))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue