パケットが壊れる IPフラグメントの欠陥
公衆電話網とは対照的に、インターネットはパケット交換設計になっています。
CC BY 2.0 image by ajmexico, inspired by
これは古い質問で、IPv4 RFC はかなり明確にそれに答えています。 3514>
-
両端のオペレーティング システムで処理できる最大パケット サイズはどのくらいか。
-
ホスト間の物理接続を通じて安全にプッシュできる最大許容データグラムサイズは何か。
パケットが物理リンクに対して大きすぎる場合、中間ルーターはそれを複数の小さなデータグラムに切り分けて適合させるかもしれない。 このプロセスは「前方」IP フラグメントと呼ばれ、より小さなデータグラムは IP フラグメントと呼ばれます。
Image by Geoff Huston, reproduced with permission
IPv4仕様は最小要件を定義しています。 RFC791 より:
Every internet destination must be able to receive a datagramof 576 octets either in one piece or in fragments tobe reassembled. Every internet module must be able to forward a datagram of 68octets without further fragmentation.
最初の値である許可された再組み立てパケットサイズは、通常問題ではありません。 IPv4では最小値を576バイトと定義していますが、一般的なオペレーティングシステムでは、通常65KiBまでの非常に大きなパケットに対処することができます。 すべての物理接続には、使用する特定のメディアに応じて、固有のデータグラムサイズ制限があります。 たとえば、フレームリレーは 46 バイトから 4,470 バイトのデータグラムを送ることができます。 ATM は固定 53 バイトを使用し、古典的なイーサネットは 64 から 1500 バイトの間で実行できます。
仕様では、最小要件として、各物理リンクは少なくとも 68 バイトのデータグラムを送信できる必要があると定義されています。 IPv6 では、その最小値が 1280 バイトに引き上げられました (RFC2460 参照)。
一方、断片化せずに送信できる最大データグラムサイズは、どの仕様でも定義されておらず、リンクタイプにより異なります。 この値はMTU(Maximum Transmission Unit)と呼ばれています。
MTUは、ローカル物理リンクにおける最大データグラムサイズを定義しています。 インターネットは非均質なネットワークから作られており、2つのホスト間の経路には、より短いMTU値を持つリンクが存在する可能性があります。 2 つのリモート ホスト間で断片化せずに送信できる最大パケット サイズはパス MTU と呼ばれ、すべての接続で異なる可能性があります。
Avoid fragmentation
非常に大きなパケットを送信するアプリケーションを構築して、IP 断片化を実行するルーターに依存しても問題ないと考える人がいるかもしれません。 これは良いアイデアではありません。 このアプローチの問題点は、1987 年に Kent と Mogul によって初めて議論されました。
-
パケットの再アセンブリを成功させるには、すべてのフラグメントを配信する必要があります。 どのフラグメントも破損したり、飛行中に失われたりすることはありません。 単に、紛失したフラグメントについて相手に通知する方法がないのです!
-
最後のフラグメントが最適なサイズであることは、ほとんどありえません。 大規模な転送の場合、これはトラフィックの大部分が最適でない短いデータグラムで構成されることを意味し、貴重なルーター リソースの無駄となります。
-
再アセンブリの前に、ホストはメモリ内で部分的なフラグメント データグラムを保持しなければなりません。 これは、メモリ枯渇攻撃の機会をもたらします。
-
Subsequent fragments には、上位層のヘッダがありません。 TCP または UDP ヘッダーは、最初のフラグメントにのみ存在します。 このため、ファイアウォールは、ソースポートや宛先ポートなどの基準に基づいてフラグメントデータグラムをフィルタリングすることができません。
IP フラグメント問題のより詳細な説明は、Geoff Huston による以下の記事で見ることができます。
- Evaluating IPv4 and IPv6 packet fragmentation
- Fragmenting IPv6
Don’t fragment – ICMP Packet too big
Image by Geoff Huston, reproduced with permission
A solution to these problems was included in the IPv4 protocol.It’s in the IPv4はIPの断片化に関する問題点の解決策です。 送信者は、IP ヘッダーに DF (Don’t Fragment) フラグを設定し、中間ルーターにパケットの断片化を実行しないよう依頼することができます。 その代わり、より小さなMTUを持つリンクを持つルーターは、ICMPメッセージを「逆方向」に送信し、この接続のMTUを減らすよう送信者に通知します。
TCPプロトコルは常にDFフラグを設定します。 ネットワークスタックは、受信した「Packet too big」ICMPメッセージを注意深く探し、すべての接続の「path MTU」特性を追跡します。 この技術は「パスMTU発見」と呼ばれ、他のIPベースのプロトコルにも適用できますが、ほとんどがTCPで一般的に使用されています。 3514>
How the Internet actually works
The perfect world, internet connected devices would cooperate and correctly handle fragment datagrams and the associated ICMP packets.The ICMPメッセージの配信は、TCPスタックが最適に動作するために非常に重要です。 しかし、現実には、IP フラグメントと ICMP パケットは非常に頻繁にフィルタリングされます。
これは、現代のインターネットが 36 年前の予想よりもはるかに複雑であるためです。 今日では、基本的に誰もパブリック インターネットに直接接続していません。
顧客のデバイスは、NAT (Network Address Translation) を行うホーム ルーターを通じて接続し、通常はファイアウォール ルールを適用します。 パケット パス上に複数の NAT インストールがある場合がますます多くなっています (キャリア グレード NAT など)。 そして、パケットはISPのインフラにぶつかり、そこにはISPの「ミドルボックス」が存在します。 プランの上限を設定したり、接続を制限したり、ロギングしたり、DNSリクエストをハイジャックしたり、政府が定めたウェブサイトを禁止したり、透明なキャッシュを強制したり、あるいは他の何らかの魔法のような方法でトラフィックを「最適化」したりと、あらゆる方法でトラフィックに対して奇妙なことを実行します。
同様に、サーバーと公衆インターネットの間にも複数のレイヤーが存在することがよくあります。 サービス プロバイダは、Anycast BGP ルーティングを使用することがあります。 つまり、世界中の複数の物理的な場所から、同じ IP 範囲を処理します。 一方、データセンター内では、負荷分散のために ECMP Equal Cost Multi Path を使用することがますます一般的になっています。
クライアントとサーバー間のこれらの各層は、パス MTU の問題を引き起こす可能性があります。
4つのシナリオで説明します。 クライアント -> サーバー DF+ / ICMP
最初のシナリオでは、クライアントが TCP を使用してサーバーにいくつかのデータをアップロードするため、すべてのパケットに DF フラッグが設定されます。 クライアントが適切な MTU を予測できない場合、中間ルーターは大きなパケットをドロップし、ICMP “Packet too big” 通知をクライアントに返送します。 これらの ICMP パケットは、顧客の誤った設定の NAT デバイスや ISP ミドルボックスによってドロップされるかもしれません。
Maikel de Boer と Jeffrey Bosma による 2012 年の論文によると、IPv4 ホストの約 5% と IPv6 ホストの 1% が受信 ICMP パケットをブロックしているそうです。 ICMPメッセージは、セキュリティ上の利点として認識されるため、確かにしばしばドロップされますが、これは比較的簡単に修正できます。 より大きな問題は、奇妙なミドルボックスを持つ特定のモバイル ISP にあります。 これらはしばしばICMPを完全に無視し、非常に積極的な接続の書き換えを実行します。 たとえば、Orange Polska は、受信した「Packet too big」ICMP メッセージを無視するだけでなく、接続状態を書き換えて、MSS を譲れない 1344 バイトに固定します。 クライアント -> サーバー DF-/フラグメント
次のシナリオでは、クライアントは TCP 以外のプロトコルによって何らかのデータをアップロードしますが、DF フラグはクリアされています。 たとえば、UDP を使用してゲームをプレイしているユーザーや、音声通話をしているユーザーなどです。
大きなペイロードサイズで ping
を起動することで、これをエミュレートできます:
$ ping -s 2048 facebook.com
この特定の ping
は、1472 バイトより大きなペイロードで失敗します。 それ以上のサイズは断片化され、適切に配信されません。 サーバーがフラグメントを誤って処理する理由は複数ありますが、よくある問題のひとつは ECMP ロードバランシングを使用していることです。 ECMP ハッシングにより、プロトコルヘッダを含む最初のデータグラムは、残りのフラグメントとは異なるサーバーに負荷分散され、再アセンブリが妨げられる可能性があります。
さらに、サーバーおよびルーターの設定ミスは重要な問題です。 RFC7852によると、サーバーの30%から55%がフラグメントヘッダーを含むIPv6データグラムをドロップします。 サーバー -> クライアント DF+ / ICMP
次のシナリオは、クライアントがTCPでデータをダウンロードすることです。 サーバーが正しい MTU を予測できない場合、ICMP 「Packet too big」メッセージを受信する必要があります。 簡単でしょう。
残念なことに、これも ECMP ルーティングが原因ですが、そうではありません。 ICMP パケットの 5 つのタプルのハッシュは、問題のある接続の 5 つのタプルのハッシュと一致しないため、ICMP メッセージは間違ったサーバーに配信される可能性が高いのです。 私たちは過去にこの件について書き、これを解決するためのシンプルなユーザースペース・デーモンを開発しました。 これは、受信 ICMP “Packet too big” 通知をすべての ECMP サーバーにブロードキャストし、問題のある接続を持つサーバーがそれを見ることを期待して動作します。
さらに、Anycast ルーティングにより、ICMP は完全に誤ったデータセンターに配信されるかもしれません!
Missing ICMP “Packet too big” notification は、接続のストールやタイムアウトを引き起こす可能性があります。 これは、しばしば PMTU ブラックホールと呼ばれます。 この悲観的なケースを助けるために、LinuxはMTU Probing RFC4821というワークアラウンドを実装しています。 MTU Probingは、間違ったMTUのためにドロップされたパケットを自動的に識別しようとし、それを調整するためにヒューリスティックを使用します。 この機能は sysctl:
$ echo 1 > /proc/sys/net/ipv4/tcp_mtu_probing
によって制御される。しかし、MTUプロービングにも問題がないわけではない。 まず、輻輳に関連するパケットロスをMTUの問題として誤って分類してしまう傾向があります。 長く実行されている接続は、MTUが減少した状態で終わる傾向があります。 第二に、Linux は IPv6 の MTU Probing を実装していません。
4. Server -> Client DF- / fragmentation
最後に、サーバーが DF ビットをクリアして非 TCP プロトコルで大きなパケットを送信する状況があります。 このシナリオでは、大きなパケットは、クライアントへのパスで断片化されます。 この状況は、大きなDNSレスポンスで最もよく説明されます。 以下は、大きな応答を生成し、複数の IP フラグメントとしてクライアントに配信される 2 つの DNS 要求です。
$ dig +notcp +dnssec DNSKEY org @199.19.56.1$ dig +notcp +dnssec DNSKEY org @2001:500:f::1
これらの要求は、すでに述べたように、誤った設定のホームルーター、壊れた NAT、壊れた ISP インストール、またはあまりにも制限的なファイアウォール設定のために失敗する可能性があります。
DNS に影響を与える特定の断片化問題の詳細については、以下のリンクをご覧ください:
- DNS-OARC 応答サイズ テスト
- IPv6, Large UDP Packets and the DNS
Yet the internet still works !
これらすべてのことがうまくいかないのに、なぜインターネットはまだ機能しているのでしょうか?
CC BY-SA 3.0、出典:NTTドコモ。 ウィキペディア
これは主に、イーサネットの成功によるものです。 公衆インターネットにおけるリンクの大部分はイーサネット (またはイーサネットから派生したもの) で、1500 バイトの MTU をサポートしています。
MTU が 1500 であると盲目的に仮定すると、それがうまく機能することがいかに多いかに驚くことでしょう。 インターネットは、私たちが皆 MTU 1500 を使用しており、IP フラグメンテーションを行い ICMP メッセージを送信する必要がほとんどないため、ほとんど機能し続けます。
これは、非標準 MTU を持つリンクの異常なセットアップで機能しなくなります。 VPN や他のネットワーク トンネル ソフトウェアは、フラグメントと ICMP メッセージが正常に動作するように注意しなければなりません。
これは、多くのユーザーがトンネルを介して接続する IPv6 の世界で特に顕著に見られます。 特に、IPv6 のフラグメントは基本的に機能しないので (IPv6 ホストの 10% ~ 50% が IPv6 フラグメント ヘッダーをブロックすると主張する 2 つのソースを引用)、両方の方法で ICMP を健全に通過させることは非常に重要です。 3514>
Online ICMP blackhole checker
これらの問題を調査しデバッグするために、オンラインチェッカーを構築しました。 このテストには2つのバージョンがあります:
- IPv4 version: http://icmpcheck.popcount.org
- IPv6 バージョン。 http://icmpcheckv6.popcount.org
これらのサイトは2つのテストを開始します:
- 最初のテストは、パスのMTUを笑えるほど小さな値に減らす目的で、コンピュータにICMPメッセージを送信します。
これらのテストの両方で「pass」を受け取ると、ケーブルのあなたの側のインターネットがうまく動作していることを合理的に保証することになります。
サーバー上で実行したい場合に備えて、コマンドラインからテストを実行することも簡単です。 ルーティングキャッシュテーブルを見ることで、これを確認することができます。
ip route get `dig +short icmpcheck.popcount.org`
Linuxでは、ルーティングキャッシュをクリアすることができます:
ip route flush cache to `dig +short icmpcheck.popcount.org`
2番目のテストは、フラグメントがクライアントに正しく配信されているかどうかを確認します:
curl -v -s http://icmpcheck.popcount.org/frag -o /dev/nullcurl -v -s http://icmpcheckv6.popcount.org/frag -o /dev/null
まとめ
このブログ記事では、インターネットにおけるパスMTU値を検出する問題点を説明しました。 ICMP およびフラグメント データグラムは、接続の両側でブロックされることがよくあります。 クライアントは、誤った設定のファイアウォールやNATデバイスに遭遇したり、接続を積極的に傍受するISPを使用したりすることがあります。
一方、サーバーは、エニーキャストまたは ECMP に依存することが多くなっています。 これらの両方、およびルーターやファイアウォールの誤設定は、しばしば ICMP およびフラグメント データグラムがドロップされる原因となります。
最後に、オンライン テストが有用で、ネットワークの内部構造についてより深い洞察を得られることを願っています。 このテストには、tcpdump 構文の有用な例があり、より多くの洞察を得るのに便利です。 ネットワーク デバッグに幸あれ!
インターネットの 10% の断片化の問題を解決することは、エキサイティングなことでしょうか? 私たちは、あらゆる種類のシステムエンジニア、Golang プログラマー、C ラングラー、およびインターンを、複数の場所で募集しています! サンフランシスコ、ロンドン、オースティン、シャンペーン、ワルシャワで一緒に働きましょう。
-
IPv6では、「転送」による断片化はIPv4とはわずかに異なる動作をします。 中間ルータはパケットの断片化を禁止されていますが、送信元はまだ断片化することができます。 これはしばしば混乱を招きます。ホストは、過去に送信したパケットを断片化するよう求められることがあります。 これは、DNSのようなステートレスプロトコルではほとんど意味を持ちません。 ︎
-
余談ですが、「最小伝送単位」というものも存在します! 一般的に使われているイーサネットのフレーミングでは、送信される各データグラムはレイヤ2で少なくとも64バイトでなければなりません。 これは、UDPでは22バイト、TCP層では10バイトに変換されます。 複数の実装で、より短いパケットで未初期化メモリがリークしていた! ︎
-
IPv4では厳密に言うとICMPパケットは「Destination Unreachable, Fragmentation Needed and Don’t Fragment was Set」という名前になっているのです。 しかし、私はIPv6のICMPエラーの記述「Packet too big」の方がより明確だと思います。 ︎
-
ヒントとして、TCPスタックはSYNパケットに最大許容値「MSS」(MSSは基本的にIPとTCPヘッダーサイズで減じたMTU値)も含んでいます。 これにより、ホストは自分のリンクのMTUが何であるかを知ることができます。 注意:これは、2つのホスト間の何十ものインターネットリンクのMTUが何であるかを言っているわけではありません。 ︎
-
安全サイドに立ちましょう。 DSLやPPPoE接続に対応するために、より良いMTUは1492です。 ︎