Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+198
-21
@@ -55,6 +55,25 @@ func commonBits(ip1, ip2 []byte) uint8 {
|
||||
}
|
||||
}
|
||||
|
||||
func commonBits4(ip1 []byte, ip2 [4]byte) uint8 {
|
||||
a := binary.BigEndian.Uint32(ip1)
|
||||
b := binary.BigEndian.Uint32(ip2[:])
|
||||
return uint8(bits.LeadingZeros32(a ^ b))
|
||||
}
|
||||
|
||||
func commonBits6(ip1 []byte, ip2 [16]byte) uint8 {
|
||||
a := binary.BigEndian.Uint64(ip1)
|
||||
b := binary.BigEndian.Uint64(ip2[:])
|
||||
x := a ^ b
|
||||
if x != 0 {
|
||||
return uint8(bits.LeadingZeros64(x))
|
||||
}
|
||||
a = binary.BigEndian.Uint64(ip1[8:])
|
||||
b = binary.BigEndian.Uint64(ip2[8:])
|
||||
x = a ^ b
|
||||
return 64 + uint8(bits.LeadingZeros64(x))
|
||||
}
|
||||
|
||||
func (node *trieEntry) addToPeerEntries() {
|
||||
node.perPeerElem = node.peer.trieEntries.PushBack(node)
|
||||
}
|
||||
@@ -188,7 +207,37 @@ func (trie parentIndirection) insert(ip []byte, cidr uint8, peer *Peer) {
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieEntry) lookup(ip []byte) *Peer {
|
||||
func (node *trieEntry) lookup4(ip [4]byte) *Peer {
|
||||
var found *Peer
|
||||
for node != nil && commonBits4(node.bits, ip) >= node.cidr {
|
||||
if node.peer != nil {
|
||||
found = node.peer
|
||||
}
|
||||
if node.bitAtByte == 4 {
|
||||
break
|
||||
}
|
||||
bit := (ip[node.bitAtByte] >> node.bitAtShift) & 1
|
||||
node = node.child[bit]
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func (node *trieEntry) lookup6(ip [16]byte) *Peer {
|
||||
var found *Peer
|
||||
for node != nil && commonBits6(node.bits, ip) >= node.cidr {
|
||||
if node.peer != nil {
|
||||
found = node.peer
|
||||
}
|
||||
if node.bitAtByte == 16 {
|
||||
break
|
||||
}
|
||||
bit := (ip[node.bitAtByte] >> node.bitAtShift) & 1
|
||||
node = node.child[bit]
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func (node *trieEntry) lookup(ip net.IP) *Peer {
|
||||
var found *Peer
|
||||
size := uint8(len(ip))
|
||||
for node != nil && commonBits(node.bits, ip) >= node.cidr {
|
||||
@@ -205,14 +254,17 @@ func (node *trieEntry) lookup(ip []byte) *Peer {
|
||||
}
|
||||
|
||||
type AllowedIPs struct {
|
||||
IPv4 *trieEntry
|
||||
IPv6 *trieEntry
|
||||
mutex sync.RWMutex
|
||||
mu sync.RWMutex
|
||||
ipv4 *trieEntry
|
||||
ipv6 *trieEntry
|
||||
|
||||
peerByIPPacketFunc PeerByIPPacketFunc // if non-nil, called to look up peers by IP
|
||||
device *Device // back-reference to parent device; non-nil only if peerByIPPacketFunc is set
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) EntriesForPeer(peer *Peer, cb func(prefix netip.Prefix) bool) {
|
||||
table.mutex.RLock()
|
||||
defer table.mutex.RUnlock()
|
||||
table.mu.RLock()
|
||||
defer table.mu.RUnlock()
|
||||
|
||||
for elem := peer.trieEntries.Front(); elem != nil; elem = elem.Next() {
|
||||
node := elem.Value.(*trieEntry)
|
||||
@@ -223,10 +275,25 @@ func (table *AllowedIPs) EntriesForPeer(peer *Peer, cb func(prefix netip.Prefix)
|
||||
}
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
// setPeerPrefixes atomically removes all of peer's existing prefixes and adds
|
||||
// the provided ones.
|
||||
func (table *AllowedIPs) setPeerPrefixes(peer *Peer, prefixes []netip.Prefix) {
|
||||
table.mu.Lock()
|
||||
defer table.mu.Unlock()
|
||||
|
||||
table.removeByPeerLocked(peer)
|
||||
for _, prefix := range prefixes {
|
||||
table.insertLocked(prefix, peer)
|
||||
}
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
|
||||
table.mu.Lock()
|
||||
defer table.mu.Unlock()
|
||||
table.removeByPeerLocked(peer)
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) removeByPeerLocked(peer *Peer) {
|
||||
var next *list.Element
|
||||
for elem := peer.trieEntries.Front(); elem != nil; elem = next {
|
||||
next = elem.Next()
|
||||
@@ -266,29 +333,139 @@ func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) Insert(prefix netip.Prefix, peer *Peer) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
table.mu.Lock()
|
||||
defer table.mu.Unlock()
|
||||
table.insertLocked(prefix, peer)
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) insertLocked(prefix netip.Prefix, peer *Peer) {
|
||||
if prefix.Addr().Is6() {
|
||||
ip := prefix.Addr().As16()
|
||||
parentIndirection{&table.IPv6, 2}.insert(ip[:], uint8(prefix.Bits()), peer)
|
||||
parentIndirection{&table.ipv6, 2}.insert(ip[:], uint8(prefix.Bits()), peer)
|
||||
} else if prefix.Addr().Is4() {
|
||||
ip := prefix.Addr().As4()
|
||||
parentIndirection{&table.IPv4, 2}.insert(ip[:], uint8(prefix.Bits()), peer)
|
||||
parentIndirection{&table.ipv4, 2}.insert(ip[:], uint8(prefix.Bits()), peer)
|
||||
} else {
|
||||
panic(errors.New("inserting unknown address type"))
|
||||
}
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) Lookup(ip []byte) *Peer {
|
||||
table.mutex.RLock()
|
||||
defer table.mutex.RUnlock()
|
||||
switch len(ip) {
|
||||
case net.IPv6len:
|
||||
return table.IPv6.lookup(ip)
|
||||
case net.IPv4len:
|
||||
return table.IPv4.lookup(ip)
|
||||
// LookupFromPacket looks up the peer to which an outbound IP packet should be
|
||||
// sent. It lives on [AllowedIPs] for legacy/structural reasons: historically
|
||||
// WireGuard's only peer-selection mechanism was the AllowedIPs trie, and the
|
||||
// send path already had a reference to the table. When a [PeerByIPPacketFunc]
|
||||
// has been registered via [Device.SetPeerByIPPacketFunc], that callback is used
|
||||
// instead of the trie and the AllowedIPs table is not consulted at all.
|
||||
//
|
||||
// When no callback is registered, only dst is used (standard WireGuard
|
||||
// AllowedIPs trie lookup). When a callback is registered, all three
|
||||
// parameters are forwarded to it; see [PeerByIPPacketFunc] for details.
|
||||
func (table *AllowedIPs) LookupFromPacket(src, dst netip.Addr, ipPkt []byte) *Peer {
|
||||
table.mu.RLock()
|
||||
if f := table.peerByIPPacketFunc; f != nil {
|
||||
device := table.device
|
||||
table.mu.RUnlock()
|
||||
|
||||
if pubk, ok := f(src, dst, ipPkt); ok {
|
||||
return device.LookupPeer(pubk)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
defer table.mu.RUnlock()
|
||||
|
||||
switch {
|
||||
case dst.Is6():
|
||||
return table.ipv6.lookup6(dst.As16())
|
||||
case dst.Is4():
|
||||
return table.ipv4.lookup4(dst.As4())
|
||||
default:
|
||||
panic(errors.New("looking up unknown address type"))
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: Lookup is only used by legacy tests. It does not call
|
||||
// [PeerByIPPacketFunc]; use [AllowedIPs.LookupFromPacket] for production lookups.
|
||||
func (table *AllowedIPs) Lookup(ip []byte) *Peer {
|
||||
table.mu.RLock()
|
||||
defer table.mu.RUnlock()
|
||||
return table.lookupLocked(ip)
|
||||
}
|
||||
|
||||
// lookupLocked looks up the peer associated with the given IP address.
|
||||
// It assumes the caller holds the read lock (or doesn't hold it, but also
|
||||
// doesn't concurrently mutate AllowedIP).
|
||||
//
|
||||
// It returns nil if no peer is associated with the given IP address.
|
||||
func (table *AllowedIPs) lookupLocked(ip []byte) *Peer {
|
||||
switch len(ip) {
|
||||
case net.IPv6len:
|
||||
return table.ipv6.lookup(ip)
|
||||
case net.IPv4len:
|
||||
return table.ipv4.lookup(ip)
|
||||
default:
|
||||
panic(errors.New("looking up unknown address type"))
|
||||
}
|
||||
}
|
||||
|
||||
// AllowedPeerSourceIP reports whether the given source IP address is allowed
|
||||
// for the given peer.
|
||||
func (peer *Peer) AllowedPeerSourceIP(src netip.Addr) bool {
|
||||
if f := peer.state.testAllowedIP.Load(); f != nil {
|
||||
return (*f)(src)
|
||||
}
|
||||
|
||||
table := &peer.device.allowedips
|
||||
table.mu.RLock()
|
||||
defer table.mu.RUnlock()
|
||||
switch {
|
||||
case src.Is6():
|
||||
return table.ipv6.lookup6(src.As16()) == peer
|
||||
case src.Is4():
|
||||
return table.ipv4.lookup4(src.As4()) == peer
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// cidrLinearSearchMax is the largest number of CIDRs for which
|
||||
// mkIPInCIDRsTestFunc does a linear search instead of building a trie.
|
||||
const cidrLinearSearchMax = 4
|
||||
|
||||
// mkIPInCIDRsTestFunc returns a function that tests whether an IP address is
|
||||
// contained in any of the given CIDRs.
|
||||
func mkIPInCIDRsTestFunc(cidrs []netip.Prefix) func(netip.Addr) bool {
|
||||
if len(cidrs) == 0 {
|
||||
return func(netip.Addr) bool { return false }
|
||||
}
|
||||
if len(cidrs) == 1 {
|
||||
return func(addr netip.Addr) bool { return cidrs[0].Contains(addr) }
|
||||
}
|
||||
if len(cidrs) <= cidrLinearSearchMax {
|
||||
// For small numbers of CIDRs, just do a linear search. The trie construction
|
||||
// is more expensive than the linear search, and the test function is faster
|
||||
// than the trie lookup, so this is a net win.
|
||||
return func(addr netip.Addr) bool {
|
||||
for _, c := range cidrs {
|
||||
if c.Contains(addr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Make a trie for faster lookups. Use a local dummy Peer so its
|
||||
// trieEntries and the whole trie can be collected once the returned
|
||||
// closure is gone.
|
||||
fakePeer := new(Peer)
|
||||
var a AllowedIPs
|
||||
for _, c := range cidrs {
|
||||
a.Insert(c, fakePeer)
|
||||
}
|
||||
return func(addr netip.Addr) bool {
|
||||
switch {
|
||||
case addr.Is4():
|
||||
return a.ipv4.lookup4(addr.As4()) == fakePeer
|
||||
default:
|
||||
return a.ipv6.lookup6(addr.As16()) == fakePeer
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user