mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-17 15:47:14 +00:00
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:
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
||||
@@ -20,7 +21,8 @@ func TestAllocateWireguardAddress(t *testing.T) {
|
||||
{name: "fills gap", used: []string{"10.0.0.3/32", "10.0.0.4/32"}, base: "10.0.0.0/24", want: "10.0.0.2/32"},
|
||||
{name: "ignores catch-all", used: []string{"0.0.0.0/0", "::/0"}, base: "10.0.0.0/24", want: "10.0.0.2/32"},
|
||||
{name: "default base when empty", used: nil, base: "", want: "10.0.0.2/32"},
|
||||
{name: "exhausted /30", used: []string{"10.9.0.2/32", "10.9.0.3/32"}, base: "10.9.0.0/30", err: true},
|
||||
{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},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -130,6 +132,40 @@ func TestDefaultWireguardClientsHonorsExistingSubnet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocateWireguardAddressWidensPastFullSlash24(t *testing.T) {
|
||||
used := make([]string, 0, 254)
|
||||
for i := 2; i <= 255; i++ {
|
||||
used = append(used, fmt.Sprintf("10.0.0.%d/32", i))
|
||||
}
|
||||
|
||||
got, err := allocateWireguardAddress(used, "10.0.0.0/24")
|
||||
if err != nil {
|
||||
t.Fatalf("allocate with a full /24: %v", err)
|
||||
}
|
||||
if got != "10.0.1.0/32" {
|
||||
t.Fatalf("address after a full /24 = %q, want 10.0.1.0/32", got)
|
||||
}
|
||||
|
||||
used = append(used, got)
|
||||
next, err := allocateWireguardAddress(used, "10.0.0.0/24")
|
||||
if err != nil {
|
||||
t.Fatalf("allocate after widening: %v", err)
|
||||
}
|
||||
if next != "10.0.1.1/32" {
|
||||
t.Fatalf("second widened address = %q, want 10.0.1.1/32", next)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocateWireguardAddressFillsItsOwnSlash24First(t *testing.T) {
|
||||
got, err := allocateWireguardAddress([]string{"172.16.0.2/32"}, "172.16.0.0/24")
|
||||
if err != nil {
|
||||
t.Fatalf("allocateWireguardAddress: %v", err)
|
||||
}
|
||||
if got != "172.16.0.3/32" {
|
||||
t.Fatalf("address = %q, want 172.16.0.3/32 — the inbound's own /24 comes first", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultWireguardClientsAllocatesDistinctIPs(t *testing.T) {
|
||||
clients := []model.Client{{Email: "x@wg"}, {Email: "y@wg"}}
|
||||
ifaces := []any{map[string]any{"email": "x@wg"}, map[string]any{"email": "y@wg"}}
|
||||
|
||||
Reference in New Issue
Block a user