Skip to content

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

@fishbits

Description

@fishbits

Windows Version

10.0.26100.8246

WSL Version

2.7.3.0

Are you using WSL 1 or WSL 2?

  • 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:
   curl -o /dev/null -w "%{time_total}\n" --max-time 15 https://example.com/large-page
  1. 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:
# 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

sudo sysctl -w net.ipv4.tcp_timestamps=0

Made persistent via /etc/wsl.conf:

[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

Metadata

Metadata

Assignees

No one assigned

    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