Mirrored networking: host->WSL localhost broken; loopback relay connects to wrong destination ports in the guest
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 33.7k
- Forks
- 1.8k
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 116
Description
## Windows Version
Microsoft Windows [Version 10.0.26200.8894]
## WSL Version
2.9.4.0 (pre-release; also reproduced on 2.7.8.0 stable)
## Are you using WSL 1 or WSL 2?
WSL 2
## Kernel Version
6.18.35.2-1
## Distro Version
Ubuntu 26.04 LTS
## Other Software
Tailscale 1.98.9 (bug reproduces with Tailscale disconnected). No third-party AV/firewall (Defender only). No Docker.
## Repro Steps
`.wslconfig`:
```ini
[wsl2]
vmIdleTimeout=-1
networkingMode=mirrored
[experimental]
bestEffortDnsParsing=true
```
1. In WSL: `python3 -m http.server 8765 --bind ::`
2. From Windows: `curl.exe -m 4 http://127.0.0.1:8765/`
## Expected Behavior
HTTP 200 — the connection should be relayed into the WSL guest at destination port 8765.
Control machine: a second machine on the same Windows build (10.0.26200.8894), WSL 2.7.10.0, mirrored mode, same Tailscale version, works correctly.
## Actual Behavior
Connection times out. **The mirrored-mode loopback relay dials into the guest at the wrong destination ports.**
An AF_PACKET capture on `loopback0` inside the guest, during a **single** `curl.exe http://127.0.0.1:8765/` from the host, shows the relay connecting to incorrect, incrementing destination ports (never 8765), each correctly RST'd by the guest since nothing listens there:
```
TCP 127.0.0.1:65292 -> 127.0.0.1:55818 [S]
TCP 127.0.0.1:55818 -> 127.0.0.1:65292 [AR]
TCP 127.0.0.1:65292 -> 127.0.0.1:55819 [S]
TCP 127.0.0.1:55819 -> 127.0.0.1:65292 [AR]
TCP 127.0.0.1:65292 -> 127.0.0.1:55820 [S]
TCP 127.0.0.1:55820 -> 127.0.0.1:65292 [AR]
TCP 127.0.0.1:65293 -> 127.0.0.1:62562 [S]
TCP 127.0.0.1:62562 -> 127.0.0.1:65293 [AR]
TCP 127.0.0.1:65293 -> 127.0.0.1:62563 [S]
TCP 127.0.0.1:62563 -> 127.0.0.1:65293 [AR]
...
```
Not port-specific: probing a listener on port 3000 produced relay SYNs to 24142 and 24145. IPv6 (`[::1]`) fails identically. Once, transiently, a probe succeeded (HTTP 200), so the relay *can* map correctly on this machine but almost never does.
Additional diagnostics performed:
- **Host-side pktmon** (filter port 8765): the original SYN (`127.0.0.1:src -> 127.0.0.1:8765`) traverses TCPIP → WFP Native Filter → VFP Extension → VMS Ext Filter/Protocol and is last seen `Rx` at the `[VM Nic]` component, correctly addressed to the guest `loopback0` MAC; no drop counters increment anywhere.
- **Inbound from the network works perfectly**: other LAN/Tailscale hosts reach the same WSL listener on this machine's mirrored addresses. Only host→guest loopback is broken.
- **Guest plumbing verified**: `loopback0` up, `net.ipv4.conf.loopback0.route_localnet=1`, rp_filter loose, `ip rule`/table 127 (`127.0.0.1 via 169.254.x.x dev loopback0`) all present and identical to the working control machine. Empty nftables ruleset, no firewall services.
- Windows→WSL loopback traffic *counters* on `loopback0` do increment during probes — but entirely from the relay's wrong-port SYNs and the guest's RSTs.
Ruled out (verified identical to the working control machine, bug persists after each):
- Hyper-V firewall: effective (ActiveStore) VMSetting identical (`Enabled True / DefaultInboundAction Block / LoopbackEnabled True / AllowHostPolicyMerge True`); also tested with explicit `DefaultInboundAction Allow`
- `netsh interface portproxy reset` (stale NAT-era rules removed)
- Windows Defender Firewall profiles identical; NDIS adapter bindings identical (all-stock Microsoft components)
- Tailscale: same version/config as control; fails with `tailscale down`
- WSL upgrade 2.7.8.0 → 2.9.4.0 (pre-release)
- Full Windows reboot
- HNS state reset (stopped `hns`, removed `HNS.data`, reboot)
This looks like the same relay port-scrambling family as #40343 (there: WSL→host direction, SYN-ACK returned to the wrong ephemeral port; here: host→guest direction, relay dials the wrong destination port).
## Diagnostic Logs
Happy to provide the pktmon ETL/text captures, the in-guest packet captures, and `collect-networking-logs.ps1` output on request.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the mirrored-mode loopback case from `.wslconfig` with `python3 -m http.server 8765 --bind ::`, then compare the behavior with the working control machine. Review the mirrored networking loopback relay and related issue #40343; done means host requests reach the guest's actual destination port and return HTTP 200 for both IPv4 and IPv6.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- networking, operating-systems
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100