fix(wireguard): widen the client address pool past a full /24 (#6089)

allocateWireguardAddress scanned exactly one /24, so a WireGuard inbound
was hard-capped at 254 clients with no way out -- the pool is not
configurable anywhere in the UI or API.

Fill the inbound's own /24 first, then widen to the enclosing /16 instead
of failing. A wireguard inbound carries no interface subnet and xray
routes purely by each peer's allowedIPs, so nothing constrains the wider
address. Capped at /16 to keep the worst-case scan bounded; IPv4 only.
This commit is contained in:
Sanaei
2026-07-24 22:21:18 +02:00
parent a652cb8cea
commit aa60d54ea5
2 changed files with 53 additions and 10 deletions
+16 -9
View File
@@ -46,9 +46,8 @@ func wireguardAllocationBase(used []string, fallback string) string {
return fallback
}
// allocateWireguardAddress returns the first free /32 host address in base that
// is not already present in used. The server holds the first host (.1), so
// allocation starts at the second host (.2).
const wireguardPoolFloorBits = 16
func allocateWireguardAddress(used []string, base string) (string, error) {
if base == "" {
base = defaultWireguardBase
@@ -63,14 +62,22 @@ func allocateWireguardAddress(used []string, base string) (string, error) {
taken[a] = struct{}{}
}
}
addr := prefix.Masked().Addr().Next().Next()
for prefix.Contains(addr) {
if _, ok := taken[addr]; !ok {
return addr.String() + "/32", nil
scopes := []netip.Prefix{prefix}
if prefix.Addr().Is4() && prefix.Bits() > wireguardPoolFloorBits {
if wider, wErr := prefix.Addr().Prefix(wireguardPoolFloorBits); wErr == nil {
scopes = append(scopes, wider)
}
addr = addr.Next()
}
return "", common.NewError("wireguard: no free address available in", base)
for _, scope := range scopes {
addr := scope.Masked().Addr().Next().Next()
for scope.Contains(addr) {
if _, ok := taken[addr]; !ok {
return addr.String() + "/32", nil
}
addr = addr.Next()
}
}
return "", common.NewError("wireguard: no free address available in", scopes[len(scopes)-1].String())
}
// normalizeWireguardAllowedIPs validates user-supplied allowedIPs entries and