2020-03-18 04:28:47 +00:00
|
|
|
// Copyright (c) 2019 Tailscale Inc & AUTHORS All rights reserved.
|
2020-02-05 22:16:58 +00:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// Package magicsock implements a socket that can change its communication path while
|
|
|
|
// in use, actively searching for the best way to communicate.
|
|
|
|
package magicsock
|
|
|
|
|
|
|
|
import (
|
2020-05-31 22:29:04 +00:00
|
|
|
"bufio"
|
2020-02-05 22:16:58 +00:00
|
|
|
"context"
|
2020-07-01 19:56:17 +00:00
|
|
|
crand "crypto/rand"
|
2020-02-05 22:16:58 +00:00
|
|
|
"encoding/binary"
|
2020-02-18 21:32:04 +00:00
|
|
|
"errors"
|
2020-02-05 22:16:58 +00:00
|
|
|
"fmt"
|
2020-03-04 06:21:56 +00:00
|
|
|
"hash/fnv"
|
|
|
|
"math/rand"
|
2020-02-05 22:16:58 +00:00
|
|
|
"net"
|
2020-02-21 22:01:51 +00:00
|
|
|
"os"
|
2020-05-17 16:51:38 +00:00
|
|
|
"reflect"
|
2020-03-23 21:12:23 +00:00
|
|
|
"sort"
|
2020-02-21 22:01:51 +00:00
|
|
|
"strconv"
|
2020-02-05 22:16:58 +00:00
|
|
|
"strings"
|
|
|
|
"sync"
|
2020-02-18 16:57:11 +00:00
|
|
|
"sync/atomic"
|
2020-02-05 22:16:58 +00:00
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
|
2020-06-30 21:37:35 +00:00
|
|
|
"github.com/golang/groupcache/lru"
|
2020-02-24 12:27:48 +00:00
|
|
|
"github.com/tailscale/wireguard-go/conn"
|
2020-02-05 22:16:58 +00:00
|
|
|
"github.com/tailscale/wireguard-go/device"
|
|
|
|
"github.com/tailscale/wireguard-go/wgcfg"
|
2020-06-28 18:53:37 +00:00
|
|
|
"go4.org/mem"
|
2020-06-26 21:38:53 +00:00
|
|
|
"golang.org/x/crypto/nacl/box"
|
2020-03-02 01:35:10 +00:00
|
|
|
"golang.org/x/time/rate"
|
2020-04-17 20:51:52 +00:00
|
|
|
"inet.af/netaddr"
|
2020-06-25 18:04:52 +00:00
|
|
|
"tailscale.com/control/controlclient"
|
2020-02-21 03:10:54 +00:00
|
|
|
"tailscale.com/derp"
|
2020-02-05 22:16:58 +00:00
|
|
|
"tailscale.com/derp/derphttp"
|
2020-06-30 19:22:42 +00:00
|
|
|
"tailscale.com/disco"
|
2020-03-26 05:57:46 +00:00
|
|
|
"tailscale.com/ipn/ipnstate"
|
2020-03-05 18:29:19 +00:00
|
|
|
"tailscale.com/net/dnscache"
|
2020-03-10 18:02:30 +00:00
|
|
|
"tailscale.com/net/interfaces"
|
2020-05-25 16:15:50 +00:00
|
|
|
"tailscale.com/net/netcheck"
|
2020-05-28 22:27:04 +00:00
|
|
|
"tailscale.com/net/netns"
|
2020-05-25 16:15:50 +00:00
|
|
|
"tailscale.com/net/stun"
|
2020-03-12 18:16:54 +00:00
|
|
|
"tailscale.com/syncs"
|
2020-03-04 06:21:56 +00:00
|
|
|
"tailscale.com/tailcfg"
|
2020-02-17 21:52:11 +00:00
|
|
|
"tailscale.com/types/key"
|
2020-03-04 06:21:56 +00:00
|
|
|
"tailscale.com/types/logger"
|
2020-06-25 21:19:12 +00:00
|
|
|
"tailscale.com/types/opt"
|
2020-05-03 20:58:39 +00:00
|
|
|
"tailscale.com/types/structs"
|
2020-03-02 20:37:52 +00:00
|
|
|
"tailscale.com/version"
|
2020-02-05 22:16:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// A Conn routes UDP packets and actively manages a list of its endpoints.
|
2020-04-06 21:44:10 +00:00
|
|
|
// It implements wireguard/conn.Bind.
|
2020-02-05 22:16:58 +00:00
|
|
|
type Conn struct {
|
2020-03-19 16:39:00 +00:00
|
|
|
pconnPort uint16 // the preferred port from opts.Port; 0 means auto
|
2020-03-19 15:49:30 +00:00
|
|
|
pconn4 *RebindingUDPConn
|
2020-03-19 16:39:00 +00:00
|
|
|
pconn6 *RebindingUDPConn // non-nil if IPv6 available
|
2020-03-13 15:55:38 +00:00
|
|
|
epFunc func(endpoints []string)
|
|
|
|
logf logger.Logf
|
|
|
|
sendLogLimit *rate.Limiter
|
|
|
|
netChecker *netcheck.Client
|
2020-06-25 21:19:12 +00:00
|
|
|
idleFunc func() time.Duration // nil means unknown
|
2020-02-18 18:55:25 +00:00
|
|
|
|
2020-03-07 21:36:18 +00:00
|
|
|
// bufferedIPv4From and bufferedIPv4Packet are owned by
|
|
|
|
// ReceiveIPv4, and used when both a DERP and IPv4 packet arrive
|
|
|
|
// at the same time. It stores the IPv4 packet for use in the next call.
|
2020-06-30 21:37:35 +00:00
|
|
|
bufferedIPv4From netaddr.IPPort // if non-zero, then bufferedIPv4Packet is valid
|
|
|
|
bufferedIPv4Packet []byte // the received packet (reused, owned by ReceiveIPv4)
|
2020-03-07 21:36:18 +00:00
|
|
|
|
2020-03-02 17:31:25 +00:00
|
|
|
connCtx context.Context // closed on Conn.Close
|
|
|
|
connCtxCancel func() // closes connCtx
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
// stunReceiveFunc holds the current STUN packet processing func.
|
|
|
|
// Its Loaded value is always non-nil.
|
|
|
|
stunReceiveFunc atomic.Value // of func(p []byte, fromAddr *net.UDPAddr)
|
|
|
|
|
|
|
|
udpRecvCh chan udpReadResult
|
|
|
|
derpRecvCh chan derpReadResult
|
|
|
|
|
2020-06-26 21:38:53 +00:00
|
|
|
// ============================================================
|
2020-03-13 15:55:38 +00:00
|
|
|
mu sync.Mutex // guards all following fields
|
|
|
|
|
2020-06-28 18:53:37 +00:00
|
|
|
started bool // Start was called
|
|
|
|
closed bool // Close was called
|
2020-03-13 15:55:38 +00:00
|
|
|
|
2020-04-27 20:03:22 +00:00
|
|
|
endpointsUpdateWaiter *sync.Cond
|
2020-03-13 15:55:38 +00:00
|
|
|
endpointsUpdateActive bool
|
2020-05-14 00:54:27 +00:00
|
|
|
wantEndpointsUpdate string // true if non-empty; string is reason
|
2020-03-13 15:55:38 +00:00
|
|
|
lastEndpoints []string
|
2020-04-18 15:48:01 +00:00
|
|
|
peerSet map[key.Public]struct{}
|
2020-03-13 15:55:38 +00:00
|
|
|
|
2020-07-02 05:15:41 +00:00
|
|
|
discoPrivate key.Private
|
|
|
|
discoPublic tailcfg.DiscoKey // public of discoPrivate
|
|
|
|
nodeOfDisco map[tailcfg.DiscoKey]*tailcfg.Node
|
|
|
|
discoOfNode map[tailcfg.NodeKey]tailcfg.DiscoKey
|
|
|
|
discoOfAddr map[netaddr.IPPort]tailcfg.DiscoKey // validated non-DERP paths only
|
2020-06-28 18:53:37 +00:00
|
|
|
endpointOfDisco map[tailcfg.DiscoKey]*discoEndpoint
|
2020-06-29 21:26:25 +00:00
|
|
|
sharedDiscoKey map[tailcfg.DiscoKey]*[32]byte // nacl/box precomputed key
|
2020-06-28 18:53:37 +00:00
|
|
|
|
2020-02-29 19:48:34 +00:00
|
|
|
// addrsByUDP is a map of every remote ip:port to a priority
|
2020-02-05 22:16:58 +00:00
|
|
|
// list of endpoint addresses for a peer.
|
|
|
|
// The priority list is provided by wgengine configuration.
|
|
|
|
//
|
|
|
|
// Given a wgcfg describing:
|
|
|
|
// machineA: 10.0.0.1:1, 10.0.0.2:2
|
|
|
|
// machineB: 10.0.0.3:3
|
2020-02-29 19:48:34 +00:00
|
|
|
// the addrsByUDP map contains:
|
|
|
|
// 10.0.0.1:1 -> [10.0.0.1:1, 10.0.0.2:2]
|
|
|
|
// 10.0.0.2:2 -> [10.0.0.1:1, 10.0.0.2:2]
|
|
|
|
// 10.0.0.3:3 -> [10.0.0.3:3]
|
2020-04-18 15:28:10 +00:00
|
|
|
addrsByUDP map[netaddr.IPPort]*AddrSet
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-04-18 15:48:01 +00:00
|
|
|
// addrsByKey maps from public keys (as seen by incoming DERP
|
2020-03-13 15:55:38 +00:00
|
|
|
// packets) to its AddrSet (the same values as in addrsByUDP).
|
2020-04-18 15:48:01 +00:00
|
|
|
addrsByKey map[key.Public]*AddrSet
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-03-04 06:21:56 +00:00
|
|
|
netInfoFunc func(*tailcfg.NetInfo) // nil until set
|
|
|
|
netInfoLast *tailcfg.NetInfo
|
|
|
|
|
2020-05-17 16:51:38 +00:00
|
|
|
derpMap *tailcfg.DERPMap // nil (or zero regions/nodes) means DERP is disabled
|
2020-06-25 18:04:52 +00:00
|
|
|
netMap *controlclient.NetworkMap
|
2020-05-17 16:51:38 +00:00
|
|
|
privateKey key.Private
|
|
|
|
myDerp int // nearest DERP region ID; 0 means none/unknown
|
|
|
|
derpStarted chan struct{} // closed on first connection to DERP; for tests
|
|
|
|
activeDerp map[int]activeDerp // DERP regionID -> connection to a node in that region
|
|
|
|
prevDerp map[int]*syncs.WaitGroupChan
|
2020-03-23 21:12:23 +00:00
|
|
|
|
|
|
|
// derpRoute contains optional alternate routes to use as an
|
|
|
|
// optimization instead of contacting a peer via their home
|
|
|
|
// DERP connection. If they sent us a message on a different
|
|
|
|
// DERP connection (which should really only be on our DERP
|
|
|
|
// home connection, or what was once our home), then we
|
|
|
|
// remember that route here to optimistically use instead of
|
|
|
|
// creating a new DERP connection back to their home.
|
2020-04-18 15:48:01 +00:00
|
|
|
derpRoute map[key.Public]derpRoute
|
2020-03-23 21:12:23 +00:00
|
|
|
|
|
|
|
// peerLastDerp tracks which DERP node we last used to speak with a
|
|
|
|
// peer. It's only used to quiet logging, so we only log on change.
|
2020-04-18 15:48:01 +00:00
|
|
|
peerLastDerp map[key.Public]int
|
2020-05-29 19:40:51 +00:00
|
|
|
|
|
|
|
// noV4 and noV6 are whether IPv4 and IPv6 are known to be
|
|
|
|
// missing. They're only used to suppress log spam. The name
|
|
|
|
// is named negatively because in early start-up, we don't yet
|
|
|
|
// necessarily have a netcheck.Report and don't want to skip
|
|
|
|
// logging.
|
|
|
|
noV4, noV6 syncs.AtomicBool
|
2020-03-22 01:24:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// derpRoute is a route entry for a public key, saying that a certain
|
|
|
|
// peer should be available at DERP node derpID, as long as the
|
|
|
|
// current connection for that derpID is dc. (but dc should not be
|
|
|
|
// used to write directly; it's owned by the read/write loops)
|
|
|
|
type derpRoute struct {
|
|
|
|
derpID int
|
|
|
|
dc *derphttp.Client // don't use directly; see comment above
|
|
|
|
}
|
|
|
|
|
|
|
|
// removeDerpPeerRoute removes a DERP route entry previously added by addDerpPeerRoute.
|
|
|
|
func (c *Conn) removeDerpPeerRoute(peer key.Public, derpID int, dc *derphttp.Client) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
r2 := derpRoute{derpID, dc}
|
|
|
|
if r, ok := c.derpRoute[peer]; ok && r == r2 {
|
|
|
|
delete(c.derpRoute, peer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// addDerpPeerRoute adds a DERP route entry, noting that peer was seen
|
|
|
|
// on DERP node derpID, at least on the connection identified by dc.
|
|
|
|
// See issue 150 for details.
|
|
|
|
func (c *Conn) addDerpPeerRoute(peer key.Public, derpID int, dc *derphttp.Client) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
if c.derpRoute == nil {
|
|
|
|
c.derpRoute = make(map[key.Public]derpRoute)
|
|
|
|
}
|
2020-03-23 21:12:23 +00:00
|
|
|
r := derpRoute{derpID, dc}
|
|
|
|
c.derpRoute[peer] = r
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
// 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()
|
2020-04-18 15:28:10 +00:00
|
|
|
var derpMagicIPAddr = netaddr.IPv4(127, 3, 3, 40)
|
2020-03-09 22:20:33 +00:00
|
|
|
|
2020-03-05 16:54:08 +00:00
|
|
|
// activeDerp contains fields for an active DERP connection.
|
|
|
|
type activeDerp struct {
|
2020-03-23 21:12:23 +00:00
|
|
|
c *derphttp.Client
|
|
|
|
cancel context.CancelFunc
|
|
|
|
writeCh chan<- derpWriteRequest
|
|
|
|
// lastWrite is the time of the last request for its write
|
|
|
|
// channel (currently even if there was no write).
|
|
|
|
// It is always non-nil and initialized to a non-zero Time[
|
|
|
|
lastWrite *time.Time
|
|
|
|
createTime time.Time
|
2020-03-05 16:54:08 +00:00
|
|
|
}
|
|
|
|
|
2020-02-14 17:28:29 +00:00
|
|
|
// DefaultPort is the default port to listen on.
|
|
|
|
// The current default (zero) means to auto-select a random free port.
|
2020-02-05 22:16:58 +00:00
|
|
|
const DefaultPort = 0
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
var DisableSTUNForTesting bool
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
// Options contains options for Listen.
|
|
|
|
type Options struct {
|
2020-03-19 16:39:00 +00:00
|
|
|
// Logf optionally provides a log function to use.
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
// Must not be nil.
|
2020-03-07 21:11:52 +00:00
|
|
|
Logf logger.Logf
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
// Port is the port to listen on.
|
|
|
|
// Zero means to pick one automatically.
|
|
|
|
Port uint16
|
|
|
|
|
|
|
|
// EndpointsFunc optionally provides a func to be called when
|
|
|
|
// endpoints change. The called func does not own the slice.
|
|
|
|
EndpointsFunc func(endpoint []string)
|
2020-06-25 21:19:12 +00:00
|
|
|
|
|
|
|
// IdleFunc optionally provides a func to return how long
|
|
|
|
// it's been since a TUN packet was sent or received.
|
|
|
|
IdleFunc func() time.Duration
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-19 16:39:00 +00:00
|
|
|
func (o *Options) logf() logger.Logf {
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
if o.Logf == nil {
|
|
|
|
panic("must provide magicsock.Options.logf")
|
2020-03-19 16:39:00 +00:00
|
|
|
}
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
return o.Logf
|
2020-03-19 16:39:00 +00:00
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
func (o *Options) endpointsFunc() func([]string) {
|
|
|
|
if o == nil || o.EndpointsFunc == nil {
|
|
|
|
return func([]string) {}
|
|
|
|
}
|
|
|
|
return o.EndpointsFunc
|
|
|
|
}
|
|
|
|
|
2020-05-17 16:51:38 +00:00
|
|
|
// newConn is the error-free, network-listening-side-effect-free based
|
|
|
|
// of NewConn. Mostly for tests.
|
|
|
|
func newConn() *Conn {
|
2020-02-05 22:16:58 +00:00
|
|
|
c := &Conn{
|
2020-06-28 18:53:37 +00:00
|
|
|
sendLogLimit: rate.NewLimiter(rate.Every(1*time.Minute), 1),
|
|
|
|
addrsByUDP: make(map[netaddr.IPPort]*AddrSet),
|
|
|
|
addrsByKey: make(map[key.Public]*AddrSet),
|
|
|
|
derpRecvCh: make(chan derpReadResult),
|
|
|
|
udpRecvCh: make(chan udpReadResult),
|
|
|
|
derpStarted: make(chan struct{}),
|
|
|
|
peerLastDerp: make(map[key.Public]int),
|
|
|
|
endpointOfDisco: make(map[tailcfg.DiscoKey]*discoEndpoint),
|
2020-06-29 21:26:25 +00:00
|
|
|
sharedDiscoKey: make(map[tailcfg.DiscoKey]*[32]byte),
|
2020-07-02 05:15:41 +00:00
|
|
|
discoOfAddr: make(map[netaddr.IPPort]tailcfg.DiscoKey),
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-04-27 20:03:22 +00:00
|
|
|
c.endpointsUpdateWaiter = sync.NewCond(&c.mu)
|
2020-05-17 16:51:38 +00:00
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewConn creates a magic Conn listening on opts.Port.
|
|
|
|
// As the set of possible endpoints for a Conn changes, the
|
|
|
|
// callback opts.EndpointsFunc is called.
|
|
|
|
//
|
|
|
|
// It doesn't start doing anything until Start is called.
|
|
|
|
func NewConn(opts Options) (*Conn, error) {
|
|
|
|
c := newConn()
|
|
|
|
c.pconnPort = opts.Port
|
|
|
|
c.logf = opts.logf()
|
|
|
|
c.epFunc = opts.endpointsFunc()
|
2020-06-25 21:19:12 +00:00
|
|
|
c.idleFunc = opts.IdleFunc
|
2020-03-19 16:39:00 +00:00
|
|
|
|
|
|
|
if err := c.initialBind(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.connCtx, c.connCtxCancel = context.WithCancel(context.Background())
|
2020-03-09 22:20:33 +00:00
|
|
|
c.netChecker = &netcheck.Client{
|
|
|
|
Logf: logger.WithPrefix(c.logf, "netcheck: "),
|
2020-03-19 15:49:30 +00:00
|
|
|
GetSTUNConn4: func() netcheck.STUNConn { return c.pconn4 },
|
2020-03-19 16:39:00 +00:00
|
|
|
}
|
|
|
|
if c.pconn6 != nil {
|
|
|
|
c.netChecker.GetSTUNConn6 = func() netcheck.STUNConn { return c.pconn6 }
|
2020-03-09 22:20:33 +00:00
|
|
|
}
|
|
|
|
|
2020-02-18 16:57:11 +00:00
|
|
|
c.ignoreSTUNPackets()
|
2020-05-17 16:51:38 +00:00
|
|
|
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Conn) Start() {
|
|
|
|
c.mu.Lock()
|
|
|
|
if c.started {
|
|
|
|
panic("duplicate Start call")
|
|
|
|
}
|
|
|
|
c.started = true
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
2020-03-13 03:10:11 +00:00
|
|
|
c.ReSTUN("initial")
|
2020-03-09 22:20:33 +00:00
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
// We assume that LinkChange notifications are plumbed through well
|
|
|
|
// on our mobile clients, so don't do the timer thing to save radio/battery/CPU/etc.
|
|
|
|
if !version.IsMobile() {
|
|
|
|
go c.periodicReSTUN()
|
|
|
|
}
|
|
|
|
go c.periodicDerpCleanup()
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-02 17:31:25 +00:00
|
|
|
func (c *Conn) donec() <-chan struct{} { return c.connCtx.Done() }
|
|
|
|
|
2020-02-18 16:57:11 +00:00
|
|
|
// ignoreSTUNPackets sets a STUN packet processing func that does nothing.
|
|
|
|
func (c *Conn) ignoreSTUNPackets() {
|
2020-06-30 20:25:13 +00:00
|
|
|
c.stunReceiveFunc.Store(func([]byte, netaddr.IPPort) {})
|
2020-02-18 16:57:11 +00:00
|
|
|
}
|
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
// c.mu must NOT be held.
|
|
|
|
func (c *Conn) updateEndpoints(why string) {
|
|
|
|
defer func() {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
why := c.wantEndpointsUpdate
|
|
|
|
c.wantEndpointsUpdate = ""
|
|
|
|
if why != "" && !c.closed {
|
|
|
|
go c.updateEndpoints(why)
|
|
|
|
} else {
|
|
|
|
c.endpointsUpdateActive = false
|
2020-04-27 20:03:22 +00:00
|
|
|
c.endpointsUpdateWaiter.Broadcast()
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
}()
|
2020-03-23 21:12:23 +00:00
|
|
|
c.logf("magicsock: starting endpoint update (%s)", why)
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-03-24 15:09:30 +00:00
|
|
|
endpoints, reasons, err := c.determineEndpoints(c.connCtx)
|
2020-03-13 15:55:38 +00:00
|
|
|
if err != nil {
|
2020-03-23 21:12:23 +00:00
|
|
|
c.logf("magicsock: endpoint update (%s) failed: %v", why, err)
|
2020-03-13 15:55:38 +00:00
|
|
|
// TODO(crawshaw): are there any conditions under which
|
|
|
|
// we should trigger a retry based on the error here?
|
|
|
|
return
|
|
|
|
}
|
2020-03-04 06:21:56 +00:00
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
if c.setEndpoints(endpoints) {
|
2020-03-24 15:09:30 +00:00
|
|
|
c.logEndpointChange(endpoints, reasons)
|
2020-03-13 15:55:38 +00:00
|
|
|
c.epFunc(endpoints)
|
|
|
|
}
|
|
|
|
}
|
2020-03-04 06:21:56 +00:00
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
// setEndpoints records the new endpoints, reporting whether they're changed.
|
|
|
|
// It takes ownership of the slice.
|
|
|
|
func (c *Conn) setEndpoints(endpoints []string) (changed bool) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
if stringsEqual(endpoints, c.lastEndpoints) {
|
|
|
|
return false
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-03-13 15:55:38 +00:00
|
|
|
c.lastEndpoints = endpoints
|
|
|
|
return true
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
func (c *Conn) updateNetInfo(ctx context.Context) (*netcheck.Report, error) {
|
2020-05-17 16:51:38 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
dm := c.derpMap
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
|
|
|
if DisableSTUNForTesting || dm == nil {
|
2020-03-10 18:35:43 +00:00
|
|
|
return new(netcheck.Report), nil
|
2020-03-06 15:47:54 +00:00
|
|
|
}
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
2020-03-04 06:21:56 +00:00
|
|
|
defer cancel()
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
c.stunReceiveFunc.Store(c.netChecker.ReceiveSTUNPacket)
|
|
|
|
defer c.ignoreSTUNPackets()
|
|
|
|
|
2020-05-17 16:51:38 +00:00
|
|
|
report, err := c.netChecker.GetReport(ctx, dm)
|
2020-03-04 06:21:56 +00:00
|
|
|
if err != nil {
|
2020-03-09 22:20:33 +00:00
|
|
|
return nil, err
|
2020-03-04 06:21:56 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 19:40:51 +00:00
|
|
|
c.noV4.Set(!report.IPv4)
|
|
|
|
c.noV6.Set(!report.IPv6)
|
|
|
|
|
2020-03-04 06:21:56 +00:00
|
|
|
ni := &tailcfg.NetInfo{
|
|
|
|
DERPLatency: map[string]float64{},
|
|
|
|
MappingVariesByDestIP: report.MappingVariesByDestIP,
|
|
|
|
HairPinning: report.HairPinning,
|
|
|
|
}
|
2020-05-17 16:51:38 +00:00
|
|
|
for rid, d := range report.RegionV4Latency {
|
|
|
|
ni.DERPLatency[fmt.Sprintf("%d-v4", rid)] = d.Seconds()
|
|
|
|
}
|
|
|
|
for rid, d := range report.RegionV6Latency {
|
|
|
|
ni.DERPLatency[fmt.Sprintf("%d-v6", rid)] = d.Seconds()
|
2020-03-04 06:21:56 +00:00
|
|
|
}
|
|
|
|
ni.WorkingIPv6.Set(report.IPv6)
|
|
|
|
ni.WorkingUDP.Set(report.UDP)
|
|
|
|
ni.PreferredDERP = report.PreferredDERP
|
|
|
|
|
|
|
|
if ni.PreferredDERP == 0 {
|
|
|
|
// Perhaps UDP is blocked. Pick a deterministic but arbitrary
|
|
|
|
// one.
|
|
|
|
ni.PreferredDERP = c.pickDERPFallback()
|
|
|
|
}
|
2020-03-04 20:21:40 +00:00
|
|
|
if !c.setNearestDERP(ni.PreferredDERP) {
|
|
|
|
ni.PreferredDERP = 0
|
|
|
|
}
|
2020-03-04 06:21:56 +00:00
|
|
|
|
|
|
|
// TODO: set link type
|
|
|
|
|
|
|
|
c.callNetInfoCallback(ni)
|
2020-03-09 22:20:33 +00:00
|
|
|
return report, nil
|
2020-03-04 06:21:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var processStartUnixNano = time.Now().UnixNano()
|
|
|
|
|
|
|
|
// pickDERPFallback returns a non-zero but deterministic DERP node to
|
|
|
|
// connect to. This is only used if netcheck couldn't find the
|
|
|
|
// nearest one (for instance, if UDP is blocked and thus STUN latency
|
|
|
|
// checks aren't working).
|
2020-03-13 15:55:38 +00:00
|
|
|
//
|
|
|
|
// c.mu must NOT be held.
|
2020-03-04 06:21:56 +00:00
|
|
|
func (c *Conn) pickDERPFallback() int {
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2020-03-04 06:21:56 +00:00
|
|
|
|
2020-05-17 16:51:38 +00:00
|
|
|
if !c.wantDerpLocked() {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
ids := c.derpMap.RegionIDs()
|
2020-03-09 22:20:33 +00:00
|
|
|
if len(ids) == 0 {
|
2020-05-17 16:51:38 +00:00
|
|
|
// No DERP regions in non-nil map.
|
2020-03-04 06:21:56 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2020-03-25 18:14:29 +00:00
|
|
|
// See where our peers are.
|
2020-03-25 05:24:59 +00:00
|
|
|
var (
|
|
|
|
peersOnDerp = map[int]int{}
|
|
|
|
best int
|
|
|
|
bestCount int
|
|
|
|
)
|
|
|
|
for _, as := range c.addrsByKey {
|
|
|
|
if id := as.derpID(); id != 0 {
|
|
|
|
peersOnDerp[id]++
|
|
|
|
if v := peersOnDerp[id]; v > bestCount {
|
|
|
|
bestCount = v
|
|
|
|
best = id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-25 18:14:29 +00:00
|
|
|
|
|
|
|
// 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.
|
2020-03-25 05:24:59 +00:00
|
|
|
if best != 0 {
|
|
|
|
return best
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise just pick something randomly.
|
2020-03-04 06:21:56 +00:00
|
|
|
h := fnv.New64()
|
|
|
|
h.Write([]byte(fmt.Sprintf("%p/%d", c, processStartUnixNano))) // arbitrary
|
2020-03-09 22:20:33 +00:00
|
|
|
return ids[rand.New(rand.NewSource(int64(h.Sum64()))).Intn(len(ids))]
|
2020-03-04 06:21:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// callNetInfoCallback calls the NetInfo callback (if previously
|
|
|
|
// registered with SetNetInfoCallback) if ni has substantially changed
|
|
|
|
// since the last state.
|
|
|
|
//
|
|
|
|
// callNetInfoCallback takes ownership of ni.
|
2020-03-13 15:55:38 +00:00
|
|
|
//
|
|
|
|
// c.mu must NOT be held.
|
2020-03-04 06:21:56 +00:00
|
|
|
func (c *Conn) callNetInfoCallback(ni *tailcfg.NetInfo) {
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2020-03-04 06:21:56 +00:00
|
|
|
if ni.BasicallyEqual(c.netInfoLast) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.netInfoLast = ni
|
|
|
|
if c.netInfoFunc != nil {
|
2020-03-24 05:11:49 +00:00
|
|
|
c.logf("magicsock: netInfo update: %+v", ni)
|
2020-03-04 06:21:56 +00:00
|
|
|
go c.netInfoFunc(ni)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Conn) SetNetInfoCallback(fn func(*tailcfg.NetInfo)) {
|
|
|
|
if fn == nil {
|
|
|
|
panic("nil NetInfoCallback")
|
|
|
|
}
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
2020-03-04 06:21:56 +00:00
|
|
|
last := c.netInfoLast
|
|
|
|
c.netInfoFunc = fn
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Unlock()
|
2020-03-04 06:21:56 +00:00
|
|
|
|
|
|
|
if last != nil {
|
|
|
|
fn(last)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-19 19:06:49 +00:00
|
|
|
// SetDiscoPrivateKey sets the discovery key.
|
|
|
|
func (c *Conn) SetDiscoPrivateKey(k key.Private) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2020-06-29 21:26:25 +00:00
|
|
|
if !c.discoPrivate.IsZero() && c.discoPrivate != k {
|
|
|
|
// TODO: support changing a key at runtime; need to
|
|
|
|
// clear a bunch of maps at least
|
|
|
|
panic("unsupported")
|
|
|
|
}
|
2020-06-19 19:06:49 +00:00
|
|
|
c.discoPrivate = k
|
2020-07-01 19:56:17 +00:00
|
|
|
c.discoPublic = tailcfg.DiscoKey(k.Public())
|
|
|
|
c.logf("magicsock: disco key set; public: %x", c.discoPublic)
|
2020-06-19 19:06:49 +00:00
|
|
|
}
|
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
// c.mu must NOT be held.
|
2020-03-04 20:21:40 +00:00
|
|
|
func (c *Conn) setNearestDERP(derpNum int) (wantDERP bool) {
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2020-05-17 16:51:38 +00:00
|
|
|
if !c.wantDerpLocked() {
|
2020-03-04 20:21:40 +00:00
|
|
|
c.myDerp = 0
|
|
|
|
return false
|
|
|
|
}
|
2020-03-05 23:00:56 +00:00
|
|
|
if derpNum == c.myDerp {
|
|
|
|
// No change.
|
|
|
|
return true
|
|
|
|
}
|
2020-03-24 15:09:30 +00:00
|
|
|
c.myDerp = derpNum
|
|
|
|
|
|
|
|
if c.privateKey.IsZero() {
|
|
|
|
// No private key yet, so DERP connections won't come up anyway.
|
|
|
|
// Return early rather than ultimately log a couple lines of noise.
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-03-19 06:32:31 +00:00
|
|
|
// On change, notify all currently connected DERP servers and
|
|
|
|
// start connecting to our home DERP if we are not already.
|
2020-05-28 07:42:03 +00:00
|
|
|
dr := c.derpMap.Regions[derpNum]
|
|
|
|
if dr == nil {
|
|
|
|
c.logf("[unexpected] magicsock: derpMap.Regions[%v] is nil", derpNum)
|
|
|
|
} else {
|
|
|
|
c.logf("magicsock: home is now derp-%v (%v)", derpNum, c.derpMap.Regions[derpNum].RegionCode)
|
|
|
|
}
|
2020-03-05 23:00:56 +00:00
|
|
|
for i, ad := range c.activeDerp {
|
|
|
|
go ad.c.NotePreferred(i == c.myDerp)
|
|
|
|
}
|
2020-04-09 21:21:36 +00:00
|
|
|
c.goDerpConnect(derpNum)
|
2020-03-04 20:21:40 +00:00
|
|
|
return true
|
2020-03-04 01:46:03 +00:00
|
|
|
}
|
|
|
|
|
2020-04-09 21:21:36 +00:00
|
|
|
// goDerpConnect starts a goroutine to start connecting to the given
|
|
|
|
// DERP node.
|
|
|
|
//
|
|
|
|
// c.mu may be held, but does not need to be.
|
|
|
|
func (c *Conn) goDerpConnect(node int) {
|
|
|
|
if node == 0 {
|
|
|
|
return
|
|
|
|
}
|
2020-06-30 19:22:42 +00:00
|
|
|
go c.derpWriteChanOfAddr(netaddr.IPPort{IP: derpMagicIPAddr, Port: uint16(node)}, key.Public{})
|
2020-04-09 21:21:36 +00:00
|
|
|
}
|
|
|
|
|
2020-02-18 16:57:11 +00:00
|
|
|
// determineEndpoints returns the machine's endpoint addresses. It
|
2020-03-13 15:55:38 +00:00
|
|
|
// does a STUN lookup (via netcheck) to determine its public address.
|
|
|
|
//
|
|
|
|
// c.mu must NOT be held.
|
2020-03-24 15:09:30 +00:00
|
|
|
func (c *Conn) determineEndpoints(ctx context.Context) (ipPorts []string, reasons map[string]string, err error) {
|
2020-03-13 15:55:38 +00:00
|
|
|
nr, err := c.updateNetInfo(ctx)
|
|
|
|
if err != nil {
|
|
|
|
c.logf("magicsock.Conn.determineEndpoints: updateNetInfo: %v", err)
|
2020-03-24 15:09:30 +00:00
|
|
|
return nil, nil, err
|
2020-03-13 15:55:38 +00:00
|
|
|
}
|
|
|
|
|
2020-03-24 15:09:30 +00:00
|
|
|
already := make(map[string]string) // endpoint -> how it was found
|
|
|
|
var eps []string // unique endpoints
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
addAddr := func(s, reason string) {
|
2020-03-24 15:09:30 +00:00
|
|
|
if _, ok := already[s]; !ok {
|
|
|
|
already[s] = reason
|
2020-02-05 22:16:58 +00:00
|
|
|
eps = append(eps, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
if nr.GlobalV4 != "" {
|
|
|
|
addAddr(nr.GlobalV4, "stun")
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-03-19 16:39:00 +00:00
|
|
|
if nr.GlobalV6 != "" {
|
2020-03-09 22:20:33 +00:00
|
|
|
addAddr(nr.GlobalV6, "stun")
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-18 16:57:11 +00:00
|
|
|
c.ignoreSTUNPackets()
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-03-19 15:49:30 +00:00
|
|
|
if localAddr := c.pconn4.LocalAddr(); localAddr.IP.IsUnspecified() {
|
2020-03-02 18:38:44 +00:00
|
|
|
ips, loopback, err := interfaces.LocalAddresses()
|
2020-02-05 22:16:58 +00:00
|
|
|
if err != nil {
|
2020-03-24 15:09:30 +00:00
|
|
|
return nil, nil, err
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-03-02 18:38:44 +00:00
|
|
|
reason := "localAddresses"
|
2020-05-28 21:16:23 +00:00
|
|
|
if len(ips) == 0 && len(eps) == 0 {
|
2020-02-05 22:16:58 +00:00
|
|
|
// Only include loopback addresses if we have no
|
2020-05-28 21:16:23 +00:00
|
|
|
// interfaces at all to use as endpoints and don't
|
|
|
|
// have a public IPv4 or IPv6 address. This allows
|
2020-02-05 22:16:58 +00:00
|
|
|
// for localhost testing when you're on a plane and
|
|
|
|
// offline, for example.
|
2020-03-02 18:38:44 +00:00
|
|
|
ips = loopback
|
|
|
|
reason = "loopback"
|
|
|
|
}
|
|
|
|
for _, ipStr := range ips {
|
|
|
|
addAddr(net.JoinHostPort(ipStr, fmt.Sprint(localAddr.Port)), reason)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Our local endpoint is bound to a particular address.
|
|
|
|
// Do not offer addresses on other local interfaces.
|
|
|
|
addAddr(localAddr.String(), "socket")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: the endpoints are intentionally returned in priority order,
|
|
|
|
// from "farthest but most reliable" to "closest but least
|
|
|
|
// reliable." Addresses returned from STUN should be globally
|
|
|
|
// addressable, but might go farther on the network than necessary.
|
|
|
|
// Local interface addresses might have lower latency, but not be
|
|
|
|
// globally addressable.
|
|
|
|
//
|
|
|
|
// 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).
|
2020-03-24 15:09:30 +00:00
|
|
|
return eps, already, nil
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func stringsEqual(x, y []string) bool {
|
|
|
|
if len(x) != len(y) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := range x {
|
|
|
|
if x[i] != y[i] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Conn) LocalPort() uint16 {
|
2020-03-19 15:49:30 +00:00
|
|
|
laddr := c.pconn4.LocalAddr()
|
2020-02-05 22:16:58 +00:00
|
|
|
return uint16(laddr.Port)
|
|
|
|
}
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
func shouldSprayPacket(b []byte) bool {
|
|
|
|
if len(b) < 4 {
|
|
|
|
return false
|
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
msgType := binary.LittleEndian.Uint32(b[:4])
|
|
|
|
switch msgType {
|
2020-02-18 21:32:04 +00:00
|
|
|
case device.MessageInitiationType,
|
|
|
|
device.MessageResponseType,
|
|
|
|
device.MessageCookieReplyType: // TODO: necessary?
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-07-01 19:56:17 +00:00
|
|
|
var logPacketDests, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_LOG_PACKET_DESTS"))
|
|
|
|
|
|
|
|
var logDisco, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_DISCO"))
|
2020-02-21 22:01:51 +00:00
|
|
|
|
2020-04-10 05:25:31 +00:00
|
|
|
const sprayPeriod = 3 * time.Second
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
// appendDests appends to dsts the destinations that b should be
|
2020-06-30 19:22:42 +00:00
|
|
|
// written to in order to reach as. Some of the returned IPPorts may
|
2020-02-18 21:32:04 +00:00
|
|
|
// be fake addrs representing DERP servers.
|
|
|
|
//
|
|
|
|
// It also returns as's current roamAddr, if any.
|
2020-06-30 19:22:42 +00:00
|
|
|
func (as *AddrSet) appendDests(dsts []netaddr.IPPort, b []byte) (_ []netaddr.IPPort, roamAddr netaddr.IPPort) {
|
2020-02-22 03:20:31 +00:00
|
|
|
spray := shouldSprayPacket(b) // true for handshakes
|
2020-03-09 16:13:28 +00:00
|
|
|
now := as.timeNow()
|
2020-02-18 21:32:04 +00:00
|
|
|
|
|
|
|
as.mu.Lock()
|
|
|
|
defer as.mu.Unlock()
|
|
|
|
|
2020-06-30 19:22:42 +00:00
|
|
|
// Some internal invariant checks.
|
|
|
|
if len(as.addrs) != len(as.ipPorts) {
|
|
|
|
panic(fmt.Sprintf("lena %d != leni %d", len(as.addrs), len(as.ipPorts)))
|
|
|
|
}
|
|
|
|
if n1, n2 := as.roamAddr != nil, as.roamAddrStd != nil; n1 != n2 {
|
|
|
|
panic(fmt.Sprintf("roamnil %v != roamstdnil %v", n1, n2))
|
|
|
|
}
|
|
|
|
|
2020-02-22 03:20:31 +00:00
|
|
|
// Spray logic.
|
|
|
|
//
|
|
|
|
// After exchanging a handshake with a peer, we send some outbound
|
|
|
|
// packets to every endpoint of that peer. These packets are spaced out
|
|
|
|
// over several seconds to make sure that our peer has an opportunity to
|
|
|
|
// send its own spray packet to us before we are done spraying.
|
|
|
|
//
|
|
|
|
// Multiple packets are necessary because we have to both establish the
|
|
|
|
// NAT mappings between two peers *and use* the mappings to switch away
|
|
|
|
// from DERP to a higher-priority UDP endpoint.
|
|
|
|
const sprayFreq = 250 * time.Millisecond
|
|
|
|
if spray {
|
|
|
|
as.lastSpray = now
|
|
|
|
as.stopSpray = now.Add(sprayPeriod)
|
2020-02-27 23:06:19 +00:00
|
|
|
|
|
|
|
// Reset our favorite route on new handshakes so we
|
|
|
|
// can downgrade to a worse path if our better path
|
|
|
|
// goes away. (https://github.com/tailscale/tailscale/issues/92)
|
|
|
|
as.curAddr = -1
|
2020-02-22 03:20:31 +00:00
|
|
|
} else if now.Before(as.stopSpray) {
|
|
|
|
// We are in the spray window. If it has been sprayFreq since we
|
|
|
|
// last sprayed a packet, spray this packet.
|
|
|
|
if now.Sub(as.lastSpray) >= sprayFreq {
|
|
|
|
spray = true
|
|
|
|
as.lastSpray = now
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pick our destination address(es).
|
wgengine/magicsock: fix destination selection logic to work with DERP.
The effect is subtle: when we're not spraying packets, and have not yet
figured out a curAddr, and we're not spraying, we end up sending to
whatever the first IP is in the iteration order. In English, that
means "when we have no idea where to send packets, and we've given
up on sending to everyone, just send to the first addr we see in
the list."
This is, in general, what we want, because the addrs are in sorted
preference order, low to high, and DERP is the least preferred
destination. So, when we have no idea where to send, send to DERP,
right?
... Except for very historical reasons, appendDests iterated through
addresses in _reverse_ order, most preferred to least preferred.
crawshaw@ believes this was part of the earliest handshaking
algorithm magicsock had, where it slowly iterated through possible
destinations and poked handshakes to them one at a time.
Anyway, because of this historical reverse iteration, in the case
described above of "we have no idea where to send", the code would
end up sending to the _most_ preferred candidate address, rather
than the _least_ preferred. So when in doubt, we'd end up firing
packets into the blackhole of some LAN address that doesn't work,
and connectivity would not work.
This case only comes up if all your non-DERP connectivity options
have failed, so we more or less failed to detect it because we
didn't have a pathological test box deployed. Worse, codependent
bug 2839854994f204a9e95e4d8d410490bb4f25e1fe made DERP accidentally
work sometimes anyway by incorrectly exploiting roamAddr behavior,
albeit at the cost of making DERP traffic symmetric. In fixing
DERP to once again be asymmetric, we effectively removed the
bandaid that was concealing this bug.
Signed-Off-By: David Anderson <danderson@tailscale.com>
2020-03-06 02:57:45 +00:00
|
|
|
switch {
|
|
|
|
case spray:
|
|
|
|
// This packet is being sprayed to all addresses.
|
2020-06-30 19:22:42 +00:00
|
|
|
for i := range as.ipPorts {
|
|
|
|
dsts = append(dsts, as.ipPorts[i])
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
wgengine/magicsock: fix destination selection logic to work with DERP.
The effect is subtle: when we're not spraying packets, and have not yet
figured out a curAddr, and we're not spraying, we end up sending to
whatever the first IP is in the iteration order. In English, that
means "when we have no idea where to send packets, and we've given
up on sending to everyone, just send to the first addr we see in
the list."
This is, in general, what we want, because the addrs are in sorted
preference order, low to high, and DERP is the least preferred
destination. So, when we have no idea where to send, send to DERP,
right?
... Except for very historical reasons, appendDests iterated through
addresses in _reverse_ order, most preferred to least preferred.
crawshaw@ believes this was part of the earliest handshaking
algorithm magicsock had, where it slowly iterated through possible
destinations and poked handshakes to them one at a time.
Anyway, because of this historical reverse iteration, in the case
described above of "we have no idea where to send", the code would
end up sending to the _most_ preferred candidate address, rather
than the _least_ preferred. So when in doubt, we'd end up firing
packets into the blackhole of some LAN address that doesn't work,
and connectivity would not work.
This case only comes up if all your non-DERP connectivity options
have failed, so we more or less failed to detect it because we
didn't have a pathological test box deployed. Worse, codependent
bug 2839854994f204a9e95e4d8d410490bb4f25e1fe made DERP accidentally
work sometimes anyway by incorrectly exploiting roamAddr behavior,
albeit at the cost of making DERP traffic symmetric. In fixing
DERP to once again be asymmetric, we effectively removed the
bandaid that was concealing this bug.
Signed-Off-By: David Anderson <danderson@tailscale.com>
2020-03-06 02:57:45 +00:00
|
|
|
if as.roamAddr != nil {
|
2020-06-30 19:22:42 +00:00
|
|
|
dsts = append(dsts, *as.roamAddr)
|
2020-02-18 21:32:04 +00:00
|
|
|
}
|
wgengine/magicsock: fix destination selection logic to work with DERP.
The effect is subtle: when we're not spraying packets, and have not yet
figured out a curAddr, and we're not spraying, we end up sending to
whatever the first IP is in the iteration order. In English, that
means "when we have no idea where to send packets, and we've given
up on sending to everyone, just send to the first addr we see in
the list."
This is, in general, what we want, because the addrs are in sorted
preference order, low to high, and DERP is the least preferred
destination. So, when we have no idea where to send, send to DERP,
right?
... Except for very historical reasons, appendDests iterated through
addresses in _reverse_ order, most preferred to least preferred.
crawshaw@ believes this was part of the earliest handshaking
algorithm magicsock had, where it slowly iterated through possible
destinations and poked handshakes to them one at a time.
Anyway, because of this historical reverse iteration, in the case
described above of "we have no idea where to send", the code would
end up sending to the _most_ preferred candidate address, rather
than the _least_ preferred. So when in doubt, we'd end up firing
packets into the blackhole of some LAN address that doesn't work,
and connectivity would not work.
This case only comes up if all your non-DERP connectivity options
have failed, so we more or less failed to detect it because we
didn't have a pathological test box deployed. Worse, codependent
bug 2839854994f204a9e95e4d8d410490bb4f25e1fe made DERP accidentally
work sometimes anyway by incorrectly exploiting roamAddr behavior,
albeit at the cost of making DERP traffic symmetric. In fixing
DERP to once again be asymmetric, we effectively removed the
bandaid that was concealing this bug.
Signed-Off-By: David Anderson <danderson@tailscale.com>
2020-03-06 02:57:45 +00:00
|
|
|
case as.roamAddr != nil:
|
|
|
|
// We have a roaming address, prefer it over other addrs.
|
|
|
|
// TODO(danderson): this is not correct, there's no reason
|
|
|
|
// roamAddr should be special like this.
|
2020-06-30 19:22:42 +00:00
|
|
|
dsts = append(dsts, *as.roamAddr)
|
wgengine/magicsock: fix destination selection logic to work with DERP.
The effect is subtle: when we're not spraying packets, and have not yet
figured out a curAddr, and we're not spraying, we end up sending to
whatever the first IP is in the iteration order. In English, that
means "when we have no idea where to send packets, and we've given
up on sending to everyone, just send to the first addr we see in
the list."
This is, in general, what we want, because the addrs are in sorted
preference order, low to high, and DERP is the least preferred
destination. So, when we have no idea where to send, send to DERP,
right?
... Except for very historical reasons, appendDests iterated through
addresses in _reverse_ order, most preferred to least preferred.
crawshaw@ believes this was part of the earliest handshaking
algorithm magicsock had, where it slowly iterated through possible
destinations and poked handshakes to them one at a time.
Anyway, because of this historical reverse iteration, in the case
described above of "we have no idea where to send", the code would
end up sending to the _most_ preferred candidate address, rather
than the _least_ preferred. So when in doubt, we'd end up firing
packets into the blackhole of some LAN address that doesn't work,
and connectivity would not work.
This case only comes up if all your non-DERP connectivity options
have failed, so we more or less failed to detect it because we
didn't have a pathological test box deployed. Worse, codependent
bug 2839854994f204a9e95e4d8d410490bb4f25e1fe made DERP accidentally
work sometimes anyway by incorrectly exploiting roamAddr behavior,
albeit at the cost of making DERP traffic symmetric. In fixing
DERP to once again be asymmetric, we effectively removed the
bandaid that was concealing this bug.
Signed-Off-By: David Anderson <danderson@tailscale.com>
2020-03-06 02:57:45 +00:00
|
|
|
case as.curAddr != -1:
|
|
|
|
if as.curAddr >= len(as.addrs) {
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
as.Logf("[unexpected] magicsock bug: as.curAddr >= len(as.addrs): %d >= %d", as.curAddr, len(as.addrs))
|
2020-02-18 21:32:04 +00:00
|
|
|
break
|
|
|
|
}
|
wgengine/magicsock: fix destination selection logic to work with DERP.
The effect is subtle: when we're not spraying packets, and have not yet
figured out a curAddr, and we're not spraying, we end up sending to
whatever the first IP is in the iteration order. In English, that
means "when we have no idea where to send packets, and we've given
up on sending to everyone, just send to the first addr we see in
the list."
This is, in general, what we want, because the addrs are in sorted
preference order, low to high, and DERP is the least preferred
destination. So, when we have no idea where to send, send to DERP,
right?
... Except for very historical reasons, appendDests iterated through
addresses in _reverse_ order, most preferred to least preferred.
crawshaw@ believes this was part of the earliest handshaking
algorithm magicsock had, where it slowly iterated through possible
destinations and poked handshakes to them one at a time.
Anyway, because of this historical reverse iteration, in the case
described above of "we have no idea where to send", the code would
end up sending to the _most_ preferred candidate address, rather
than the _least_ preferred. So when in doubt, we'd end up firing
packets into the blackhole of some LAN address that doesn't work,
and connectivity would not work.
This case only comes up if all your non-DERP connectivity options
have failed, so we more or less failed to detect it because we
didn't have a pathological test box deployed. Worse, codependent
bug 2839854994f204a9e95e4d8d410490bb4f25e1fe made DERP accidentally
work sometimes anyway by incorrectly exploiting roamAddr behavior,
albeit at the cost of making DERP traffic symmetric. In fixing
DERP to once again be asymmetric, we effectively removed the
bandaid that was concealing this bug.
Signed-Off-By: David Anderson <danderson@tailscale.com>
2020-03-06 02:57:45 +00:00
|
|
|
// No roaming addr, but we've seen packets from a known peer
|
|
|
|
// addr, so keep using that one.
|
2020-06-30 19:22:42 +00:00
|
|
|
dsts = append(dsts, as.ipPorts[as.curAddr])
|
wgengine/magicsock: fix destination selection logic to work with DERP.
The effect is subtle: when we're not spraying packets, and have not yet
figured out a curAddr, and we're not spraying, we end up sending to
whatever the first IP is in the iteration order. In English, that
means "when we have no idea where to send packets, and we've given
up on sending to everyone, just send to the first addr we see in
the list."
This is, in general, what we want, because the addrs are in sorted
preference order, low to high, and DERP is the least preferred
destination. So, when we have no idea where to send, send to DERP,
right?
... Except for very historical reasons, appendDests iterated through
addresses in _reverse_ order, most preferred to least preferred.
crawshaw@ believes this was part of the earliest handshaking
algorithm magicsock had, where it slowly iterated through possible
destinations and poked handshakes to them one at a time.
Anyway, because of this historical reverse iteration, in the case
described above of "we have no idea where to send", the code would
end up sending to the _most_ preferred candidate address, rather
than the _least_ preferred. So when in doubt, we'd end up firing
packets into the blackhole of some LAN address that doesn't work,
and connectivity would not work.
This case only comes up if all your non-DERP connectivity options
have failed, so we more or less failed to detect it because we
didn't have a pathological test box deployed. Worse, codependent
bug 2839854994f204a9e95e4d8d410490bb4f25e1fe made DERP accidentally
work sometimes anyway by incorrectly exploiting roamAddr behavior,
albeit at the cost of making DERP traffic symmetric. In fixing
DERP to once again be asymmetric, we effectively removed the
bandaid that was concealing this bug.
Signed-Off-By: David Anderson <danderson@tailscale.com>
2020-03-06 02:57:45 +00:00
|
|
|
default:
|
|
|
|
// We know nothing about how to reach this peer, and we're not
|
|
|
|
// spraying. Use the first address in the array, which will
|
|
|
|
// usually be a DERP address that guarantees connectivity.
|
2020-06-30 19:22:42 +00:00
|
|
|
if len(as.ipPorts) > 0 {
|
|
|
|
dsts = append(dsts, as.ipPorts[0])
|
wgengine/magicsock: fix destination selection logic to work with DERP.
The effect is subtle: when we're not spraying packets, and have not yet
figured out a curAddr, and we're not spraying, we end up sending to
whatever the first IP is in the iteration order. In English, that
means "when we have no idea where to send packets, and we've given
up on sending to everyone, just send to the first addr we see in
the list."
This is, in general, what we want, because the addrs are in sorted
preference order, low to high, and DERP is the least preferred
destination. So, when we have no idea where to send, send to DERP,
right?
... Except for very historical reasons, appendDests iterated through
addresses in _reverse_ order, most preferred to least preferred.
crawshaw@ believes this was part of the earliest handshaking
algorithm magicsock had, where it slowly iterated through possible
destinations and poked handshakes to them one at a time.
Anyway, because of this historical reverse iteration, in the case
described above of "we have no idea where to send", the code would
end up sending to the _most_ preferred candidate address, rather
than the _least_ preferred. So when in doubt, we'd end up firing
packets into the blackhole of some LAN address that doesn't work,
and connectivity would not work.
This case only comes up if all your non-DERP connectivity options
have failed, so we more or less failed to detect it because we
didn't have a pathological test box deployed. Worse, codependent
bug 2839854994f204a9e95e4d8d410490bb4f25e1fe made DERP accidentally
work sometimes anyway by incorrectly exploiting roamAddr behavior,
albeit at the cost of making DERP traffic symmetric. In fixing
DERP to once again be asymmetric, we effectively removed the
bandaid that was concealing this bug.
Signed-Off-By: David Anderson <danderson@tailscale.com>
2020-03-06 02:57:45 +00:00
|
|
|
}
|
2020-02-18 21:32:04 +00:00
|
|
|
}
|
wgengine/magicsock: fix destination selection logic to work with DERP.
The effect is subtle: when we're not spraying packets, and have not yet
figured out a curAddr, and we're not spraying, we end up sending to
whatever the first IP is in the iteration order. In English, that
means "when we have no idea where to send packets, and we've given
up on sending to everyone, just send to the first addr we see in
the list."
This is, in general, what we want, because the addrs are in sorted
preference order, low to high, and DERP is the least preferred
destination. So, when we have no idea where to send, send to DERP,
right?
... Except for very historical reasons, appendDests iterated through
addresses in _reverse_ order, most preferred to least preferred.
crawshaw@ believes this was part of the earliest handshaking
algorithm magicsock had, where it slowly iterated through possible
destinations and poked handshakes to them one at a time.
Anyway, because of this historical reverse iteration, in the case
described above of "we have no idea where to send", the code would
end up sending to the _most_ preferred candidate address, rather
than the _least_ preferred. So when in doubt, we'd end up firing
packets into the blackhole of some LAN address that doesn't work,
and connectivity would not work.
This case only comes up if all your non-DERP connectivity options
have failed, so we more or less failed to detect it because we
didn't have a pathological test box deployed. Worse, codependent
bug 2839854994f204a9e95e4d8d410490bb4f25e1fe made DERP accidentally
work sometimes anyway by incorrectly exploiting roamAddr behavior,
albeit at the cost of making DERP traffic symmetric. In fixing
DERP to once again be asymmetric, we effectively removed the
bandaid that was concealing this bug.
Signed-Off-By: David Anderson <danderson@tailscale.com>
2020-03-06 02:57:45 +00:00
|
|
|
|
2020-02-21 22:01:51 +00:00
|
|
|
if logPacketDests {
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
as.Logf("spray=%v; roam=%v; dests=%v", spray, as.roamAddr, dsts)
|
2020-02-21 22:01:51 +00:00
|
|
|
}
|
2020-06-30 19:22:42 +00:00
|
|
|
if as.roamAddr != nil {
|
|
|
|
roamAddr = *as.roamAddr
|
|
|
|
}
|
|
|
|
return dsts, roamAddr
|
2020-02-18 21:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var errNoDestinations = errors.New("magicsock: no destinations")
|
|
|
|
|
2020-02-24 12:27:48 +00:00
|
|
|
func (c *Conn) Send(b []byte, ep conn.Endpoint) error {
|
2020-02-24 16:47:20 +00:00
|
|
|
var as *AddrSet
|
|
|
|
switch v := ep.(type) {
|
|
|
|
default:
|
2020-03-05 16:18:12 +00:00
|
|
|
panic(fmt.Sprintf("[unexpected] Endpoint type %T", v))
|
2020-06-28 18:53:37 +00:00
|
|
|
case *discoEndpoint:
|
|
|
|
return v.send(b)
|
2020-02-24 16:47:20 +00:00
|
|
|
case *singleEndpoint:
|
2020-02-29 20:48:50 +00:00
|
|
|
addr := (*net.UDPAddr)(v)
|
|
|
|
if addr.IP.Equal(derpMagicIP) {
|
2020-03-24 05:11:49 +00:00
|
|
|
c.logf("magicsock: [unexpected] DERP BUG: attempting to send packet to DERP address %v", addr)
|
2020-02-29 20:48:50 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-07-01 21:39:21 +00:00
|
|
|
_, err := c.sendUDPStd(addr, b)
|
|
|
|
return err
|
2020-02-24 16:47:20 +00:00
|
|
|
case *AddrSet:
|
|
|
|
as = v
|
|
|
|
}
|
2020-02-18 21:32:04 +00:00
|
|
|
|
2020-06-30 19:22:42 +00:00
|
|
|
var addrBuf [8]netaddr.IPPort
|
2020-03-09 16:13:28 +00:00
|
|
|
dsts, roamAddr := as.appendDests(addrBuf[:0], b)
|
2020-02-18 21:32:04 +00:00
|
|
|
|
|
|
|
if len(dsts) == 0 {
|
|
|
|
return errNoDestinations
|
|
|
|
}
|
|
|
|
|
|
|
|
var success bool
|
|
|
|
var ret error
|
|
|
|
for _, addr := range dsts {
|
2020-07-01 21:39:21 +00:00
|
|
|
sent, err := c.sendAddr(addr, as.publicKey, b)
|
|
|
|
if sent {
|
2020-02-18 21:32:04 +00:00
|
|
|
success = true
|
|
|
|
} else if ret == nil {
|
|
|
|
ret = err
|
|
|
|
}
|
2020-03-02 01:35:10 +00:00
|
|
|
if err != nil && addr != roamAddr && c.sendLogLimit.Allow() {
|
2020-03-09 22:20:33 +00:00
|
|
|
if c.connCtx.Err() == nil { // don't log if we're closed
|
|
|
|
c.logf("magicsock: Conn.Send(%v): %v", addr, err)
|
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-02-18 21:32:04 +00:00
|
|
|
}
|
|
|
|
if success {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
var errConnClosed = errors.New("Conn closed")
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
var errDropDerpPacket = errors.New("too many DERP packets queued; dropping")
|
|
|
|
|
2020-06-30 19:22:42 +00:00
|
|
|
// sendUDP sends UDP packet b to ipp.
|
2020-07-01 21:39:21 +00:00
|
|
|
// See sendAddr's docs on the return value meanings.
|
|
|
|
func (c *Conn) sendUDP(ipp netaddr.IPPort, b []byte) (sent bool, err error) {
|
2020-07-01 17:17:08 +00:00
|
|
|
ua := ipp.UDPAddr()
|
|
|
|
defer netaddr.PutUDPAddr(ua)
|
|
|
|
return c.sendUDPStd(ua, b)
|
2020-06-30 19:22:42 +00:00
|
|
|
}
|
|
|
|
|
2020-07-01 21:39:21 +00:00
|
|
|
// sendUDP sends UDP packet b to addr.
|
|
|
|
// See sendAddr's docs on the return value meanings.
|
|
|
|
func (c *Conn) sendUDPStd(addr *net.UDPAddr, b []byte) (sent bool, err error) {
|
2020-06-30 19:22:42 +00:00
|
|
|
switch {
|
|
|
|
case addr.IP.To4() != nil:
|
|
|
|
_, err = c.pconn4.WriteTo(b, addr)
|
2020-05-29 19:40:51 +00:00
|
|
|
if err != nil && c.noV4.Get() {
|
2020-07-01 21:39:21 +00:00
|
|
|
return false, nil
|
2020-05-29 19:40:51 +00:00
|
|
|
}
|
2020-06-30 19:22:42 +00:00
|
|
|
case len(addr.IP) == net.IPv6len:
|
|
|
|
if c.pconn6 == nil {
|
|
|
|
// ignore IPv6 dest if we don't have an IPv6 address.
|
2020-07-01 21:39:21 +00:00
|
|
|
return false, nil
|
2020-06-30 19:22:42 +00:00
|
|
|
}
|
|
|
|
_, err = c.pconn6.WriteTo(b, addr)
|
2020-05-29 19:40:51 +00:00
|
|
|
if err != nil && c.noV6.Get() {
|
2020-07-01 21:39:21 +00:00
|
|
|
return false, nil
|
2020-05-29 19:40:51 +00:00
|
|
|
}
|
2020-06-30 19:22:42 +00:00
|
|
|
default:
|
2020-07-01 21:39:21 +00:00
|
|
|
panic("bogus sendUDPStd addr type")
|
2020-03-20 20:38:21 +00:00
|
|
|
}
|
2020-07-01 21:39:21 +00:00
|
|
|
return err == nil, err
|
2020-03-20 20:38:21 +00:00
|
|
|
}
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
// sendAddr sends packet b to addr, which is either a real UDP address
|
|
|
|
// or a fake UDP address representing a DERP server (see derpmap.go).
|
|
|
|
// The provided public key identifies the recipient.
|
2020-07-01 21:39:21 +00:00
|
|
|
//
|
|
|
|
// The returned err is whether there was an error writing when it
|
|
|
|
// should've worked.
|
|
|
|
// The returned sent is whether a packet went out at all.
|
|
|
|
// An example of when they might be different: sending to an
|
|
|
|
// IPv6 address when the local machine doesn't have IPv6 support
|
|
|
|
// returns (false, nil); it's not an error, but nothing was sent.
|
|
|
|
func (c *Conn) sendAddr(addr netaddr.IPPort, pubKey key.Public, b []byte) (sent bool, err error) {
|
2020-06-30 19:22:42 +00:00
|
|
|
if addr.IP != derpMagicIPAddr {
|
2020-03-20 20:38:21 +00:00
|
|
|
return c.sendUDP(addr, b)
|
2020-03-04 20:21:40 +00:00
|
|
|
}
|
|
|
|
|
2020-03-22 01:24:28 +00:00
|
|
|
ch := c.derpWriteChanOfAddr(addr, pubKey)
|
2020-03-04 20:21:40 +00:00
|
|
|
if ch == nil {
|
2020-07-01 21:39:21 +00:00
|
|
|
return false, nil
|
2020-03-04 20:21:40 +00:00
|
|
|
}
|
2020-03-12 19:05:32 +00:00
|
|
|
|
|
|
|
// TODO(bradfitz): this makes garbage for now; we could use a
|
|
|
|
// buffer pool later. Previously we passed ownership of this
|
|
|
|
// to derpWriteRequest and waited for derphttp.Client.Send to
|
|
|
|
// complete, but that's too slow while holding wireguard-go
|
|
|
|
// internal locks.
|
|
|
|
pkt := make([]byte, len(b))
|
|
|
|
copy(pkt, b)
|
|
|
|
|
2020-03-04 20:21:40 +00:00
|
|
|
select {
|
|
|
|
case <-c.donec():
|
2020-07-01 21:39:21 +00:00
|
|
|
return false, errConnClosed
|
2020-03-12 19:05:32 +00:00
|
|
|
case ch <- derpWriteRequest{addr, pubKey, pkt}:
|
2020-07-01 21:39:21 +00:00
|
|
|
return true, nil
|
2020-03-04 20:21:40 +00:00
|
|
|
default:
|
|
|
|
// Too many writes queued. Drop packet.
|
2020-07-01 21:39:21 +00:00
|
|
|
return false, errDropDerpPacket
|
2020-02-18 21:32:04 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
// bufferedDerpWritesBeforeDrop is how many packets writes can be
|
|
|
|
// queued up the DERP client to write on the wire before we start
|
|
|
|
// dropping.
|
|
|
|
//
|
|
|
|
// TODO: this is currently arbitrary. Figure out something better?
|
2020-03-12 18:16:54 +00:00
|
|
|
const bufferedDerpWritesBeforeDrop = 32
|
2020-02-18 21:32:04 +00:00
|
|
|
|
2020-03-22 01:24:28 +00:00
|
|
|
// debugUseDerpRoute temporarily (2020-03-22) controls whether DERP
|
|
|
|
// reverse routing is enabled (Issue 150). It will become always true
|
|
|
|
// later.
|
|
|
|
var debugUseDerpRoute, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_ENABLE_DERP_ROUTE"))
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
// derpWriteChanOfAddr returns a DERP client for fake UDP addresses that
|
|
|
|
// represent DERP servers, creating them as necessary. For real UDP
|
|
|
|
// addresses, it returns nil.
|
2020-03-22 01:24:28 +00:00
|
|
|
//
|
|
|
|
// If peer is non-zero, it can be used to find an active reverse
|
|
|
|
// path, without using addr.
|
2020-06-30 19:22:42 +00:00
|
|
|
func (c *Conn) derpWriteChanOfAddr(addr netaddr.IPPort, peer key.Public) chan<- derpWriteRequest {
|
|
|
|
if addr.IP != derpMagicIPAddr {
|
2020-02-18 21:32:04 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-06-30 19:22:42 +00:00
|
|
|
regionID := int(addr.Port)
|
2020-03-22 21:08:59 +00:00
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2020-05-17 16:51:38 +00:00
|
|
|
if !c.wantDerpLocked() || c.closed {
|
2020-03-04 20:21:40 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-02-28 19:13:28 +00:00
|
|
|
if c.privateKey.IsZero() {
|
2020-03-24 05:11:49 +00:00
|
|
|
c.logf("magicsock: DERP lookup of %v with no private key; ignoring", addr)
|
2020-02-28 19:13:28 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-03-09 22:20:33 +00:00
|
|
|
|
2020-03-22 01:24:28 +00:00
|
|
|
// See if we have a connection open to that DERP node ID
|
|
|
|
// first. If so, might as well use it. (It's a little
|
|
|
|
// arbitrary whether we use this one vs. the reverse route
|
|
|
|
// below when we have both.)
|
2020-05-17 16:51:38 +00:00
|
|
|
ad, ok := c.activeDerp[regionID]
|
2020-03-22 21:08:59 +00:00
|
|
|
if ok {
|
|
|
|
*ad.lastWrite = time.Now()
|
2020-05-17 16:51:38 +00:00
|
|
|
c.setPeerLastDerpLocked(peer, regionID, regionID)
|
2020-03-22 21:08:59 +00:00
|
|
|
return ad.writeCh
|
|
|
|
}
|
2020-03-12 18:16:54 +00:00
|
|
|
|
2020-03-22 01:24:28 +00:00
|
|
|
// If we don't have an open connection to the peer's home DERP
|
|
|
|
// node, see if we have an open connection to a DERP node
|
|
|
|
// where we'd heard from that peer already. For instance,
|
|
|
|
// perhaps peer's home is Frankfurt, but they dialed our home DERP
|
|
|
|
// node in SF to reach us, so we can reply to them using our
|
|
|
|
// SF connection rather than dialing Frankfurt. (Issue 150)
|
|
|
|
if !peer.IsZero() && debugUseDerpRoute {
|
|
|
|
if r, ok := c.derpRoute[peer]; ok {
|
|
|
|
if ad, ok := c.activeDerp[r.derpID]; ok && ad.c == r.dc {
|
2020-05-17 16:51:38 +00:00
|
|
|
c.setPeerLastDerpLocked(peer, r.derpID, regionID)
|
2020-03-22 01:24:28 +00:00
|
|
|
*ad.lastWrite = time.Now()
|
|
|
|
return ad.writeCh
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-24 15:09:30 +00:00
|
|
|
why := "home-keep-alive"
|
|
|
|
if !peer.IsZero() {
|
|
|
|
why = peerShort(peer)
|
|
|
|
}
|
2020-05-17 16:51:38 +00:00
|
|
|
c.logf("magicsock: adding connection to derp-%v for %v", regionID, why)
|
2020-03-23 21:12:23 +00:00
|
|
|
|
2020-05-14 00:54:27 +00:00
|
|
|
firstDerp := false
|
2020-03-22 21:08:59 +00:00
|
|
|
if c.activeDerp == nil {
|
2020-05-14 00:54:27 +00:00
|
|
|
firstDerp = true
|
2020-03-22 21:08:59 +00:00
|
|
|
c.activeDerp = make(map[int]activeDerp)
|
|
|
|
c.prevDerp = make(map[int]*syncs.WaitGroupChan)
|
|
|
|
}
|
2020-05-17 16:51:38 +00:00
|
|
|
if c.derpMap == nil || c.derpMap.Regions[regionID] == nil {
|
2020-03-22 21:08:59 +00:00
|
|
|
return nil
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-03-22 21:08:59 +00:00
|
|
|
|
|
|
|
// Note that derphttp.NewClient does not dial the server
|
|
|
|
// so it is safe to do under the mu lock.
|
2020-05-17 16:51:38 +00:00
|
|
|
dc := derphttp.NewRegionClient(c.privateKey, c.logf, func() *tailcfg.DERPRegion {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
if c.derpMap == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return c.derpMap.Regions[regionID]
|
|
|
|
})
|
2020-03-22 01:24:28 +00:00
|
|
|
|
2020-05-17 16:51:38 +00:00
|
|
|
dc.NotePreferred(c.myDerp == regionID)
|
2020-03-22 21:08:59 +00:00
|
|
|
dc.DNSCache = dnscache.Get()
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(c.connCtx)
|
|
|
|
ch := make(chan derpWriteRequest, bufferedDerpWritesBeforeDrop)
|
|
|
|
|
|
|
|
ad.c = dc
|
|
|
|
ad.writeCh = ch
|
|
|
|
ad.cancel = cancel
|
|
|
|
ad.lastWrite = new(time.Time)
|
2020-03-23 21:12:23 +00:00
|
|
|
*ad.lastWrite = time.Now()
|
|
|
|
ad.createTime = time.Now()
|
2020-05-17 16:51:38 +00:00
|
|
|
c.activeDerp[regionID] = ad
|
2020-03-23 21:12:23 +00:00
|
|
|
c.logActiveDerpLocked()
|
2020-05-17 16:51:38 +00:00
|
|
|
c.setPeerLastDerpLocked(peer, regionID, regionID)
|
2020-03-22 21:08:59 +00:00
|
|
|
|
|
|
|
// Build a startGate for the derp reader+writer
|
|
|
|
// goroutines, so they don't start running until any
|
|
|
|
// previous generation is closed.
|
|
|
|
startGate := syncs.ClosedChan()
|
2020-05-17 16:51:38 +00:00
|
|
|
if prev := c.prevDerp[regionID]; prev != nil {
|
2020-03-22 21:08:59 +00:00
|
|
|
startGate = prev.DoneChan()
|
|
|
|
}
|
|
|
|
// And register a WaitGroup(Chan) for this generation.
|
|
|
|
wg := syncs.NewWaitGroupChan()
|
|
|
|
wg.Add(2)
|
2020-05-17 16:51:38 +00:00
|
|
|
c.prevDerp[regionID] = wg
|
2020-03-22 21:08:59 +00:00
|
|
|
|
2020-05-14 17:01:48 +00:00
|
|
|
if firstDerp {
|
|
|
|
startGate = c.derpStarted
|
|
|
|
go func() {
|
|
|
|
dc.Connect(ctx)
|
2020-05-14 00:54:27 +00:00
|
|
|
close(c.derpStarted)
|
2020-05-14 17:01:48 +00:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
go c.runDerpReader(ctx, addr, dc, wg, startGate)
|
2020-06-30 19:22:42 +00:00
|
|
|
go c.runDerpWriter(ctx, dc, ch, wg, startGate)
|
2020-03-22 21:08:59 +00:00
|
|
|
|
2020-03-05 16:54:08 +00:00
|
|
|
return ad.writeCh
|
2020-02-18 21:32:04 +00:00
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-03-24 15:09:30 +00:00
|
|
|
// setPeerLastDerpLocked notes that peer is now being written to via
|
2020-05-17 16:51:38 +00:00
|
|
|
// the provided DERP regionID, and that the peer advertises a DERP
|
|
|
|
// home region ID of homeID.
|
2020-03-24 15:09:30 +00:00
|
|
|
//
|
|
|
|
// If there's any change, it logs.
|
|
|
|
//
|
2020-03-23 21:12:23 +00:00
|
|
|
// c.mu must be held.
|
2020-05-17 16:51:38 +00:00
|
|
|
func (c *Conn) setPeerLastDerpLocked(peer key.Public, regionID, homeID int) {
|
2020-03-23 21:12:23 +00:00
|
|
|
if peer.IsZero() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
old := c.peerLastDerp[peer]
|
2020-05-17 16:51:38 +00:00
|
|
|
if old == regionID {
|
2020-03-23 21:12:23 +00:00
|
|
|
return
|
|
|
|
}
|
2020-05-17 16:51:38 +00:00
|
|
|
c.peerLastDerp[peer] = regionID
|
2020-03-23 21:12:23 +00:00
|
|
|
|
2020-03-24 15:09:30 +00:00
|
|
|
var newDesc string
|
|
|
|
switch {
|
2020-05-17 16:51:38 +00:00
|
|
|
case regionID == homeID && regionID == c.myDerp:
|
2020-03-24 15:09:30 +00:00
|
|
|
newDesc = "shared home"
|
2020-05-17 16:51:38 +00:00
|
|
|
case regionID == homeID:
|
2020-03-24 15:09:30 +00:00
|
|
|
newDesc = "their home"
|
2020-05-17 16:51:38 +00:00
|
|
|
case regionID == c.myDerp:
|
2020-03-24 15:09:30 +00:00
|
|
|
newDesc = "our home"
|
2020-05-17 16:51:38 +00:00
|
|
|
case regionID != homeID:
|
2020-03-24 15:09:30 +00:00
|
|
|
newDesc = "alt"
|
|
|
|
}
|
|
|
|
if old == 0 {
|
2020-05-17 16:51:38 +00:00
|
|
|
c.logf("magicsock: derp route for %s set to derp-%d (%s)", peerShort(peer), regionID, newDesc)
|
2020-03-24 15:09:30 +00:00
|
|
|
} else {
|
2020-05-17 16:51:38 +00:00
|
|
|
c.logf("magicsock: derp route for %s changed from derp-%d => derp-%d (%s)", peerShort(peer), old, regionID, newDesc)
|
2020-03-24 15:09:30 +00:00
|
|
|
}
|
2020-03-23 21:12:23 +00:00
|
|
|
}
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
// derpReadResult is the type sent by runDerpClient to ReceiveIPv4
|
|
|
|
// when a DERP packet is available.
|
2020-03-04 17:35:32 +00:00
|
|
|
//
|
|
|
|
// Notably, it doesn't include the derp.ReceivedPacket because we
|
|
|
|
// don't want to give the receiver access to the aliased []byte. To
|
|
|
|
// get at the packet contents they need to call copyBuf to copy it
|
|
|
|
// out, which also releases the buffer.
|
2020-02-18 21:32:04 +00:00
|
|
|
type derpReadResult struct {
|
2020-06-30 19:22:42 +00:00
|
|
|
regionID int
|
2020-03-04 17:35:32 +00:00
|
|
|
n int // length of data received
|
|
|
|
src key.Public // may be zero until server deployment if v2+
|
2020-02-18 21:32:04 +00:00
|
|
|
// copyBuf is called to copy the data to dst. It returns how
|
|
|
|
// much data was copied, which will be n if dst is large
|
2020-03-04 17:35:32 +00:00
|
|
|
// enough. copyBuf can only be called once.
|
2020-02-18 21:32:04 +00:00
|
|
|
copyBuf func(dst []byte) int
|
|
|
|
}
|
|
|
|
|
2020-02-24 18:23:28 +00:00
|
|
|
var logDerpVerbose, _ = strconv.ParseBool(os.Getenv("DEBUG_DERP_VERBOSE"))
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
// runDerpReader runs in a goroutine for the life of a DERP
|
|
|
|
// connection, handling received packets.
|
2020-06-30 19:22:42 +00:00
|
|
|
func (c *Conn) runDerpReader(ctx context.Context, derpFakeAddr netaddr.IPPort, dc *derphttp.Client, wg *syncs.WaitGroupChan, startGate <-chan struct{}) {
|
2020-03-12 18:16:54 +00:00
|
|
|
defer wg.Decr()
|
|
|
|
defer dc.Close()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-startGate:
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
didCopy := make(chan struct{}, 1)
|
2020-06-30 19:22:42 +00:00
|
|
|
regionID := int(derpFakeAddr.Port)
|
|
|
|
res := derpReadResult{regionID: regionID}
|
2020-03-04 17:35:32 +00:00
|
|
|
var pkt derp.ReceivedPacket
|
|
|
|
res.copyBuf = func(dst []byte) int {
|
|
|
|
n := copy(dst, pkt.Data)
|
2020-02-18 21:32:04 +00:00
|
|
|
didCopy <- struct{}{}
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2020-03-22 01:24:28 +00:00
|
|
|
// peerPresent is the set of senders we know are present on this
|
|
|
|
// connection, based on messages we've received from the server.
|
|
|
|
peerPresent := map[key.Public]bool{}
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
for {
|
2020-06-15 17:26:50 +00:00
|
|
|
msg, err := dc.Recv()
|
2020-02-28 19:13:28 +00:00
|
|
|
if err == derphttp.ErrClientClosed {
|
|
|
|
return
|
|
|
|
}
|
2020-02-18 21:32:04 +00:00
|
|
|
if err != nil {
|
2020-03-22 01:24:28 +00:00
|
|
|
// Forget that all these peers have routes.
|
|
|
|
for peer := range peerPresent {
|
|
|
|
delete(peerPresent, peer)
|
2020-06-30 19:22:42 +00:00
|
|
|
c.removeDerpPeerRoute(peer, regionID, dc)
|
2020-03-22 01:24:28 +00:00
|
|
|
}
|
2020-02-18 21:32:04 +00:00
|
|
|
select {
|
2020-02-28 19:13:28 +00:00
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
2020-02-18 21:32:04 +00:00
|
|
|
default:
|
|
|
|
}
|
2020-03-23 20:19:33 +00:00
|
|
|
c.ReSTUN("derp-close")
|
2020-06-30 19:22:42 +00:00
|
|
|
c.logf("magicsock: [%p] derp.Recv(derp-%d): %v", dc, regionID, err)
|
2020-02-18 21:32:04 +00:00
|
|
|
time.Sleep(250 * time.Millisecond)
|
|
|
|
continue
|
|
|
|
}
|
2020-02-21 03:10:54 +00:00
|
|
|
switch m := msg.(type) {
|
|
|
|
case derp.ReceivedPacket:
|
2020-03-04 17:35:32 +00:00
|
|
|
pkt = m
|
|
|
|
res.n = len(m.Data)
|
|
|
|
res.src = m.Source
|
|
|
|
if logDerpVerbose {
|
2020-06-30 19:22:42 +00:00
|
|
|
c.logf("magicsock: got derp-%v packet: %q", regionID, m.Data)
|
2020-03-04 17:35:32 +00:00
|
|
|
}
|
2020-03-22 01:24:28 +00:00
|
|
|
// If this is a new sender we hadn't seen before, remember it and
|
|
|
|
// register a route for this peer.
|
|
|
|
if _, ok := peerPresent[m.Source]; !ok {
|
|
|
|
peerPresent[m.Source] = true
|
2020-06-30 19:22:42 +00:00
|
|
|
c.addDerpPeerRoute(m.Source, regionID, dc)
|
2020-03-22 01:24:28 +00:00
|
|
|
}
|
2020-02-21 03:10:54 +00:00
|
|
|
default:
|
|
|
|
// Ignore.
|
|
|
|
// TODO: handle endpoint notification messages.
|
|
|
|
continue
|
|
|
|
}
|
2020-02-18 21:32:04 +00:00
|
|
|
select {
|
2020-03-12 18:16:54 +00:00
|
|
|
case <-ctx.Done():
|
2020-02-18 21:32:04 +00:00
|
|
|
return
|
2020-03-04 17:35:32 +00:00
|
|
|
case c.derpRecvCh <- res:
|
2020-02-18 21:32:04 +00:00
|
|
|
<-didCopy
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type derpWriteRequest struct {
|
2020-06-30 19:22:42 +00:00
|
|
|
addr netaddr.IPPort
|
2020-02-18 21:32:04 +00:00
|
|
|
pubKey key.Public
|
2020-03-12 19:05:32 +00:00
|
|
|
b []byte // copied; ownership passed to receiver
|
2020-02-18 21:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// runDerpWriter runs in a goroutine for the life of a DERP
|
|
|
|
// connection, handling received packets.
|
2020-06-30 19:22:42 +00:00
|
|
|
func (c *Conn) runDerpWriter(ctx context.Context, dc *derphttp.Client, ch <-chan derpWriteRequest, wg *syncs.WaitGroupChan, startGate <-chan struct{}) {
|
2020-03-12 18:16:54 +00:00
|
|
|
defer wg.Decr()
|
|
|
|
select {
|
|
|
|
case <-startGate:
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
for {
|
|
|
|
select {
|
2020-02-28 19:13:28 +00:00
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
2020-02-18 21:32:04 +00:00
|
|
|
case wr := <-ch:
|
|
|
|
err := dc.Send(wr.pubKey, wr.b)
|
|
|
|
if err != nil {
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: derp.Send(%v): %v", wr.addr, err)
|
2020-02-18 21:32:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-07 21:36:18 +00:00
|
|
|
// findEndpoint maps from a UDP address to a WireGuard endpoint, for
|
|
|
|
// ReceiveIPv4/ReceiveIPv6.
|
2020-06-30 21:37:35 +00:00
|
|
|
// The provided addr and ipp must match.
|
2020-07-02 05:15:41 +00:00
|
|
|
//
|
|
|
|
// TODO(bradfitz): add a fast path that returns nil here for normal
|
|
|
|
// wireguard-go transport packets; IIRC wireguard-go only uses this
|
|
|
|
// Endpoint for the relatively rare non-data packets.
|
2020-06-30 21:37:35 +00:00
|
|
|
func (c *Conn) findEndpoint(ipp netaddr.IPPort, addr *net.UDPAddr) conn.Endpoint {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2020-03-07 21:36:18 +00:00
|
|
|
|
2020-06-30 21:37:35 +00:00
|
|
|
// See if they have a discoEndpoint, for a set of peers
|
|
|
|
// both supporting active discovery.
|
2020-07-02 05:15:41 +00:00
|
|
|
if dk, ok := c.discoOfAddr[ipp]; ok {
|
|
|
|
if ep, ok := c.endpointOfDisco[dk]; ok {
|
|
|
|
return ep
|
|
|
|
}
|
2020-04-17 20:51:52 +00:00
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-06-30 21:37:35 +00:00
|
|
|
// Pre-disco: look up their AddrSet.
|
|
|
|
if as, ok := c.addrsByUDP[ipp]; ok {
|
|
|
|
return as
|
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-06-30 21:37:35 +00:00
|
|
|
// Pre-disco: the peer that sent this packet has roamed beyond
|
|
|
|
// the knowledge provided by the control server. If the
|
|
|
|
// packet is valid wireguard will call UpdateDst on the
|
|
|
|
// original endpoint using this addr.
|
|
|
|
return (*singleEndpoint)(addr)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
type udpReadResult struct {
|
2020-05-03 20:58:39 +00:00
|
|
|
_ structs.Incomparable
|
2020-02-18 21:32:04 +00:00
|
|
|
n int
|
|
|
|
err error
|
|
|
|
addr *net.UDPAddr
|
2020-06-30 21:37:35 +00:00
|
|
|
ipp netaddr.IPPort
|
2020-02-18 21:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// aLongTimeAgo is a non-zero time, far in the past, used for
|
|
|
|
// immediate cancellation of network operations.
|
|
|
|
var aLongTimeAgo = time.Unix(233431200, 0)
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
// awaitUDP4 reads a single IPv4 UDP packet (or an error) and sends it
|
|
|
|
// to c.udpRecvCh, skipping over (but handling) any STUN replies.
|
|
|
|
func (c *Conn) awaitUDP4(b []byte) {
|
|
|
|
for {
|
2020-03-19 15:49:30 +00:00
|
|
|
n, pAddr, err := c.pconn4.ReadFrom(b)
|
2020-03-09 22:20:33 +00:00
|
|
|
if err != nil {
|
|
|
|
select {
|
|
|
|
case c.udpRecvCh <- udpReadResult{err: err}:
|
|
|
|
case <-c.donec():
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
addr := pAddr.(*net.UDPAddr)
|
2020-06-30 21:37:35 +00:00
|
|
|
ipp, ok := c.pconn4.ippCache.IPPort(addr)
|
2020-06-30 20:25:13 +00:00
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
2020-03-09 22:20:33 +00:00
|
|
|
if stun.Is(b[:n]) {
|
2020-06-30 20:25:13 +00:00
|
|
|
c.stunReceiveFunc.Load().(func([]byte, netaddr.IPPort))(b[:n], ipp)
|
2020-03-09 22:20:33 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-06-30 20:25:13 +00:00
|
|
|
if c.handleDiscoMessage(b[:n], ipp) {
|
2020-06-26 21:38:53 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-03-09 22:20:33 +00:00
|
|
|
|
|
|
|
select {
|
2020-06-30 21:37:35 +00:00
|
|
|
case c.udpRecvCh <- udpReadResult{n: n, addr: addr, ipp: ipp}:
|
2020-03-09 22:20:33 +00:00
|
|
|
case <-c.donec():
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
2020-03-09 22:20:33 +00:00
|
|
|
|
2020-07-01 16:36:19 +00:00
|
|
|
// wgRecvAddr returns the net.UDPAddr we tell wireguard-go the address
|
|
|
|
// from which we received a packet for an endpoint.
|
|
|
|
//
|
|
|
|
// ipp is required. addr can be optionally provided.
|
|
|
|
func wgRecvAddr(e conn.Endpoint, ipp netaddr.IPPort, addr *net.UDPAddr) *net.UDPAddr {
|
|
|
|
if ipp == (netaddr.IPPort{}) {
|
|
|
|
panic("zero ipp")
|
|
|
|
}
|
2020-06-28 18:53:37 +00:00
|
|
|
if de, ok := e.(*discoEndpoint); ok {
|
2020-06-30 19:22:42 +00:00
|
|
|
return de.fakeWGAddrStd
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
2020-07-01 16:36:19 +00:00
|
|
|
if addr != nil {
|
|
|
|
return addr
|
|
|
|
}
|
|
|
|
return ipp.UDPAddr()
|
2020-03-09 22:20:33 +00:00
|
|
|
}
|
|
|
|
|
2020-02-24 12:27:48 +00:00
|
|
|
func (c *Conn) ReceiveIPv4(b []byte) (n int, ep conn.Endpoint, addr *net.UDPAddr, err error) {
|
2020-06-30 19:22:42 +00:00
|
|
|
Top:
|
2020-03-07 21:36:18 +00:00
|
|
|
// First, process any buffered packet from earlier.
|
2020-06-30 21:37:35 +00:00
|
|
|
if from := c.bufferedIPv4From; from != (netaddr.IPPort{}) {
|
|
|
|
c.bufferedIPv4From = netaddr.IPPort{}
|
2020-07-01 16:36:19 +00:00
|
|
|
addr = from.UDPAddr()
|
|
|
|
ep := c.findEndpoint(from, addr)
|
|
|
|
return copy(b, c.bufferedIPv4Packet), ep, wgRecvAddr(ep, from, addr), nil
|
2020-03-07 21:36:18 +00:00
|
|
|
}
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
go c.awaitUDP4(b)
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-03-07 04:39:40 +00:00
|
|
|
// Once the above goroutine has started, it owns b until it writes
|
|
|
|
// to udpRecvCh. The code below must not access b until it's
|
|
|
|
// completed a successful receive on udpRecvCh.
|
|
|
|
|
2020-03-04 17:35:32 +00:00
|
|
|
var addrSet *AddrSet
|
2020-06-28 18:53:37 +00:00
|
|
|
var discoEp *discoEndpoint
|
2020-06-30 21:37:35 +00:00
|
|
|
var ipp netaddr.IPPort
|
2020-03-04 17:35:32 +00:00
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
select {
|
|
|
|
case dm := <-c.derpRecvCh:
|
|
|
|
// Cancel the pconn read goroutine
|
2020-03-19 15:49:30 +00:00
|
|
|
c.pconn4.SetReadDeadline(aLongTimeAgo)
|
2020-03-07 21:36:18 +00:00
|
|
|
// Wait for the UDP-reading goroutine to be done, since it's currently
|
|
|
|
// the owner of the b []byte buffer:
|
2020-02-18 21:32:04 +00:00
|
|
|
select {
|
2020-03-07 21:36:18 +00:00
|
|
|
case um := <-c.udpRecvCh:
|
|
|
|
if um.err != nil {
|
|
|
|
// The normal case. The SetReadDeadline interrupted
|
|
|
|
// the read and we get an error which we now ignore.
|
|
|
|
} else {
|
|
|
|
// The pconn.ReadFrom succeeded and was about to send,
|
|
|
|
// but DERP sent first. So now we have both ready.
|
|
|
|
// Save the UDP packet away for use by the next
|
|
|
|
// ReceiveIPv4 call.
|
2020-06-30 21:37:35 +00:00
|
|
|
c.bufferedIPv4From = um.ipp
|
2020-03-07 21:36:18 +00:00
|
|
|
c.bufferedIPv4Packet = append(c.bufferedIPv4Packet[:0], b[:um.n]...)
|
|
|
|
}
|
2020-03-19 15:49:30 +00:00
|
|
|
c.pconn4.SetReadDeadline(time.Time{})
|
2020-03-02 17:31:25 +00:00
|
|
|
case <-c.donec():
|
2020-02-18 21:32:04 +00:00
|
|
|
return 0, nil, nil, errors.New("Conn closed")
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-06-30 19:22:42 +00:00
|
|
|
var regionID int
|
|
|
|
n, regionID = dm.n, dm.regionID
|
2020-02-18 21:32:04 +00:00
|
|
|
ncopy := dm.copyBuf(b)
|
|
|
|
if ncopy != n {
|
|
|
|
err = fmt.Errorf("received DERP packet of length %d that's too big for WireGuard ReceiveIPv4 buf size %d", n, ncopy)
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: %v", err)
|
2020-02-18 21:32:04 +00:00
|
|
|
return 0, nil, nil, err
|
|
|
|
}
|
|
|
|
|
2020-06-30 21:37:35 +00:00
|
|
|
ipp = netaddr.IPPort{IP: derpMagicIPAddr, Port: uint16(regionID)}
|
|
|
|
if c.handleDiscoMessage(b[:n], ipp) {
|
2020-06-30 19:22:42 +00:00
|
|
|
goto Top
|
|
|
|
}
|
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
2020-06-28 18:53:37 +00:00
|
|
|
if dk, ok := c.discoOfNode[tailcfg.NodeKey(dm.src)]; ok {
|
|
|
|
discoEp = c.endpointOfDisco[dk]
|
|
|
|
}
|
|
|
|
if discoEp == nil {
|
|
|
|
addrSet = c.addrsByKey[dm.src]
|
|
|
|
}
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Unlock()
|
2020-03-04 18:43:06 +00:00
|
|
|
|
2020-06-28 18:53:37 +00:00
|
|
|
if addrSet == nil && discoEp == nil {
|
2020-03-04 18:43:06 +00:00
|
|
|
key := wgcfg.Key(dm.src)
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: DERP packet from unknown key: %s", key.ShortString())
|
2020-03-04 18:43:06 +00:00
|
|
|
}
|
2020-03-04 17:35:32 +00:00
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
case um := <-c.udpRecvCh:
|
|
|
|
if um.err != nil {
|
|
|
|
return 0, nil, nil, err
|
|
|
|
}
|
2020-06-30 21:37:35 +00:00
|
|
|
n, addr, ipp = um.n, um.addr, um.ipp
|
2020-03-07 01:50:36 +00:00
|
|
|
|
|
|
|
case <-c.donec():
|
|
|
|
// Socket has been shut down. All the producers of packets
|
|
|
|
// respond to the context cancellation and go away, so we have
|
|
|
|
// to also unblock and return an error, to inform wireguard-go
|
|
|
|
// that this socket has gone away.
|
|
|
|
//
|
|
|
|
// Specifically, wireguard-go depends on its bind.Conn having
|
|
|
|
// the standard socket behavior, which is that a Close()
|
|
|
|
// unblocks any concurrent Read()s. wireguard-go itself calls
|
|
|
|
// Clos() on magicsock, and expects ReceiveIPv4 to unblock
|
|
|
|
// with an error so it can clean up.
|
|
|
|
return 0, nil, nil, errors.New("socket closed")
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-07 21:36:18 +00:00
|
|
|
if addrSet != nil {
|
|
|
|
ep = addrSet
|
2020-06-28 18:53:37 +00:00
|
|
|
} else if discoEp != nil {
|
|
|
|
ep = discoEp
|
2020-03-07 21:36:18 +00:00
|
|
|
} else {
|
2020-06-30 21:37:35 +00:00
|
|
|
ep = c.findEndpoint(ipp, addr)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-07-01 16:36:19 +00:00
|
|
|
return n, ep, wgRecvAddr(ep, ipp, addr), nil
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-19 16:39:00 +00:00
|
|
|
func (c *Conn) ReceiveIPv6(b []byte) (int, conn.Endpoint, *net.UDPAddr, error) {
|
|
|
|
if c.pconn6 == nil {
|
|
|
|
return 0, nil, nil, syscall.EAFNOSUPPORT
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
n, pAddr, err := c.pconn6.ReadFrom(b)
|
|
|
|
if err != nil {
|
|
|
|
return 0, nil, nil, err
|
|
|
|
}
|
|
|
|
addr := pAddr.(*net.UDPAddr)
|
2020-06-30 21:37:35 +00:00
|
|
|
ipp, ok := c.pconn6.ippCache.IPPort(addr)
|
2020-06-30 20:25:13 +00:00
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
2020-03-19 16:39:00 +00:00
|
|
|
if stun.Is(b[:n]) {
|
2020-06-30 20:25:13 +00:00
|
|
|
c.stunReceiveFunc.Load().(func([]byte, netaddr.IPPort))(b[:n], ipp)
|
2020-03-19 16:39:00 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-06-30 20:25:13 +00:00
|
|
|
if c.handleDiscoMessage(b[:n], ipp) {
|
2020-06-30 19:22:42 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-06-30 21:37:35 +00:00
|
|
|
ep := c.findEndpoint(ipp, addr)
|
2020-07-01 16:36:19 +00:00
|
|
|
return n, ep, wgRecvAddr(ep, ipp, addr), nil
|
2020-03-19 16:39:00 +00:00
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-07-01 21:39:21 +00:00
|
|
|
func (c *Conn) sendDiscoMessage(dst netaddr.IPPort, dstKey key.Public, dstDisco tailcfg.DiscoKey, m disco.Message) (sent bool, err error) {
|
2020-07-01 19:56:17 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
var nonce [disco.NonceLen]byte
|
|
|
|
if _, err := crand.Read(nonce[:]); err != nil {
|
|
|
|
panic(err) // worth dying for
|
|
|
|
}
|
|
|
|
pkt := make([]byte, 0, 512) // TODO: size it correctly? pool? if it matters.
|
|
|
|
pkt = append(pkt, disco.Magic...)
|
|
|
|
pkt = append(pkt, c.discoPublic[:]...)
|
|
|
|
pkt = append(pkt, nonce[:]...)
|
|
|
|
sharedKey := c.sharedDiscoKeyLocked(dstDisco)
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
|
|
|
pkt = box.SealAfterPrecomputation(pkt, m.AppendMarshal(nil), &nonce, sharedKey)
|
2020-07-01 21:39:21 +00:00
|
|
|
sent, err = c.sendAddr(dst, dstKey, pkt)
|
|
|
|
if sent {
|
|
|
|
c.logf("magicsock: disco: sent %T to %v", m, dst)
|
|
|
|
} else if err == nil {
|
|
|
|
c.logf("magicsock: disco: can't send %T to %v", m, dst)
|
|
|
|
} else {
|
|
|
|
c.logf("magicsock: disco: failed to send %T to %v: %v", m, dst, err)
|
|
|
|
}
|
|
|
|
return sent, err
|
2020-07-01 19:56:17 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 21:38:53 +00:00
|
|
|
// handleDiscoMessage reports whether msg was a Tailscale inter-node discovery message
|
|
|
|
// that was handled.
|
|
|
|
//
|
|
|
|
// A discovery message has the form:
|
|
|
|
//
|
|
|
|
// * magic [6]byte
|
|
|
|
// * senderDiscoPubKey [32]byte
|
|
|
|
// * nonce [24]byte
|
2020-06-30 19:22:42 +00:00
|
|
|
// * naclbox of payload (see tailscale.com/disco package for inner payload format)
|
|
|
|
//
|
|
|
|
// For messages received over DERP, the addr will be derpMagicIP (with
|
|
|
|
// port being the region)
|
2020-06-30 20:25:13 +00:00
|
|
|
func (c *Conn) handleDiscoMessage(msg []byte, src netaddr.IPPort) bool {
|
2020-07-01 19:56:17 +00:00
|
|
|
const headerLen = len(disco.Magic) + len(tailcfg.DiscoKey{}) + disco.NonceLen
|
|
|
|
if len(msg) < headerLen || string(msg[:len(disco.Magic)]) != disco.Magic {
|
2020-06-26 21:38:53 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
var sender tailcfg.DiscoKey
|
2020-07-01 19:56:17 +00:00
|
|
|
copy(sender[:], msg[len(disco.Magic):])
|
2020-06-26 21:38:53 +00:00
|
|
|
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
2020-07-01 19:56:17 +00:00
|
|
|
if logDisco {
|
|
|
|
c.logf("magicsock: disco: got disco-looking frame %v", sender)
|
|
|
|
}
|
2020-06-26 21:38:53 +00:00
|
|
|
if c.discoPrivate.IsZero() {
|
2020-07-01 19:56:17 +00:00
|
|
|
if logDisco {
|
|
|
|
c.logf("magicsock: disco: ignoring disco-looking frame, no local key")
|
|
|
|
}
|
2020-06-26 21:38:53 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-07-01 22:28:14 +00:00
|
|
|
de, ok := c.endpointOfDisco[sender]
|
2020-06-26 21:38:53 +00:00
|
|
|
if !ok {
|
2020-07-01 19:56:17 +00:00
|
|
|
if logDisco {
|
|
|
|
c.logf("magicsock: disco: ignoring disco-looking frame, don't know about %v", sender)
|
|
|
|
}
|
2020-07-01 22:28:14 +00:00
|
|
|
// Returning false keeps passing it down, to WireGuard.
|
|
|
|
// WireGuard will almost surely reject it, but give it a chance.
|
2020-06-26 21:38:53 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// First, do we even know (and thus care) about this sender? If not,
|
|
|
|
// don't bother decrypting it.
|
|
|
|
|
2020-07-01 19:56:17 +00:00
|
|
|
var nonce [disco.NonceLen]byte
|
|
|
|
copy(nonce[:], msg[len(disco.Magic)+len(key.Public{}):])
|
2020-06-26 21:38:53 +00:00
|
|
|
sealedBox := msg[headerLen:]
|
2020-06-29 21:26:25 +00:00
|
|
|
payload, ok := box.OpenAfterPrecomputation(nil, sealedBox, &nonce, c.sharedDiscoKeyLocked(sender))
|
2020-06-26 21:38:53 +00:00
|
|
|
if !ok {
|
2020-06-30 20:14:41 +00:00
|
|
|
// This might be have been intended for a previous
|
|
|
|
// disco key. When we restart we get a new disco key
|
|
|
|
// and old packets might've still been in flight (or
|
|
|
|
// scheduled). This is particularly the case for LANs
|
|
|
|
// or non-NATed endpoints.
|
2020-07-01 19:56:17 +00:00
|
|
|
// Don't log in normal case. Pass on to wireguard, in case
|
2020-06-30 20:14:41 +00:00
|
|
|
// it's actually a a wireguard packet (super unlikely,
|
|
|
|
// but).
|
2020-07-01 19:56:17 +00:00
|
|
|
if logDisco {
|
|
|
|
c.logf("magicsock: disco: failed to open naclbox from %v (wrong rcpt?)", sender)
|
|
|
|
}
|
2020-06-30 20:14:41 +00:00
|
|
|
// TODO(bradfitz): add some counter for this that logs rarely
|
2020-06-26 21:38:53 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-06-30 19:22:42 +00:00
|
|
|
dm, err := disco.Parse(payload)
|
2020-07-01 19:56:17 +00:00
|
|
|
if logDisco {
|
|
|
|
c.logf("magicsock: disco: disco.Parse = %T, %v", dm, err)
|
|
|
|
}
|
2020-06-30 19:22:42 +00:00
|
|
|
if err != nil {
|
|
|
|
// Couldn't parse it, but it was inside a correctly
|
|
|
|
// signed box, so just ignore it, assuming it's from a
|
|
|
|
// newer version of Tailscale that we don't
|
|
|
|
// understand. Not even worth logging about, lest it
|
|
|
|
// be too spammy for old clients.
|
2020-06-30 20:14:41 +00:00
|
|
|
// TODO(bradfitz): add some counter for this that logs rarely
|
2020-06-30 19:22:42 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
switch dm := dm.(type) {
|
|
|
|
case *disco.Ping:
|
2020-07-02 15:37:46 +00:00
|
|
|
c.handlePingLocked(dm, de, src)
|
2020-06-30 19:22:42 +00:00
|
|
|
case *disco.Pong:
|
2020-07-02 05:15:41 +00:00
|
|
|
de.handlePongConnLocked(dm, src)
|
2020-06-30 19:22:42 +00:00
|
|
|
case disco.CallMeMaybe:
|
2020-06-30 20:25:13 +00:00
|
|
|
if src.IP != derpMagicIPAddr {
|
2020-06-30 19:22:42 +00:00
|
|
|
// CallMeMaybe messages should only come via DERP.
|
2020-06-30 20:14:41 +00:00
|
|
|
c.logf("[unexpected] CallMeMaybe packets should only come via DERP")
|
|
|
|
return true
|
2020-06-30 19:22:42 +00:00
|
|
|
}
|
2020-07-01 22:28:14 +00:00
|
|
|
go de.handleCallMeMaybe()
|
2020-06-30 19:22:42 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 21:38:53 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-07-02 15:37:46 +00:00
|
|
|
func (c *Conn) handlePingLocked(dm *disco.Ping, de *discoEndpoint, src netaddr.IPPort) {
|
|
|
|
c.logf("magicsock: disco: got ping tx %x from %s/%x via %v", dm.TxID, de.publicKey.ShortString(), de.discoKey[:8], src)
|
|
|
|
|
|
|
|
// Remember this this route if not present.
|
|
|
|
c.setAddrToDiscoLocked(src, de.discoKey, nil)
|
|
|
|
|
|
|
|
go de.sendDiscoMessage(src, &disco.Pong{
|
|
|
|
TxID: dm.TxID,
|
|
|
|
Src: src,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// setAddrToDiscoLocked records that newk is at src.
|
|
|
|
//
|
|
|
|
// c.mu must be held.
|
|
|
|
//
|
|
|
|
// If the caller already has a discoEndpoint mutex held as well, it
|
|
|
|
// can be passed in as alreadyLocked so it won't be re-acquired during
|
|
|
|
// any lazy cleanup of the mapping.
|
|
|
|
func (c *Conn) setAddrToDiscoLocked(src netaddr.IPPort, newk tailcfg.DiscoKey, alreadyLocked *discoEndpoint) {
|
|
|
|
oldk, ok := c.discoOfAddr[src]
|
|
|
|
if ok && oldk == newk {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if ok {
|
|
|
|
c.logf("magicsock: disco: changing mapping of %v from disco %x=>%x", src, oldk[:8], newk[:8])
|
|
|
|
} else {
|
|
|
|
c.logf("magicsock: disco: adding mapping of %v to disco %x", src, newk[:8])
|
|
|
|
}
|
|
|
|
c.discoOfAddr[src] = newk
|
|
|
|
if !ok {
|
|
|
|
c.cleanDiscoOfAddrLocked(alreadyLocked)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-02 05:15:41 +00:00
|
|
|
// cleanDiscoOfAddrLocked lazily checks a few entries in c.discoOfAddr
|
|
|
|
// and deletes them if they're stale. It has no pointers in it so we
|
|
|
|
// don't go through the effort of keeping it aggressively
|
|
|
|
// pruned. Instead, we lazily clean it whenever it grows.
|
|
|
|
//
|
|
|
|
// c.mu must be held.
|
|
|
|
//
|
|
|
|
// If the caller already has a discoEndpoint mutex held as well, it
|
|
|
|
// can be passed in as alreadyLocked so it won't be re-acquired.
|
|
|
|
func (c *Conn) cleanDiscoOfAddrLocked(alreadyLocked *discoEndpoint) {
|
|
|
|
// If it's small enough, don't worry about it.
|
|
|
|
if len(c.discoOfAddr) < 16 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const checkEntries = 5 // per one unit of growth
|
|
|
|
|
|
|
|
// Take advantage of Go's random map iteration to check & clean
|
|
|
|
// a few entries.
|
|
|
|
n := 0
|
|
|
|
for ipp, dk := range c.discoOfAddr {
|
|
|
|
n++
|
|
|
|
if n > checkEntries {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
de, ok := c.endpointOfDisco[dk]
|
|
|
|
if !ok {
|
|
|
|
// This discokey isn't even known anymore. Clean.
|
|
|
|
delete(c.discoOfAddr, ipp)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if de != alreadyLocked {
|
|
|
|
de.mu.Lock()
|
|
|
|
}
|
|
|
|
if _, ok := de.endpointState[ipp]; !ok {
|
|
|
|
// The discoEndpoint no longer knows about that endpoint.
|
|
|
|
// It must've changed. Clean.
|
|
|
|
delete(c.discoOfAddr, ipp)
|
|
|
|
}
|
|
|
|
if de != alreadyLocked {
|
|
|
|
de.mu.Unlock()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-29 21:26:25 +00:00
|
|
|
func (c *Conn) sharedDiscoKeyLocked(k tailcfg.DiscoKey) *[32]byte {
|
|
|
|
if v, ok := c.sharedDiscoKey[k]; ok {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
shared := new([32]byte)
|
|
|
|
box.Precompute(shared, key.Public(k).B32(), c.discoPrivate.B32())
|
|
|
|
c.sharedDiscoKey[k] = shared
|
|
|
|
return shared
|
|
|
|
}
|
|
|
|
|
2020-03-02 17:31:25 +00:00
|
|
|
// SetPrivateKey sets the connection's private key.
|
|
|
|
//
|
|
|
|
// This is only used to be able prove our identity when connecting to
|
|
|
|
// DERP servers.
|
|
|
|
//
|
|
|
|
// If the private key changes, any DERP connections are torn down &
|
|
|
|
// recreated when needed.
|
2020-02-17 21:52:11 +00:00
|
|
|
func (c *Conn) SetPrivateKey(privateKey wgcfg.PrivateKey) error {
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2020-02-28 19:13:28 +00:00
|
|
|
|
|
|
|
oldKey, newKey := c.privateKey, key.Private(privateKey)
|
|
|
|
if newKey == oldKey {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
c.privateKey = newKey
|
|
|
|
|
2020-03-24 15:09:30 +00:00
|
|
|
if oldKey.IsZero() {
|
|
|
|
c.logf("magicsock: SetPrivateKey called (init)")
|
2020-04-28 20:41:18 +00:00
|
|
|
go c.ReSTUN("set-private-key")
|
2020-03-24 15:09:30 +00:00
|
|
|
} else {
|
|
|
|
c.logf("magicsock: SetPrivateKey called (changed")
|
|
|
|
}
|
2020-03-23 21:12:23 +00:00
|
|
|
c.closeAllDerpLocked("new-private-key")
|
2020-03-24 15:09:30 +00:00
|
|
|
|
|
|
|
// Key changed. Close existing DERP connections and reconnect to home.
|
2020-04-09 21:21:36 +00:00
|
|
|
if c.myDerp != 0 {
|
|
|
|
c.logf("magicsock: private key changed, reconnecting to home derp-%d", c.myDerp)
|
|
|
|
c.goDerpConnect(c.myDerp)
|
2020-03-24 15:09:30 +00:00
|
|
|
}
|
2020-03-02 17:31:25 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-18 15:48:01 +00:00
|
|
|
// UpdatePeers is called when the set of WireGuard peers changes. It
|
|
|
|
// then removes any state for old peers.
|
|
|
|
//
|
|
|
|
// The caller passes ownership of newPeers map to UpdatePeers.
|
|
|
|
func (c *Conn) UpdatePeers(newPeers map[key.Public]struct{}) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
oldPeers := c.peerSet
|
|
|
|
c.peerSet = newPeers
|
|
|
|
|
|
|
|
// Clean up any key.Public-keyed maps for peers that no longer
|
|
|
|
// exist.
|
|
|
|
for peer := range oldPeers {
|
|
|
|
if _, ok := newPeers[peer]; !ok {
|
|
|
|
delete(c.addrsByKey, peer)
|
|
|
|
delete(c.derpRoute, peer)
|
|
|
|
delete(c.peerLastDerp, peer)
|
|
|
|
}
|
|
|
|
}
|
2020-04-28 20:41:18 +00:00
|
|
|
|
|
|
|
if len(oldPeers) == 0 && len(newPeers) > 0 {
|
|
|
|
go c.ReSTUN("non-zero-peers")
|
|
|
|
}
|
2020-04-18 15:48:01 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 16:51:38 +00:00
|
|
|
// SetDERPMap controls which (if any) DERP servers are used.
|
|
|
|
// A nil value means to disable DERP; it's disabled by default.
|
|
|
|
func (c *Conn) SetDERPMap(dm *tailcfg.DERPMap) {
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2020-03-04 20:21:40 +00:00
|
|
|
|
2020-05-17 16:51:38 +00:00
|
|
|
if reflect.DeepEqual(dm, c.derpMap) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.derpMap = dm
|
|
|
|
if dm == nil {
|
2020-03-23 21:12:23 +00:00
|
|
|
c.closeAllDerpLocked("derp-disabled")
|
2020-05-17 16:51:38 +00:00
|
|
|
return
|
2020-03-04 20:21:40 +00:00
|
|
|
}
|
2020-05-17 16:51:38 +00:00
|
|
|
|
|
|
|
go c.ReSTUN("derp-map-update")
|
2020-03-04 20:21:40 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 18:04:52 +00:00
|
|
|
// SetNetworkMap is called when the control client gets a new network
|
|
|
|
// map from the control server.
|
|
|
|
//
|
|
|
|
// It should not use the DERPMap field of NetworkMap; that's
|
|
|
|
// conditionally sent to SetDERPMap instead.
|
|
|
|
func (c *Conn) SetNetworkMap(nm *controlclient.NetworkMap) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
if reflect.DeepEqual(nm, c.netMap) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-26 21:38:53 +00:00
|
|
|
numDisco := 0
|
|
|
|
for _, n := range nm.Peers {
|
2020-06-28 18:53:37 +00:00
|
|
|
if n.DiscoKey.IsZero() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
numDisco++
|
|
|
|
if ep, ok := c.endpointOfDisco[n.DiscoKey]; ok {
|
|
|
|
ep.updateFromNode(n)
|
2020-06-26 21:38:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.logf("magicsock: got updated network map; %d peers (%d with discokey)", len(nm.Peers), numDisco)
|
2020-06-25 18:04:52 +00:00
|
|
|
c.netMap = nm
|
2020-06-26 21:38:53 +00:00
|
|
|
|
|
|
|
// Build and/or update node<->disco maps, only reallocating if
|
|
|
|
// the set of discokeys changed.
|
|
|
|
for pass := 1; pass <= 2; pass++ {
|
|
|
|
if c.nodeOfDisco == nil || pass == 2 {
|
2020-06-28 18:53:37 +00:00
|
|
|
c.nodeOfDisco = map[tailcfg.DiscoKey]*tailcfg.Node{}
|
2020-06-26 21:38:53 +00:00
|
|
|
c.discoOfNode = map[tailcfg.NodeKey]tailcfg.DiscoKey{}
|
|
|
|
}
|
|
|
|
for _, n := range nm.Peers {
|
|
|
|
if !n.DiscoKey.IsZero() {
|
2020-06-28 18:53:37 +00:00
|
|
|
c.nodeOfDisco[n.DiscoKey] = n
|
2020-06-26 21:38:53 +00:00
|
|
|
if old, ok := c.discoOfNode[n.Key]; ok && old != n.DiscoKey {
|
|
|
|
c.logf("magicsock: node %s changed discovery key from %x to %x", n.Key.ShortString(), old[:8], n.DiscoKey[:8])
|
|
|
|
// TODO: reset AddrSet states, reset wireguard session key, etc.
|
|
|
|
}
|
|
|
|
c.discoOfNode[n.Key] = n.DiscoKey
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(c.nodeOfDisco) == numDisco && len(c.discoOfNode) == numDisco {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-28 18:53:37 +00:00
|
|
|
// Clean c.endpointOfDisco for discovery keys that are no longer present.
|
|
|
|
for dk, de := range c.endpointOfDisco {
|
|
|
|
if _, ok := c.nodeOfDisco[dk]; !ok {
|
|
|
|
de.cleanup()
|
|
|
|
delete(c.endpointOfDisco, dk)
|
2020-06-29 21:26:25 +00:00
|
|
|
delete(c.sharedDiscoKey, dk)
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 18:04:52 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 16:51:38 +00:00
|
|
|
func (c *Conn) wantDerpLocked() bool { return c.derpMap != nil }
|
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
// c.mu must be held.
|
2020-03-23 21:12:23 +00:00
|
|
|
func (c *Conn) closeAllDerpLocked(why string) {
|
|
|
|
if len(c.activeDerp) == 0 {
|
|
|
|
return // without the useless log statement
|
|
|
|
}
|
2020-03-05 16:54:08 +00:00
|
|
|
for i := range c.activeDerp {
|
2020-03-23 21:12:23 +00:00
|
|
|
c.closeDerpLocked(i, why)
|
2020-02-28 19:13:28 +00:00
|
|
|
}
|
2020-03-23 21:12:23 +00:00
|
|
|
c.logActiveDerpLocked()
|
2020-03-05 16:54:08 +00:00
|
|
|
}
|
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
// c.mu must be held.
|
2020-03-23 21:12:23 +00:00
|
|
|
// It is the responsibility of the caller to call logActiveDerpLocked after any set of closes.
|
|
|
|
func (c *Conn) closeDerpLocked(node int, why string) {
|
2020-03-05 16:54:08 +00:00
|
|
|
if ad, ok := c.activeDerp[node]; ok {
|
2020-03-24 15:09:30 +00:00
|
|
|
c.logf("magicsock: closing connection to derp-%v (%v), age %v", node, why, time.Since(ad.createTime).Round(time.Second))
|
2020-03-05 16:54:08 +00:00
|
|
|
go ad.c.Close()
|
|
|
|
ad.cancel()
|
|
|
|
delete(c.activeDerp, node)
|
2020-02-28 19:13:28 +00:00
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-23 21:12:23 +00:00
|
|
|
// c.mu must be held.
|
|
|
|
func (c *Conn) logActiveDerpLocked() {
|
|
|
|
now := time.Now()
|
2020-05-31 22:29:04 +00:00
|
|
|
c.logf("magicsock: %v active derp conns%s", len(c.activeDerp), logger.ArgWriter(func(buf *bufio.Writer) {
|
|
|
|
if len(c.activeDerp) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
buf.WriteString(":")
|
|
|
|
c.foreachActiveDerpSortedLocked(func(node int, ad activeDerp) {
|
|
|
|
fmt.Fprintf(buf, " derp-%d=cr%v,wr%v", node, simpleDur(now.Sub(ad.createTime)), simpleDur(now.Sub(*ad.lastWrite)))
|
|
|
|
})
|
|
|
|
}))
|
2020-03-23 21:12:23 +00:00
|
|
|
}
|
|
|
|
|
2020-03-24 15:09:30 +00:00
|
|
|
func (c *Conn) logEndpointChange(endpoints []string, reasons map[string]string) {
|
2020-05-31 22:29:04 +00:00
|
|
|
c.logf("magicsock: endpoints changed: %s", logger.ArgWriter(func(buf *bufio.Writer) {
|
|
|
|
for i, ep := range endpoints {
|
|
|
|
if i > 0 {
|
|
|
|
buf.WriteString(", ")
|
|
|
|
}
|
|
|
|
fmt.Fprintf(buf, "%s (%s)", ep, reasons[ep])
|
2020-03-24 15:09:30 +00:00
|
|
|
}
|
2020-05-31 22:29:04 +00:00
|
|
|
}))
|
2020-03-24 15:09:30 +00:00
|
|
|
}
|
|
|
|
|
2020-03-23 21:12:23 +00:00
|
|
|
// c.mu must be held.
|
2020-05-17 16:51:38 +00:00
|
|
|
func (c *Conn) foreachActiveDerpSortedLocked(fn func(regionID int, ad activeDerp)) {
|
2020-03-23 21:12:23 +00:00
|
|
|
if len(c.activeDerp) < 2 {
|
|
|
|
for id, ad := range c.activeDerp {
|
|
|
|
fn(id, ad)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ids := make([]int, 0, len(c.activeDerp))
|
|
|
|
for id := range c.activeDerp {
|
|
|
|
ids = append(ids, id)
|
|
|
|
}
|
|
|
|
sort.Ints(ids)
|
|
|
|
for _, id := range ids {
|
|
|
|
fn(id, c.activeDerp[id])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-05 20:47:54 +00:00
|
|
|
func (c *Conn) cleanStaleDerp() {
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2020-03-05 20:47:54 +00:00
|
|
|
const inactivityTime = 60 * time.Second
|
|
|
|
tooOld := time.Now().Add(-inactivityTime)
|
2020-03-23 21:12:23 +00:00
|
|
|
dirty := false
|
2020-03-05 20:47:54 +00:00
|
|
|
for i, ad := range c.activeDerp {
|
|
|
|
if i == c.myDerp {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if ad.lastWrite.Before(tooOld) {
|
2020-03-23 21:12:23 +00:00
|
|
|
c.closeDerpLocked(i, "idle")
|
|
|
|
dirty = true
|
2020-03-05 20:47:54 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-23 21:12:23 +00:00
|
|
|
if dirty {
|
|
|
|
c.logActiveDerpLocked()
|
|
|
|
}
|
2020-03-05 20:47:54 +00:00
|
|
|
}
|
|
|
|
|
2020-03-19 06:55:14 +00:00
|
|
|
// DERPs reports the number of active DERP connections.
|
|
|
|
func (c *Conn) DERPs() int {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
return len(c.activeDerp)
|
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
func (c *Conn) SetMark(value uint32) error { return nil }
|
2020-02-24 12:27:48 +00:00
|
|
|
func (c *Conn) LastMark() uint32 { return 0 }
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
// Close closes the connection.
|
|
|
|
//
|
|
|
|
// Only the first close does anything. Any later closes return nil.
|
2020-02-05 22:16:58 +00:00
|
|
|
func (c *Conn) Close() error {
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
if c.closed {
|
|
|
|
c.mu.Unlock()
|
2020-02-18 21:32:04 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-03-13 15:55:38 +00:00
|
|
|
defer c.mu.Unlock()
|
2020-03-02 17:31:25 +00:00
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
c.closed = true
|
|
|
|
c.connCtxCancel()
|
2020-03-23 21:12:23 +00:00
|
|
|
c.closeAllDerpLocked("conn-close")
|
2020-03-19 16:39:00 +00:00
|
|
|
if c.pconn6 != nil {
|
|
|
|
c.pconn6.Close()
|
|
|
|
}
|
2020-04-27 20:03:22 +00:00
|
|
|
err := c.pconn4.Close()
|
|
|
|
// Wait on endpoints updating right at the end, once everything is
|
|
|
|
// already closed. We want everything else in the Conn to be
|
|
|
|
// consistently in the closed state before we release mu to wait
|
|
|
|
// on the endpoint updater.
|
|
|
|
for c.endpointsUpdateActive {
|
|
|
|
c.endpointsUpdateWaiter.Wait()
|
|
|
|
}
|
|
|
|
return err
|
2020-03-13 15:55:38 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 21:19:12 +00:00
|
|
|
var debugReSTUNStopOnIdle, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_RESTUN_STOP_ON_IDLE"))
|
|
|
|
|
|
|
|
func maxIdleBeforeSTUNShutdown() time.Duration {
|
|
|
|
if debugReSTUNStopOnIdle {
|
|
|
|
return time.Minute
|
|
|
|
}
|
|
|
|
return 5 * time.Minute
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Conn) shouldDoPeriodicReSTUN() bool {
|
2020-04-28 20:41:18 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2020-06-25 21:19:12 +00:00
|
|
|
if len(c.peerSet) == 0 {
|
|
|
|
// No peers, so not worth doing.
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
// If it turns out this optimization was a mistake, we can
|
|
|
|
// override it from the control server without waiting for a
|
|
|
|
// new software rollout:
|
|
|
|
if c.netMap != nil && c.netMap.Debug != nil && c.netMap.Debug.ForceBackgroundSTUN && !debugReSTUNStopOnIdle {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if f := c.idleFunc; f != nil {
|
|
|
|
idleFor := f()
|
|
|
|
if debugReSTUNStopOnIdle {
|
|
|
|
c.logf("magicsock: periodicReSTUN: idle for %v", idleFor.Round(time.Second))
|
|
|
|
}
|
|
|
|
if idleFor > maxIdleBeforeSTUNShutdown() {
|
|
|
|
if debugReSTUNStopOnIdle || version.IsMobile() { // TODO: make this unconditional later
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
2020-04-28 20:41:18 +00:00
|
|
|
}
|
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
func (c *Conn) periodicReSTUN() {
|
2020-03-25 21:19:21 +00:00
|
|
|
prand := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
|
|
dur := func() time.Duration {
|
|
|
|
// Just under 30s, a common UDP NAT timeout (Linux at least)
|
|
|
|
return time.Duration(20+prand.Intn(7)) * time.Second
|
|
|
|
}
|
|
|
|
timer := time.NewTimer(dur())
|
|
|
|
defer timer.Stop()
|
2020-06-25 21:19:12 +00:00
|
|
|
var lastIdleState opt.Bool
|
2020-03-13 15:55:38 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-c.donec():
|
|
|
|
return
|
2020-03-25 21:19:21 +00:00
|
|
|
case <-timer.C:
|
2020-06-25 21:19:12 +00:00
|
|
|
doReSTUN := c.shouldDoPeriodicReSTUN()
|
|
|
|
if !lastIdleState.EqualBool(doReSTUN) {
|
|
|
|
if doReSTUN {
|
|
|
|
c.logf("magicsock: periodicReSTUN enabled")
|
|
|
|
} else {
|
|
|
|
c.logf("magicsock: periodicReSTUN disabled due to inactivity")
|
|
|
|
}
|
|
|
|
lastIdleState.Set(doReSTUN)
|
|
|
|
}
|
|
|
|
if doReSTUN {
|
2020-04-28 20:41:18 +00:00
|
|
|
c.ReSTUN("periodic")
|
|
|
|
}
|
2020-03-25 21:19:21 +00:00
|
|
|
timer.Reset(dur())
|
2020-03-13 15:55:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-02 17:31:25 +00:00
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
func (c *Conn) periodicDerpCleanup() {
|
|
|
|
ticker := time.NewTicker(15 * time.Second) // arbitrary
|
|
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-c.donec():
|
|
|
|
return
|
|
|
|
case <-ticker.C:
|
|
|
|
c.cleanStaleDerp()
|
|
|
|
}
|
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-13 03:10:11 +00:00
|
|
|
// ReSTUN triggers an address discovery.
|
|
|
|
// The provided why string is for debug logging only.
|
|
|
|
func (c *Conn) ReSTUN(why string) {
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2020-05-17 16:51:38 +00:00
|
|
|
if !c.started {
|
|
|
|
panic("call to ReSTUN before Start")
|
|
|
|
}
|
2020-04-27 20:03:22 +00:00
|
|
|
if c.closed {
|
|
|
|
// raced with a shutdown.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
if c.endpointsUpdateActive {
|
|
|
|
if c.wantEndpointsUpdate != why {
|
2020-03-24 05:11:49 +00:00
|
|
|
c.logf("magicsock: ReSTUN: endpoint update active, need another later (%q)", why)
|
2020-03-13 15:55:38 +00:00
|
|
|
c.wantEndpointsUpdate = why
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
c.endpointsUpdateActive = true
|
|
|
|
go c.updateEndpoints(why)
|
2020-02-18 18:55:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-19 16:39:00 +00:00
|
|
|
func (c *Conn) initialBind() error {
|
|
|
|
if err := c.bind1(&c.pconn4, "udp4"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := c.bind1(&c.pconn6, "udp6"); err != nil {
|
2020-03-24 05:11:49 +00:00
|
|
|
c.logf("magicsock: ignoring IPv6 bind failure: %v", err)
|
2020-03-19 16:39:00 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Conn) bind1(ruc **RebindingUDPConn, which string) error {
|
2020-04-29 02:20:02 +00:00
|
|
|
host := ""
|
|
|
|
if v, _ := strconv.ParseBool(os.Getenv("IN_TS_TEST")); v {
|
|
|
|
host = "127.0.0.1"
|
|
|
|
}
|
2020-03-19 16:39:00 +00:00
|
|
|
var pc net.PacketConn
|
|
|
|
var err error
|
2020-05-28 22:27:04 +00:00
|
|
|
listenCtx := context.Background() // unused without DNS name to resolve
|
2020-03-19 16:39:00 +00:00
|
|
|
if c.pconnPort == 0 && DefaultPort != 0 {
|
2020-05-28 22:27:04 +00:00
|
|
|
pc, err = netns.Listener().ListenPacket(listenCtx, which, fmt.Sprintf("%s:%d", host, DefaultPort))
|
2020-03-19 16:39:00 +00:00
|
|
|
if err != nil {
|
|
|
|
c.logf("magicsock: bind: default port %s/%v unavailable; picking random", which, DefaultPort)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if pc == nil {
|
2020-05-28 22:27:04 +00:00
|
|
|
pc, err = netns.Listener().ListenPacket(listenCtx, which, fmt.Sprintf("%s:%d", host, c.pconnPort))
|
2020-03-19 16:39:00 +00:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
c.logf("magicsock: bind(%s/%v): %v", which, c.pconnPort, err)
|
|
|
|
return fmt.Errorf("magicsock: bind: %s/%d: %v", which, c.pconnPort, err)
|
|
|
|
}
|
|
|
|
if *ruc == nil {
|
|
|
|
*ruc = new(RebindingUDPConn)
|
|
|
|
}
|
|
|
|
(*ruc).Reset(pc.(*net.UDPConn))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-03-13 03:10:11 +00:00
|
|
|
// Rebind closes and re-binds the UDP sockets.
|
|
|
|
// It should be followed by a call to ReSTUN.
|
|
|
|
func (c *Conn) Rebind() {
|
2020-04-29 02:20:02 +00:00
|
|
|
host := ""
|
|
|
|
if v, _ := strconv.ParseBool(os.Getenv("IN_TS_TEST")); v {
|
|
|
|
host = "127.0.0.1"
|
|
|
|
}
|
2020-05-28 22:27:04 +00:00
|
|
|
listenCtx := context.Background() // unused without DNS name to resolve
|
2020-02-05 22:16:58 +00:00
|
|
|
if c.pconnPort != 0 {
|
2020-03-19 15:49:30 +00:00
|
|
|
c.pconn4.mu.Lock()
|
|
|
|
if err := c.pconn4.pconn.Close(); err != nil {
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: link change close failed: %v", err)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-05-28 22:27:04 +00:00
|
|
|
packetConn, err := netns.Listener().ListenPacket(listenCtx, "udp4", fmt.Sprintf("%s:%d", host, c.pconnPort))
|
2020-02-05 22:16:58 +00:00
|
|
|
if err == nil {
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: link change rebound port: %d", c.pconnPort)
|
2020-03-19 15:49:30 +00:00
|
|
|
c.pconn4.pconn = packetConn.(*net.UDPConn)
|
|
|
|
c.pconn4.mu.Unlock()
|
2020-02-05 22:16:58 +00:00
|
|
|
return
|
|
|
|
}
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: link change unable to bind fixed port %d: %v, falling back to random port", c.pconnPort, err)
|
2020-03-19 15:49:30 +00:00
|
|
|
c.pconn4.mu.Unlock()
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: link change, binding new port")
|
2020-05-28 22:27:04 +00:00
|
|
|
packetConn, err := netns.Listener().ListenPacket(listenCtx, "udp4", host+":0")
|
2020-02-05 22:16:58 +00:00
|
|
|
if err != nil {
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: link change failed to bind new port: %v", err)
|
2020-02-05 22:16:58 +00:00
|
|
|
return
|
|
|
|
}
|
2020-03-19 15:49:30 +00:00
|
|
|
c.pconn4.Reset(packetConn.(*net.UDPConn))
|
2020-04-09 21:21:36 +00:00
|
|
|
|
|
|
|
c.mu.Lock()
|
|
|
|
c.closeAllDerpLocked("rebind")
|
|
|
|
c.mu.Unlock()
|
|
|
|
c.goDerpConnect(c.myDerp)
|
2020-04-10 05:25:31 +00:00
|
|
|
c.resetAddrSetStates()
|
|
|
|
}
|
|
|
|
|
|
|
|
// resetAddrSetStates resets the preferred address for all peers and
|
|
|
|
// re-enables spraying.
|
|
|
|
// This is called when connectivity changes enough that we no longer
|
|
|
|
// trust the old routes.
|
|
|
|
func (c *Conn) resetAddrSetStates() {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
for _, as := range c.addrsByKey {
|
|
|
|
as.curAddr = -1
|
|
|
|
as.stopSpray = as.timeNow().Add(sprayPeriod)
|
|
|
|
}
|
2020-06-28 18:53:37 +00:00
|
|
|
for _, de := range c.endpointOfDisco {
|
|
|
|
de.noteConnectivityChange()
|
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-24 12:27:48 +00:00
|
|
|
// AddrSet is a set of UDP addresses that implements wireguard/conn.Endpoint.
|
2020-06-28 18:53:37 +00:00
|
|
|
//
|
|
|
|
// This is the legacy endpoint for peers that don't support discovery;
|
|
|
|
// it predates discoEndpoint.
|
2020-02-05 22:16:58 +00:00
|
|
|
type AddrSet struct {
|
2020-03-06 05:17:41 +00:00
|
|
|
publicKey key.Public // peer public key used for DERP communication
|
|
|
|
|
|
|
|
// addrs is an ordered priority list provided by wgengine,
|
|
|
|
// sorted from expensive+slow+reliable at the begnining to
|
|
|
|
// fast+cheap at the end. More concretely, it's typically:
|
|
|
|
//
|
|
|
|
// [DERP fakeip:node, Global IP:port, LAN ip:port]
|
|
|
|
//
|
|
|
|
// But there could be multiple or none of each.
|
2020-04-17 22:15:42 +00:00
|
|
|
addrs []net.UDPAddr
|
|
|
|
ipPorts []netaddr.IPPort // same as addrs, in different form
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-03-09 16:13:28 +00:00
|
|
|
// clock, if non-nil, is used in tests instead of time.Now.
|
|
|
|
clock func() time.Time
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
Logf logger.Logf // must not be nil
|
2020-03-09 16:13:28 +00:00
|
|
|
|
2020-02-22 03:20:31 +00:00
|
|
|
mu sync.Mutex // guards following fields
|
2020-02-18 21:32:04 +00:00
|
|
|
|
|
|
|
// roamAddr is non-nil if/when we receive a correctly signed
|
|
|
|
// WireGuard packet from an unexpected address. If so, we
|
|
|
|
// remember it and send responses there in the future, but
|
|
|
|
// this should hopefully never be used (or at least used
|
|
|
|
// rarely) in the case that all the components of Tailscale
|
|
|
|
// are correctly learning/sharing the network map details.
|
2020-06-30 19:22:42 +00:00
|
|
|
roamAddr *netaddr.IPPort
|
|
|
|
roamAddrStd *net.UDPAddr
|
2020-02-18 21:32:04 +00:00
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
// curAddr is an index into addrs of the highest-priority
|
|
|
|
// address a valid packet has been received from so far.
|
|
|
|
// If no valid packet from addrs has been received, curAddr is -1.
|
|
|
|
curAddr int
|
2020-02-22 03:20:31 +00:00
|
|
|
|
|
|
|
// stopSpray is the time after which we stop spraying packets.
|
|
|
|
stopSpray time.Time
|
|
|
|
|
2020-04-06 21:44:10 +00:00
|
|
|
// lastSpray is the last time we sprayed a packet.
|
2020-02-22 03:20:31 +00:00
|
|
|
lastSpray time.Time
|
2020-06-11 16:40:21 +00:00
|
|
|
|
|
|
|
// loggedLogPriMask is a bit field of that tracks whether
|
|
|
|
// we've already logged about receiving a packet from a low
|
|
|
|
// priority ("low-pri") address when we already have curAddr
|
|
|
|
// set to a better one. This is only to suppress some
|
|
|
|
// redundant logs.
|
|
|
|
loggedLogPriMask uint32
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-25 05:24:59 +00:00
|
|
|
// derpID returns this AddrSet's home DERP node, or 0 if none is found.
|
|
|
|
func (as *AddrSet) derpID() int {
|
|
|
|
for _, ua := range as.addrs {
|
|
|
|
if ua.IP.Equal(derpMagicIP) {
|
|
|
|
return ua.Port
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2020-03-09 16:13:28 +00:00
|
|
|
func (as *AddrSet) timeNow() time.Time {
|
|
|
|
if as.clock != nil {
|
|
|
|
return as.clock()
|
|
|
|
}
|
|
|
|
return time.Now()
|
|
|
|
}
|
|
|
|
|
2020-06-30 19:22:42 +00:00
|
|
|
var noAddr, _ = netaddr.FromStdAddr(net.ParseIP("127.127.127.127"), 127, "")
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-06-30 19:22:42 +00:00
|
|
|
func (a *AddrSet) dst() netaddr.IPPort {
|
2020-02-05 22:16:58 +00:00
|
|
|
a.mu.Lock()
|
|
|
|
defer a.mu.Unlock()
|
|
|
|
|
|
|
|
if a.roamAddr != nil {
|
2020-06-30 19:22:42 +00:00
|
|
|
return *a.roamAddr
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
if len(a.addrs) == 0 {
|
|
|
|
return noAddr
|
|
|
|
}
|
|
|
|
i := a.curAddr
|
|
|
|
if i == -1 {
|
|
|
|
i = 0
|
|
|
|
}
|
2020-06-30 19:22:42 +00:00
|
|
|
return a.ipPorts[i]
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-14 17:28:29 +00:00
|
|
|
// packUDPAddr packs a UDPAddr in the form wanted by WireGuard.
|
|
|
|
func packUDPAddr(ua *net.UDPAddr) []byte {
|
|
|
|
ip := ua.IP.To4()
|
|
|
|
if ip == nil {
|
|
|
|
ip = ua.IP
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-02-14 17:28:29 +00:00
|
|
|
b := make([]byte, 0, len(ip)+2)
|
|
|
|
b = append(b, ip...)
|
|
|
|
b = append(b, byte(ua.Port))
|
|
|
|
b = append(b, byte(ua.Port>>8))
|
2020-02-05 22:16:58 +00:00
|
|
|
return b
|
|
|
|
}
|
2020-02-14 17:28:29 +00:00
|
|
|
|
2020-06-30 19:22:42 +00:00
|
|
|
// packIPPort packs an IPPort into the form wanted by WireGuard.
|
|
|
|
func packIPPort(ua netaddr.IPPort) []byte {
|
|
|
|
ip := ua.IP.Unmap()
|
|
|
|
a := ip.As16()
|
|
|
|
ipb := a[:]
|
|
|
|
if ip.Is4() {
|
|
|
|
ipb = ipb[12:]
|
|
|
|
}
|
|
|
|
b := make([]byte, 0, len(ipb)+2)
|
|
|
|
b = append(b, ipb...)
|
|
|
|
b = append(b, byte(ua.Port))
|
|
|
|
b = append(b, byte(ua.Port>>8))
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
2020-02-14 17:28:29 +00:00
|
|
|
func (a *AddrSet) DstToBytes() []byte {
|
2020-06-30 19:22:42 +00:00
|
|
|
return packIPPort(a.dst())
|
2020-02-14 17:28:29 +00:00
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
func (a *AddrSet) DstToString() string {
|
|
|
|
dst := a.dst()
|
|
|
|
return dst.String()
|
|
|
|
}
|
|
|
|
func (a *AddrSet) DstIP() net.IP {
|
2020-06-30 19:22:42 +00:00
|
|
|
return a.dst().IP.IPAddr().IP // TODO: add netaddr accessor to cut an alloc here?
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
func (a *AddrSet) SrcIP() net.IP { return nil }
|
|
|
|
func (a *AddrSet) SrcToString() string { return "" }
|
|
|
|
func (a *AddrSet) ClearSrc() {}
|
|
|
|
|
|
|
|
func (a *AddrSet) UpdateDst(new *net.UDPAddr) error {
|
2020-03-06 18:36:33 +00:00
|
|
|
if new.IP.Equal(derpMagicIP) {
|
|
|
|
// Never consider DERP addresses as a viable candidate for
|
|
|
|
// either curAddr or roamAddr. It's only ever a last resort
|
|
|
|
// choice, never a preferred choice.
|
|
|
|
// This is a hot path for established connections.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
a.mu.Lock()
|
|
|
|
defer a.mu.Unlock()
|
|
|
|
|
2020-06-30 19:22:42 +00:00
|
|
|
if a.roamAddrStd != nil && equalUDPAddr(new, a.roamAddrStd) {
|
2020-03-06 18:36:33 +00:00
|
|
|
// Packet from the current roaming address, no logging.
|
|
|
|
// This is a hot path for established connections.
|
|
|
|
return nil
|
|
|
|
}
|
2020-03-09 16:13:28 +00:00
|
|
|
if a.roamAddr == nil && a.curAddr >= 0 && equalUDPAddr(new, &a.addrs[a.curAddr]) {
|
2020-02-05 22:16:58 +00:00
|
|
|
// Packet from current-priority address, no logging.
|
|
|
|
// This is a hot path for established connections.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-30 19:22:42 +00:00
|
|
|
newa, ok := netaddr.FromStdAddr(new.IP, new.Port, new.Zone)
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
index := -1
|
|
|
|
for i := range a.addrs {
|
|
|
|
if equalUDPAddr(new, &a.addrs[i]) {
|
|
|
|
index = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
publicKey := wgcfg.Key(a.publicKey)
|
|
|
|
pk := publicKey.ShortString()
|
|
|
|
old := "<none>"
|
|
|
|
if a.curAddr >= 0 {
|
|
|
|
old = a.addrs[a.curAddr].String()
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case index == -1:
|
|
|
|
if a.roamAddr == nil {
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
a.Logf("magicsock: rx %s from roaming address %s, set as new priority", pk, new)
|
2020-02-05 22:16:58 +00:00
|
|
|
} else {
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
a.Logf("magicsock: rx %s from roaming address %s, replaces roaming address %s", pk, new, a.roamAddr)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-06-30 19:22:42 +00:00
|
|
|
a.roamAddr = &newa
|
|
|
|
a.roamAddrStd = new
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
case a.roamAddr != nil:
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
a.Logf("magicsock: rx %s from known %s (%d), replaces roaming address %s", pk, new, index, a.roamAddr)
|
2020-02-05 22:16:58 +00:00
|
|
|
a.roamAddr = nil
|
2020-06-30 19:22:42 +00:00
|
|
|
a.roamAddrStd = nil
|
2020-02-05 22:16:58 +00:00
|
|
|
a.curAddr = index
|
2020-06-11 16:40:21 +00:00
|
|
|
a.loggedLogPriMask = 0
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
case a.curAddr == -1:
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
a.Logf("magicsock: rx %s from %s (%d/%d), set as new priority", pk, new, index, len(a.addrs))
|
2020-02-05 22:16:58 +00:00
|
|
|
a.curAddr = index
|
2020-06-11 16:40:21 +00:00
|
|
|
a.loggedLogPriMask = 0
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
case index < a.curAddr:
|
2020-06-11 16:40:21 +00:00
|
|
|
if 1 <= index && index <= 32 && (a.loggedLogPriMask&1<<(index-1)) == 0 {
|
|
|
|
a.Logf("magicsock: rx %s from low-pri %s (%d), keeping current %s (%d)", pk, new, index, old, a.curAddr)
|
|
|
|
a.loggedLogPriMask |= 1 << (index - 1)
|
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
default: // index > a.curAddr
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
a.Logf("magicsock: rx %s from %s (%d/%d), replaces old priority %s", pk, new, index, len(a.addrs), old)
|
2020-02-05 22:16:58 +00:00
|
|
|
a.curAddr = index
|
2020-06-11 16:40:21 +00:00
|
|
|
a.loggedLogPriMask = 0
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func equalUDPAddr(x, y *net.UDPAddr) bool {
|
|
|
|
return x.Port == y.Port && x.IP.Equal(y.IP)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AddrSet) String() string {
|
|
|
|
a.mu.Lock()
|
|
|
|
defer a.mu.Unlock()
|
|
|
|
|
|
|
|
buf := new(strings.Builder)
|
|
|
|
buf.WriteByte('[')
|
|
|
|
if a.roamAddr != nil {
|
2020-03-24 20:40:43 +00:00
|
|
|
buf.WriteString("roam:")
|
2020-06-30 19:22:42 +00:00
|
|
|
sbPrintAddr(buf, *a.roamAddrStd)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
for i, addr := range a.addrs {
|
|
|
|
if i > 0 || a.roamAddr != nil {
|
|
|
|
buf.WriteString(", ")
|
|
|
|
}
|
2020-03-24 20:40:43 +00:00
|
|
|
sbPrintAddr(buf, addr)
|
2020-02-05 22:16:58 +00:00
|
|
|
if a.curAddr == i {
|
|
|
|
buf.WriteByte('*')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buf.WriteByte(']')
|
|
|
|
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
2020-02-11 03:04:52 +00:00
|
|
|
func (a *AddrSet) Addrs() []wgcfg.Endpoint {
|
|
|
|
var eps []wgcfg.Endpoint
|
|
|
|
for _, addr := range a.addrs {
|
|
|
|
eps = append(eps, wgcfg.Endpoint{
|
|
|
|
Host: addr.IP.String(),
|
|
|
|
Port: uint16(addr.Port),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
a.mu.Lock()
|
|
|
|
defer a.mu.Unlock()
|
|
|
|
if a.roamAddr != nil {
|
|
|
|
eps = append(eps, wgcfg.Endpoint{
|
|
|
|
Host: a.roamAddr.IP.String(),
|
|
|
|
Port: uint16(a.roamAddr.Port),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return eps
|
|
|
|
}
|
|
|
|
|
2020-03-03 15:39:02 +00:00
|
|
|
// CreateBind is called by WireGuard to create a UDP binding.
|
|
|
|
func (c *Conn) CreateBind(uint16) (conn.Bind, uint16, error) {
|
|
|
|
return c, c.LocalPort(), nil
|
|
|
|
}
|
|
|
|
|
2020-02-18 16:57:11 +00:00
|
|
|
// CreateEndpoint is called by WireGuard to connect to an endpoint.
|
2020-06-28 18:53:37 +00:00
|
|
|
//
|
|
|
|
// The key is the public key of the peer and addrs is either:
|
|
|
|
//
|
|
|
|
// 1) a comma-separated list of UDP ip:ports (the the peer doesn't have a discovery key)
|
|
|
|
// 2) "<hex-discovery-key>.disco.tailscale:12345", a magic value that means the peer
|
|
|
|
// is running code that supports active discovery, so CreateEndpoint returns
|
|
|
|
// a discoEndpoint.
|
|
|
|
//
|
|
|
|
|
2020-04-18 15:28:10 +00:00
|
|
|
func (c *Conn) CreateEndpoint(pubKey [32]byte, addrs string) (conn.Endpoint, error) {
|
|
|
|
pk := key.Public(pubKey)
|
2020-03-24 15:09:30 +00:00
|
|
|
c.logf("magicsock: CreateEndpoint: key=%s: %s", pk.ShortString(), strings.ReplaceAll(addrs, "127.3.3.40:", "derp-"))
|
2020-06-28 18:53:37 +00:00
|
|
|
|
|
|
|
if strings.HasSuffix(addrs, controlclient.EndpointDiscoSuffix) {
|
|
|
|
discoHex := strings.TrimSuffix(addrs, controlclient.EndpointDiscoSuffix)
|
|
|
|
discoKey, err := key.NewPublicFromHexMem(mem.S(discoHex))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("magicsock: invalid discokey endpoint %q for %v: %w", addrs, pk.ShortString(), err)
|
|
|
|
}
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
de := &discoEndpoint{
|
|
|
|
c: c,
|
|
|
|
publicKey: pk, // peer public key (for WireGuard + DERP)
|
|
|
|
discoKey: tailcfg.DiscoKey(discoKey), // for discovery mesages
|
|
|
|
wgEndpointHostPort: addrs,
|
2020-06-30 22:32:19 +00:00
|
|
|
sentPing: map[stun.TxID]sentPing{},
|
|
|
|
endpointState: map[netaddr.IPPort]*endpointState{},
|
|
|
|
timers: map[*time.Timer]bool{},
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
|
|
|
de.initFakeUDPAddr()
|
|
|
|
de.updateFromNode(c.nodeOfDisco[de.discoKey])
|
|
|
|
c.endpointOfDisco[de.discoKey] = de
|
|
|
|
return de, nil
|
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
a := &AddrSet{
|
Add tstest.PanicOnLog(), and fix various problems detected by this.
If a test calls log.Printf, 'go test' horrifyingly rearranges the
output to no longer be in chronological order, which makes debugging
virtually impossible. Let's stop that from happening by making
log.Printf panic if called from any module, no matter how deep, during
tests.
This required us to change the default error handler in at least one
http.Server, as well as plumbing a bunch of logf functions around,
especially in magicsock and wgengine, but also in logtail and backoff.
To add insult to injury, 'go test' also rearranges the output when a
parent test has multiple sub-tests (all the sub-test's t.Logf is always
printed after all the parent tests t.Logf), so we need to screw around
with a special Logf that can point at the "current" t (current_t.Logf)
in some places. Probably our entire way of using subtests is wrong,
since 'go test' would probably like to run them all in parallel if you
called t.Parallel(), but it definitely can't because the're all
manipulating the shared state created by the parent test. They should
probably all be separate toplevel tests instead, with common
setup/teardown logic. But that's a job for another time.
Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2020-05-14 02:59:54 +00:00
|
|
|
Logf: c.logf,
|
2020-04-18 15:28:10 +00:00
|
|
|
publicKey: pk,
|
2020-02-05 22:16:58 +00:00
|
|
|
curAddr: -1,
|
|
|
|
}
|
|
|
|
|
2020-02-18 16:57:11 +00:00
|
|
|
if addrs != "" {
|
|
|
|
for _, ep := range strings.Split(addrs, ",") {
|
2020-07-01 15:23:37 +00:00
|
|
|
ipp, err := netaddr.ParseIPPort(ep)
|
2020-02-05 22:16:58 +00:00
|
|
|
if err != nil {
|
2020-04-17 22:15:42 +00:00
|
|
|
return nil, fmt.Errorf("bogus address %q", ep)
|
|
|
|
}
|
|
|
|
a.ipPorts = append(a.ipPorts, ipp)
|
2020-07-01 15:23:37 +00:00
|
|
|
a.addrs = append(a.addrs, *ipp.UDPAddr())
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-13 15:55:38 +00:00
|
|
|
c.mu.Lock()
|
2020-04-18 15:28:10 +00:00
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
// If this endpoint is being updated, remember its old set of
|
|
|
|
// endpoints so we can remove any (from c.addrsByUDP) that are
|
|
|
|
// not in the new set.
|
|
|
|
var oldIPP []netaddr.IPPort
|
|
|
|
if preva, ok := c.addrsByKey[pk]; ok {
|
|
|
|
oldIPP = preva.ipPorts
|
|
|
|
}
|
|
|
|
c.addrsByKey[pk] = a
|
|
|
|
|
|
|
|
// Add entries to c.addrsByUDP.
|
|
|
|
for _, ipp := range a.ipPorts {
|
|
|
|
if ipp.IP == derpMagicIPAddr {
|
2020-02-29 20:48:50 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-04-18 15:28:10 +00:00
|
|
|
c.addrsByUDP[ipp] = a
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove previous c.addrsByUDP entries that are no longer in the new set.
|
|
|
|
for _, ipp := range oldIPP {
|
|
|
|
if ipp.IP != derpMagicIPAddr && c.addrsByUDP[ipp] != a {
|
|
|
|
delete(c.addrsByUDP, ipp)
|
2020-04-17 20:51:52 +00:00
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
2020-06-28 18:53:37 +00:00
|
|
|
// singleEndpoint is a wireguard-go/conn.Endpoint used for "roaming
|
|
|
|
// addressed" in releases of Tailscale that predate discovery
|
|
|
|
// messages. New peers use discoEndpoint.
|
2020-02-05 22:16:58 +00:00
|
|
|
type singleEndpoint net.UDPAddr
|
|
|
|
|
|
|
|
func (e *singleEndpoint) ClearSrc() {}
|
|
|
|
func (e *singleEndpoint) DstIP() net.IP { return (*net.UDPAddr)(e).IP }
|
|
|
|
func (e *singleEndpoint) SrcIP() net.IP { return nil }
|
|
|
|
func (e *singleEndpoint) SrcToString() string { return "" }
|
|
|
|
func (e *singleEndpoint) DstToString() string { return (*net.UDPAddr)(e).String() }
|
2020-02-14 17:28:29 +00:00
|
|
|
func (e *singleEndpoint) DstToBytes() []byte { return packUDPAddr((*net.UDPAddr)(e)) }
|
2020-02-05 22:16:58 +00:00
|
|
|
func (e *singleEndpoint) UpdateDst(dst *net.UDPAddr) error {
|
|
|
|
return fmt.Errorf("magicsock.singleEndpoint(%s).UpdateDst(%s): should never be called", (*net.UDPAddr)(e), dst)
|
|
|
|
}
|
2020-02-11 03:04:52 +00:00
|
|
|
func (e *singleEndpoint) Addrs() []wgcfg.Endpoint {
|
|
|
|
return []wgcfg.Endpoint{{
|
|
|
|
Host: e.IP.String(),
|
|
|
|
Port: uint16(e.Port),
|
|
|
|
}}
|
|
|
|
}
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
// RebindingUDPConn is a UDP socket that can be re-bound.
|
|
|
|
// Unix has no notion of re-binding a socket, so we swap it out for a new one.
|
|
|
|
type RebindingUDPConn struct {
|
2020-06-30 21:37:35 +00:00
|
|
|
// ippCache is a cache from UDPAddr => netaddr.IPPort. It's not safe for concurrent use.
|
|
|
|
// This is used by ReceiveIPv6 and awaitUDP4 (called from ReceiveIPv4).
|
|
|
|
ippCache ippCache
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
mu sync.Mutex
|
|
|
|
pconn *net.UDPConn
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *RebindingUDPConn) Reset(pconn *net.UDPConn) {
|
|
|
|
c.mu.Lock()
|
|
|
|
old := c.pconn
|
|
|
|
c.pconn = pconn
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
|
|
|
if old != nil {
|
|
|
|
old.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *RebindingUDPConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
|
|
|
for {
|
|
|
|
c.mu.Lock()
|
|
|
|
pconn := c.pconn
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
|
|
|
n, addr, err := pconn.ReadFrom(b)
|
|
|
|
if err != nil {
|
|
|
|
c.mu.Lock()
|
|
|
|
pconn2 := c.pconn
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
|
|
|
if pconn != pconn2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return n, addr, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *RebindingUDPConn) LocalAddr() *net.UDPAddr {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
return c.pconn.LocalAddr().(*net.UDPAddr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *RebindingUDPConn) Close() error {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
return c.pconn.Close()
|
|
|
|
}
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
func (c *RebindingUDPConn) SetReadDeadline(t time.Time) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
c.pconn.SetReadDeadline(t)
|
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
func (c *RebindingUDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
|
|
|
|
for {
|
|
|
|
c.mu.Lock()
|
|
|
|
pconn := c.pconn
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
|
|
|
n, err := pconn.WriteToUDP(b, addr)
|
|
|
|
if err != nil {
|
|
|
|
c.mu.Lock()
|
|
|
|
pconn2 := c.pconn
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
|
|
|
if pconn != pconn2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *RebindingUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
|
|
|
for {
|
|
|
|
c.mu.Lock()
|
|
|
|
pconn := c.pconn
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
|
|
|
n, err := pconn.WriteTo(b, addr)
|
|
|
|
if err != nil {
|
|
|
|
c.mu.Lock()
|
|
|
|
pconn2 := c.pconn
|
|
|
|
c.mu.Unlock()
|
|
|
|
|
|
|
|
if pconn != pconn2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
}
|
2020-03-23 21:12:23 +00:00
|
|
|
|
|
|
|
// simpleDur rounds d such that it stringifies to something short.
|
|
|
|
func simpleDur(d time.Duration) time.Duration {
|
|
|
|
if d < time.Second {
|
|
|
|
return d.Round(time.Millisecond)
|
|
|
|
}
|
|
|
|
if d < time.Minute {
|
|
|
|
return d.Round(time.Second)
|
|
|
|
}
|
|
|
|
return d.Round(time.Minute)
|
|
|
|
}
|
2020-03-24 15:09:30 +00:00
|
|
|
|
|
|
|
func peerShort(k key.Public) string {
|
|
|
|
k2 := wgcfg.Key(k)
|
|
|
|
return k2.ShortString()
|
|
|
|
}
|
2020-03-24 17:56:22 +00:00
|
|
|
|
2020-03-24 20:40:43 +00:00
|
|
|
func sbPrintAddr(sb *strings.Builder, a net.UDPAddr) {
|
|
|
|
is6 := a.IP.To4() == nil
|
|
|
|
if is6 {
|
|
|
|
sb.WriteByte('[')
|
|
|
|
}
|
|
|
|
fmt.Fprintf(sb, "%s", a.IP)
|
|
|
|
if is6 {
|
|
|
|
sb.WriteByte(']')
|
|
|
|
}
|
|
|
|
fmt.Fprintf(sb, ":%d", a.Port)
|
|
|
|
}
|
2020-03-26 05:57:46 +00:00
|
|
|
|
|
|
|
func (c *Conn) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
for k, as := range c.addrsByKey {
|
|
|
|
ps := &ipnstate.PeerStatus{
|
|
|
|
InMagicSock: true,
|
|
|
|
}
|
|
|
|
for i, ua := range as.addrs {
|
|
|
|
uaStr := udpAddrDebugString(ua)
|
|
|
|
ps.Addrs = append(ps.Addrs, uaStr)
|
|
|
|
if as.curAddr == i {
|
|
|
|
ps.CurAddr = uaStr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if as.roamAddr != nil {
|
2020-06-30 19:22:42 +00:00
|
|
|
ps.CurAddr = udpAddrDebugString(*as.roamAddrStd)
|
2020-03-26 05:57:46 +00:00
|
|
|
}
|
|
|
|
sb.AddPeer(k, ps)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.foreachActiveDerpSortedLocked(func(node int, ad activeDerp) {
|
|
|
|
// TODO(bradfitz): add to ipnstate.StatusBuilder
|
|
|
|
//f("<li><b>derp-%v</b>: cr%v,wr%v</li>", node, simpleDur(now.Sub(ad.createTime)), simpleDur(now.Sub(*ad.lastWrite)))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func udpAddrDebugString(ua net.UDPAddr) string {
|
|
|
|
if ua.IP.Equal(derpMagicIP) {
|
|
|
|
return fmt.Sprintf("derp-%d", ua.Port)
|
|
|
|
}
|
|
|
|
return ua.String()
|
|
|
|
}
|
2020-06-28 18:53:37 +00:00
|
|
|
|
|
|
|
// discoEndpoint is a wireguard/conn.Endpoint for new-style peers that
|
|
|
|
// advertise a DiscoKey and participate in active discovery.
|
|
|
|
type discoEndpoint struct {
|
2020-07-02 05:15:41 +00:00
|
|
|
// These fields are initialized once and never modified.
|
2020-06-28 18:53:37 +00:00
|
|
|
c *Conn
|
|
|
|
publicKey key.Public // peer public key (for WireGuard + DERP)
|
|
|
|
discoKey tailcfg.DiscoKey // for discovery mesages
|
2020-06-30 19:22:42 +00:00
|
|
|
fakeWGAddr netaddr.IPPort // the UDP address we tell wireguard-go we're using
|
|
|
|
fakeWGAddrStd *net.UDPAddr // the *net.UDPAddr form of fakeWGAddr
|
2020-06-28 18:53:37 +00:00
|
|
|
wgEndpointHostPort string // string from CreateEndpoint: "<hex-discovery-key>.disco.tailscale:12345"
|
|
|
|
|
2020-06-30 22:32:19 +00:00
|
|
|
// mu protects all following fields.
|
|
|
|
mu sync.Mutex // Lock ordering: Conn.mu, then discoEndpoint.mu
|
|
|
|
|
2020-07-01 21:39:21 +00:00
|
|
|
lastSend time.Time // last time there was outgoing packets sent to this peer (from wireguard-go)
|
2020-06-30 22:32:19 +00:00
|
|
|
derpAddr netaddr.IPPort // fallback/bootstrap path, if non-zero (non-zero for well-behaved clients)
|
|
|
|
|
2020-07-01 22:28:14 +00:00
|
|
|
bestAddr netaddr.IPPort // best non-DERP path; zero if none
|
|
|
|
bestAddrLatency time.Duration
|
|
|
|
bestAddrAt time.Time // time best address re-confirmed
|
|
|
|
trustBestAddrUntil time.Time // time when bestAddr expires
|
|
|
|
sentPing map[stun.TxID]sentPing
|
|
|
|
endpointState map[netaddr.IPPort]*endpointState
|
2020-06-30 22:32:19 +00:00
|
|
|
|
2020-07-01 21:39:21 +00:00
|
|
|
// timers are all outstanding timers. They're all stopped on
|
|
|
|
// cleanup if the discovery endpoint is removed from the
|
|
|
|
// network map.
|
2020-06-30 22:32:19 +00:00
|
|
|
timers map[*time.Timer]bool
|
|
|
|
}
|
|
|
|
|
|
|
|
type endpointState struct {
|
2020-07-01 19:56:17 +00:00
|
|
|
lastPing time.Time
|
2020-06-30 22:32:19 +00:00
|
|
|
// TODO: lastPong time.Time
|
|
|
|
index int // index in nodecfg.Node.Endpoints
|
|
|
|
}
|
|
|
|
|
|
|
|
type sentPing struct {
|
2020-07-01 21:39:21 +00:00
|
|
|
to netaddr.IPPort
|
|
|
|
at time.Time
|
|
|
|
timer *time.Timer // timeout timer
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// initFakeUDPAddr populates fakeWGAddr with a globally unique fake UDPAddr.
|
|
|
|
// The current implementation just uses the pointer value of de jammed into an IPv6
|
|
|
|
// address, but it could also be, say, a counter.
|
|
|
|
func (de *discoEndpoint) initFakeUDPAddr() {
|
|
|
|
var addr [16]byte
|
|
|
|
addr[0] = 0xfd
|
|
|
|
addr[1] = 0x00
|
|
|
|
binary.BigEndian.PutUint64(addr[2:], uint64(reflect.ValueOf(de).Pointer()))
|
2020-06-30 19:22:42 +00:00
|
|
|
de.fakeWGAddr = netaddr.IPPort{
|
2020-06-28 18:53:37 +00:00
|
|
|
IP: netaddr.IPFrom16(addr),
|
|
|
|
Port: 12345,
|
|
|
|
}
|
2020-06-30 19:22:42 +00:00
|
|
|
de.fakeWGAddrStd = de.fakeWGAddr.UDPAddr()
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (de *discoEndpoint) Addrs() []wgcfg.Endpoint {
|
|
|
|
// This has to be the same string that was passed to
|
|
|
|
// CreateEndpoint, otherwise Reconfig will end up recreating
|
|
|
|
// Endpoints and losing state over time.
|
|
|
|
host, portStr, err := net.SplitHostPort(de.wgEndpointHostPort)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
port, err := strconv.ParseUint(portStr, 10, 16)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return []wgcfg.Endpoint{{host, uint16(port)}}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (de *discoEndpoint) ClearSrc() {}
|
|
|
|
func (de *discoEndpoint) SrcToString() string { panic("unused") } // unused by wireguard-go
|
|
|
|
func (de *discoEndpoint) SrcIP() net.IP { panic("unused") } // unused by wireguard-go
|
|
|
|
func (de *discoEndpoint) DstToString() string { return de.wgEndpointHostPort }
|
|
|
|
func (de *discoEndpoint) DstIP() net.IP { panic("unused") }
|
2020-06-30 19:22:42 +00:00
|
|
|
func (de *discoEndpoint) DstToBytes() []byte { return packIPPort(de.fakeWGAddr) }
|
2020-06-28 18:53:37 +00:00
|
|
|
func (de *discoEndpoint) UpdateDst(addr *net.UDPAddr) error {
|
|
|
|
// This is called ~per packet (and requiring a mutex acquisition inside wireguard-go).
|
|
|
|
// TODO(bradfitz): make that cheaper and/or remove it. We don't need it.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (de *discoEndpoint) send(b []byte) error {
|
2020-06-30 22:32:19 +00:00
|
|
|
now := time.Now()
|
|
|
|
|
2020-06-28 18:53:37 +00:00
|
|
|
de.mu.Lock()
|
2020-06-30 22:32:19 +00:00
|
|
|
de.lastSend = now
|
2020-06-28 18:53:37 +00:00
|
|
|
derpAddr := de.derpAddr
|
2020-07-01 22:28:14 +00:00
|
|
|
haveDerp := !derpAddr.IsZero()
|
2020-06-30 22:32:19 +00:00
|
|
|
bestAddr := de.bestAddr
|
2020-07-01 22:28:14 +00:00
|
|
|
bestOld := now.After(de.trustBestAddrUntil)
|
|
|
|
if bestAddr.IsZero() || bestOld {
|
|
|
|
de.sendPingsLocked(now, true)
|
2020-06-30 22:32:19 +00:00
|
|
|
}
|
2020-06-28 18:53:37 +00:00
|
|
|
de.mu.Unlock()
|
|
|
|
|
2020-07-01 22:28:14 +00:00
|
|
|
var didDerp bool
|
|
|
|
if bestAddr.IsZero() {
|
|
|
|
if !haveDerp {
|
2020-06-30 22:32:19 +00:00
|
|
|
return errors.New("no DERP addr")
|
|
|
|
}
|
2020-07-01 22:28:14 +00:00
|
|
|
bestAddr = derpAddr
|
|
|
|
} else if bestOld && haveDerp {
|
|
|
|
// We have a bestAddr, but it hasn't been confirmed in a while,
|
|
|
|
// so let's not entirely trust it and also send via DERP.
|
|
|
|
didDerp, _ = de.c.sendAddr(derpAddr, de.publicKey, b)
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
2020-07-01 22:28:14 +00:00
|
|
|
|
2020-07-01 21:39:21 +00:00
|
|
|
_, err := de.c.sendAddr(bestAddr, de.publicKey, b)
|
2020-07-01 22:28:14 +00:00
|
|
|
if didDerp {
|
|
|
|
return nil
|
|
|
|
}
|
2020-07-01 21:39:21 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// newTimerLocked creates a new AfterFunc(d, f) and returns it after
|
|
|
|
// registering it with de.timers.
|
|
|
|
//
|
|
|
|
// The timer will unregister itself from de.timers after it fires and after f runs.
|
|
|
|
func (de *discoEndpoint) newTimerLocked(d time.Duration, f func()) *time.Timer {
|
|
|
|
var t *time.Timer
|
|
|
|
t = time.AfterFunc(d, func() {
|
|
|
|
f()
|
|
|
|
|
|
|
|
de.mu.Lock()
|
|
|
|
delete(de.timers, t)
|
|
|
|
de.mu.Unlock()
|
|
|
|
})
|
|
|
|
de.timers[t] = true
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
// forgetPing is called by a timer when a ping either fails to send or
|
|
|
|
// has taken too long to get a pong reply.
|
|
|
|
func (de *discoEndpoint) forgetPing(txid stun.TxID) {
|
|
|
|
de.mu.Lock()
|
|
|
|
defer de.mu.Unlock()
|
|
|
|
if sp, ok := de.sentPing[txid]; ok {
|
|
|
|
// Stop the timer for the case where sendPing failed to write to UDP.
|
|
|
|
// In the case of a timer already having fired, this is a no-op:
|
|
|
|
sp.timer.Stop()
|
|
|
|
delete(de.sentPing, txid)
|
|
|
|
delete(de.timers, sp.timer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// sendPing sends a ping with the provided txid to ep.
|
|
|
|
// The caller should've already been recorded the ping in sentPing
|
|
|
|
// and set up the timer.
|
|
|
|
func (de *discoEndpoint) sendPing(ep netaddr.IPPort, txid stun.TxID) {
|
|
|
|
sent, _ := de.sendDiscoMessage(ep, &disco.Ping{TxID: [12]byte(txid)})
|
|
|
|
if !sent {
|
|
|
|
de.forgetPing(txid)
|
|
|
|
}
|
2020-06-30 22:32:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-01 22:28:14 +00:00
|
|
|
func (de *discoEndpoint) sendPingsLocked(now time.Time, sendCallMeMaybe bool) {
|
2020-07-01 19:56:17 +00:00
|
|
|
sent := false
|
|
|
|
for ep, st := range de.endpointState {
|
2020-07-01 21:39:21 +00:00
|
|
|
ep := ep
|
2020-07-01 19:56:17 +00:00
|
|
|
if !st.lastPing.IsZero() && now.Sub(st.lastPing) < 5*time.Second {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
st.lastPing = now
|
|
|
|
|
|
|
|
txid := stun.NewTxID()
|
2020-07-01 21:39:21 +00:00
|
|
|
t := de.newTimerLocked(5*time.Second, func() {
|
|
|
|
de.c.logf("magicsock: disco: timeout waiting for ping %x from %v", txid, ep)
|
|
|
|
de.forgetPing(txid)
|
|
|
|
})
|
2020-07-01 19:56:17 +00:00
|
|
|
de.sentPing[txid] = sentPing{
|
2020-07-01 21:39:21 +00:00
|
|
|
to: ep,
|
|
|
|
at: now,
|
|
|
|
timer: t,
|
2020-07-01 19:56:17 +00:00
|
|
|
}
|
|
|
|
sent = true
|
2020-07-01 21:39:21 +00:00
|
|
|
go de.sendPing(ep, txid)
|
2020-07-01 19:56:17 +00:00
|
|
|
}
|
|
|
|
derpAddr := de.derpAddr
|
2020-07-01 22:28:14 +00:00
|
|
|
if sent && sendCallMeMaybe && !derpAddr.IsZero() {
|
2020-07-01 19:56:17 +00:00
|
|
|
// In just a bit of a time (for goroutines above to schedule and run),
|
|
|
|
// send a message to peer via DERP informing them that we've sent
|
|
|
|
// so our firewall ports are probably open and now would be a good time
|
|
|
|
// for them to connect.
|
|
|
|
time.AfterFunc(5*time.Millisecond, func() {
|
|
|
|
de.sendDiscoMessage(derpAddr, disco.CallMeMaybe{})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-01 21:39:21 +00:00
|
|
|
func (de *discoEndpoint) sendDiscoMessage(dst netaddr.IPPort, dm disco.Message) (sent bool, err error) {
|
2020-07-01 19:56:17 +00:00
|
|
|
return de.c.sendDiscoMessage(dst, de.publicKey, de.discoKey, dm)
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (de *discoEndpoint) updateFromNode(n *tailcfg.Node) {
|
|
|
|
if n == nil {
|
|
|
|
// TODO: log, error, count? if this even happens.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
de.mu.Lock()
|
|
|
|
defer de.mu.Unlock()
|
|
|
|
|
|
|
|
if n.DERP == "" {
|
2020-06-30 19:22:42 +00:00
|
|
|
de.derpAddr = netaddr.IPPort{}
|
2020-06-28 18:53:37 +00:00
|
|
|
} else {
|
2020-06-30 19:22:42 +00:00
|
|
|
de.derpAddr, _ = netaddr.ParseIPPort(n.DERP)
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
|
|
|
|
2020-06-30 22:32:19 +00:00
|
|
|
for _, st := range de.endpointState {
|
|
|
|
st.index = -1 // assume deleted until updated in next loop
|
|
|
|
}
|
|
|
|
for i, epStr := range n.Endpoints {
|
|
|
|
ipp, err := netaddr.ParseIPPort(epStr)
|
|
|
|
if err != nil {
|
|
|
|
de.c.logf("magicsock: bogus netmap endpoint %q", epStr)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if st, ok := de.endpointState[ipp]; ok {
|
|
|
|
st.index = i
|
|
|
|
} else {
|
|
|
|
de.endpointState[ipp] = &endpointState{index: i}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Now delete anything that wasn't updated.
|
|
|
|
for ipp, st := range de.endpointState {
|
|
|
|
if st.index == -1 {
|
|
|
|
delete(de.endpointState, ipp)
|
|
|
|
if de.bestAddr == ipp {
|
|
|
|
de.bestAddr = netaddr.IPPort{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// noteConnectivityChange is called when connectivity changes enough
|
|
|
|
// that we should question our earlier assumptions about which paths
|
|
|
|
// work.
|
|
|
|
func (de *discoEndpoint) noteConnectivityChange() {
|
|
|
|
de.mu.Lock()
|
|
|
|
defer de.mu.Unlock()
|
|
|
|
|
2020-07-01 22:28:14 +00:00
|
|
|
de.trustBestAddrUntil = time.Time{}
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
|
|
|
|
2020-07-02 05:15:41 +00:00
|
|
|
// handlePongConnLocked handles a Pong message (a reply to an earlier ping).
|
|
|
|
// It should be called with the Conn.mu held.
|
|
|
|
func (de *discoEndpoint) handlePongConnLocked(m *disco.Pong, src netaddr.IPPort) {
|
2020-07-01 19:56:17 +00:00
|
|
|
de.mu.Lock()
|
|
|
|
defer de.mu.Unlock()
|
|
|
|
|
2020-07-02 05:15:41 +00:00
|
|
|
if src.IP == derpMagicIPAddr {
|
|
|
|
// We might support pinging a node via DERP in the
|
|
|
|
// future to see if it's still there, but we don't
|
|
|
|
// yet. We shouldn't ever get here, but bail out early
|
|
|
|
// in case we do in the future. (In which case, hi!,
|
|
|
|
// you'll be modifying this code.)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-07-01 19:56:17 +00:00
|
|
|
sp, ok := de.sentPing[m.TxID]
|
|
|
|
if !ok {
|
|
|
|
// This is not a pong for a ping we sent. Ignore.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
delete(de.sentPing, m.TxID)
|
|
|
|
|
2020-07-02 15:37:46 +00:00
|
|
|
de.c.setAddrToDiscoLocked(src, de.discoKey, de)
|
2020-07-02 05:15:41 +00:00
|
|
|
|
2020-07-01 19:56:17 +00:00
|
|
|
now := time.Now()
|
|
|
|
delay := now.Sub(sp.at)
|
2020-07-02 15:37:46 +00:00
|
|
|
de.c.logf("magicsock: disco: got pong reply from %v after %v", de.discoKey, delay)
|
2020-07-01 19:56:17 +00:00
|
|
|
|
|
|
|
// Expire our best address if we haven't heard from it in awhile.
|
|
|
|
tooOld := now.Add(-15 * time.Second)
|
|
|
|
if !de.bestAddr.IsZero() && de.bestAddrAt.Before(tooOld) {
|
|
|
|
de.bestAddr = netaddr.IPPort{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Promote this pong response to our current best address if it's lower latency.
|
|
|
|
// TODO(bradfitz): decide how latency vs. preference order affects decision
|
|
|
|
if de.bestAddr.IsZero() || delay < de.bestAddrLatency {
|
|
|
|
de.bestAddr = sp.to
|
|
|
|
de.bestAddrLatency = delay
|
|
|
|
de.bestAddrAt = now
|
2020-07-01 22:28:14 +00:00
|
|
|
de.trustBestAddrUntil = now.Add(5 * time.Second)
|
2020-07-02 15:37:46 +00:00
|
|
|
de.c.logf("magicsock: disco: promoted %v to best address for %v/%v", sp.to, de.publicKey.ShortString(), de.discoKey)
|
2020-07-01 19:56:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-01 22:28:14 +00:00
|
|
|
// handleCallMeMaybe handles a CallMeMaybe discovery message via
|
|
|
|
// DERP. The contract for use of this message is that the peer has
|
|
|
|
// already sent to us via UDP, so their stateful firewall should be
|
|
|
|
// open. Now we can Ping back and make it through.
|
|
|
|
func (de *discoEndpoint) handleCallMeMaybe() {
|
|
|
|
de.mu.Lock()
|
|
|
|
defer de.mu.Unlock()
|
|
|
|
|
|
|
|
// Zero out all the lastPing times to force sendPingsLocked to send new ones,
|
|
|
|
// even if it's been less than 5 seconds ago.
|
|
|
|
for _, st := range de.endpointState {
|
|
|
|
st.lastPing = time.Time{}
|
|
|
|
}
|
|
|
|
de.sendPingsLocked(time.Now(), false)
|
|
|
|
}
|
|
|
|
|
2020-06-28 18:53:37 +00:00
|
|
|
// cleanup is called when a discovery endpoint is no longer present in the NetworkMap.
|
|
|
|
// This is where we can do cleanup such as closing goroutines or canceling timers.
|
|
|
|
func (de *discoEndpoint) cleanup() {
|
|
|
|
de.mu.Lock()
|
|
|
|
defer de.mu.Unlock()
|
|
|
|
|
|
|
|
de.c.logf("magicsock: doing cleanup for discovery key %x", de.discoKey[:])
|
2020-06-30 22:32:19 +00:00
|
|
|
|
|
|
|
// TODO: real work later, when there's stuff to do
|
|
|
|
for t := range de.timers {
|
|
|
|
t.Stop()
|
|
|
|
delete(de.timers, t)
|
|
|
|
}
|
2020-06-28 18:53:37 +00:00
|
|
|
}
|
2020-06-30 21:37:35 +00:00
|
|
|
|
|
|
|
// ippCache is a cache of *net.UDPAddr => netaddr.IPPort mappings.
|
|
|
|
//
|
|
|
|
// It's not safe for concurrent use.
|
|
|
|
type ippCache struct {
|
|
|
|
c *lru.Cache
|
|
|
|
}
|
|
|
|
|
|
|
|
// IPPort is a caching wrapper around netaddr.FromStdAddr.
|
|
|
|
//
|
|
|
|
// It is not safe for concurrent use.
|
|
|
|
func (ic *ippCache) IPPort(u *net.UDPAddr) (netaddr.IPPort, bool) {
|
|
|
|
if u == nil || len(u.IP) > 16 {
|
|
|
|
return netaddr.IPPort{}, false
|
|
|
|
}
|
|
|
|
if ic.c == nil {
|
|
|
|
ic.c = lru.New(64) // arbitrary
|
|
|
|
}
|
|
|
|
|
|
|
|
key := ippCacheKey{ipLen: uint8(len(u.IP)), port: uint16(u.Port), zone: u.Zone}
|
|
|
|
copy(key.ip[:], u.IP[:])
|
|
|
|
|
|
|
|
if v, ok := ic.c.Get(key); ok {
|
|
|
|
return v.(netaddr.IPPort), true
|
|
|
|
}
|
|
|
|
ipp, ok := netaddr.FromStdAddr(u.IP, u.Port, u.Zone)
|
|
|
|
if ok {
|
|
|
|
ic.c.Add(key, ipp)
|
|
|
|
}
|
|
|
|
return ipp, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// ippCacheKey is the cache key type used by ippCache.IPPort.
|
|
|
|
// It must be comparable, being used as a map key in the lru package.
|
|
|
|
type ippCacheKey struct {
|
|
|
|
ip [16]byte
|
|
|
|
port uint16
|
|
|
|
ipLen uint8 // bytes in ip that are valid; rest are zero
|
|
|
|
zone string
|
|
|
|
}
|