VPN が大きな転送で止まり、小さな転送では止まらない理由
小さなリクエストは応答するのに、大きな転送は固まる。固定の MTU を写す前に、インターフェースの上限、経路の上限、告知される TCP ペイロードを比較する。
ページは読み込まれ、ping は通り、ファイルのコピーは毎回同じ場所で止まる。このパターンは帯域幅や認証情報ではなく、パケットサイズを示している。カプセル化は各パケットにバイトを追加するため、元の経路で収まっていたペイロードも、トンネルを通した後に上限を超えることがある。この会話では三つの異なる数が MTU と呼ばれており、それらを混同すると、一方のアプリを直し、もう一方を壊す設定になる。
人が MTU と呼ぶ三つの数
インターフェース MTU は、そのリンク上でパケットに設定された上限である。経路 MTU はルート上の最小の上限で、設定するものではなく発見するものだ。TCP MSS は相手が受け取れると宣言するペイロードの大きさで、残り二つから導かれ、別個の物理的な上限ではない。
トンネルには内側と外側の見え方がある。外側のパケットは、カプセル化されたパケットに加えてプロトコルが必要とするヘッダーを運ぶため、使える内側の大きさは外側の経路が許す大きさより小さくなる。VPN サーバーまでの経路も、サーバーから宛先までの経路とは異なる。
経路 MTU の探索によって送信側は動作する大きさを知れるが、フィードバックが届くかどうかにかかっている。中間機器が該当する ICMP メッセージを捨てると、送信側は決して完結しないパケットを送り続け、これが典型的なブラックホールである。症状は知ってしまえば特徴的で、ハンドシェイクは成功し、最初のフルサイズのペイロードが消え、やがて接続がリセットされるか固まる。アプリケーションのログにはネットワーク問題らしいものは何も残らない。
自分で管理できるペイロードで経路を調べる
調査は一つの経路と一つの大きさについての証拠であり、推奨設定ではない。応答するホストを使い、変えるのはペイロードの大きさだけにする。
ping -M do -s 1372 -c 2 203.0.113.20 ping -4 -f -l 1372 vpn.example
最初のコマンドはフラグメント化を無効にし、1372 バイトのペイロードで Linux 用の調査を送り、二つ目はペイロードを -l で指定する Windows で同じことを行う。応答があれば、その大きさは経路を通り抜けたことになる。フラグメント化が必要という通知は有益だが、タイムアウトはホストや中間機器が単に ICMP を捨てているだけかもしれないため曖昧だ。
バイト単位の二分探索ではなく、数段階で大きさを下げていく。確かめたいのは大きさの上限が存在するかどうかであり、すべてのネットワーク向けに数値を公表することではない。見つけた数値は、この経路、このプロトコル、このアドレスファミリーに当てはまる。
IPv4 と IPv6 ではフラグメント化の仕方が違うため、一方のファミリーの結果は他方については何も語らない。アプリが実際に使っているファミリーを試すこと。
MSS クランプが効くのは TCP だけ
TCP では実効ペイロードがハンドシェイク中に交渉され、経路上のルーターがその値を下方向に書き換えることで、両端がより小さいセグメントを送るようになる。これが MSS クランプの役割であり、クライアントには一切触れずに機能する。
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu ip link show tun0
最初のルールは転送される TCP ハンドシェイクについて、告知される MSS を経路 MTU にクランプし、二つ目はトンネルインターフェース自身の MTU を表示するので、経路が許す値と比べられる。ハンドシェイク後に固まる TCP アプリにとって、クランプは最も侵襲の少ない修正だ。
UDP には何もしない。大きなデータグラムを送る音声やゲームのクライアントは、書き換えるハンドシェイクがないため、MSS 設定を完全に無視する。失敗しているアプリが UDP なら、代わりにクライアント自身のパケットサイズ設定を見るべきだ。二つのプロトコルは壊れ方も直し方も違うため、チケットの最初の一文にプロトコルを書くべきだ。
OpenVPN は tun-mtu や mssfix、フラグメント関連の設定などを提供し、その挙動はバージョンとデータチャネルが AEAD かどうかによって変わる。使っているバージョンのマニュアルを読み、一度に一つのオプションだけを変えること。
対応している設定を一つだけ変えて両方の実行を繰り返す
元のプロファイルを残し、対応している値を一つだけ変えて、うまくいった小さな要求と失敗した大きな転送の両方を繰り返す。大きい方だけが変われば、大きさを追いかけていたことになる。両方が変われば、別の何かが一緒に動いている。
値を下げれば症状は和らぐがオーバーヘッドが増えるので、可能な限り小さい数値が目的ではない。目的は、経路が最も無駄なく確実に運べる大きさだ。同じ数値を他所に当てはめるのは推測にすぎないため、値、経路、クライアントのバージョンをチケットに記録すること。
同じプロファイルが別の信頼できるネットワークから動くなら、クライアントを直ったと宣言するのではなく、その差を診断に残す。変わった変数は経路であり、経路こそが調査で測ったものだ。