【実務・中級編】 pingによるMTUパス探索(Path MTU Discovery)とDFフラグの制御 – トラブルシューティング&ネットワーク運用監視実践ガイド

ネットワークの「見えない壁」を突破せよ:pingを使ったMTUサイズ探索の極意

深夜2時のデータセンター。監視画面が真っ赤に染まり、アプリケーションチームから「APIのレスポンスが極端に遅い、あるいは特定のデータ長でタイムアウトする」という悲鳴のような連絡が入る。

多くのエンジニアはここでDBの負荷やアプリケーションのロジックを疑うが、百戦錬磨のNOCエンジニアなら、まず「パケットの通り道」にあるMTU(Maximum Transmission Unit)のミスマッチを疑う。特に、クラウド間通信やVPNトンネルを経由する現代のインフラでは、この「断片化(フラグメンテーション)」という古くて新しい罠が、通信を静かに殺し続けている。

今回は、教科書を閉じて、現場で実際に使っている「DF(Don’t Fragment)フラグを制御したMTU探索」の実践テクニックを伝授しよう。

—

なぜ「断片化」が敵なのか

通常、MTUはイーサネット標準の 1500 bytes だ。しかし、トンネル技術(VXLAN, GRE, IPsec)を使うと、カプセル化オーバーヘッドによって実効MTUは削られる。

パケットがMTUを超えたとき、ルーターは「断片化」を行う。これ自体は仕様だが、最近のセキュリティ機器やロードバランサーは、断片化されたパケットを「攻撃の予兆」として破棄したり、処理負荷を嫌ってパケットを落とすケースが非常に多い。

ここで登場するのが、DF (Don't Fragment) フラグだ。「分割するな、さもなくば捨てろ」というこのビットを立てることで、ネットワーク上のどこでパケットが「太すぎて通れない」かを特定できる。

—

現場で使う「MTU探索」コマンドの作法

まずは、Linux環境で実際にパケットを投げ、どこで弾かれるかを確認する基本形だ。

Linux (iputils-ping) での探索

# -M do: DFフラグを立てる (Don't Fragment)
# -s 1472: ペイロードサイズを指定 (IPヘッダー20 + ICMPヘッダー8 = 28 bytes)
# つまり、1472 + 28 = 1500 bytes となる
ping -M do -s 1472 192.168.1.1

もし 1500 でパケットが通らず、1472 で通るなら、その経路のどこかに 1472 より小さいMTUを持つリンクが存在する。

Windows (コマンドプロンプト) での探索

:: -f: DFフラグをセット
:: -l: バッファサイズを指定
ping -f -l 1472 192.168.1.1

—

プログラムからMTUを考慮する:API設計の現場から

インフラエンジニアだけでなく、Web APIを設計する開発者もこの問題とは無縁ではない。巨大なJSONレスポンスを返すAPIを設計する際、特定の環境下でだけ Connection reset by peer や Timeout が発生するなら、それはMTUの問題である可能性が高い。

PythonによるMTUチェックの自動化

自作ツールで経路MTUを自動検出したい場合は、socket モジュールを使って IP_MTU_DISCOVER を設定する。

import socket

def check_mtu(target_host, mtu_size):
    # ICMPソケットを作成
    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
    
    # DFフラグを立てる (IP_PMTUDISC_DO)
    s.setsockopt(socket.IPPROTO_IP, socket.IP_MTU_DISCOVER, socket.IP_PMTUDISC_DO)
    
    # ペイロードサイズ計算 (MTU - IPヘッダー20 - ICMPヘッダー8)
    payload_size = mtu_size - 28
    packet = b'A' * payload_size
    
    try:
        s.sendto(packet, (target_host, 1))
        print(f"MTU {mtu_size} は正常に通過しました。")
    except OSError as e:
        # パケットが大きすぎてDFフラグにより破棄された場合
        print(f"MTU {mtu_size} でエラー: {e}")
    finally:
        s.close()

# 1400から徐々に上げていくようなループを組むのがコツ
check_mtu("8.8.8.8", 1472)

—

よくあるエッジケース:PMTUDの「黒穴(Black Hole)」

現場で最も厄介なのは、Path MTU Discovery (PMTUD) が機能しないケースだ。

RFC1191では、MTUを超えたパケットに対してルーターは「ICMP Type 3, Code 4(Fragmentation Needed and DF set)」を送信元に返すと定められている。しかし、最近のファイアウォールは、セキュリティポリシーによりこのICMPを全て遮断していることが非常に多い。

結果として、送信元は「パケットが捨てられたこと」すら知らされず、タイムアウトまで待機し続けることになる。これが「通信が確立しない」「最初の数バイトは届くが、続きが来ない」という症状の正体だ。

対策のTips:
1. MSSクランプの設定: ルーターやロードバランサーのインターフェース設定で、TCPの MSS (Maximum Segment Size) を強制的に小さく(例えば 1360 などに)書き換える。これが最も泥臭く、かつ確実な解決策だ。
2. ICMPの許可: 経路上のファイアウォールで、Type 3, Code 4 のICMPパケットだけは許可するようにACL(Access Control List)を調整する。

—

最後に:シニアからのアドバイス

ネットワークトラブルシューティングにおいて、ツールはあくまで「自分の予測」を確認するための補助に過ぎない。
「この回線はVPNを通っているから、オーバーヘッド分を引いた1400から当たりをつけよう」という推論が、コマンドを打つ前に脳内でできていることが重要だ。

まずは ping でパケットの境界線を叩く。そして、それがなぜそこで止まるのか、RFCの仕様を背景に想像力を巡らせる。この訓練を繰り返せば、どんなに不可解なパケットロスにも必ずロジカルな答えが見つかるはずだ。

現場での奮闘を祈る。健闘を!

コメント

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