WSL mirrored networking leaks >16,000 UDP port reservations, causing host-wide vSwitch DPC latency and packet loss
- Dominant language
- C++
- Stars
- 33.7k
- Forks
- 1.8k
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 116
Description
# WSL mirrored networking leaks >16,000 UDP port reservations, causing host-wide vSwitch DPC latency and packet loss
## Summary
This is a host-wide performance failure, not merely a WSL connectivity problem.
WSL mirrored networking accumulates thousands of stale HCN/FSE port reservations. `fse.sys` then performs a linear reservation-table scan in the vSwitch DPC path for incoming packets. Once the table becomes sufficiently large, latency to the local gateway jumps from sub-millisecond to 70–116 ms, packets time out, and the physical Windows host’s network becomes unreliable.
A complete WSL shutdown removes the pathological state and immediately restores normal latency. The problem later returns as reservations accumulate again.
A VM networking feature must not be able to degrade the host’s physical LAN because its port-release bookkeeping failed. Periodically restarting every WSL distribution is not an acceptable workaround.
## Environment
- Windows build: `10.0.26200.8875`
- WSL: `2.6.3.0`
- Kernel: `6.6.87.2-1`
- Network mode: `mirrored`
- Physical adapter: Realtek RTL8125BG 2.5GbE
- Realtek NDIS driver: `10.80.50`
- Connection: wired Ethernet
Relevant configuration:
```ini
[wsl2]
networkingMode=mirrored
firewall=false
[experimental]
hostAddressLoopback=true
```
Other devices on the same LAN remain unaffected.
## Symptoms
- Local-gateway latency repeatedly rises from `<1 ms` to approximately `70–116 ms`.
- Intermittent packet loss and ICMP timeouts occur.
- The Ethernet link remains established.
- NIC hardware error and discard counters remain zero.
- A full `wsl --shutdown` restores normal network behavior.
- The degradation returns after WSL has been running again.
## HNS/FSE evidence
A read-only HNS diagnostic captured the following state:
- Total FSE port reservations: **16,671**
- Reservations owned by the active WSL partition: **16,169**
- WSL UDP reservations: **16,145**
- Of those UDP reservations:
- **16,144** were unique single-port reservations
- One was the expected larger reserved range
At the same time, the WSL network namespace contained only:
- Five IPv4 UDP socket entries
- Two IPv6 UDP socket entries
- Seven UDP entries in total
Therefore, virtually all 16,145 HNS UDP reservations had no corresponding live WSL socket.
A long-running TCP service was also checked. Its many accepted connections did not multiply HNS reservations: its listening port appeared only once. The explosion is specifically stale UDP single-port state, not connection volume or Ethernet saturation.
## ETW/WPA evidence
A 7.8-second ETW capture showed:
- `vmswitch!VmsVrssDpc`: **14,398 calls**
- Total time in that routine: **6.389 seconds**
- Calls exceeding 1 ms: **1,180**
- Maximum single call: **27.691 ms**
On CPU 0:
- **6,317 of 8,138 DPC leaf samples — 77.6% — were in `fse.sys`**
A longer 66-second capture showed:
- **17,049 of 24,338 relevant CPU-0 samples — 70% — in FSE**
The dominant hot routine was:
```text
FsePortPoolForEachReservationThatContainsPort
```
Disassembly shows this routine walking the reservation nodes linearly. Approximately 99.3% of FSE leaf samples landed in the node/range comparison loop.
For comparison, the Realtek driver accounted for only 27 leaf samples in the longer trace. This rules out the Ethernet driver as the source of the CPU consumption.
The observed chain is:
```text
stale WSL port reservations
-> linear FSE scan for incoming packets
-> prolonged vSwitch DPC execution
-> delayed/dropped host LAN packets
-> high gateway latency and packet loss
```
## Suspected WSL cleanup defect
The WSL 2.6.3 source contains failure paths where tracking state is forgotten without a successful, retryable release:
- Linux-side port tracker:
https://github.com/microsoft/WSL/blob/eb7b0cff015998c199f62b2150c1cd8b5fcfe7b3/src/linux/init/GnsPortTracker.cpp#L203-L240
- Windows-side guest network service:
https://github.com/microsoft/WSL/blob/eb7b0cff015998c199f62b2150c1cd8b5fcfe7b3/src/windows/service/exe/WslCoreGuestNetworkService.cpp#L265-L297
The Linux tracker removes its local allocation record even when deallocation reports failure. The Windows side also discards its stored reservation handle after attempting release, with no durable retry mechanism.
The same failed-release/no-retry structure still appears in the WSL 2.9.4 prerelease source:
- Linux side:
https://github.com/microsoft/WSL/blob/40d5f8ff2c80bc0cfef24dac0f5be94ccbd3558d/src/linux/init/GnsPortTracker.cpp#L270-L300
- Windows side:
https://github.com/microsoft/WSL/blob/40d5f8ff2c80bc0cfef24dac0f5be94ccbd3558d/src/windows/service/exe/WslCoreGuestNetworkService.cpp#L340-L371
The exact HRESULT associated with each historical failed release was not available after the fact, but the resulting stale HNS table and its direct effect on the FSE hot path are measurable.
## Observed reproduction pattern
This has not yet been reduced to a single minimal application:
1. Configure WSL with `networkingMode=mirrored`.
2. Start a normal long-running WSL workload that creates and closes UDP sockets.
3. Leave WSL running.
4. Compare live WSL UDP sockets with the active partition’s HNS/FSE reservations.
5. Observe thousands of single-port reservations remaining after the sockets are gone.
6. Observe increasing host gateway latency and packet loss as the table grows.
7. Run `wsl --shutdown`.
8. Restart WSL and observe that host latency returns to normal until the table grows again.
## Expected behavior
- Releasing a Linux socket must eventually release its associated HCN/FSE reservation.
- A transient release failure must remain tracked and be retried.
- Partition or GNS teardown must reliably remove every associated reservation.
- Stale guest state must not cause unbounded per-packet work in the host vSwitch.
- WSL networking must not degrade the physical host’s LAN connection.
## Actual behavior
- Closed UDP sockets leave thousands of single-port reservations behind.
- WSL forgets the allocations while FSE retains them.
- FSE linearly scans the bloated table in the vSwitch DPC path.
- The Windows host experiences severe local-network latency and packet loss.
- The only effective recovery is stopping the entire WSL VM.
## Why alternative modes are not an adequate answer
Mirrored networking is required here for native LAN visibility and complete IPv6 behavior.
- NAT does not provide equivalent inbound LAN or IPv6 behavior.
- VirtioProxy provides proxied address/port behavior rather than complete native LAN IPv6 semantics.
- Bridged mode meets the networking requirements but Microsoft has deprecated it.
- Restarting WSL interrupts all distributions and hosted services.
## Requested fixes
Please:
1. Preserve failed port releases and retry them until HCN confirms cleanup.
2. Reconcile HCN reservations against the authoritative live-port set instead of forgetting failed releases.
3. Guarantee partition/GNS teardown removes all associated reservations.
4. Replace or bound the per-packet linear reservation scan so stale state cannot monopolize a DPC.
5. Add diagnostics exposing reservation count, owner and age without requiring private tooling.
6. Add a stress test that repeatedly binds and closes UDP sockets, injects release failures, and verifies that the reservation count returns to baseline.
7. Treat this as a host reliability issue: a WSL guest currently has a path to effectively denial-of-service the Windows host’s physical network stack.
Sanitized HNS reservation summaries and ETW/WPA stack summaries can be provided. Raw traces are intentionally not attached publicly because they may contain process names, local paths, interface identifiers, or network addresses.
Contributor guide
Research direction
Start with the cited sections of src/linux/init/GnsPortTracker.cpp and src/windows/service/exe/WslCoreGuestNetworkService.cpp, then reproduce mirrored networking with repeated UDP socket creation and closure. Compare live sockets with HNS/FSE reservations and observe behavior after wsl --shutdown. Done means failed releases remain retryable, teardown returns reservations to baseline, and the requested stress test verifies cleanup.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- networking, operating-systems, performance
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100