mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 19:25:05 +03:00
fix(amneziawg): attribute an equal-prefix source to the peer the device picks
PeerIndex names the peer a source address came from, but for two peers holding the same prefix, which only a default route can be after the overlap check, it kept the first while amneziawg-go's allowed-IPs table hands the prefix to the peer configured last. Traffic was then billed to a different client than the one the device accepted it from. An equal-length match now goes to the later peer, as in the device.
This commit is contained in:
@@ -49,7 +49,9 @@ func (idx *PeerIndex) Lookup(addr netip.Addr) (amneziawg.Peer, bool) {
|
|||||||
bestBits := -1
|
bestBits := -1
|
||||||
var bestPeer amneziawg.Peer
|
var bestPeer amneziawg.Peer
|
||||||
for _, e := range idx.entries {
|
for _, e := range idx.entries {
|
||||||
if e.prefix.Bits() <= bestBits || !e.prefix.Contains(addr) {
|
// Equal length: the later peer wins, as in the device's allowed-IPs
|
||||||
|
// table, which buildUAPIConfig fills from the same peers in this order.
|
||||||
|
if e.prefix.Bits() < bestBits || !e.prefix.Contains(addr) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bestBits = e.prefix.Bits()
|
bestBits = e.prefix.Bits()
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package amneziawgnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mhsanaei/3x-ui/v3/internal/amneziawg"
|
||||||
|
"github.com/mhsanaei/3x-ui/v3/internal/util/wireguard"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestPeerIndexLookupMatchesDeviceAllowedIPs catches Lookup attributing a
|
||||||
|
// prefix two peers both carry to a peer the device accepts no such source from.
|
||||||
|
func TestPeerIndexLookupMatchesDeviceAllowedIPs(t *testing.T) {
|
||||||
|
serverPriv, serverPub, err := wireguard.GenerateWireguardKeypair()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("generate server keypair: %v", err)
|
||||||
|
}
|
||||||
|
var peers []amneziawg.Peer
|
||||||
|
for _, p := range []amneziawg.Peer{
|
||||||
|
{Email: "first@example.com", AllowedIPs: []string{"10.202.0.2/32", "fd00:202::2/128", "0.0.0.0/0", "::/0"}},
|
||||||
|
{Email: "last@example.com", AllowedIPs: []string{"10.202.0.3/32", "fd00:202::3/128", "0.0.0.0/0", "::/0"}},
|
||||||
|
} {
|
||||||
|
if _, p.PublicKey, err = wireguard.GenerateWireguardKeypair(); err != nil {
|
||||||
|
t.Fatalf("generate peer keypair: %v", err)
|
||||||
|
}
|
||||||
|
peers = append(peers, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
dev, err := NewDevice(amneziawg.Instance{
|
||||||
|
Id: 1,
|
||||||
|
PrivateKey: serverPriv,
|
||||||
|
PublicKey: serverPub,
|
||||||
|
Address: []string{"10.202.0.1/24"},
|
||||||
|
MTU: 1420,
|
||||||
|
Peers: peers,
|
||||||
|
}, DeviceOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewDevice: %v", err)
|
||||||
|
}
|
||||||
|
defer dev.Close()
|
||||||
|
|
||||||
|
dump, err := dev.IpcGet()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("IpcGet: %v", err)
|
||||||
|
}
|
||||||
|
_, states := parseUAPIDump(dump)
|
||||||
|
|
||||||
|
idx := NewPeerIndex(peers)
|
||||||
|
for _, tc := range []struct{ addr, prefix string }{
|
||||||
|
{"10.99.0.1", "0.0.0.0/0"},
|
||||||
|
{"fd00:99::1", "::/0"},
|
||||||
|
{"10.202.0.2", "10.202.0.2/32"},
|
||||||
|
{"10.202.0.3", "10.202.0.3/32"},
|
||||||
|
{"fd00:202::2", "fd00:202::2/128"},
|
||||||
|
{"fd00:202::3", "fd00:202::3/128"},
|
||||||
|
} {
|
||||||
|
t.Run(tc.addr, func(t *testing.T) {
|
||||||
|
got, ok := idx.Lookup(netip.MustParseAddr(tc.addr))
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Lookup(%s) found no peer", tc.addr)
|
||||||
|
}
|
||||||
|
gotHex, err := wireguard.KeyToHex(got.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("KeyToHex: %v", err)
|
||||||
|
}
|
||||||
|
if !slices.Contains(states[gotHex].allowedIPs, tc.prefix) {
|
||||||
|
t.Errorf("Lookup(%s) = %s, but the device holds %s for another peer", tc.addr, got.Email, tc.prefix)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user