# WSL MTU 卡住

WSL2 主機是唯一一種工作階段能正常連上、然後就這麼停住，而兩端都沒有錯誤的情境。
問題幾乎不會出在 SSH 或 Tessera —— 而是 WSL2 背後那個虛擬介面卡的 MTU。當
Tessera 在連線期間觀察到這種特徵性的卡住時，會顯示一張琥珀色的
*可能是 WSL2 / Tailscale 的 MTU 問題*卡片，並連到這裡。

## 症狀

- 工作階段連上了，提示字元出現，短指令也能正常執行。
- 任何會送出大量資料的操作都會凍結：對長檔案執行 `cat`、寬幅的 `ls`、tmux 的
  完整重繪、[檔案面板](https://bambouville.com/docs/zh-hant/files/)中的傳輸。
- 沒有任何東西回報失敗。工作階段就是不動了。重新連線可以成功，然後在同一類
  輸出上再次卡住。
- 同一台主機透過 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。一個停在 1400 的 VPN 介面卡，旁邊是 1500 的
`vEthernet (WSL)`，這就是你要找的不相符。

## 在 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 和區域網路的可達性
都會變得不同 —— 因此如果你唯一的問題只是工作階段卡住，還是優先使用開機指令。

## 當低 MTU 的那一跳在伺服器那一側

受限的一端不一定是 WSL；從伺服器執行同樣的 ping 測試，就能知道是哪一側被卡住。
如果中間的路由器由你掌控，把 TCP 的 MSS 箝制到探測出的路徑 MTU，就能一次修好
經過它的所有連線：

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

## 在你還卡著的時候

把主機的[傳輸方式](https://bambouville.com/docs/zh-hant/connections/#傳輸方式ssh-或-mosh)改成 mosh 是權宜之計，
而不是修復。mosh 的資料包夠小，能從有問題的 MTU 底下溜過去，而且它是重繪畫面
而非重播位元組串流，所以互動式工作階段能撐下來。檔案面板和 tmux 側通道仍走
SSH，因此大型傳輸仍可能繼續卡住。

如果以上都不符合你看到的狀況，請擷取一份日誌
（**設定 → 診斷 → 輸出日誌**，見[診斷](https://bambouville.com/docs/zh-hant/security/#診斷)），
並到
[GitHub Issues](https://github.com/bambouville/tessera/issues) 開立議題。
