microsoft / microsoft/WSL

WSL2 NAT Mode: TCP Timestamps Cause Sustained Streams to Stall After April 2026 Updates

Open
#40,615 2 comments 0 reactions 0 assignees View on GitHub
network
Dominant language
C++
Stars
33.7k
Forks
1.8k
Avg merge
3d 17h
Merged PRs (30d)
116

Description

### Windows Version

10.0.26100.8246

### WSL Version

2.7.3.0

### Are you using WSL 1 or WSL 2?

- [x] WSL 2
- [ ] WSL 1

### Kernel Version

6.6.114.1-1

### Distro Version

Ubuntu 24.04 LTS (Noble)

### Other Software

- curl 8.5.0 (x86_64-pc-linux-gnu) — used to reproduce the issue
- No VPN client active during testing
- No Docker Desktop installed
- Issue reproduces with any application making sustained HTTPS connections from WSL (curl, Python requests, Rust/hyper-based CLI tools)

### Repro Steps

1. Ensure WSL2 is in NAT networking mode (the default)
2. Confirm `net.ipv4.tcp_timestamps` is enabled (default): `sysctl net.ipv4.tcp_timestamps` → 1
3. Attempt to download any resource larger than ~50KB via a sustained TCP stream:
```bash
curl -o /dev/null -w "%{time_total}\n" --max-time 15 https://example.com/large-page
```
4. Observe the download starts, transfers 40-80KB, then stalls until timeout

### Expected Behavior

The download completes in under 1 second (as it does from Windows natively on the same machine at the same moment).

### Actual Behavior

The download stalls after receiving ~47-80KB of data and eventually times out. The TCP connection is established and data begins flowing, but the stream hangs mid-transfer.

**Key observations:**
- Small responses (<10KB) complete fine
- The same endpoint accessed via `curl.exe` (Windows networking stack) from within WSL completes instantly (~0.14s)
- The issue is **deterministically caused by TCP timestamps** — toggling the setting on/off immediately reproduces/resolves the issue:

```bash
# Timestamps ON → stalls
$ sudo sysctl -w net.ipv4.tcp_timestamps=1
$ curl -o /dev/null -w "%{time_total}\n" --max-time 15 https://releases.ubuntu.com/noble/SHA256SUMS
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
20 229k 20 47632 0 0 3174 0 0:01:14 0:00:15 0:00:59 0
curl: (28) Operation timed out after 15002 milliseconds with 47632 out of 235038 bytes received

# Timestamps OFF → instant
$ sudo sysctl -w net.ipv4.tcp_timestamps=0
$ curl -o /dev/null -w "%{time_total}\n" --max-time 15 https://releases.ubuntu.com/noble/SHA256SUMS
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 229k 100 229k 0 0 2563k 0 --:--:-- --:--:-- --:--:-- 2578k
0.089521
```

This is **100% reproducible** — toggling back and forth immediately changes behavior. Tested dozens of times across multiple `wsl --shutdown` cycles.

## Additional Context

- **Mirrored networking mode is unavailable** on this system because IPv6 is disabled via registry (`DisabledComponents=0xff`), which is common in enterprise environments.
- The issue appears to have been introduced by the **April 2026 cumulative updates** (KB5088467 / KB5083769 / KB5082420, installed April 22-24). Prior to these updates, WSL2 NAT mode worked without issue for months with TCP timestamps at the default (enabled).
- The issue is **worse under network load** — during low-traffic periods (early morning), the stall sometimes doesn't occur. During peak hours (afternoon/evening), it is 100% reproducible. This suggests the Hyper-V virtual switch's NAT connection tracking has a race condition or resource contention issue when processing TCP timestamp options.
- MSS clamping (`iptables -t mangle ... --set-mss 1360`) partially mitigated the issue during low-load periods but did not resolve it during peak hours. Only disabling timestamps fully resolves it.
- Disabling Large Send Offload (LSO) on the vEthernet (WSL) adapter did **not** help and actually made connectivity worse.

## Workaround

```bash
sudo sysctl -w net.ipv4.tcp_timestamps=0
```

Made persistent via `/etc/wsl.conf`:
```ini
[boot]
command=sysctl -w net.ipv4.tcp_timestamps=0
systemd=true
```

## Root Cause Hypothesis

The Hyper-V virtual switch's NAT implementation appears to mishandle TCP timestamp options (RFC 7323) in packet headers during sustained transfers. When the NAT rewrites packets, it may be corrupting or incorrectly tracking timestamp values, causing the remote server's TCP stack to interpret the timestamps as invalid and stop sending data (or causing the NAT itself to drop packets with "unexpected" timestamp values).

This would explain:
- Why small transfers work (few packets, timestamps don't diverge)
- Why it's worse under load (more connections = more timestamp state to track = higher chance of collision/corruption)
- Why `curl.exe` from WSL works (bypasses the virtual switch entirely, uses Windows TCP stack)
- Why disabling timestamps completely eliminates the issue (no timestamp options in packets = nothing for the NAT to mishandle)

### Diagnostic Logs

_No response_

Contributor guide

Open the contributing guide

Research direction

No source file or test is named. Start by reproducing the sustained curl transfer in WSL2 NAT mode, then compare tcp_timestamps=1 and tcp_timestamps=0 across the stated Windows, WSL, kernel, and Ubuntu versions. Done means the transfer completes reliably with timestamps enabled, including under network load, without relying on the workaround.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, linux
Domain
networking, operating-systems
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.