Updates tailscale/corp#13464 Signed-off-by: David Anderson <danderson@tailscale.com>main
parent
f7016d8c00
commit
cde37f5307
@ -0,0 +1,183 @@ |
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package magicsock |
||||
|
||||
import ( |
||||
"net/netip" |
||||
|
||||
"tailscale.com/types/key" |
||||
) |
||||
|
||||
// peerInfo is all the information magicsock tracks about a particular
|
||||
// peer.
|
||||
type peerInfo struct { |
||||
ep *endpoint // always non-nil.
|
||||
// ipPorts is an inverted version of peerMap.byIPPort (below), so
|
||||
// that when we're deleting this node, we can rapidly find out the
|
||||
// keys that need deleting from peerMap.byIPPort without having to
|
||||
// iterate over every IPPort known for any peer.
|
||||
ipPorts map[netip.AddrPort]bool |
||||
} |
||||
|
||||
func newPeerInfo(ep *endpoint) *peerInfo { |
||||
return &peerInfo{ |
||||
ep: ep, |
||||
ipPorts: map[netip.AddrPort]bool{}, |
||||
} |
||||
} |
||||
|
||||
// peerMap is an index of peerInfos by node (WireGuard) key, disco
|
||||
// key, and discovered ip:port endpoints.
|
||||
//
|
||||
// Doesn't do any locking, all access must be done with Conn.mu held.
|
||||
type peerMap struct { |
||||
byNodeKey map[key.NodePublic]*peerInfo |
||||
byIPPort map[netip.AddrPort]*peerInfo |
||||
|
||||
// nodesOfDisco contains the set of nodes that are using a
|
||||
// DiscoKey. Usually those sets will be just one node.
|
||||
nodesOfDisco map[key.DiscoPublic]map[key.NodePublic]bool |
||||
} |
||||
|
||||
func newPeerMap() peerMap { |
||||
return peerMap{ |
||||
byNodeKey: map[key.NodePublic]*peerInfo{}, |
||||
byIPPort: map[netip.AddrPort]*peerInfo{}, |
||||
nodesOfDisco: map[key.DiscoPublic]map[key.NodePublic]bool{}, |
||||
} |
||||
} |
||||
|
||||
// nodeCount returns the number of nodes currently in m.
|
||||
func (m *peerMap) nodeCount() int { |
||||
return len(m.byNodeKey) |
||||
} |
||||
|
||||
// anyEndpointForDiscoKey reports whether there exists any
|
||||
// peers in the netmap with dk as their DiscoKey.
|
||||
func (m *peerMap) anyEndpointForDiscoKey(dk key.DiscoPublic) bool { |
||||
return len(m.nodesOfDisco[dk]) > 0 |
||||
} |
||||
|
||||
// endpointForNodeKey returns the endpoint for nk, or nil if
|
||||
// nk is not known to us.
|
||||
func (m *peerMap) endpointForNodeKey(nk key.NodePublic) (ep *endpoint, ok bool) { |
||||
if nk.IsZero() { |
||||
return nil, false |
||||
} |
||||
if info, ok := m.byNodeKey[nk]; ok { |
||||
return info.ep, true |
||||
} |
||||
return nil, false |
||||
} |
||||
|
||||
// endpointForIPPort returns the endpoint for the peer we
|
||||
// believe to be at ipp, or nil if we don't know of any such peer.
|
||||
func (m *peerMap) endpointForIPPort(ipp netip.AddrPort) (ep *endpoint, ok bool) { |
||||
if info, ok := m.byIPPort[ipp]; ok { |
||||
return info.ep, true |
||||
} |
||||
return nil, false |
||||
} |
||||
|
||||
// forEachEndpoint invokes f on every endpoint in m.
|
||||
func (m *peerMap) forEachEndpoint(f func(ep *endpoint)) { |
||||
for _, pi := range m.byNodeKey { |
||||
f(pi.ep) |
||||
} |
||||
} |
||||
|
||||
// forEachEndpointWithDiscoKey invokes f on every endpoint in m that has the
|
||||
// provided DiscoKey until f returns false or there are no endpoints left to
|
||||
// iterate.
|
||||
func (m *peerMap) forEachEndpointWithDiscoKey(dk key.DiscoPublic, f func(*endpoint) (keepGoing bool)) { |
||||
for nk := range m.nodesOfDisco[dk] { |
||||
pi, ok := m.byNodeKey[nk] |
||||
if !ok { |
||||
// Unexpected. Data structures would have to
|
||||
// be out of sync. But we don't have a logger
|
||||
// here to log [unexpected], so just skip.
|
||||
// Maybe log later once peerMap is merged back
|
||||
// into Conn.
|
||||
continue |
||||
} |
||||
if !f(pi.ep) { |
||||
return |
||||
} |
||||
} |
||||
} |
||||
|
||||
// upsertEndpoint stores endpoint in the peerInfo for
|
||||
// ep.publicKey, and updates indexes. m must already have a
|
||||
// tailcfg.Node for ep.publicKey.
|
||||
func (m *peerMap) upsertEndpoint(ep *endpoint, oldDiscoKey key.DiscoPublic) { |
||||
if m.byNodeKey[ep.publicKey] == nil { |
||||
m.byNodeKey[ep.publicKey] = newPeerInfo(ep) |
||||
} |
||||
epDisco := ep.disco.Load() |
||||
if epDisco == nil || oldDiscoKey != epDisco.key { |
||||
delete(m.nodesOfDisco[oldDiscoKey], ep.publicKey) |
||||
} |
||||
if ep.isWireguardOnly { |
||||
// If the peer is a WireGuard only peer, add all of its endpoints.
|
||||
|
||||
// TODO(raggi,catzkorn): this could mean that if a "isWireguardOnly"
|
||||
// peer has, say, 192.168.0.2 and so does a tailscale peer, the
|
||||
// wireguard one will win. That may not be the outcome that we want -
|
||||
// perhaps we should prefer bestAddr.AddrPort if it is set?
|
||||
// see tailscale/tailscale#7994
|
||||
for ipp := range ep.endpointState { |
||||
m.setNodeKeyForIPPort(ipp, ep.publicKey) |
||||
} |
||||
|
||||
return |
||||
} |
||||
set := m.nodesOfDisco[epDisco.key] |
||||
if set == nil { |
||||
set = map[key.NodePublic]bool{} |
||||
m.nodesOfDisco[epDisco.key] = set |
||||
} |
||||
set[ep.publicKey] = true |
||||
} |
||||
|
||||
// setNodeKeyForIPPort makes future peer lookups by ipp return the
|
||||
// same endpoint as a lookup by nk.
|
||||
//
|
||||
// This should only be called with a fully verified mapping of ipp to
|
||||
// nk, because calling this function defines the endpoint we hand to
|
||||
// WireGuard for packets received from ipp.
|
||||
func (m *peerMap) setNodeKeyForIPPort(ipp netip.AddrPort, nk key.NodePublic) { |
||||
if pi := m.byIPPort[ipp]; pi != nil { |
||||
delete(pi.ipPorts, ipp) |
||||
delete(m.byIPPort, ipp) |
||||
} |
||||
if pi, ok := m.byNodeKey[nk]; ok { |
||||
pi.ipPorts[ipp] = true |
||||
m.byIPPort[ipp] = pi |
||||
} |
||||
} |
||||
|
||||
// deleteEndpoint deletes the peerInfo associated with ep, and
|
||||
// updates indexes.
|
||||
func (m *peerMap) deleteEndpoint(ep *endpoint) { |
||||
if ep == nil { |
||||
return |
||||
} |
||||
ep.stopAndReset() |
||||
|
||||
epDisco := ep.disco.Load() |
||||
|
||||
pi := m.byNodeKey[ep.publicKey] |
||||
if epDisco != nil { |
||||
delete(m.nodesOfDisco[epDisco.key], ep.publicKey) |
||||
} |
||||
delete(m.byNodeKey, ep.publicKey) |
||||
if pi == nil { |
||||
// Kneejerk paranoia from earlier issue 2801.
|
||||
// Unexpected. But no logger plumbed here to log so.
|
||||
return |
||||
} |
||||
for ip := range pi.ipPorts { |
||||
delete(m.byIPPort, ip) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue