udp_test.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package amneziawgnet
  2. import (
  3. "fmt"
  4. "net/netip"
  5. "testing"
  6. "time"
  7. awgconn "github.com/amnezia-vpn/amneziawg-go/v3/conn"
  8. "github.com/amnezia-vpn/amneziawg-go/v3/device"
  9. "github.com/amnezia-vpn/amneziawg-go/v3/tun/netstack"
  10. "github.com/mhsanaei/3x-ui/v3/internal/amneziawg"
  11. "github.com/mhsanaei/3x-ui/v3/internal/util/wireguard"
  12. )
  13. // TestNewDeviceUDPHandlerAndReply is the UDP counterpart of
  14. // TestNewDeviceHandshakeForwarderAndIdentity: this package's own udp.go was
  15. // refactored from the Phase 0 spike's bake-the-dial-in version to a generic
  16. // handler-plus-reply-injection design (see AttachUDPHandler/WriteUDPReply's
  17. // doc comments), a real behavior change worth its own verification rather
  18. // than assuming the port preserved correctness -- UDP was flagged as "the
  19. // harder half" in the migration plan's own risk list, precisely because
  20. // gVisor has no udp.NewForwarder and the reply path has to be constructed
  21. // by hand.
  22. func TestNewDeviceUDPHandlerAndReply(t *testing.T) {
  23. serverPriv, serverPub, err := wireguard.GenerateWireguardKeypair()
  24. if err != nil {
  25. t.Fatalf("generate server keypair: %v", err)
  26. }
  27. clientPriv, clientPub, err := wireguard.GenerateWireguardKeypair()
  28. if err != nil {
  29. t.Fatalf("generate client keypair: %v", err)
  30. }
  31. const listenPort = 58713 // distinct from the TCP test's port
  32. const wantEmail = "[email protected]"
  33. const echoPayload = "hello-from-client"
  34. inst := amneziawg.Instance{
  35. Id: 2,
  36. InterfaceName: "awgtest2",
  37. ListenPort: listenPort,
  38. PrivateKey: serverPriv,
  39. PublicKey: serverPub,
  40. Address: []string{"10.202.0.1/24"},
  41. MTU: 1420,
  42. Obfuscation: amneziawg.Obfuscation31{
  43. Jc: 4, Jmin: 40, Jmax: 70,
  44. S1: 20, S2: 30, S3: 20, S4: 20,
  45. },
  46. Peers: []amneziawg.Peer{{
  47. Email: wantEmail,
  48. PublicKey: clientPub,
  49. AllowedIPs: []string{"10.202.0.2/32"},
  50. }},
  51. }
  52. dev, err := newUnconfiguredDevice(inst, DeviceOptions{})
  53. if err != nil {
  54. t.Fatalf("newUnconfiguredDevice: %v", err)
  55. }
  56. defer dev.Close()
  57. idx := NewPeerIndex(inst.Peers)
  58. // Never configured anywhere server-side, same idea as the TCP test.
  59. wantDest := netip.MustParseAddrPort("10.202.9.9:5353")
  60. identityErrCh := make(chan error, 8)
  61. AttachUDPHandler(dev.Stack, func(src, dst netip.AddrPort, payload []byte) {
  62. if peer, ok := idx.Lookup(src.Addr()); !ok || peer.Email != wantEmail {
  63. identityErrCh <- fmt.Errorf("peer identity lookup for src %v: ok=%v email=%q, want %q", src, ok, peer.Email, wantEmail)
  64. return
  65. }
  66. if dst != wantDest {
  67. identityErrCh <- fmt.Errorf("recovered dest = %v, want %v", dst, wantDest)
  68. return
  69. }
  70. // Echo the payload back, posing as a reply from the destination the
  71. // client dialed -- exactly what a real relay's downstream reply
  72. // would look like from the tunnel's point of view.
  73. if err := WriteUDPReply(dev.Stack, dst, src, payload); err != nil {
  74. identityErrCh <- fmt.Errorf("WriteUDPReply: %w", err)
  75. }
  76. })
  77. // Configure (IpcSet) must come after AttachUDPHandler -- see
  78. // newUnconfiguredDevice's doc comment.
  79. if err := dev.Configure(inst, DeviceOptions{}); err != nil {
  80. t.Fatalf("Configure: %v", err)
  81. }
  82. clientTun, clientNet, err := netstack.CreateNetTUN(
  83. []netip.Addr{netip.MustParseAddr("10.202.0.2")},
  84. []netip.Addr{netip.MustParseAddr("1.1.1.1")}, 1420)
  85. if err != nil {
  86. t.Fatalf("client CreateNetTUN: %v", err)
  87. }
  88. clientDev := device.NewDevice(clientTun, awgconn.NewDefaultBind(), device.NewLogger(device.LogLevelSilent, ""))
  89. defer clientDev.Close()
  90. clientPrivHex, err := wireguard.KeyToHex(clientPriv)
  91. if err != nil {
  92. t.Fatalf("client key to hex: %v", err)
  93. }
  94. serverPubHex, err := wireguard.KeyToHex(serverPub)
  95. if err != nil {
  96. t.Fatalf("server key to hex: %v", err)
  97. }
  98. clientConf := fmt.Sprintf(
  99. "private_key=%s\njc=4\njmin=40\njmax=70\ns1=20\ns2=30\ns3=20\ns4=20\npublic_key=%s\nendpoint=127.0.0.1:%d\nallowed_ip=0.0.0.0/0\n",
  100. clientPrivHex, serverPubHex, listenPort)
  101. if err := clientDev.IpcSet(clientConf); err != nil {
  102. t.Fatalf("client IpcSet: %v", err)
  103. }
  104. if err := clientDev.Up(); err != nil {
  105. t.Fatalf("client Up: %v", err)
  106. }
  107. conn, err := clientNet.DialUDPAddrPort(netip.AddrPort{}, wantDest)
  108. if err != nil {
  109. t.Fatalf("client DialUDPAddrPort: %v", err)
  110. }
  111. defer conn.Close()
  112. deadline := time.Now().Add(5 * time.Second)
  113. var buf [256]byte
  114. for {
  115. select {
  116. case err := <-identityErrCh:
  117. t.Fatal(err)
  118. default:
  119. }
  120. _ = conn.SetWriteDeadline(time.Now().Add(200 * time.Millisecond))
  121. if _, err := conn.Write([]byte(echoPayload)); err != nil {
  122. if time.Now().After(deadline) {
  123. t.Fatalf("client write never succeeded: %v", err)
  124. }
  125. continue
  126. }
  127. _ = conn.SetReadDeadline(time.Now().Add(200 * time.Millisecond))
  128. n, err := conn.Read(buf[:])
  129. if err != nil {
  130. if time.Now().After(deadline) {
  131. t.Fatalf("client never received a reply: %v", err)
  132. }
  133. continue
  134. }
  135. if got := string(buf[:n]); got != echoPayload {
  136. t.Fatalf("echoed payload = %q, want %q", got, echoPayload)
  137. }
  138. return
  139. }
  140. }