【テクニカル・上級編】 IGWおよびNATゲートウェイにおけるパスMTUディスカバリー(PMTUD)とICMPメッセージの転送制御 – クラウド&コンテナネットワーク実践ガイド

パケットが「黒塗り」される恐怖:PMTUDとICMPが握るネットワークの生命線

インフラエンジニアのキャリアにおいて、避けては通れない「ブラックホール現象」がある。特定の通信だけがなぜかタイムアウトする、あるいはコネクション確立後のデータ転送でパケットがピタリと止まる。Wiresharkを立ち上げ、SYNパケットが飛び交うのを見つめながら、「なぜACKが返ってこないのか」と溜息をついた経験は一度や二度ではないはずだ。

その元凶の多くは、MTU(Maximum Transmission Unit)の不一致と、それに伴う「パケットの沈黙」にある。今日は、クラウド環境のネットワークの深淵、特にIGW(Internet Gateway)とNATゲートウェイを巡るPMTUD(Path MTU Discovery)の挙動について、現場の血肉となった知見を共有したい。

—

なぜ「断片化」は現代のネットワークで嫌われるのか

かつてIP層での断片化(Fragmentation)は、ルーターの基本的な機能として当然視されていた。しかし、今日のハイパフォーマンスなクラウドネットワークにおいて、フラグメンテーションは「悪」である。

1. CPU負荷の増大: ルーターやロードバランサーがフラグメントの再構築や分割を行うのは、ステートフルな負荷を強いる行為だ。
2. セキュリティリスク: 断片化されたパケットを悪用したIDS/IPS回避や、リソース枯渇攻撃の踏み台にされやすい。
3. ドロップ率の向上: 多くのモダンなファイアウォールやセキュリティグループは、断片化されたパケットを最初から破棄するように設計されている。

だからこそ、私たちは「断片化させるな(DFビットを立てろ)」というポリシーを採用し、その結果としてPMTUDを正しく機能させる必要がある。

—

PMTUDの死角:ICMP Type 3 Code 4の帰還

PMTUDのメカニズムはシンプルだ。送信元ホストはIPヘッダーに DF(Don’t Fragment)ビットを立ててパケットを送出する。経路上のリンクMTUを超えるパケットに遭遇すると、ルーターはパケットを破棄し、送信元へ「ICMP Type 3 Code 4(Destination Unreachable, Fragmentation Needed and DF set)」を送り返す。

ここで問題になるのが、プライベートサブネット内のインスタンスだ。

NATゲートウェイ越しにインターネットと通信する場合、帰りのICMPメッセージはNATゲートウェイを経由してインスタンスに戻らねばならない。もし、NATゲートウェイの背後のネットワークACLやセキュリティグループが、このICMPメッセージを「不要なトラフィック」として遮断していれば、PMTUDは機能不全に陥る。これが「コネクションは確立するが、大きなペイロードを流すと固まる」という悪夢の正体だ。

—

現場で打つべき対策:ブラックホールを防ぐ設定

1. セキュリティグループとACLの盲点

まず、インスタンスのセキュリティグループ(インバウンド)において、ICMPタイプ3の全コードを許可することを強く推奨する。

# AWS CLI例: ICMP 3:4 (Fragmentation Needed) を許可するためのルール追加
aws ec2 authorize-security-group-ingress \
    --group-id sg-xxxxxxxx \
    --protocol icmp \
    --port -1 \
    --icmp-type-code type=3,code=4 \
    --cidr 0.0.0.0/0 # 実際にはNATゲートウェイのIP範囲に絞るのがベスト

2. TCP MSS クランプの強制

PMTUDがうまく機能しないケース(例えば、特定のパスでICMPが常にフィルタリングされている場合)に備え、OSレベルで MSS(Maximum Segment Size)を調整する。これにより、TCPハンドシェイクの段階でパケットサイズを強制的に絞り込む。

Linuxカーネルの iptables を使用した強硬手段は以下の通りだ。

# TCP SYNパケットのMSSを1360バイトに制限し、オーバーヘッドを考慮する
# 1500(MTU) - 20(IPヘッダー) - 20(TCPヘッダー) = 1460だが、
# VPNやトンネリングを考慮し、1360程度まで下げるのが経験則。
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
    -j TCPMSS --set-mss 1360

—

パフォーマンスの極致:TLSハンドシェイクとバッファチューニング

MTUの最適化は、単なる接続性の問題ではない。TLS 1.3のような現代のプロトコルでは、ハンドシェイクのパケットサイズが重要になる。

パケットがMTUを超えて分割されると、RTTが1往復増えるのと同等のレイテンシロスが発生する。これを防ぐためには、TCP_NODELAY を有効にし、かつバッファサイズを適切にチューニングする必要がある。

import socket

# ソケットレベルでバッファを最適化する例
def optimize_socket(sock):
    # Nagleアルゴリズムを無効化(小パケットを即時送信)
    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
    
    # 送受信バッファを拡大(高スループット環境用)
    # 大規模なTLSデータを扱う際にパケットロスによる再送負荷を低減する
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 262144)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 262144)

—

最後に:ネットワークを「見る」エンジニアであれ

「ネットワークは繋がっていて当たり前」という時代だが、クラウドの抽象化されたレイヤーの裏側では、相変わらずIPプロトコルの厳格なルールが支配している。

もしあなたが大規模なマイクロサービスを運用しているなら、NATゲートウェイのメトリクスを監視するだけでは足りない。パケットがどこでドロップしているのかを、ICMPのフローという視点から追いかける能力こそが、トラブルシューティングの最終兵器となる。

教科書を閉じて、tcpdumpを握りしめろ。パケットが教えてくれる真実は、常にドキュメントの行間にあるのだから。

コメント

タイトルとURLをコピーしました