ICMPとPMTUD

このページの目次

WebページはTCPハンドシェイクを完了できるが、大きなレスポンスの転送時に停止することがある。これはパスのサイズフィードバックに問題がある可能性がある:ハンドシェイクの小さなパケットは通過するが、データパケットが特定の出口MTUを超えると、返されるICMPが送信元に到達しない。しかし、この現象はフィルタリング、輻輳、またはサーバー側の停止によっても引き起こされる可能性がある。ICMPの価値は、元のデータグラムに関連付けられるフィードバックを提供し、診断を「原因を推測する」ことから「証拠を検証する」ことへ移行させる点にある。

確認したいこと観察すべきこと単独では証明できないこと
IP Echoの往復可用性一致するEcho ReplyとRTTTCPサービス、DNS、またはすべてのパケット長が正常であること
どのノードがプローブパスに関与しているかTTL/Hop Limit期限切れのフィードバック各ホップが応答すること、または戻りパスが同じであること
MTU制限の有無対応する元のデータグラムに対するToo Big/Frag Needed1回のタイムアウトがMTUブラックホールであることを意味すること
送信元がフィードバックを採用しているか後続のパケット長、再送間隔、およびパス推定ICMPを取得すれば、アプリケーションが回復したことを意味すること

2つのプロトコルファミリ、タイプ番号を混同しない

ICMPv4のIPv4におけるProtocol番号は1、ICMPv6のNext Header番号は58である。これらはIPのエラー報告と情報交換に使用され、汎用の信頼できるバイトストリームは提供しない。Echoはデータを含むことができるが、これによりICMPがTCPのようなアプリケーション層転送サービスになるわけではない。

用途ICMPv4 Type/CodeICMPv6 Type/Code
Echo Request / Reply8/0、0/0128/0、129/0
ポート未到達3/31/4
パケットが大きすぎる3/4、断片化が必要かつDFビットが設定されている2/0、Packet Too Big
フォワーディング寿命の尽きた11/03/0
分片再組み立てのタイムアウト11/13/1
パラメータ問題Type 12、CodeによるType 4、Codeによる

これは一般的なサブセット表であり、これに基づいてすべてのUnreachableを同じエラーに分類してはならない。ICMPv6はさらに、Neighbor DiscoveryやRouter Discoveryなどのメッセージも伝達しており、プロトコル全体を無闇に遮断すると、基本的なネットワーク運用に影響を与える。RFC 792:ICMPv4、RFC 4443:ICMPv6

エラーメッセージはなぜ元の接続にたどり着くのか

ICMPの一般的な開始部分はType、Code、Checksumであり、その後の構造はタイプによって変化する。エラーメッセージは、エラーを引き起こした元のIPデータグラムを参照し、エンドポイントはアドレス、プロトコル、および利用可能なポート/シーケンス番号などを組み合わせて、フィードバックを対応する転送層またはプローブに関連付ける。

IPv4のFragmentation Neededを例にとると、最初の8バイトのICMPヘッダーには16ビットのNext-Hop MTUが含まれ、その後に元のIPヘッダーと元のデータの開始部分が続く。古典的な最小参照範囲には元のIPヘッダーと最初の8バイトのデータが含まれるが、より多くの参照コンテンツを受信しても異常ではない。参照データグラムを解析する際は、その実際のIHL、長さ、および利用可能なバイト数に基づいてチェックする必要があり、そこに宣言されている元のTotal Lengthを信じて境界外読み出しを行ってはならない。RFC 1191:MTUフィールドとフィードバック

ICMPv4のチェックサムはICMPメッセージをカバーする。ICMPv6のチェックサムはIPv6擬似ヘッダーも使用する。チェックサムが成功することは、対応するエラー検出が通過したことを示すだけで、送信者を認証するものではない。エンドポイントは、参照コンテンツが既存の送信状態と一致していることを検証する必要があり、無関係または偽造されたフィードバックによって接続状態が任意に変更されないようにする。

エラーフィードバックも信頼できる配信ではない:再帰を避けるため、通常、別のICMPエラーに対してはエラーを生成しない。マルチキャスト、ブロードキャスト、非最初のフラグメントなどには、各プロトコルの制限と例外があり、IPv6のPacket Too Bigには重要な例外がある。さらに、レート制限、パケットドロップ、フィルタリングを加えると、⁠フィードバックがない=エラーがない ということにはならない。RFC 4443:エラー生成ルール

ping:一致する往復のサンプル

EchoのIdentifierとSequenceはリクエスト/レスポンスの関連付けに役立つが、IdentifierをプロセスPIDと固定して理解してはならない。RTTには往路、相手側の処理、復路が含まれる。2で割って片方向遅延として直接使用することはできず、高負荷下でのアプリケーションリクエスト遅延を表すものでもない。

ReplyのTTL=57は、初期TTLが確かに64である場合にのみ、復路が約7回のデクリメントを経たという推論をサポートする。初期値は設定可能であり、中間にトンネルやアドレス変換がある可能性があるため、これに基づいてオペレーティングシステムを特定することはできず、ましてや順方向のホップ数を直接推測することもできない。

