device: add API for on-demand configuration of peers
Updates tailscale/tailscale#17858 Signed-off-by: Brad Fitzpatrick <brad@danga.com>
This commit is contained in:
parent
70b09a6edd
commit
e924a91e99
5 changed files with 151 additions and 10 deletions
|
|
@ -7,6 +7,8 @@ package device
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
|
@ -59,6 +61,7 @@ type Device struct {
|
|||
peers struct {
|
||||
sync.RWMutex // protects keyMap
|
||||
keyMap map[NoisePublicKey]*Peer
|
||||
lookupFunc PeerLookupFunc // or nil if unused
|
||||
}
|
||||
|
||||
rate struct {
|
||||
|
|
@ -345,13 +348,63 @@ func (device *Device) BatchSize() int {
|
|||
return size
|
||||
}
|
||||
|
||||
// LookupPeer looks up a peer by its public key.
|
||||
//
|
||||
// If the peer does not exist and a [PeerLookupFunc] is set (via
|
||||
// [Device.SetPeerLookupFunc]), then that function is used to create the peer
|
||||
// before returning it. Peers created via this mechanism exist only until their
|
||||
// state machine reaches idle, and then the peers are removed.
|
||||
//
|
||||
// If the peer does not exist and no [PeerLookupFunc] is set, nil is returned.
|
||||
//
|
||||
// Use [Device.LookupActivePeer] to only return already-existing peers, without
|
||||
// using a [PeerLookupFunc].
|
||||
func (device *Device) LookupPeer(pk NoisePublicKey) *Peer {
|
||||
device.peers.RLock()
|
||||
defer device.peers.RUnlock()
|
||||
p, ok := device.peers.keyMap[pk]
|
||||
lookupFunc := device.peers.lookupFunc
|
||||
device.peers.RUnlock()
|
||||
if ok || lookupFunc == nil {
|
||||
return p
|
||||
}
|
||||
|
||||
return device.peers.keyMap[pk]
|
||||
allowedIPs := lookupFunc(pk)
|
||||
if allowedIPs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p, err := device.NewPeer(pk)
|
||||
if err != nil {
|
||||
if errors.Is(err, errAddExistingPeer) {
|
||||
device.peers.RLock()
|
||||
defer device.peers.RUnlock()
|
||||
return device.peers.keyMap[pk]
|
||||
}
|
||||
device.log.Errorf("Failed to create peer: %v", err)
|
||||
return nil
|
||||
}
|
||||
p.SetAllowedIPs(allowedIPs)
|
||||
p.deleteOnIdle = true
|
||||
p.Start()
|
||||
return p
|
||||
}
|
||||
|
||||
// LookupActivePeer looks up a peer by its public key.
|
||||
//
|
||||
// Unlike [Device.LookupPeer], this function does not use a [PeerLookupFunc] to
|
||||
// create the peer if it does not already exist.
|
||||
//
|
||||
// If the peer does not exist or was created lazily via [PeerLookupFunc]
|
||||
// and has subsequently idled away, it returns (nil, false).
|
||||
func (device *Device) LookupActivePeer(pk NoisePublicKey) (_ *Peer, ok bool) {
|
||||
device.peers.RLock()
|
||||
defer device.peers.RUnlock()
|
||||
p, ok := device.peers.keyMap[pk]
|
||||
return p, ok
|
||||
}
|
||||
|
||||
var errAddExistingPeer = errors.New("adding existing peer")
|
||||
|
||||
func (device *Device) RemovePeer(key NoisePublicKey) {
|
||||
device.peers.Lock()
|
||||
defer device.peers.Unlock()
|
||||
|
|
@ -374,6 +427,41 @@ func (device *Device) RemoveAllPeers() {
|
|||
device.peers.keyMap = make(map[NoisePublicKey]*Peer)
|
||||
}
|
||||
|
||||
// RemoveMatchingPeers removes all peers for which shouldRemove returns true.
|
||||
//
|
||||
// It returns the number of peers removed.
|
||||
func (device *Device) RemoveMatchingPeers(shouldRemove func(NoisePublicKey) bool) (numRemoved int) {
|
||||
device.peers.Lock()
|
||||
defer device.peers.Unlock()
|
||||
|
||||
for key, peer := range device.peers.keyMap {
|
||||
if shouldRemove(key) {
|
||||
removePeerLocked(device, peer, key)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
return numRemoved
|
||||
}
|
||||
|
||||
// PeerLookupFunc is the type of function used to look up peers by public key
|
||||
// when receiving packets for unknown peers.
|
||||
//
|
||||
// If it returns nil, the peer is not known.
|
||||
//
|
||||
// Otherwise, returning non-nil signals that wireguard-go should create the peer
|
||||
// with the provided allowed IPs.
|
||||
//
|
||||
// See [Device.SetPeerLookupFunc] and [Device.LookupPeer].
|
||||
type PeerLookupFunc func(NoisePublicKey) (allowedIPs []netip.Prefix)
|
||||
|
||||
// SetPeerLookupFunc sets the function used to look up peers by public key
|
||||
// when receiving packets for unknown peers.
|
||||
func (device *Device) SetPeerLookupFunc(f PeerLookupFunc) {
|
||||
device.peers.Lock()
|
||||
defer device.peers.Unlock()
|
||||
device.peers.lookupFunc = f
|
||||
}
|
||||
|
||||
func (device *Device) Close() {
|
||||
device.state.Lock()
|
||||
defer device.state.Unlock()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue