ページが読み込まれ、小さなAPI呼び出しが返り、そのあとファイルのアップロードがクライアントの諦めまで固まる。サーバーのログには何の説明もない、パケットが経路の途中で、役に立つ応答を返さないまま破棄されているからだ。このパターンがMTUブラックホールであり、送信側の想定より狭いリンクと、それを告げない沈黙の拒否の組み合わせだ。失敗するサイズは再現性があり、だからこそ診断できる。

穴を黒くするもの

どのリンクにも最大転送単位があり、次のホップの上限を超えるパケットは、分割するか、そう告げるICMPメッセージで拒否しなければならない。経路MTU探索は、そのメッセージが届いて尊重されることに依存している。それをフィルタしたり無視したりすると、送信側は決して収まらないパケットを再送し続ける。

カプセル化はVPNの周りでこれをよく起こす、トンネルが、すでに物理リンク用に大きさを決められたパケットに外側のヘッダーを足すからだ。内側のパケットは1500バイトで合法だが、カプセル化されたものはそうではなく、境界で死ぬ一方、小さなパケットは通り抜ける。ハンドシェイクの完了もログインページの動作も、小さなパケットが通ることしか証明しない。アプリが送ろうとする最大のパケットこそが重要だ。

壊れるサイズを見つける

断片化しないテストでペイロードの大きさを二分探索する、トンネルの片側のホストから反対側のホストへ。通る最大のペイロードが、使える経路MTUからヘッダーのオーバーヘッドを引いたものだ。

ping -M do -s 1400 -c 2 192.0.2.20
ping -M do -s 1200 -c 2 192.0.2.20
ping -f -l 1372 192.0.2.20

1472バイトのペイロードは、IPv4とICMPのヘッダー28バイトを足すと、きれいな1500バイトのEthernet経路を通る。トンネルを張ったあとに同じテストがより小さなペイロードでしか通らないなら、その差がカプセル化のオーバーヘッドであり、トンネルの境界が拒否メッセージを生成すべき場所だった。両方の数値を接続前と接続後に記録しよう、その組が証拠になる。アプリがIPv6を使うときは同じテストを繰り返そう、ヘッダーが大きく、失敗のしきい値が動くからだ。

トンネルを責める前に証拠を読む

遠隔アプリのエラー、輻輳、通常の損失もアップロードを止めるので、一度の転送失敗ではブラックホールを特定できない。両者を分けるのはサイズの関係で、1200バイトで再現性のある成功、1400バイトで再現性のある停止、同じ方向、同じ端点だ。

両端でのキャプチャは、大きなパケットが出ていって届かず、その間に拒否もないことを示す。そのとき見るべきはサーバーではなく境界装置だ、サーバーは実際に受け取ったすべてのリクエストに応答しているからだ。輻輳も止めるが、違うサイズを違う時刻に止めるので、それは別の署名だ。

両方のトランスポートで修復を確かめる

支持される修理は、境界に本当の上限を報告させるか、そこTCPの最大セグメントサイズを絞って大きすぎるセグメントを試させないことだ。ネットワークのすべてのMTUを下げたり、ファイアウォールを無効にしたりすると、症状を隠し、後から誰も推論できない設定を残す。

ip link show dev tun0
sudo nft list ruleset | grep -i -E 'mtu|mss'
sysctl net.ipv4.tcp_mtu_probing
ping -M do -s 1400 -c 2 192.0.2.20

同じサイズテストで確認し、次に失敗したアプリで確認し、二つ目のプロトコルも確かめよう、UDPアプリはTCPのセグメントサイズ補正を決して受け取らないからだ。元の値をチケットに残そう、元に戻せない修理は、起きるのを待つ二つ目の問題だ。サイズテストが通り、アップロードがまだ止まるなら、ブラックホールが原因ではなかったので、変更を広げるのではなく調査を先へ進めるべきだ。

関連記事