2020-02-05 22:16:58 +00:00
|
|
|
// Copyright 2019 Tailscale & AUTHORS. All rights reserved.
|
|
|
|
// 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 (
|
|
|
|
"context"
|
2020-03-04 16:48:24 +00:00
|
|
|
"crypto/tls"
|
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"
|
2020-02-05 22:16:58 +00:00
|
|
|
"log"
|
2020-03-04 06:21:56 +00:00
|
|
|
"math/rand"
|
2020-02-05 22:16:58 +00:00
|
|
|
"net"
|
2020-02-21 22:01:51 +00:00
|
|
|
"os"
|
|
|
|
"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-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-03-02 01:35:10 +00:00
|
|
|
"golang.org/x/time/rate"
|
2020-02-21 03:10:54 +00:00
|
|
|
"tailscale.com/derp"
|
2020-02-05 22:16:58 +00:00
|
|
|
"tailscale.com/derp/derphttp"
|
2020-03-09 22:20:33 +00:00
|
|
|
"tailscale.com/derp/derpmap"
|
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-03-04 06:21:56 +00:00
|
|
|
"tailscale.com/netcheck"
|
2020-02-05 22:16:58 +00:00
|
|
|
"tailscale.com/stun"
|
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-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.
|
|
|
|
// It implements wireguard/device.Bind.
|
|
|
|
type Conn struct {
|
2020-02-18 18:55:25 +00:00
|
|
|
pconn *RebindingUDPConn
|
|
|
|
pconnPort uint16
|
|
|
|
startEpUpdate chan struct{} // send to trigger endpoint update
|
|
|
|
epFunc func(endpoints []string)
|
2020-03-07 21:11:52 +00:00
|
|
|
logf logger.Logf
|
2020-03-02 01:35:10 +00:00
|
|
|
sendLogLimit *rate.Limiter
|
2020-03-09 22:20:33 +00:00
|
|
|
derps *derpmap.World
|
|
|
|
netChecker *netcheck.Client
|
|
|
|
goroutines sync.WaitGroup
|
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.
|
|
|
|
bufferedIPv4From *net.UDPAddr // if non-nil, then bufferedIPv4Packet is valid
|
|
|
|
bufferedIPv4Packet []byte // the received packet (reused, owned by ReceiveIPv4)
|
|
|
|
|
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-10 19:25:42 +00:00
|
|
|
linkChangeMu sync.Mutex
|
|
|
|
linkState *interfaces.State
|
|
|
|
|
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]
|
|
|
|
addrsMu sync.Mutex
|
2020-03-04 18:43:06 +00:00
|
|
|
addrsByUDP map[udpAddr]*AddrSet // TODO: clean up this map sometime?
|
|
|
|
addrsByKey map[key.Public]*AddrSet // TODO: clean up this map sometime?
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-02-18 16:57:11 +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)
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-03-04 06:21:56 +00:00
|
|
|
netInfoMu sync.Mutex
|
|
|
|
netInfoFunc func(*tailcfg.NetInfo) // nil until set
|
|
|
|
netInfoLast *tailcfg.NetInfo
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
udpRecvCh chan udpReadResult
|
|
|
|
derpRecvCh chan derpReadResult
|
|
|
|
|
2020-03-04 16:48:24 +00:00
|
|
|
derpMu sync.Mutex
|
2020-03-04 20:21:40 +00:00
|
|
|
wantDerp bool
|
2020-03-04 16:48:24 +00:00
|
|
|
privateKey key.Private
|
2020-03-05 16:54:08 +00:00
|
|
|
myDerp int // nearest DERP server; 0 means none/unknown
|
|
|
|
activeDerp map[int]activeDerp
|
2020-03-04 16:48:24 +00:00
|
|
|
derpTLSConfig *tls.Config // normally nil; used by tests
|
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-03-05 16:54:08 +00:00
|
|
|
// activeDerp contains fields for an active DERP connection.
|
|
|
|
type activeDerp struct {
|
2020-03-05 20:47:54 +00:00
|
|
|
c *derphttp.Client
|
|
|
|
cancel context.CancelFunc
|
|
|
|
writeCh chan<- derpWriteRequest
|
|
|
|
lastWrite *time.Time
|
2020-03-05 16:54:08 +00:00
|
|
|
}
|
|
|
|
|
2020-02-29 19:48:34 +00:00
|
|
|
// udpAddr is the key in the addrsByUDP map.
|
|
|
|
// It maps an ip:port onto an *AddrSet.
|
2020-02-05 22:16:58 +00:00
|
|
|
type udpAddr struct {
|
|
|
|
ip wgcfg.IP
|
|
|
|
port uint16
|
|
|
|
}
|
|
|
|
|
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-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
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
// STUN, if non-empty, specifies alternate STUN servers for testing.
|
|
|
|
// If empty, the production DERP servers are used.
|
2020-02-05 22:16:58 +00:00
|
|
|
STUN []string
|
|
|
|
|
|
|
|
// EndpointsFunc optionally provides a func to be called when
|
|
|
|
// endpoints change. The called func does not own the slice.
|
|
|
|
EndpointsFunc func(endpoint []string)
|
2020-03-07 00:20:05 +00:00
|
|
|
|
|
|
|
derpTLSConfig *tls.Config // normally nil; used by tests
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// Listen creates a magic Conn listening on opts.Port.
|
|
|
|
// As the set of possible endpoints for a Conn changes, the
|
|
|
|
// callback opts.EndpointsFunc is called.
|
|
|
|
func Listen(opts Options) (*Conn, error) {
|
|
|
|
var packetConn net.PacketConn
|
|
|
|
var err error
|
2020-03-07 21:11:52 +00:00
|
|
|
|
|
|
|
logf := log.Printf
|
|
|
|
if opts.Logf != nil {
|
|
|
|
logf = opts.Logf
|
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
if opts.Port == 0 {
|
|
|
|
// Our choice of port. Start with DefaultPort.
|
|
|
|
// If unavailable, pick any port.
|
|
|
|
want := fmt.Sprintf(":%d", DefaultPort)
|
2020-03-07 21:11:52 +00:00
|
|
|
logf("magicsock: bind: trying %v\n", want)
|
2020-02-05 22:16:58 +00:00
|
|
|
packetConn, err = net.ListenPacket("udp4", want)
|
|
|
|
if err != nil {
|
|
|
|
want = ":0"
|
2020-03-07 21:11:52 +00:00
|
|
|
logf("magicsock: bind: falling back to %v (%v)\n", want, err)
|
2020-02-05 22:16:58 +00:00
|
|
|
packetConn, err = net.ListenPacket("udp4", want)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
packetConn, err = net.ListenPacket("udp4", fmt.Sprintf(":%d", opts.Port))
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("magicsock.Listen: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-03-02 17:31:25 +00:00
|
|
|
connCtx, connCtxCancel := context.WithCancel(context.Background())
|
2020-02-05 22:16:58 +00:00
|
|
|
c := &Conn{
|
2020-03-02 17:31:25 +00:00
|
|
|
pconn: new(RebindingUDPConn),
|
|
|
|
pconnPort: opts.Port,
|
|
|
|
sendLogLimit: rate.NewLimiter(rate.Every(1*time.Minute), 1),
|
|
|
|
startEpUpdate: make(chan struct{}, 1),
|
|
|
|
connCtx: connCtx,
|
|
|
|
connCtxCancel: connCtxCancel,
|
|
|
|
epFunc: opts.endpointsFunc(),
|
2020-03-07 21:11:52 +00:00
|
|
|
logf: logf,
|
2020-03-02 17:31:25 +00:00
|
|
|
addrsByUDP: make(map[udpAddr]*AddrSet),
|
2020-03-04 18:43:06 +00:00
|
|
|
addrsByKey: make(map[key.Public]*AddrSet),
|
2020-03-04 20:21:40 +00:00
|
|
|
wantDerp: true,
|
2020-03-02 17:31:25 +00:00
|
|
|
derpRecvCh: make(chan derpReadResult),
|
|
|
|
udpRecvCh: make(chan udpReadResult),
|
2020-03-07 00:20:05 +00:00
|
|
|
derpTLSConfig: opts.derpTLSConfig,
|
2020-03-09 22:20:33 +00:00
|
|
|
derps: derpmap.Prod(),
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-03-10 19:25:42 +00:00
|
|
|
c.linkState, _ = getLinkState()
|
2020-03-09 22:20:33 +00:00
|
|
|
if len(opts.STUN) > 0 {
|
|
|
|
c.derps = derpmap.NewTestWorld(opts.STUN...)
|
|
|
|
}
|
|
|
|
c.netChecker = &netcheck.Client{
|
|
|
|
DERP: c.derps,
|
|
|
|
Logf: logger.WithPrefix(c.logf, "netcheck: "),
|
|
|
|
GetSTUNConn4: func() netcheck.STUNConn { return c.pconn },
|
|
|
|
// TODO: add GetSTUNConn6 once Conn has a pconn6
|
|
|
|
}
|
|
|
|
|
2020-02-18 16:57:11 +00:00
|
|
|
c.ignoreSTUNPackets()
|
2020-02-05 22:16:58 +00:00
|
|
|
c.pconn.Reset(packetConn.(*net.UDPConn))
|
2020-02-18 18:55:25 +00:00
|
|
|
c.reSTUN()
|
2020-03-09 22:20:33 +00:00
|
|
|
|
|
|
|
c.goroutines.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer c.goroutines.Done()
|
|
|
|
c.epUpdate(connCtx)
|
|
|
|
}()
|
2020-02-05 22:16:58 +00:00
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
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() {
|
|
|
|
c.stunReceiveFunc.Store(func([]byte, *net.UDPAddr) {})
|
|
|
|
}
|
|
|
|
|
|
|
|
// epUpdate runs in its own goroutine until ctx is shut down.
|
|
|
|
// Whenever c.startEpUpdate receives a value, it starts an
|
|
|
|
// STUN endpoint lookup.
|
2020-02-05 22:16:58 +00:00
|
|
|
func (c *Conn) epUpdate(ctx context.Context) {
|
|
|
|
var lastEndpoints []string
|
|
|
|
var lastCancel func()
|
|
|
|
var lastDone chan struct{}
|
2020-03-02 20:37:52 +00:00
|
|
|
|
|
|
|
var regularUpdate <-chan time.Time
|
|
|
|
if !version.IsMobile() {
|
|
|
|
// 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.
|
|
|
|
ticker := time.NewTicker(28 * time.Second) // just under 30s, a likely UDP NAT timeout
|
|
|
|
defer ticker.Stop()
|
|
|
|
regularUpdate = ticker.C
|
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
if lastCancel != nil {
|
|
|
|
lastCancel()
|
2020-03-11 00:29:45 +00:00
|
|
|
<-lastDone
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
case <-c.startEpUpdate:
|
2020-03-02 20:37:52 +00:00
|
|
|
case <-regularUpdate:
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if lastCancel != nil {
|
|
|
|
lastCancel()
|
|
|
|
<-lastDone
|
|
|
|
}
|
|
|
|
var epCtx context.Context
|
|
|
|
epCtx, lastCancel = context.WithCancel(ctx)
|
|
|
|
lastDone = make(chan struct{})
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
defer close(lastDone)
|
2020-03-04 06:21:56 +00:00
|
|
|
|
2020-03-05 20:47:54 +00:00
|
|
|
c.cleanStaleDerp()
|
2020-03-04 06:21:56 +00:00
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
netReport, err := c.updateNetInfo(epCtx)
|
|
|
|
if err != nil {
|
|
|
|
c.logf("magicsock.Conn: updateNetInfo failed: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
endpoints, err := c.determineEndpoints(epCtx, netReport)
|
2020-02-05 22:16:58 +00:00
|
|
|
if err != nil {
|
|
|
|
c.logf("magicsock.Conn: endpoint update failed: %v", err)
|
|
|
|
// TODO(crawshaw): are there any conditions under which
|
|
|
|
// we should trigger a retry based on the error here?
|
|
|
|
return
|
|
|
|
}
|
2020-03-04 06:21:56 +00:00
|
|
|
if stringsEqual(endpoints, lastEndpoints) {
|
2020-02-05 22:16:58 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
lastEndpoints = endpoints
|
|
|
|
c.epFunc(endpoints)
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
func (c *Conn) updateNetInfo(ctx context.Context) (*netcheck.Report, error) {
|
|
|
|
if DisableSTUNForTesting {
|
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()
|
|
|
|
|
|
|
|
report, err := c.netChecker.GetReport(ctx)
|
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
|
|
|
}
|
|
|
|
|
|
|
|
ni := &tailcfg.NetInfo{
|
|
|
|
DERPLatency: map[string]float64{},
|
|
|
|
MappingVariesByDestIP: report.MappingVariesByDestIP,
|
|
|
|
HairPinning: report.HairPinning,
|
|
|
|
}
|
|
|
|
for server, d := range report.DERPLatency {
|
|
|
|
ni.DERPLatency[server] = d.Seconds()
|
|
|
|
}
|
|
|
|
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).
|
|
|
|
func (c *Conn) pickDERPFallback() int {
|
|
|
|
c.derpMu.Lock()
|
|
|
|
defer c.derpMu.Unlock()
|
|
|
|
|
|
|
|
if c.myDerp != 0 {
|
|
|
|
// If we already had one in the past, stay on it.
|
|
|
|
return c.myDerp
|
|
|
|
}
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
ids := c.derps.IDs()
|
|
|
|
if len(ids) == 0 {
|
2020-03-04 06:21:56 +00:00
|
|
|
// No DERP nodes registered.
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
func (c *Conn) callNetInfoCallback(ni *tailcfg.NetInfo) {
|
|
|
|
c.netInfoMu.Lock()
|
|
|
|
defer c.netInfoMu.Unlock()
|
|
|
|
if ni.BasicallyEqual(c.netInfoLast) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.netInfoLast = ni
|
|
|
|
if c.netInfoFunc != nil {
|
|
|
|
c.logf("netInfo update: %+v", ni)
|
|
|
|
go c.netInfoFunc(ni)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Conn) SetNetInfoCallback(fn func(*tailcfg.NetInfo)) {
|
|
|
|
if fn == nil {
|
|
|
|
panic("nil NetInfoCallback")
|
|
|
|
}
|
|
|
|
c.netInfoMu.Lock()
|
|
|
|
last := c.netInfoLast
|
|
|
|
c.netInfoFunc = fn
|
|
|
|
c.netInfoMu.Unlock()
|
|
|
|
|
|
|
|
if last != nil {
|
|
|
|
fn(last)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 20:21:40 +00:00
|
|
|
func (c *Conn) setNearestDERP(derpNum int) (wantDERP bool) {
|
2020-03-04 01:46:03 +00:00
|
|
|
c.derpMu.Lock()
|
|
|
|
defer c.derpMu.Unlock()
|
2020-03-04 20:21:40 +00:00
|
|
|
if !c.wantDerp {
|
|
|
|
c.myDerp = 0
|
|
|
|
return false
|
|
|
|
}
|
2020-03-05 23:00:56 +00:00
|
|
|
if derpNum == c.myDerp {
|
|
|
|
// No change.
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
c.myDerp = derpNum
|
2020-03-09 22:20:33 +00:00
|
|
|
c.logf("home DERP server is now %v, %v", derpNum, c.derps.ServerByID(derpNum))
|
2020-03-05 23:00:56 +00:00
|
|
|
for i, ad := range c.activeDerp {
|
|
|
|
go ad.c.NotePreferred(i == c.myDerp)
|
|
|
|
}
|
2020-03-04 20:21:40 +00:00
|
|
|
if derpNum != 0 && derpNum != c.myDerp {
|
2020-03-04 01:46:03 +00:00
|
|
|
// On change, start connecting to it:
|
|
|
|
go c.derpWriteChanOfAddr(&net.UDPAddr{IP: derpMagicIP, Port: derpNum})
|
|
|
|
}
|
2020-03-04 20:21:40 +00:00
|
|
|
return true
|
2020-03-04 01:46:03 +00:00
|
|
|
}
|
|
|
|
|
2020-02-18 16:57:11 +00:00
|
|
|
// determineEndpoints returns the machine's endpoint addresses. It
|
|
|
|
// does a STUN lookup to determine its public address.
|
2020-03-09 22:20:33 +00:00
|
|
|
func (c *Conn) determineEndpoints(ctx context.Context, nr *netcheck.Report) (ipPorts []string, err error) {
|
|
|
|
already := make(map[string]bool) // endpoint -> true
|
|
|
|
var eps []string // unique endpoints
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
addAddr := func(s, reason string) {
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: found local %s (%s)\n", s, reason)
|
2020-02-18 16:57:11 +00:00
|
|
|
if !already[s] {
|
|
|
|
already[s] = true
|
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-09 22:20:33 +00:00
|
|
|
const tailControlDoesIPv6 = false // TODO: when IPv6 filtering/splitting is enabled in tailcontrol
|
|
|
|
if nr.GlobalV6 != "" && tailControlDoesIPv6 {
|
|
|
|
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
|
|
|
|
|
|
|
if localAddr := c.pconn.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-04 06:21:56 +00:00
|
|
|
return nil, err
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-03-02 18:38:44 +00:00
|
|
|
reason := "localAddresses"
|
|
|
|
if len(ips) == 0 {
|
2020-02-05 22:16:58 +00:00
|
|
|
// Only include loopback addresses if we have no
|
|
|
|
// interfaces at all to use as endpoints. This allows
|
|
|
|
// 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-04 06:21:56 +00:00
|
|
|
return eps, 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 {
|
|
|
|
laddr := c.pconn.LocalAddr()
|
|
|
|
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-02-21 22:01:51 +00:00
|
|
|
var logPacketDests, _ = strconv.ParseBool(os.Getenv("DEBUG_LOG_PACKET_DESTS"))
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
// appendDests appends to dsts the destinations that b should be
|
|
|
|
// written to in order to reach as. Some of the returned UDPAddrs may
|
|
|
|
// be fake addrs representing DERP servers.
|
|
|
|
//
|
|
|
|
// It also returns as's current roamAddr, if any.
|
2020-03-09 16:13:28 +00:00
|
|
|
func (as *AddrSet) appendDests(dsts []*net.UDPAddr, b []byte) (_ []*net.UDPAddr, roamAddr *net.UDPAddr) {
|
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-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 sprayPeriod = 3 * time.Second
|
|
|
|
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.
|
|
|
|
for i := range as.addrs {
|
|
|
|
dsts = append(dsts, &as.addrs[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 {
|
|
|
|
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.
|
|
|
|
dsts = append(dsts, as.roamAddr)
|
|
|
|
case as.curAddr != -1:
|
|
|
|
if as.curAddr >= len(as.addrs) {
|
|
|
|
log.Printf("[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.
|
|
|
|
dsts = append(dsts, &as.addrs[as.curAddr])
|
|
|
|
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.
|
|
|
|
if len(as.addrs) > 0 {
|
|
|
|
dsts = append(dsts, &as.addrs[0])
|
|
|
|
}
|
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 {
|
2020-03-09 16:13:28 +00:00
|
|
|
log.Printf("spray=%v; roam=%v; dests=%v", spray, as.roamAddr, dsts)
|
2020-02-21 22:01:51 +00:00
|
|
|
}
|
2020-03-09 16:13:28 +00:00
|
|
|
return dsts, as.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-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-05 16:18:12 +00:00
|
|
|
c.logf("[unexpected] DERP BUG: attempting to send packet to DERP address %v", addr)
|
2020-02-29 20:48:50 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
_, err := c.pconn.WriteTo(b, addr)
|
2020-02-24 16:47:20 +00:00
|
|
|
return err
|
|
|
|
case *AddrSet:
|
|
|
|
as = v
|
|
|
|
}
|
2020-02-18 21:32:04 +00:00
|
|
|
|
|
|
|
var addrBuf [8]*net.UDPAddr
|
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 {
|
|
|
|
err := c.sendAddr(addr, as.publicKey, b)
|
|
|
|
if err == nil {
|
|
|
|
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")
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
func (c *Conn) sendAddr(addr *net.UDPAddr, pubKey key.Public, b []byte) error {
|
2020-03-04 20:21:40 +00:00
|
|
|
if !addr.IP.Equal(derpMagicIP) {
|
|
|
|
_, err := c.pconn.WriteTo(b, addr)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
ch := c.derpWriteChanOfAddr(addr)
|
|
|
|
if ch == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
errc := make(chan error, 1)
|
|
|
|
select {
|
|
|
|
case <-c.donec():
|
|
|
|
return errConnClosed
|
|
|
|
case ch <- derpWriteRequest{addr, pubKey, b, errc}:
|
2020-02-18 21:32:04 +00:00
|
|
|
select {
|
2020-03-02 17:31:25 +00:00
|
|
|
case <-c.donec():
|
2020-02-18 21:32:04 +00:00
|
|
|
return errConnClosed
|
2020-03-04 20:21:40 +00:00
|
|
|
case err := <-errc:
|
|
|
|
return err // usually nil
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-03-04 20:21:40 +00:00
|
|
|
default:
|
|
|
|
// Too many writes queued. Drop packet.
|
|
|
|
return 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?
|
|
|
|
const bufferedDerpWritesBeforeDrop = 4
|
|
|
|
|
|
|
|
// derpWriteChanOfAddr returns a DERP client for fake UDP addresses that
|
|
|
|
// represent DERP servers, creating them as necessary. For real UDP
|
|
|
|
// addresses, it returns nil.
|
|
|
|
func (c *Conn) derpWriteChanOfAddr(addr *net.UDPAddr) chan<- derpWriteRequest {
|
|
|
|
if !addr.IP.Equal(derpMagicIP) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
c.derpMu.Lock()
|
|
|
|
defer c.derpMu.Unlock()
|
2020-03-04 20:21:40 +00:00
|
|
|
if !c.wantDerp {
|
|
|
|
return nil
|
|
|
|
}
|
2020-02-28 19:13:28 +00:00
|
|
|
if c.privateKey.IsZero() {
|
|
|
|
c.logf("DERP lookup of %v with no private key; ignoring", addr.IP)
|
|
|
|
return nil
|
|
|
|
}
|
2020-03-05 16:54:08 +00:00
|
|
|
ad, ok := c.activeDerp[addr.Port]
|
2020-02-18 21:32:04 +00:00
|
|
|
if !ok {
|
2020-03-05 16:54:08 +00:00
|
|
|
if c.activeDerp == nil {
|
|
|
|
c.activeDerp = make(map[int]activeDerp)
|
2020-02-18 21:32:04 +00:00
|
|
|
}
|
2020-03-09 22:20:33 +00:00
|
|
|
derpSrv := c.derps.ServerByID(addr.Port)
|
|
|
|
if derpSrv == nil || derpSrv.HostHTTPS == "" {
|
2020-03-05 15:58:16 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-03-09 22:20:33 +00:00
|
|
|
|
2020-03-05 16:54:08 +00:00
|
|
|
// TODO(bradfitz): don't hold derpMu here. It's slow. Release first and use singleflight to dial+re-lock to add.
|
2020-03-09 22:20:33 +00:00
|
|
|
dc, err := derphttp.NewClient(c.privateKey, "https://"+derpSrv.HostHTTPS+"/derp", c.logf)
|
2020-02-18 21:32:04 +00:00
|
|
|
if err != nil {
|
2020-03-09 22:20:33 +00:00
|
|
|
c.logf("derphttp.NewClient: port %d, host %q invalid? err: %v", addr.Port, derpSrv.HostHTTPS, err)
|
2020-02-05 22:16:58 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-03-05 23:00:56 +00:00
|
|
|
dc.NotePreferred(c.myDerp == addr.Port)
|
2020-03-05 18:29:19 +00:00
|
|
|
dc.DNSCache = dnscache.Get()
|
2020-03-04 16:48:24 +00:00
|
|
|
dc.TLSConfig = c.derpTLSConfig
|
2020-02-18 21:32:04 +00:00
|
|
|
|
2020-02-28 19:13:28 +00:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
2020-03-05 16:54:08 +00:00
|
|
|
ch := make(chan derpWriteRequest, bufferedDerpWritesBeforeDrop)
|
|
|
|
|
|
|
|
ad.c = dc
|
|
|
|
ad.writeCh = ch
|
|
|
|
ad.cancel = cancel
|
2020-03-05 20:47:54 +00:00
|
|
|
ad.lastWrite = new(time.Time)
|
2020-03-05 20:22:33 +00:00
|
|
|
c.activeDerp[addr.Port] = ad
|
2020-03-05 16:54:08 +00:00
|
|
|
|
2020-02-28 19:13:28 +00:00
|
|
|
go c.runDerpReader(ctx, addr, dc)
|
2020-03-05 16:54:08 +00:00
|
|
|
go c.runDerpWriter(ctx, addr, dc, ch)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-03-05 20:47:54 +00:00
|
|
|
*ad.lastWrite = time.Now()
|
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-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 {
|
|
|
|
derpAddr *net.UDPAddr
|
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-02-28 19:13:28 +00:00
|
|
|
func (c *Conn) runDerpReader(ctx context.Context, derpFakeAddr *net.UDPAddr, dc *derphttp.Client) {
|
2020-02-18 21:32:04 +00:00
|
|
|
didCopy := make(chan struct{}, 1)
|
2020-02-21 03:10:54 +00:00
|
|
|
var buf [derp.MaxPacketSize]byte
|
2020-03-04 17:35:32 +00:00
|
|
|
|
|
|
|
res := derpReadResult{derpAddr: derpFakeAddr}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
2020-02-21 03:10:54 +00:00
|
|
|
msg, err := dc.Recv(buf[:])
|
2020-02-28 19:13:28 +00:00
|
|
|
if err == derphttp.ErrClientClosed {
|
|
|
|
return
|
|
|
|
}
|
2020-02-18 21:32:04 +00:00
|
|
|
if err != nil {
|
|
|
|
select {
|
2020-03-02 17:31:25 +00:00
|
|
|
case <-c.donec():
|
2020-02-18 21:32:04 +00:00
|
|
|
return
|
2020-02-28 19:13:28 +00:00
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
2020-02-18 21:32:04 +00:00
|
|
|
default:
|
|
|
|
}
|
2020-03-05 20:22:33 +00:00
|
|
|
c.logf("derp.Recv(derp%d): %v", derpFakeAddr.Port, 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-03-07 21:11:52 +00:00
|
|
|
c.logf("got derp %v packet: %q", derpFakeAddr, m.Data)
|
2020-03-04 17:35:32 +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-02 17:31:25 +00:00
|
|
|
case <-c.donec():
|
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 {
|
|
|
|
addr *net.UDPAddr
|
|
|
|
pubKey key.Public
|
|
|
|
b []byte
|
|
|
|
errc chan<- error
|
|
|
|
}
|
|
|
|
|
|
|
|
// runDerpWriter runs in a goroutine for the life of a DERP
|
|
|
|
// connection, handling received packets.
|
2020-02-28 19:13:28 +00:00
|
|
|
func (c *Conn) runDerpWriter(ctx context.Context, derpFakeAddr *net.UDPAddr, dc *derphttp.Client, ch <-chan derpWriteRequest) {
|
2020-02-18 21:32:04 +00:00
|
|
|
for {
|
|
|
|
select {
|
2020-02-28 19:13:28 +00:00
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
2020-03-02 17:31:25 +00:00
|
|
|
case <-c.donec():
|
2020-02-18 21:32:04 +00:00
|
|
|
return
|
|
|
|
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
|
|
|
}
|
|
|
|
select {
|
|
|
|
case wr.errc <- err:
|
2020-03-02 17:31:25 +00:00
|
|
|
case <-c.donec():
|
2020-02-18 21:32:04 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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.
|
|
|
|
func (c *Conn) findEndpoint(addr *net.UDPAddr) conn.Endpoint {
|
|
|
|
if as := c.findAddrSet(addr); as != nil {
|
|
|
|
return as
|
|
|
|
}
|
|
|
|
// 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-29 19:48:34 +00:00
|
|
|
func (c *Conn) findAddrSet(addr *net.UDPAddr) *AddrSet {
|
2020-02-05 22:16:58 +00:00
|
|
|
var epAddr udpAddr
|
|
|
|
copy(epAddr.ip.Addr[:], addr.IP.To16())
|
|
|
|
epAddr.port = uint16(addr.Port)
|
|
|
|
|
2020-02-29 19:48:34 +00:00
|
|
|
c.addrsMu.Lock()
|
|
|
|
defer c.addrsMu.Unlock()
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-02-29 19:48:34 +00:00
|
|
|
return c.addrsByUDP[epAddr]
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-18 21:32:04 +00:00
|
|
|
type udpReadResult struct {
|
|
|
|
n int
|
|
|
|
err error
|
|
|
|
addr *net.UDPAddr
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
n, pAddr, err := c.pconn.ReadFrom(b)
|
|
|
|
if err != nil {
|
|
|
|
select {
|
|
|
|
case c.udpRecvCh <- udpReadResult{err: err}:
|
|
|
|
case <-c.donec():
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
addr := pAddr.(*net.UDPAddr)
|
|
|
|
if stun.Is(b[:n]) {
|
|
|
|
c.stunReceiveFunc.Load().(func([]byte, *net.UDPAddr))(b, addr)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
addr.IP = addr.IP.To4()
|
|
|
|
select {
|
|
|
|
case c.udpRecvCh <- udpReadResult{n: n, addr: addr}:
|
|
|
|
case <-c.donec():
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
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-03-07 21:36:18 +00:00
|
|
|
// First, process any buffered packet from earlier.
|
|
|
|
if addr := c.bufferedIPv4From; addr != nil {
|
|
|
|
c.bufferedIPv4From = nil
|
|
|
|
return copy(b, c.bufferedIPv4Packet), c.findEndpoint(addr), addr, nil
|
|
|
|
}
|
|
|
|
|
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-02-18 21:32:04 +00:00
|
|
|
select {
|
|
|
|
case dm := <-c.derpRecvCh:
|
|
|
|
// Cancel the pconn read goroutine
|
|
|
|
c.pconn.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.
|
|
|
|
c.bufferedIPv4From = um.addr
|
|
|
|
c.bufferedIPv4Packet = append(c.bufferedIPv4Packet[:0], b[:um.n]...)
|
|
|
|
}
|
2020-02-18 21:32:04 +00:00
|
|
|
c.pconn.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-02-18 21:32:04 +00:00
|
|
|
n, addr = dm.n, dm.derpAddr
|
|
|
|
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-03-04 18:43:06 +00:00
|
|
|
c.addrsMu.Lock()
|
|
|
|
addrSet = c.addrsByKey[dm.src]
|
|
|
|
c.addrsMu.Unlock()
|
|
|
|
|
|
|
|
if addrSet == nil {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
n, addr = um.n, um.addr
|
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
|
|
|
|
} else {
|
|
|
|
ep = c.findEndpoint(addr)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-03-07 21:36:18 +00:00
|
|
|
return n, ep, addr, nil
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-24 12:27:48 +00:00
|
|
|
func (c *Conn) ReceiveIPv6(buff []byte) (int, conn.Endpoint, *net.UDPAddr, error) {
|
2020-02-05 22:16:58 +00:00
|
|
|
// TODO(crawshaw): IPv6 support
|
|
|
|
return 0, nil, nil, syscall.EAFNOSUPPORT
|
|
|
|
}
|
|
|
|
|
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-02-28 19:13:28 +00:00
|
|
|
c.derpMu.Lock()
|
|
|
|
defer c.derpMu.Unlock()
|
|
|
|
|
|
|
|
oldKey, newKey := c.privateKey, key.Private(privateKey)
|
|
|
|
if newKey == oldKey {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
c.privateKey = newKey
|
|
|
|
if oldKey.IsZero() {
|
|
|
|
// Initial configuration on start.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Key changed. Close any DERP connections.
|
2020-03-02 17:31:25 +00:00
|
|
|
c.closeAllDerpLocked()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-03-04 20:21:40 +00:00
|
|
|
// SetDERPEnabled controls whether DERP is used.
|
|
|
|
// New connections have it enabled by default.
|
|
|
|
func (c *Conn) SetDERPEnabled(wantDerp bool) {
|
|
|
|
c.derpMu.Lock()
|
|
|
|
defer c.derpMu.Unlock()
|
|
|
|
|
|
|
|
c.wantDerp = wantDerp
|
|
|
|
if !wantDerp {
|
|
|
|
c.closeAllDerpLocked()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-02 17:31:25 +00:00
|
|
|
// c.derpMu must be held.
|
|
|
|
func (c *Conn) closeAllDerpLocked() {
|
2020-03-05 16:54:08 +00:00
|
|
|
for i := range c.activeDerp {
|
|
|
|
c.closeDerpLocked(i)
|
2020-02-28 19:13:28 +00:00
|
|
|
}
|
2020-03-05 16:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// c.derpMu must be held.
|
|
|
|
func (c *Conn) closeDerpLocked(node int) {
|
|
|
|
if ad, ok := c.activeDerp[node]; ok {
|
2020-03-05 20:22:33 +00:00
|
|
|
c.logf("closing connection to derp%v", node)
|
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-05 20:47:54 +00:00
|
|
|
func (c *Conn) cleanStaleDerp() {
|
|
|
|
c.derpMu.Lock()
|
|
|
|
defer c.derpMu.Unlock()
|
|
|
|
const inactivityTime = 60 * time.Second
|
|
|
|
tooOld := time.Now().Add(-inactivityTime)
|
|
|
|
for i, ad := range c.activeDerp {
|
|
|
|
if i == c.myDerp {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if ad.lastWrite.Before(tooOld) {
|
|
|
|
c.logf("closing stale DERP connection to derp%v", i)
|
|
|
|
c.closeDerpLocked(i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
func (c *Conn) Close() error {
|
2020-03-02 17:31:25 +00:00
|
|
|
// TODO: make this safe for concurrent Close? it's safe now only if Close calls are serialized.
|
2020-02-18 21:32:04 +00:00
|
|
|
select {
|
2020-03-02 17:31:25 +00:00
|
|
|
case <-c.donec():
|
2020-02-18 21:32:04 +00:00
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
}
|
2020-03-02 17:31:25 +00:00
|
|
|
c.connCtxCancel()
|
|
|
|
|
|
|
|
c.derpMu.Lock()
|
|
|
|
c.closeAllDerpLocked()
|
|
|
|
c.derpMu.Unlock()
|
|
|
|
|
2020-03-09 22:20:33 +00:00
|
|
|
err := c.pconn.Close()
|
|
|
|
c.goroutines.Wait()
|
|
|
|
return err
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-18 18:55:25 +00:00
|
|
|
func (c *Conn) reSTUN() {
|
|
|
|
select {
|
|
|
|
case c.startEpUpdate <- struct{}{}:
|
2020-03-02 17:31:25 +00:00
|
|
|
case <-c.donec():
|
2020-02-18 18:55:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-10 19:25:42 +00:00
|
|
|
// LinkChange should be called whenever something changed with the
|
|
|
|
// network, no matter how minor. The LinkChange method then looks
|
|
|
|
// at the state of the network and decides whether the change from
|
|
|
|
// before is interesting enough to warrant taking action on.
|
2020-02-05 22:16:58 +00:00
|
|
|
func (c *Conn) LinkChange() {
|
2020-02-18 18:55:25 +00:00
|
|
|
defer c.reSTUN()
|
2020-02-05 22:16:58 +00:00
|
|
|
|
2020-03-10 19:25:42 +00:00
|
|
|
c.linkChangeMu.Lock()
|
|
|
|
defer c.linkChangeMu.Unlock()
|
|
|
|
|
|
|
|
cur, err := getLinkState()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if c.linkState != nil && !cur.Equal(c.linkState) {
|
|
|
|
c.linkState = cur
|
|
|
|
c.rebind()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getLinkState() (*interfaces.State, error) {
|
|
|
|
s, err := interfaces.GetState()
|
|
|
|
if s != nil {
|
|
|
|
s.RemoveTailscaleInterfaces()
|
|
|
|
}
|
|
|
|
return s, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Conn) rebind() {
|
2020-02-05 22:16:58 +00:00
|
|
|
if c.pconnPort != 0 {
|
|
|
|
c.pconn.mu.Lock()
|
|
|
|
if err := c.pconn.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
|
|
|
}
|
|
|
|
packetConn, err := net.ListenPacket("udp4", fmt.Sprintf(":%d", c.pconnPort))
|
|
|
|
if err == nil {
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: link change rebound port: %d", c.pconnPort)
|
2020-02-05 22:16:58 +00:00
|
|
|
c.pconn.pconn = packetConn.(*net.UDPConn)
|
|
|
|
c.pconn.mu.Unlock()
|
|
|
|
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-02-05 22:16:58 +00:00
|
|
|
c.pconn.mu.Unlock()
|
|
|
|
}
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: link change, binding new port")
|
2020-02-05 22:16:58 +00:00
|
|
|
packetConn, err := net.ListenPacket("udp4", ":0")
|
|
|
|
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
|
|
|
|
}
|
|
|
|
c.pconn.Reset(packetConn.(*net.UDPConn))
|
|
|
|
}
|
|
|
|
|
2020-02-24 12:27:48 +00:00
|
|
|
// AddrSet is a set of UDP addresses that implements wireguard/conn.Endpoint.
|
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.
|
|
|
|
addrs []net.UDPAddr
|
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
|
|
|
|
Logf logger.Logf // Logf, if non-nil, is used instead of log.Printf
|
|
|
|
|
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.
|
|
|
|
roamAddr *net.UDPAddr
|
|
|
|
|
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
|
|
|
|
|
|
|
|
// lastSpray is the lsat time we sprayed a packet.
|
|
|
|
lastSpray time.Time
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-03-09 16:13:28 +00:00
|
|
|
func (as *AddrSet) timeNow() time.Time {
|
|
|
|
if as.clock != nil {
|
|
|
|
return as.clock()
|
|
|
|
}
|
|
|
|
return time.Now()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (as *AddrSet) logf(format string, args ...interface{}) {
|
|
|
|
if as.Logf != nil {
|
|
|
|
as.Logf(format, args...)
|
|
|
|
} else {
|
|
|
|
log.Printf(format, args...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
var noAddr = &net.UDPAddr{
|
|
|
|
IP: net.ParseIP("127.127.127.127"),
|
|
|
|
Port: 127,
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AddrSet) dst() *net.UDPAddr {
|
|
|
|
a.mu.Lock()
|
|
|
|
defer a.mu.Unlock()
|
|
|
|
|
|
|
|
if a.roamAddr != nil {
|
|
|
|
return a.roamAddr
|
|
|
|
}
|
|
|
|
if len(a.addrs) == 0 {
|
|
|
|
return noAddr
|
|
|
|
}
|
|
|
|
i := a.curAddr
|
|
|
|
if i == -1 {
|
|
|
|
i = 0
|
|
|
|
}
|
|
|
|
return &a.addrs[i]
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
func (a *AddrSet) DstToBytes() []byte {
|
|
|
|
return packUDPAddr(a.dst())
|
|
|
|
}
|
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 {
|
|
|
|
return a.dst().IP
|
|
|
|
}
|
|
|
|
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-03-06 18:36:33 +00:00
|
|
|
if a.roamAddr != nil && equalUDPAddr(new, a.roamAddr) {
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2020-03-09 16:13:28 +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 {
|
2020-03-09 16:13:28 +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
|
|
|
}
|
|
|
|
a.roamAddr = new
|
|
|
|
|
|
|
|
case a.roamAddr != nil:
|
2020-03-09 16:13:28 +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
|
|
|
|
a.curAddr = index
|
|
|
|
|
|
|
|
case a.curAddr == -1:
|
2020-03-09 16:13:28 +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
|
|
|
|
|
|
|
|
case index < a.curAddr:
|
2020-03-09 16:13:28 +00:00
|
|
|
a.logf("magicsock: rx %s from low-pri %s (%d), keeping current %s (%d)", pk, new, index, old, a.curAddr)
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
default: // index > a.curAddr
|
2020-03-09 16:13:28 +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
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
|
|
|
fmt.Fprintf(buf, "roam:%s:%d", a.roamAddr.IP, a.roamAddr.Port)
|
|
|
|
}
|
|
|
|
for i, addr := range a.addrs {
|
|
|
|
if i > 0 || a.roamAddr != nil {
|
|
|
|
buf.WriteString(", ")
|
|
|
|
}
|
|
|
|
fmt.Fprintf(buf, "%s:%d", addr.IP, addr.Port)
|
|
|
|
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.
|
|
|
|
// The key is the public key of the peer and addrs is a
|
|
|
|
// comma-separated list of UDP ip:ports.
|
2020-02-24 12:27:48 +00:00
|
|
|
func (c *Conn) CreateEndpoint(key [32]byte, addrs string) (conn.Endpoint, error) {
|
2020-02-05 22:16:58 +00:00
|
|
|
pk := wgcfg.Key(key)
|
2020-03-07 21:11:52 +00:00
|
|
|
c.logf("magicsock: CreateEndpoint: key=%s: %s", pk.ShortString(), addrs)
|
2020-02-05 22:16:58 +00:00
|
|
|
a := &AddrSet{
|
|
|
|
publicKey: key,
|
|
|
|
curAddr: -1,
|
|
|
|
}
|
|
|
|
|
2020-02-18 16:57:11 +00:00
|
|
|
if addrs != "" {
|
|
|
|
for _, ep := range strings.Split(addrs, ",") {
|
2020-02-05 22:16:58 +00:00
|
|
|
addr, err := net.ResolveUDPAddr("udp", ep)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if ip4 := addr.IP.To4(); ip4 != nil {
|
|
|
|
addr.IP = ip4
|
|
|
|
}
|
|
|
|
a.addrs = append(a.addrs, *addr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-29 19:48:34 +00:00
|
|
|
c.addrsMu.Lock()
|
|
|
|
for _, addr := range a.addrs {
|
2020-02-29 20:48:50 +00:00
|
|
|
if addr.IP.Equal(derpMagicIP) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
var epAddr udpAddr
|
|
|
|
copy(epAddr.ip.Addr[:], addr.IP.To16())
|
|
|
|
epAddr.port = uint16(addr.Port)
|
2020-02-29 19:48:34 +00:00
|
|
|
c.addrsByUDP[epAddr] = a
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-03-06 02:12:45 +00:00
|
|
|
c.addrsByKey[key] = a
|
2020-02-29 19:48:34 +00:00
|
|
|
c.addrsMu.Unlock()
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|