同一 1500 B MTU:アプリケーション利用可能バイト数は具体的なヘッダーに依存 IPv4 Echo IP 20 B 上位ヘッダー 8 B データ 1472 B IPv6 Echo IP 40 B 上位ヘッダー 8 B データ 1452 B IPv4 TCP IP 20 B 上位ヘッダー 20 B データ 1460 B IPv6 TCP IP 40 B 上位ヘッダー 20 B データ 1440 B IPv4オプション、IPv6拡張ヘッダー、TCPオプションは含まない;ボックス幅は可読性のためであり、バイト比で描画されていない。

Linux iputilsのping -sはEchoデータのバイト数を指す。オプションなしのIPv4において-s 1472は、20+8+1472=1500 BのIPパケットに対応する。IPv6で拡張ヘッダーがない場合、対応する値は1452である。イーサネットヘッダーとFCSはこのIP MTUには含まれない。iputils pingマニュアル

# プレースホルダーを診断対象に置き換えてください。これはコマンドの説明であり、今回のプローブは実行していません。
ping -4 -c 3 -M do -s 1472 <対象IPv4>
ping -4 -c 3 -M do -s 1464 <対象IPv4>
ping -6 -c 3 -M do -s 1452 <対象IPv6>

-M doはカーネルのPMTUチェックの影響を受け、大きすぎる送信は中間ルーターに到達する前にローカルでエラーになる可能性がある。ローカル送信の失敗、ネットワークからのエラー、および待機タイムアウトを区別する必要がある。3つのプローブサンプルは迅速な観察に過ぎず、安定したパケット損失率を推定するには不十分である。

traceroute:段階的にプローブ寿命を延長

TTL=1から開始し、最初の転送ルーターがこれを期限切れにし、条件に応じてTime Exceededを返す。TTLを増やすと、プローブはより遠くのノードに到達できる。最終的に到達したかどうかの判定はプローブプロトコルに依存する:UDPは通常、宛先ポート未到達を利用し、ICMP EchoはReplyを利用し、TCPはSYN-ACKやRSTなどの結果を利用できる。

各ホップに表示される時間は、プローブソースからそのフィードバックノードを経て戻るまでの往復時間であり、隣接するRTTを単純に減算して2つのルーター間のリンク遅延を得たとは主張できない。フィードバックは異なる復路を通過する可能性があり、デバイスがICMPを処理する優先度も異なる可能性がある。

観察される現象より確実な解釈
中間のホップが*で、その後続と宛先が応答するそのホップは時間内に可視なフィードバックを提供しなかったが、転送は正常である可能性がある
中間のホップで「高いパケット損失」があり、終点が正常フィードバックのレート制限である可能性があり、転送パケット損失を直接非難することはできない
同じTTLで複数のアドレスが表示されるECMP、パスの変更、またはReplyソースアドレスの違いがある可能性がある
UDPとTCPのパス結果が異なる5タプルハッシュ、ポリシー、およびフィルタリングが異なる可能性がある

従来のプローブはUDPの宛先ポートを変更する際にECMPハッシュを変更する可能性があるため、連続して表示されるアドレスが必ずしも実際の固定パスに属しているわけではない。ビジネスの問題を特定する際は、宛先、プロトコル、ポート、時間、および方向を可能な限り一致させ、パス図を観測結果として扱うべきである。

PMTUD:1500 → 1492の調整を完全に追跡

PMTUDのフィードバックは元の送信元に戻り、元の方向には継続しない 送信元 S 1500 Bを送信、DF=1 ルーター R 出口MTU=1492 受信元 D 今回の大きなパケットは受信されなかった 破棄 ICMP 3/4:MTU=1492 + 元のデータグラム参照 後続は利用可能なサイズで分割して送信;フィードバックパス自体もフィルタリングされる可能性がある IPv4の古典的なPMTUDを示す;IPv6では対応するPacket Too Bigであり、ルーターによる分片で回避できない。

オプションなしのIPv4/TCPを仮定し、受信側がSYNでMSS=1460を通知していたとする。送信元は1460 BのTCPデータを含む1500 BのIPパケット(DF=1)を送信し、ルーターの出口MTU=1492、フィードバックには1492が含まれる。送信元はフィードバックを検証し、パス推定を更新した後、後続のパケットの有効データ上限は1492−20−20=1452 Bに低下する可能性がある。

ここで変化するのは送信元が現在使用しているセグメントサイズであり、接続の途中で再ハンドシェイクを行って受信側が通知したMSSを1452に変更したわけではない。TCPオプションが12 B含まれる場合、同じPMTU下ではデータからさらに12 Bを差し引く必要がある。IPv6やトンネルの場合も、実際のヘッダー予算に従う必要がある。

TCPは必要に応じて確認されていないデータ区間を再送するが、IP自体は元のデータグラムの再送を保証しない。パスが再度変更される可能性や、さらに小さな後続のボトルネックがある可能性もあるため、1492を1回受信したからといって、永久に唯一の正しい値が見つかったわけではない。RFC 1191

