bambouville

WSL MTU 卡顿

WSL2 主机是唯一一种会话能正常连上、随后干脆停住,而且两端都不报错的情形。 问题几乎从来不在 SSH 或 Tessera —— 而在 WSL2 所处的虚拟网卡的 MTU。当 Tessera 在连接过程中观察到这种典型卡顿时,会显示一张琥珀色的 可能是 WSL2 / Tailscale 的 MTU 问题卡片,并链接到这里。

症状

  • 会话连上了,提示符出现了,短命令也运行正常。
  • 任何会产生大量输出的操作都会冻结:对长文件执行 cat、很宽的 ls、一次 完整的 tmux 重绘,或在文件面板中传输文件。
  • 没有任何东西报告失败。会话就是不动了。重新连接可以成功,然后在同类输出上 再次卡住。
  • 同一台主机在 mosh 下的表现比在 ssh 下好。

如果连接根本到不了提示符,同样的故障也可能吞掉了 SSH 的密钥交换 —— 那本身 就是一大块数据。下面的测试同样适用。

为什么会这样

WSL2 运行在 Hyper-V 虚拟交换机之后,它的 eth0 启动时的 MTU 是 1500。而当 Windows 主机实际是通过更小的通道接入网络时 —— VPN、PPPoE 或任何隧道 —— 超过真实路径 MTU 的数据包会在中途某处被丢弃。

路径 MTU 发现本应正好处理这种情况:无法转发该数据包的那一跳会返回一个 ICMP 需要分片,发送方随之退让。但许多 VPN 和防火墙会丢弃那个 ICMP,于是回应 永远不会到来。小包照常通过,大包则悄无声息地消失 —— 这就是 PMTU 黑洞。在 SSH 看来,这就像一条无缘无故停住的连接。

确认问题

在 WSL 内部,先看看接口声称的值:

ip link show eth0

然后找出这条路径上真正能通过的大小。下面的 ping 设置了「不分片」,负载加上 28 字节的头部即为总大小,因此 -s 1472 探测的是 1500 字节的数据包。目标要 选在同一条路径的另一端 —— 你要连接的服务器,或者任何公网地址:

ping -M do -s 1472 -c 1 example.com   # 1500 — usually times out
ping -M do -s 1372 -c 1 example.com   # 1400 — usually gets through

超时就是黑洞。(如果反而立即出现本地错误,说明该大小超过了你自己接口的 MTU —— 调低 -s 再试。)不断缩小范围,直到找出能得到回应的最大负载;加上 28,那就是你真实的路径 MTU。

Windows 一侧的等效命令,在 PowerShell 中执行:

ping -f -l 1472 example.com
netsh interface ipv4 show subinterfaces

第二条会列出每个网卡的 MTU。如果一个 VPN 网卡停在 1400,而 vEthernet (WSL) 是 1500,那就是你要找的不匹配。

在 WSL 中修复

把接口设为你测出的大小。这会立即生效,但在下次关机后失效:

sudo ip link set dev eth0 mtu 1400

重新连接,再做一遍原先会卡住的操作。如果输出顺畅,诊断就得到了确认。

要让它持久生效,就让 WSL 在启动时应用它。在发行版内的 /etc/wsl.conf 中:

[boot]
command = ip link set dev eth0 mtu 1400

然后在 Windows 中运行 wsl --shutdown,再重新启动该发行版。[boot] 命令 需要一个相对较新的 WSL —— 用 wsl --version 查看。

请使用你的 ping 测试得出的数值,而不是照抄 1400。1400 是一个几乎能通过所有 VPN 的保险值,但与你的路径相匹配的数值可以少浪费每个数据包的空间。

或者让 WSL 镜像宿主机

在 Windows 11 22H2 及更新版本上,配合 WSL 2.0+,镜像网络模式会把宿主机自身 的网络接口(包括 MTU)直接交给发行版,因此根本不会出现不匹配。在 %UserProfile%\.wslconfig 中:

[wsl2]
networkingMode=mirrored

wsl --shutdown 使其生效。这项改动影响的远不止 MTU —— localhost 和局域网 可达性的行为都会改变 —— 因此如果你唯一的问题只是会话卡顿,还是优先使用 boot 命令。

当低 MTU 的那一跳在服务器一侧

受限的一端未必总是 WSL;从服务器上运行同样的 ping 测试就能知道是哪一侧被 钳住了。如果你能控制中间的路由器,把 TCP 的 MSS 钳制到探测出的路径 MTU, 可以一次性修复经过它的所有连接:

iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --clamp-mss-to-pmtu

在问题解决之前

把该主机的传输方式改为 mosh 是权宜 之计,而不是真正的修复。mosh 的数据报足够小,能从有问题的 MTU 下溜过去, 而且它是重绘屏幕而不是重放字节流,因此交互式会话能撑下来。文件面板和 tmux 侧信道仍然走 SSH,所以大文件传输仍可能卡住。

如果以上都不符合你看到的情况,请抓取一份日志 (设置 → 诊断 → 导出日志,参见诊断),并在 GitHub Issues 提交问题。