mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-05 09:57:14 +00:00
58671533bb
New internal/amneziawgnet package: builds a real amneziawg-go Device over a gVisor netstack from an existing amneziawg.Instance, with a TCP/UDP forwarder that recovers each tunnel connection's real destination and a peer-identity index keyed by AllowedIPs. This is the foundation for migrating AmneziaWG off the kernel-module+TPROXY path (see the AmneziaWG-go vs kernel-module decision) -- nothing wires into live traffic yet, that's Phase 2 (relay into Xray's own SOCKS5 inbound). Covered by three real end-to-end tests: a genuine handshake + TCP forwarder + identity resolution, the same for UDP (including a reply routed back through the tunnel), and the manager's reconfigure-in-place vs. rebuild lifecycle. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
86 lines
2.6 KiB
Go
86 lines
2.6 KiB
Go
package amneziawgnet
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/amneziawg"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/util/wireguard"
|
|
)
|
|
|
|
// TestManagerLifecycle exercises Ensure/Reconcile's reconfigure-in-place vs.
|
|
// rebuild split (see ensureLocked's doc comment) and Reconcile's stop path,
|
|
// using a throwaway Manager rather than the process-wide singleton so this
|
|
// test doesn't interact with any other test's state.
|
|
func TestManagerLifecycle(t *testing.T) {
|
|
priv, pub, err := wireguard.GenerateWireguardKeypair()
|
|
if err != nil {
|
|
t.Fatalf("generate keypair: %v", err)
|
|
}
|
|
|
|
m := &Manager{ifaces: map[int]*managed{}}
|
|
inst := amneziawg.Instance{
|
|
Id: 3,
|
|
InterfaceName: "awgtest3",
|
|
ListenPort: 58714,
|
|
PrivateKey: priv,
|
|
PublicKey: pub,
|
|
Address: []string{"10.203.0.1/24"},
|
|
MTU: 1420,
|
|
Obfuscation: amneziawg.Obfuscation20{
|
|
Jc: 4, Jmin: 40, Jmax: 70,
|
|
S1: 20, S2: 30, S3: 20, S4: 20,
|
|
},
|
|
}
|
|
defer m.StopAll()
|
|
|
|
if err := m.Ensure(Desired{Instance: inst}); err != nil {
|
|
t.Fatalf("Ensure (create): %v", err)
|
|
}
|
|
if !m.HasRunning() {
|
|
t.Fatal("HasRunning() = false after Ensure created an interface")
|
|
}
|
|
dev1, _, ok := m.Lookup(inst.Id)
|
|
if !ok {
|
|
t.Fatal("Lookup after Ensure: not found")
|
|
}
|
|
|
|
// Same Instance again: same address fingerprint, so this should
|
|
// reconfigure the existing Device via IpcSet rather than rebuild it --
|
|
// verify by checking the *Device pointer survived unchanged.
|
|
if err := m.Ensure(Desired{Instance: inst}); err != nil {
|
|
t.Fatalf("Ensure (unchanged): %v", err)
|
|
}
|
|
dev2, _, ok := m.Lookup(inst.Id)
|
|
if !ok {
|
|
t.Fatal("Lookup after second Ensure: not found")
|
|
}
|
|
if dev1 != dev2 {
|
|
t.Error("Ensure with an unchanged Instance rebuilt the Device; expected an in-place reconfigure")
|
|
}
|
|
|
|
// Changing the interface address is structural (fixed at netstack
|
|
// construction time) and must force a rebuild -- verify by checking the
|
|
// *Device pointer changed.
|
|
changed := inst
|
|
changed.Address = []string{"10.203.1.1/24"}
|
|
if err := m.Ensure(Desired{Instance: changed}); err != nil {
|
|
t.Fatalf("Ensure (address changed): %v", err)
|
|
}
|
|
dev3, _, ok := m.Lookup(inst.Id)
|
|
if !ok {
|
|
t.Fatal("Lookup after address-changing Ensure: not found")
|
|
}
|
|
if dev3 == dev2 {
|
|
t.Error("Ensure with a changed address reconfigured in place; expected a rebuild")
|
|
}
|
|
|
|
// Reconcile with nothing desired stops every managed interface.
|
|
m.Reconcile(nil)
|
|
if m.HasRunning() {
|
|
t.Error("HasRunning() = true after Reconcile([]) should have stopped everything")
|
|
}
|
|
if _, _, ok := m.Lookup(inst.Id); ok {
|
|
t.Error("Lookup succeeded after Reconcile([]) removed the interface")
|
|
}
|
|
}
|