mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-27 10:47:35 +00:00
wgengine/magicsock: sprinkle more docstrings.
Magicsock is too damn big, but this might help me page it back in faster next time. Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:
parent
34a0292433
commit
9cee0bfa8c
@ -110,37 +110,58 @@ func inTest() bool {
|
|||||||
// A Conn routes UDP packets and actively manages a list of its endpoints.
|
// A Conn routes UDP packets and actively manages a list of its endpoints.
|
||||||
// It implements wireguard/conn.Bind.
|
// It implements wireguard/conn.Bind.
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
pconnPort uint16 // the preferred port from opts.Port; 0 means auto
|
// This block mirrors the contents and field order of the Options
|
||||||
pconn4 *RebindingUDPConn
|
// struct. Initialized once at construction, then constant.
|
||||||
pconn6 *RebindingUDPConn // non-nil if IPv6 available
|
|
||||||
|
logf logger.Logf
|
||||||
|
port uint16 // the preferred port from opts.Port; 0 means auto
|
||||||
epFunc func(endpoints []string)
|
epFunc func(endpoints []string)
|
||||||
derpActiveFunc func()
|
derpActiveFunc func()
|
||||||
logf logger.Logf
|
idleFunc func() time.Duration // nil means unknown
|
||||||
sendLogLimit *rate.Limiter
|
packetListener nettype.PacketListener
|
||||||
netChecker *netcheck.Client
|
|
||||||
idleFunc func() time.Duration // nil means unknown
|
|
||||||
noteRecvActivity func(tailcfg.DiscoKey) // or nil, see Options.NoteRecvActivity
|
noteRecvActivity func(tailcfg.DiscoKey) // or nil, see Options.NoteRecvActivity
|
||||||
simulatedNetwork bool
|
simulatedNetwork bool
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// No locking required to access these fields, either because
|
||||||
|
// they're static after construction, or are wholly owned by a
|
||||||
|
// single goroutine.
|
||||||
|
|
||||||
|
connCtx context.Context // closed on Conn.Close
|
||||||
|
connCtxCancel func() // closes connCtx
|
||||||
|
|
||||||
|
// pconn4 and pconn6 are the underlying UDP sockets used to
|
||||||
|
// send/receive packets for wireguard and other magicsock
|
||||||
|
// protocols.
|
||||||
|
pconn4 *RebindingUDPConn
|
||||||
|
pconn6 *RebindingUDPConn
|
||||||
|
|
||||||
|
// netChecker is the prober that discovers local network
|
||||||
|
// conditions, including the closest DERP relay and NAT mappings.
|
||||||
|
netChecker *netcheck.Client
|
||||||
|
|
||||||
|
// sendLogLimit is a rate limiter for errors logged in the (hot)
|
||||||
|
// packet sending codepath. It's so that, if magicsock gets into a
|
||||||
|
// bad state, we don't spam one error per wireguard packet being
|
||||||
|
// transmitted.
|
||||||
|
// TODO(danderson): now that we have global rate-limiting, is this still useful?
|
||||||
|
sendLogLimit *rate.Limiter
|
||||||
|
|
||||||
// bufferedIPv4From and bufferedIPv4Packet are owned by
|
// bufferedIPv4From and bufferedIPv4Packet are owned by
|
||||||
// ReceiveIPv4, and used when both a DERP and IPv4 packet arrive
|
// 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.
|
// at the same time. It stores the IPv4 packet for use in the next call.
|
||||||
bufferedIPv4From netaddr.IPPort // if non-zero, then bufferedIPv4Packet is valid
|
bufferedIPv4From netaddr.IPPort // if non-zero, then bufferedIPv4Packet is valid
|
||||||
bufferedIPv4Packet []byte // the received packet (reused, owned by ReceiveIPv4)
|
bufferedIPv4Packet []byte // the received packet (reused, owned by ReceiveIPv4)
|
||||||
|
|
||||||
connCtx context.Context // closed on Conn.Close
|
|
||||||
connCtxCancel func() // closes connCtx
|
|
||||||
|
|
||||||
// stunReceiveFunc holds the current STUN packet processing func.
|
// stunReceiveFunc holds the current STUN packet processing func.
|
||||||
// Its Loaded value is always non-nil.
|
// Its Loaded value is always non-nil.
|
||||||
stunReceiveFunc atomic.Value // of func(p []byte, fromAddr *net.UDPAddr)
|
stunReceiveFunc atomic.Value // of func(p []byte, fromAddr *net.UDPAddr)
|
||||||
|
|
||||||
|
// udpRecvCh and derpRecvCh are used by ReceiveIPv4 to multiplex
|
||||||
|
// reads from DERP and the pconn4.
|
||||||
udpRecvCh chan udpReadResult
|
udpRecvCh chan udpReadResult
|
||||||
derpRecvCh chan derpReadResult
|
derpRecvCh chan derpReadResult
|
||||||
|
|
||||||
// packetListener optionally specifies a test hook to open a PacketConn.
|
|
||||||
packetListener nettype.PacketListener
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
mu sync.Mutex // guards all following fields; see userspaceEngine lock ordering rules
|
mu sync.Mutex // guards all following fields; see userspaceEngine lock ordering rules
|
||||||
muCond *sync.Cond
|
muCond *sync.Cond
|
||||||
@ -148,17 +169,43 @@ type Conn struct {
|
|||||||
started bool // Start was called
|
started bool // Start was called
|
||||||
closed bool // Close was called
|
closed bool // Close was called
|
||||||
|
|
||||||
|
// endpointsUpdateActive indicates that updateEndpoints is
|
||||||
|
// currently running. It's used to deduplicate concurrent endpoint
|
||||||
|
// update requests.
|
||||||
endpointsUpdateActive bool
|
endpointsUpdateActive bool
|
||||||
wantEndpointsUpdate string // true if non-empty; string is reason
|
// wantEndpointsUpdate, if non-empty, means that a new endpoints
|
||||||
lastEndpoints []string
|
// update should begin immediately after the currently-running one
|
||||||
peerSet map[key.Public]struct{}
|
// completes. It can only be non-empty if
|
||||||
|
// endpointsUpdateActive==true.
|
||||||
|
wantEndpointsUpdate string // true if non-empty; string is reason
|
||||||
|
// lastEndpoints records the endpoints found during the previous
|
||||||
|
// endpoint discovery. It's used to avoid duplicate endpoint
|
||||||
|
// change notifications.
|
||||||
|
lastEndpoints []string
|
||||||
|
|
||||||
discoPrivate key.Private
|
// peerSet is the set of peers that are currently configured in
|
||||||
discoPublic tailcfg.DiscoKey // public of discoPrivate
|
// WireGuard. These are not used to filter inbound or outbound
|
||||||
discoShort string // ShortString of discoPublic (to save logging work later)
|
// traffic at all, but only to track what state can be cleaned up
|
||||||
nodeOfDisco map[tailcfg.DiscoKey]*tailcfg.Node
|
// in other maps below that are keyed by peer public key.
|
||||||
discoOfNode map[tailcfg.NodeKey]tailcfg.DiscoKey
|
peerSet map[key.Public]struct{}
|
||||||
discoOfAddr map[netaddr.IPPort]tailcfg.DiscoKey // validated non-DERP paths only
|
|
||||||
|
// discoPrivate is the private naclbox key used for active
|
||||||
|
// discovery traffic. It's created once near (but not during)
|
||||||
|
// construction.
|
||||||
|
discoPrivate key.Private
|
||||||
|
discoPublic tailcfg.DiscoKey // public of discoPrivate
|
||||||
|
discoShort string // ShortString of discoPublic (to save logging work later)
|
||||||
|
// nodeOfDisco tracks the networkmap Node entity for each peer
|
||||||
|
// discovery key.
|
||||||
|
//
|
||||||
|
// TODO(danderson): the only thing we ever use from this is the
|
||||||
|
// peer's WireGuard public key. This could be a map of DiscoKey to
|
||||||
|
// NodeKey.
|
||||||
|
nodeOfDisco map[tailcfg.DiscoKey]*tailcfg.Node
|
||||||
|
discoOfNode map[tailcfg.NodeKey]tailcfg.DiscoKey
|
||||||
|
discoOfAddr map[netaddr.IPPort]tailcfg.DiscoKey // validated non-DERP paths only
|
||||||
|
// endpointsOfDisco tracks the wireguard-go endpoints for peers
|
||||||
|
// with recent activity.
|
||||||
endpointOfDisco map[tailcfg.DiscoKey]*discoEndpoint // those with activity only
|
endpointOfDisco map[tailcfg.DiscoKey]*discoEndpoint // those with activity only
|
||||||
sharedDiscoKey map[tailcfg.DiscoKey]*[32]byte // nacl/box precomputed key
|
sharedDiscoKey map[tailcfg.DiscoKey]*[32]byte // nacl/box precomputed key
|
||||||
|
|
||||||
@ -173,18 +220,35 @@ type Conn struct {
|
|||||||
// 10.0.0.1:1 -> [10.0.0.1:1, 10.0.0.2:2]
|
// 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.2:2 -> [10.0.0.1:1, 10.0.0.2:2]
|
||||||
// 10.0.0.3:3 -> [10.0.0.3:3]
|
// 10.0.0.3:3 -> [10.0.0.3:3]
|
||||||
|
//
|
||||||
|
// Used only to communicate with legacy, pre-active-discovery
|
||||||
|
// clients.
|
||||||
addrsByUDP map[netaddr.IPPort]*AddrSet
|
addrsByUDP map[netaddr.IPPort]*AddrSet
|
||||||
|
|
||||||
// addrsByKey maps from public keys (as seen by incoming DERP
|
// addrsByKey maps from public keys (as seen by incoming DERP
|
||||||
// packets) to its AddrSet (the same values as in addrsByUDP).
|
// packets) to its AddrSet (the same values as in addrsByUDP).
|
||||||
|
//
|
||||||
|
// Used only to communicate with legacy, pre-active-discovery
|
||||||
|
// clients.
|
||||||
addrsByKey map[key.Public]*AddrSet
|
addrsByKey map[key.Public]*AddrSet
|
||||||
|
|
||||||
|
// netInfoFunc is a callback that provides a tailcfg.NetInfo when
|
||||||
|
// discovered network conditions change.
|
||||||
|
//
|
||||||
|
// TODO(danderson): why can't it be set at construction time?
|
||||||
|
// There seem to be a few natural places in ipn/local.go to
|
||||||
|
// swallow untimely invocations.
|
||||||
netInfoFunc func(*tailcfg.NetInfo) // nil until set
|
netInfoFunc func(*tailcfg.NetInfo) // nil until set
|
||||||
|
// netInfoLast is the NetInfo provided in the last call to
|
||||||
|
// netInfoFunc. It's used to deduplicate calls to netInfoFunc.
|
||||||
|
//
|
||||||
|
// TODO(danderson): should all the deduping happen in
|
||||||
|
// ipn/local.go? We seem to be doing dedupe at several layers, and
|
||||||
|
// magicsock could do with any complexity reduction it can get.
|
||||||
netInfoLast *tailcfg.NetInfo
|
netInfoLast *tailcfg.NetInfo
|
||||||
|
|
||||||
derpMap *tailcfg.DERPMap // nil (or zero regions/nodes) means DERP is disabled
|
derpMap *tailcfg.DERPMap // nil (or zero regions/nodes) means DERP is disabled
|
||||||
netMap *controlclient.NetworkMap
|
netMap *controlclient.NetworkMap
|
||||||
privateKey key.Private
|
privateKey key.Private // WireGuard private key for this node
|
||||||
everHadKey bool // whether we ever had a non-zero private key
|
everHadKey bool // whether we ever had a non-zero private key
|
||||||
myDerp int // nearest DERP region ID; 0 means none/unknown
|
myDerp int // nearest DERP region ID; 0 means none/unknown
|
||||||
derpStarted chan struct{} // closed on first connection to DERP; for tests & cleaner Close
|
derpStarted chan struct{} // closed on first connection to DERP; for tests & cleaner Close
|
||||||
@ -370,7 +434,7 @@ func newConn() *Conn {
|
|||||||
// It doesn't start doing anything until Start is called.
|
// It doesn't start doing anything until Start is called.
|
||||||
func NewConn(opts Options) (*Conn, error) {
|
func NewConn(opts Options) (*Conn, error) {
|
||||||
c := newConn()
|
c := newConn()
|
||||||
c.pconnPort = opts.Port
|
c.port = opts.Port
|
||||||
c.logf = opts.logf()
|
c.logf = opts.logf()
|
||||||
c.epFunc = opts.endpointsFunc()
|
c.epFunc = opts.endpointsFunc()
|
||||||
c.derpActiveFunc = opts.derpActiveFunc()
|
c.derpActiveFunc = opts.derpActiveFunc()
|
||||||
@ -834,9 +898,9 @@ func (c *Conn) determineEndpoints(ctx context.Context) (ipPorts []string, reason
|
|||||||
// port mapping on their router to the same explicit
|
// port mapping on their router to the same explicit
|
||||||
// port that tailscaled is running with. Worst case
|
// port that tailscaled is running with. Worst case
|
||||||
// it's an invalid candidate mapping.
|
// it's an invalid candidate mapping.
|
||||||
if nr.MappingVariesByDestIP.EqualBool(true) && c.pconnPort != 0 {
|
if nr.MappingVariesByDestIP.EqualBool(true) && c.port != 0 {
|
||||||
if ip, _, err := net.SplitHostPort(nr.GlobalV4); err == nil {
|
if ip, _, err := net.SplitHostPort(nr.GlobalV4); err == nil {
|
||||||
addAddr(net.JoinHostPort(ip, strconv.Itoa(int(c.pconnPort))), "port_in")
|
addAddr(net.JoinHostPort(ip, strconv.Itoa(int(c.port))), "port_in")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2499,18 +2563,18 @@ func (c *Conn) bind1(ruc **RebindingUDPConn, which string) error {
|
|||||||
var pc net.PacketConn
|
var pc net.PacketConn
|
||||||
var err error
|
var err error
|
||||||
listenCtx := context.Background() // unused without DNS name to resolve
|
listenCtx := context.Background() // unused without DNS name to resolve
|
||||||
if c.pconnPort == 0 && DefaultPort != 0 {
|
if c.port == 0 && DefaultPort != 0 {
|
||||||
pc, err = c.listenPacket(listenCtx, which, net.JoinHostPort(host, fmt.Sprint(DefaultPort)))
|
pc, err = c.listenPacket(listenCtx, which, net.JoinHostPort(host, fmt.Sprint(DefaultPort)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logf("magicsock: bind: default port %s/%v unavailable; picking random", which, DefaultPort)
|
c.logf("magicsock: bind: default port %s/%v unavailable; picking random", which, DefaultPort)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pc == nil {
|
if pc == nil {
|
||||||
pc, err = c.listenPacket(listenCtx, which, net.JoinHostPort(host, fmt.Sprint(c.pconnPort)))
|
pc, err = c.listenPacket(listenCtx, which, net.JoinHostPort(host, fmt.Sprint(c.port)))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logf("magicsock: bind(%s/%v): %v", which, c.pconnPort, err)
|
c.logf("magicsock: bind(%s/%v): %v", which, c.port, err)
|
||||||
return fmt.Errorf("magicsock: bind: %s/%d: %v", which, c.pconnPort, err)
|
return fmt.Errorf("magicsock: bind: %s/%d: %v", which, c.port, err)
|
||||||
}
|
}
|
||||||
if *ruc == nil {
|
if *ruc == nil {
|
||||||
*ruc = new(RebindingUDPConn)
|
*ruc = new(RebindingUDPConn)
|
||||||
@ -2527,19 +2591,19 @@ func (c *Conn) Rebind() {
|
|||||||
host = "127.0.0.1"
|
host = "127.0.0.1"
|
||||||
}
|
}
|
||||||
listenCtx := context.Background() // unused without DNS name to resolve
|
listenCtx := context.Background() // unused without DNS name to resolve
|
||||||
if c.pconnPort != 0 {
|
if c.port != 0 {
|
||||||
c.pconn4.mu.Lock()
|
c.pconn4.mu.Lock()
|
||||||
if err := c.pconn4.pconn.Close(); err != nil {
|
if err := c.pconn4.pconn.Close(); err != nil {
|
||||||
c.logf("magicsock: link change close failed: %v", err)
|
c.logf("magicsock: link change close failed: %v", err)
|
||||||
}
|
}
|
||||||
packetConn, err := c.listenPacket(listenCtx, "udp4", fmt.Sprintf("%s:%d", host, c.pconnPort))
|
packetConn, err := c.listenPacket(listenCtx, "udp4", fmt.Sprintf("%s:%d", host, c.port))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
c.logf("magicsock: link change rebound port: %d", c.pconnPort)
|
c.logf("magicsock: link change rebound port: %d", c.port)
|
||||||
c.pconn4.pconn = packetConn.(*net.UDPConn)
|
c.pconn4.pconn = packetConn.(*net.UDPConn)
|
||||||
c.pconn4.mu.Unlock()
|
c.pconn4.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.logf("magicsock: link change unable to bind fixed port %d: %v, falling back to random port", c.pconnPort, err)
|
c.logf("magicsock: link change unable to bind fixed port %d: %v, falling back to random port", c.port, err)
|
||||||
c.pconn4.mu.Unlock()
|
c.pconn4.mu.Unlock()
|
||||||
}
|
}
|
||||||
c.logf("magicsock: link change, binding new port")
|
c.logf("magicsock: link change, binding new port")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user