bambouville

WSL の MTU による停止

WSL2 のホストは、セッションが普通につながったあと、どちらの端にもエラーを 出さずにただ止まる、唯一の構成です。原因が SSH や Tessera であることは ほとんどありません。原因は、WSL2 がその背後に置かれている仮想アダプタの MTU です。接続時にこの特徴的な停止を観測すると、Tessera はここへリンクする アンバーの WSL2 / Tailscale の MTU の問題の可能性があります カードを表示 します。

症状

  • セッションはつながり、プロンプトが現れ、短いコマンドは問題なく動きます。
  • 大きなまとまりを送るものはすべて固まります。長いファイルへの cat、幅の 広い ls、tmux の全画面再描画、ファイルパネルでの転送。
  • 何も失敗を報告しません。セッションがただ動かなくなるだけです。接続し直せば 動きますが、同じ種類の出力でまた止まります。
  • 同じホストでも、ssh より mosh のほうが調子よく動きます。

接続がプロンプトにたどり着きさえしない場合は、同じ不具合が、それ自体が 大きい SSH の鍵交換を飲み込んでいる可能性があります。以下のテストはそのまま 当てはまります。

なぜ起きるのか

WSL2 は Hyper-V の仮想スイッチの背後で動き、その eth0 は MTU 1500 で 立ち上がります。Windows ホストが実際にはもっと小さい経路 — VPN、PPPoE、 あるいは何らかのトンネル — でネットワークに出ている場合、その実際の経路 MTU を超えるパケットは途中のどこかで捨てられます。

経路 MTU 探索は、まさにこれを扱うためのものです。パケットを転送できない ホップが ICMP の fragmentation needed を返し、送信側が下げる、という 仕組みです。ところが多くの VPN やファイアウォールはその ICMP を捨てるため、 返事は届きません。小さいパケットは流れ続け、大きいものは黙って消えます — PMTU のブラックホールです。SSH からは、理由もなく止まった接続に見えます。

確かめる

WSL の中から、インターフェイスが何と言っているかを確認します:

ip link show eth0

次に、実際に経路を通り抜けられるサイズを探します。以下の ping は don't-fragment を立てており、ペイロードにヘッダの 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

2 つ目は各アダプタの 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 で確認してください。

1400 をそのまま写すのではなく、ping のテストで出た数値を使ってください。 1400 はほとんどの VPN を通せる安全な落としどころですが、自分の経路に 合わせた値のほうが、1 パケットあたりの無駄が少なくなります。

あるいは WSL にホストをミラーさせる

Windows 11 22H2 以降で WSL 2.0 以上なら、ミラーモードのネットワークが ホスト自身のインターフェイスを MTU ごとディストリビューションに渡すため、 そもそも不一致が起きません。%UserProfile%\.wslconfig に:

[wsl2]
networkingMode=mirrored

wsl --shutdown で適用します。これは MTU よりずっと多くのものを変え、 localhost と LAN の到達性の挙動もどちらも変わります。停止するセッション だけが問題なのであれば、起動コマンドのほうを選んでください。

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に issue を 立ててください。