Skip to content

[Bug] WireGuard endpoint: handshake completes but no return traffic — works in mihomo with identical config #4157

@drstrangerujn

Description

@drstrangerujn

Operating system

Android

System version

Android 14 (Samsung Android device)

Installation type

sing-box for Android Graphical Client (SFA)

If you are using a graphical client, please provide the version of the client.

SFA latest stable (sing-box 1.13.0 embedded)

Version

sing-box 1.13.0 (as embedded in SFA stable)

Description

WireGuard endpoint completes handshake successfully (multiple received handshake response debug logs) but no application traffic reaches the peer. The exact same WireGuard configuration (same server, same keys, same client device, same network) works perfectly when used through mihomo (Clash Meta) kernel — both desktop (mihomo-party on Windows) and Android (ClashMetaForAndroid / CMFA).

This strongly suggests a divergence between sagernet/wireguard-go fork and metacubex/wireguard-go fork at the userspace WireGuard protocol implementation level when interoperating with a specific WG server.

Symptom

  • WG handshake succeeds repeatedly (every 15s retry due to no app traffic)
  • TUN inbound routes 10.66.66.0/24 correctly to the WG endpoint
  • Application TCP SYN to 10.66.66.4:5000 (NAS in WG mesh) times out after ~2 minutes
  • SFA UI shows the WG outbound has 0 bytes upload/download throughout

Verified evidence chain

Client Kernel WireGuard impl Same config? Works?
mihomo-party (Windows) mihomo metacubex/wireguard-go Yes
ClashMetaForAndroid (Samsung Android) mihomo metacubex/wireguard-go Yes
Surge (macOS / iOS) reading Clash YAML Clash Meta compat n/a Yes
sing-box for Android (Samsung Android) sing-box 1.13.0 sagernet/wireguard-go Yes ❌ 0 bytes transferred

Only the kernel / wireguard-go fork differs. Same client device, same network, same WG server, same keys.

Hypothesis (for investigation)

Possible causes for divergence between sagernet/wireguard-go and metacubex/wireguard-go:

  1. Different handling of WG Transport Data (type 4) packets: the peer sends them but sagernet's fork may not dispatch them up to the tunnel TCP stack the same way metacubex's fork does
  2. Different keepalive / handshake renegotiation timing: the 15-second retry suggests session is being torn down too aggressively while data should be flowing
  3. Possibly related to AmneziaWG patches in sagernet's fork breaking interop with vanilla wireguard-go server on certain Transport Data formats

Related issues that may share the same root cause:

Reproduction

Minimal sing-box JSON (anonymized)

{
  "log": {
    "level": "debug",
    "timestamp": true
  },
  "dns": {
    "servers": [
      { "type": "udp", "tag": "local", "server": "223.5.5.5" }
    ],
    "final": "local"
  },
  "inbounds": [
    {
      "type": "tun",
      "tag": "tun-in",
      "address": ["172.19.0.1/30"],
      "auto_route": true,
      "strict_route": true,
      "stack": "mixed"
    }
  ],
  "endpoints": [
    {
      "type": "wireguard",
      "tag": "wg-test",
      "address": ["10.66.66.6/32"],
      "private_key": "<CLIENT_PRIVATE_KEY>",
      "mtu": 1408,
      "peers": [
        {
          "address": "<WG_SERVER_IP>",
          "port": 61818,
          "public_key": "<PEER_PUBLIC_KEY>",
          "allowed_ips": ["0.0.0.0/0", "::/0"],
          "persistent_keepalive_interval": 25,
          "reserved": [0, 0, 0]
        }
      ]
    }
  ],
  "outbounds": [
    { "type": "direct", "tag": "direct" }
  ],
  "route": {
    "rules": [
      { "action": "sniff" },
      { "protocol": "dns", "action": "hijack-dns" },
      { "ip_cidr": ["10.66.66.0/24"], "action": "route", "outbound": "wg-test" }
    ],
    "auto_detect_interface": true,
    "final": "direct",
    "default_domain_resolver": { "server": "local" }
  }
}

sing-box check passes.

Equivalent Clash YAML (works in mihomo)

proxies:
  - name: "WireGuard Node"
    type: wireguard
    server: <WG_SERVER_IP>
    port: 61818
    ip: 10.66.66.6
    public-key: <PEER_PUBLIC_KEY>
    private-key: <CLIENT_PRIVATE_KEY>
    udp: true

Steps to reproduce

  1. Run mihomo (any frontend: mihomo-party / CMFA / FlClash) with the Clash YAML above → access http://10.66.66.4:5000works
  2. Run SFA with the sing-box JSON above → access http://10.66.66.4:50000 bytes, timeout after ~2 min
  3. WG server in both cases is the same instance, accepting the same client public key.

Tried mitigations (none fixed the issue)

  • mtu: 1280 (IPv6 safe)
  • mtu: 1408 (mihomo default)
  • mtu: 1420 (WG standard)
  • persistent_keepalive_interval: 25
  • reserved: [0, 0, 0] on the peer
  • ✗ Minimal config (above) — only WG endpoint + direct outbound, no DNS rules, no fakeip, no rule-set
  • ✗ Different DNS strategy
  • ✗ Restart, reconnect

Logs

INFO[0293] [1954512094 0ms] inbound/tun[tun-in]: inbound connection to 10.66.66.4:5000
DEBUG[0293] [1954512094 0ms] router: match[0] => sniff
DEBUG[0293] [1954512094 4ms] router: sniffed protocol: http, domain: 10.66.66.4
DEBUG[0293] [1954512094 4ms] router: match[2] ip_cidr=10.66.66.0/24 => route(WG)
INFO[0293] [1954512094 4ms] endpoint/wireguard[wg-test]: outbound connection to 10.66.66.4:5000

DEBUG[0124] endpoint/wireguard[wg-test]: peer(<KEY>) - sending handshake initiation
DEBUG[0124] endpoint/wireguard[wg-test]: peer(<KEY>) - received handshake response       ← handshake OK
DEBUG[0124] endpoint/wireguard[wg-test]: peer(<KEY>) - sending keepalive packet

(every 15 seconds, repeat:)
DEBUG[0139] endpoint/wireguard[wg-test]: peer(<KEY>) - retrying handshake because we stopped hearing back after 15 seconds
DEBUG[0139] endpoint/wireguard[wg-test]: peer(<KEY>) - sending handshake initiation
DEBUG[0139] endpoint/wireguard[wg-test]: peer(<KEY>) - received handshake response

(eventually:)
ERROR[0163] [<conn-id> 2m7s] connection: open connection to 10.66.66.4:5000 using outbound/wireguard[wg-test]: connect tcp 10.66.66.4:5000: operation timed out

Note: handshake responses are received continuously, but no Transport Data (type 4 WG packet) appears to reach the kernel from the peer, OR the userspace wireguard-go fork is not handing TCP traffic to the in-tunnel layer correctly.

What would help maintainers

Happy to:

  • Provide full debug logs (anonymized)
  • Test against any patched build of sagernet/wireguard-go
  • Run a side-by-side packet capture comparing mihomo's WG traffic vs sing-box's WG traffic on the same WAN egress

Supporter

Integrity requirements

  • I confirm that I have read the documentation, understand the meaning of all the configuration items I wrote
  • I confirm that I have reproduced this on the latest stable client
  • I confirm that I have tested with a minimal config

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions