本文へ移動
QKQuickKitEN

ping は通るのに大きな通信だけ止まる:MTU・MSS・PMTUD の切り分け

Web ページの読み込みや scp だけが途中で止まるとき、Path MTU Discovery の失敗(PMTUD ブラックホール)を疑う手順。DF 付き ping での確認、MSS の計算、MSS クランプの限界まで。

一次資料とコマンドの確認日:

ping は返ってくる。SSH でログインもできる。それなのに Web ページの読み込みが途中で止まる、scp や大きなメールの送信だけが固まる——この組み合わせは、経路上のどこかで大きなパケットが通らず、しかもその通知が送信元に届いていないときの典型的な症状です。RFC 2923 §2.1 は、これを Path MTU Discovery(PMTUD)の「ブラックホール」として、ping や対話的な TCP 接続は動くのに大量転送だけが最初の大きなパケットで失敗する、と説明しています。

仕組み:PMTUD と、それが失敗するとき

  • IPv4:送信元は DF(Don't Fragment)ビットを立てて送ります。経路上のルーターは、次のリンクの MTU より大きいパケットを転送できないとき、ICMP Destination Unreachable の Type 3 Code 4(Fragmentation Needed and DF set)を返し、その中の Next-Hop MTU に次のリンクの MTU を入れます(RFC 1191 §4)。送信元はそれを見てパケットを小さくします。
  • IPv6:分割は送信元だけが行い、途中のルーターは分割しません(RFC 8200 §4.5)。大きすぎるパケットに対しては ICMPv6 の Type 2(Packet Too Big)と次のリンクの MTU を返します(RFC 4443 §3.2、RFC 8201)。
  • この ICMP / ICMPv6 がファイアウォールなどで落とされると、送信元は大きなパケットを送り続け、相手に届かないまま再送とタイムアウトを繰り返します(RFC 2923 §2.1)。RFC 4890 §4.3.1 は、ファイアウォールが落としてはならない ICMPv6 として Packet Too Big を挙げています。

確かめ方:DF を立てた ping でサイズを絞る

ping のデータ長に ICMP ヘッダー(8 バイト)と IP ヘッダー(IPv4 は 20、IPv6 は 40 バイト)を足したものが IP パケットの大きさです。MTU 1500 の経路なら、IPv4 では 1472 バイト、IPv6 では 1452 バイトのデータがちょうど収まります。

# Linux(iputils):-M do で DF を立て、-s でデータ長を指定
ping -M do -s 1472 -c 3 192.0.2.10
ping -M do -s 1464 -c 3 192.0.2.10   # 1492 の経路(PPPoE など)に収まる大きさ
ping -6 -M do -s 1452 -c 3 2001:db8::10

# Windows:/f で DF を立て(IPv4 のみ)、/l でデータ長を指定
ping /f /l 1472 192.0.2.10

結果の読み方:

  • 1472 が失敗し、小さくすると通る:経路のどこかの MTU が 1500 より小さい。通る最大のデータ長 + 28(IPv6 は + 48)が、その経路の Path MTU の目安です。
  • Linux の ping が Frag needed and DF set (mtu = 1400)(IPv6 では Packet too big: mtu=1400)と表示する:途中のルーターが ICMP で次のリンクの MTU を知らせてきました。PMTUD 自体は機能しており、表示された mtu が手がかりです。
  • local error: message too long, mtu=1500 と表示する:自分のホストが知っている MTU(インターフェースの MTU、または学習済みの Path MTU)を超えたため、送信前に止められました。
  • 大きいサイズで何も返らない(タイムアウト)のに小さいサイズは通る:大きなパケットが落ちているのに ICMP が届いていない可能性があります。ブラックホールの疑いが強い状態です。ただし、相手や途中の機器が ping そのものを制限している場合もあるので、別の宛先とも比べてください。

tracepath(iputils)は、経路をたどりながら途中で分かった MTU(pmtu)を表示します。どの区間で小さくなるかの手がかりになります。

tracepath -n 192.0.2.10

パケットを取得できる場所では、ICMP が返ってきているかを直接見られます(tcpdump / libpcap のフィルター)。

tcpdump -n 'icmp[icmptype] == icmp-unreach and icmp[icmpcode] == 4'
tcpdump -n 'icmp6[icmp6type] == icmp6-packettoobig'

取得した IPv4 の ICMP メッセージ(Type から始まるバイト列)は、ICMP デコーダー で Type / Code を確認できます。Next-Hop MTU は独立した項目としては表示されません。Type 3 Code 4 では、「Rest of Header」に表示される 4 バイトのうち下位 16 ビット(後ろの 4 桁)が Next-Hop MTU です(RFC 1191 §4。例:00000578 → 0x0578 = 1400)。この値が 0 なら、そのルーターは MTU を知らせていません(RFC 1191 §3)。

ICMP デコーダーは ICMPv4 専用で、ICMPv6 の Packet Too Big は解析できません。ICMPv6 の Type / Code の意味は ICMP・ICMPv6 タイプ/コード リファレンス で確認できます。

MSS の計算

TCP の MSS(Maximum Segment Size)オプションで広告する値は、実効 MTU から固定の IP ヘッダーと TCP ヘッダーを引いたものです(RFC 9293 §3.7.1)。

Path MTUIPv4 の MSS(−40)IPv6 の MSS(−60)
1500(一般的な Ethernet)14601440
1492(PPPoE)14521432
1280(IPv6 の最小リンク MTU)12401220

表の値は、受信側が SYN で相手に広告する MSS です。実際に 1 セグメントで送られるデータ量(実効送信 MSS)は、相手から受け取った MSS と自分側で送れる大きさの小さいほうから、TCP オプションと IP オプション(IPv6 では拡張ヘッダー)の分を引いた値になります(RFC 9293 §3.7.1)。たとえば Timestamps オプション(RFC 7323 §3.2 で 10 バイト。同 Appendix A の推奨どおり NOP 2 つと合わせると 12 バイト)を毎回付けると、広告された MSS が 1460 でも 1 セグメントのデータは 1448 バイトになります。

トンネル(GRE、IPsec、VXLAN、WireGuard など)を通る場合の MTU と MSS は、MTU/MSS計算機 でカプセル化のオーバーヘッドから計算できます。

対処:MSS クランプと、その限界

経路上の ICMP を通せない場合の回避策として、ルーターが TCP の SYN に含まれる MSS を書き換える「MSS クランプ」があります。Linux の iptables では次のように指定します(iptables-extensions(8) の TCPMSS ターゲット)。

iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
  • --clamp-mss-to-pmtu は MSS を Path MTU − 40(IPv4)/ − 60(IPv6)に抑えます。往路と復路で Path MTU が違う非対称な経路では期待どおりに働かない場合がある、と同じ文書が注意しています。
  • MSS クランプが効くのは TCP だけです。UDP や、UDP を使う QUIC(HTTP/3)、VPN などの通信は小さくなりません。UDP を使うプロトコルの経路 MTU 探索については RFC 8899(DPLPMTUD)があります。
  • 根本的な対処は、ICMP の Fragmentation Needed / Packet Too Big を落とさないことです。

限界と注意

  • ping のサイズ確認は ICMP Echo の結果です。途中の機器が ICMP Echo を制限・優先度変更している場合、TCP の通信と結果が一致しないことがあります。
  • 経路は宛先や時間帯で変わることがあります。問題のある宛先そのものに対して確認してください。
  • 機器ごとの MSS クランプの設定方法は製品の公式ドキュメントで確認してください。このガイドは Linux の例だけを示しています。

参考資料(一次資料)