IPv6の対応するものはPacket Too Bigであり、基本ヘッダーにはDFビットがなく、ルーターは分片しない。拡張ヘッダーもスペースを消費するため、基本ヘッダーが固定だからといって無視してはならない。古典的なPMTUDは、信頼できるサイズフィードバックを受信する必要がある。具体的なキャッシュ対象、寿命、および再プローブの動作は、実装と設定によって決定される。RFC 8201

Too Bigを受信すると、MSS通知が変更されるのか?

ハンドシェイクの通知を保持することで、後続の調整が実際の変数でどこで行われるのかを明確に見ることができる。まずサイズフィードバックを破棄して再送信し、その後リセットし、フィードバックを配信して再送信することで、2つの異なる因果過程を比較対照できる。過去の結果は、フィードバックを受信したからといって、「そのパケットは実際には成功していた」ものにはならない。

図を準備しています
Too Bigを受信すると、MSS通知が変更されるのか?

パスフィードバックは現在の送信予算を更新するが、ハンドシェイク内のMSSを書き換えることはない。フィードバックがない場合、同じ大きなパケットを繰り返しても失敗する可能性があります;TCPオプションはパケット長から差し引く必要があります。

ブラックホール:因果連鎖に従って証拠を収集する

信頼できる診断では、次のことを順次確認できる:送信元が実際に特定のサイズのパケットを送信したか、対応する出口がそれを収容できないか、エラーフィードバックがどこで生成され、到達し、または損失したか、後続の送信が依然として大きすぎるパケットを使用しているか。エンドポイントしか見られない場合は、制御可能なパケット長の変化、再送、およびアプリケーションの進行状況と組み合わせて範囲を絞り込み、同時に他の原因も保持する。

観察点支持される判断次のステップ
大きなパケットをローカルで送信すると即座に失敗するローカルインターフェース/パス推定が送信を制限しているエラー、インターフェースMTU、および宛先ルーティングを確認
一致するToo Bigを受信するパスがサイズ制約を提供した送信元がそれを受け入れ、縮小したかどうかを確認
小さなパケットは安定して通過し、大きなパケットは繰り返し確認されないサイズ関連の障害が候補プロトコルフィルタリング、ランダムなパケット損失、およびプローブパスの違いを除外
縮小後に回復する変更と回復に相関関係がある元のフィードバック/出口証拠と組み合わせて根本原因を判断

パケットキャプチャでは、TSO/GSO/GROにも注意する必要がある:ホスト上の「巨大パケット」は、セグメント化前または集約後のビューであり、必ずしもそのサイズでオンライン転送されたわけではない。MSS clampingはTCPのみに関与し、すべてのUDP/QUICパスの問題を修復できるわけではない。

PLPMTUDとLinuxの実装スイッチ

パケット分割層は、プローブと明確な確認を利用して、特定のパケット長が使用可能かどうかを検証でき、ICMPの唯一のフィードバックパスへの依存を減らすことができる。DPLPMTUDには、基本サイズ、上向き検索、プローブ失敗時のリトライ、およびブラックホール回復などの状態が必要である。1回の大きなパケットの損失は単なる輻輳である可能性があり、1回で最大値を宣言するのは単純すぎる。UDPアプリケーションに確認メカニズムがない場合、ローカルのsend成功をもって遠端が受信したと断言することはできない。RFC 8899:DPLPMTUD

Linuxのnet.ipv4.tcp_mtu_probingドキュメントは、0(無効)、1(対応するブラックホールが検出された場合に有効)、2(常に有効)を区別している。これはTCP実装の動作であり、すべての転送プロトコルを修復するグローバルスイッチではない。適用性はカーネル、接続、およびネットワークポリシーと組み合わせて判断する必要がある。本稿ではメカニズムの説明を保持し、無条件のsysctl書き込みで診断を代替しない。Linux IP sysctlドキュメント

ローカル検証と長さ予算

import struct

def checksum(data):
    if len(data)%2:
        data += bytes(1)
    total = sum(struct.unpack('!%dH' % (len(data)//2), data))
    while total>>16:
        total = (total&65535)+(total>>16)
    return (~total)&65535

message = bytearray(struct.pack('!BBHHH',8,0,0,0x1234,7)+b'hello')
message[2:4] = struct.pack('!H',checksum(message))
assert checksum(message) == 0
message[-1] ^= 1
assert checksum(message) != 0
assert 1500-20-8 == 1472
assert 1500-40-8 == 1452
assert 1492-20-20 == 1452
assert 1492-20-32 == 1440
assert 1464+8+20 == 1492
print('ICMP 1の補数チェックサム、奇数バイトパディング、およびMTUデータ予算が通過')

ここではICMPv4 Echoのローカルメッセージのみを構築し、長さを計算している。ネットワークプローブは送信されておらず、PMTU、MSS、またはsysctlも変更されていない。

参照