mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 03:05:02 +03:00
fix(wireguard): step over wide address claims when allocating
allocateWireguardAddress walked the scope one address at a time and tested each against every wide claim, so a claim covering an IPv6 /64 (a node can report such allowedIPs) turned the next address-less client add into a 2^64-step loop that also held every later client change on that inbound. The scan now jumps past a wide claim in one step, so it is bounded by the number of claims.
This commit is contained in:
@@ -117,16 +117,15 @@ func allocateWireguardAddress(used []string, base string, allowWidening bool) (s
|
||||
wide = append(wide, p)
|
||||
}
|
||||
}
|
||||
isTaken := func(a netip.Addr) bool {
|
||||
if _, ok := taken[a]; ok {
|
||||
return true
|
||||
}
|
||||
// wideClaimOf returns a wide claim holding a, so the scan can step over the whole claim:
|
||||
// a node may report one spanning the scope, and walking it is 2^64 steps in IPv6.
|
||||
wideClaimOf := func(a netip.Addr) (netip.Prefix, bool) {
|
||||
for _, p := range wide {
|
||||
if p.Contains(a) {
|
||||
return true
|
||||
return p, true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return netip.Prefix{}, false
|
||||
}
|
||||
scopes := []netip.Prefix{prefix}
|
||||
if allowWidening && prefix.Addr().Is4() && prefix.Bits() > wireguardPoolFloorBits {
|
||||
@@ -136,16 +135,31 @@ func allocateWireguardAddress(used []string, base string, allowWidening bool) (s
|
||||
}
|
||||
for _, scope := range scopes {
|
||||
addr := scope.Masked().Addr().Next().Next()
|
||||
for scope.Contains(addr) {
|
||||
if !isTaken(addr) {
|
||||
return addr.String() + "/" + hostBits, nil
|
||||
for addr.IsValid() && scope.Contains(addr) {
|
||||
if _, ok := taken[addr]; ok {
|
||||
addr = addr.Next()
|
||||
continue
|
||||
}
|
||||
addr = addr.Next()
|
||||
if claim, ok := wideClaimOf(addr); ok {
|
||||
addr = lastPrefixAddr(claim).Next()
|
||||
continue
|
||||
}
|
||||
return addr.String() + "/" + hostBits, nil
|
||||
}
|
||||
}
|
||||
return "", common.NewError("wireguard: no free address available in", scopes[len(scopes)-1].String())
|
||||
}
|
||||
|
||||
// lastPrefixAddr is the highest address of p.
|
||||
func lastPrefixAddr(p netip.Prefix) netip.Addr {
|
||||
bytes := p.Masked().Addr().AsSlice()
|
||||
for bit := p.Bits(); bit < len(bytes)*8; bit++ {
|
||||
bytes[bit/8] |= 0x80 >> (bit % 8)
|
||||
}
|
||||
addr, _ := netip.AddrFromSlice(bytes)
|
||||
return addr
|
||||
}
|
||||
|
||||
// normalizeWireguardAllowedIPs validates user-supplied allowedIPs entries and
|
||||
// canonicalizes them: bare addresses become single-host prefixes, duplicates drop.
|
||||
func normalizeWireguardAllowedIPs(values []string) ([]string, error) {
|
||||
|
||||
@@ -24,6 +24,9 @@ func TestAllocateWireguardAddress(t *testing.T) {
|
||||
{name: "default base when empty", used: nil, base: "", want: "10.0.0.2/32"},
|
||||
{name: "full ipv4 scope widens instead of failing", used: []string{"10.9.0.2/32", "10.9.0.3/32"}, base: "10.9.0.0/30", want: "10.9.0.4/32"},
|
||||
{name: "exhausted ipv6 scope errors", used: []string{"fd00::2/128", "fd00::3/128"}, base: "fd00::/126", err: true},
|
||||
// A node can report a wide claim; the scan must step over it, not through 2^64 addresses.
|
||||
{name: "wide claim over an ipv6 scope", used: []string{"fd00::/64"}, base: "fd00::/64", err: true},
|
||||
{name: "first address past a wide claim", used: []string{"10.0.0.0/25"}, base: "10.0.0.0/24", want: "10.0.0.128/32"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user