「パケットが泣いている」:MTUの壁とDFフラグが教えるネットワークの真実
夜中の3時、データセンターのフロアで轟音を響かせる冷却ファンの音を聞きながら、ふと考えることがあります。我々が叩いているコマンドの裏側で、パケットは一体どんな旅をしているのか、と。
特に厄介なのが「MTU(Maximum Transmission Unit)問題」です。クラウド環境やVPN越しに通信をする際、なぜか特定のパケットだけがドロップする。Web APIを叩いてもレスポンスが返ってこず、curl が虚しくタイムアウトを繰り返す……そんな時、教科書通りのマニュアルを眺めていても解決はしません。
今回は、現場のエンジニアなら避けては通れない「DFフラグを用いたMTUパスの検証」について、泥臭い知見を共有しましょう。
—
1. なぜ「フラグメント」は悪者なのか?
ネットワークの基礎知識として、IPヘッダーには DF (Don't Fragment) というビットがあります。これを立てて送出すると、途中のルーターはパケットを分割(フラグメンテーション)することが許されません。
もし、そのパケットがルーターの通過可能な最大サイズ(MTU)を超えていたらどうなるか? ルーターは律儀に「これ以上は運べないから、ICMP Type 3 Code 4(Destination Unreachable, Fragmentation Needed and DF set)」というエラーを送信元へ投げ返します。
現代の高速なネットワーク運用において、フラグメンテーションは「悪」です。ルーターがわざわざCPUを使ってパケットを切り刻むのはオーバーヘッドが大きく、パフォーマンスを著しく低下させるからです。だからこそ、我々は「パケットを分割させない(DFフラグを立てる)」設計を行い、あらかじめ最適なサイズを把握しておく必要があるのです。
—
2. CLIで直感的に「壁」を見抜く
まずは、現場の基本ツールである ping を使って、経路上のボトルネックを特定する手法を叩き込みましょう。
Linux/macOSでの実践
ping コマンドでDFフラグを立てるには、環境に合わせてオプションを変える必要があります。
# Linuxの場合: -M do が「フラグメント禁止」を意味します
# -s でペイロードサイズを指定(IPヘッダー20byte + ICMPヘッダー8byteを考慮すること)
ping -M do -s 1472 192.168.1.1
# macOSの場合: -D が「フラグメント禁止」を意味します
ping -D -s 1472 192.168.1.1
ここで重要なのは、1472 という数字です。イーサネットの標準MTUである 1500 から、IPヘッダー(20byte)とICMPヘッダー(8byte)を引いた値が 1472 になります。これを超えるサイズを指定して ping が通らなくなれば、そこがその経路の限界点です。
—
3. アプリケーションコードで「MTUの壁」を再現する
API開発者が疎通確認を行う際、curl やPythonを使って「実際の通信経路」のMTUを測定する方法を知っておくと、インフラ担当者との会話が劇的にスムーズになります。
curl での検証
--trace-ascii を併用すると、どこでパケットが詰まっているか、ヘッダーのやり取りが可視化されます。
# MTUを意識した通信テスト
# --limit-rate などで帯域を絞りつつ、失敗する挙動を確認する
curl -v --max-time 5 http://api.example.com/health
Pythonでの実装例
実際のアプリケーションで「大きなデータ」を送信する際のチェックロジックとして活用してください。
import socket
import struct
def check_mtu(host, port, size):
# ソケットを作成
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# IP_MTU_DISCOVER を IP_PMTUDISC_DO に設定 (DFフラグを立てる)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MTU_DISCOVER, socket.IP_PMTUDISC_DO)
payload = b'A' * size
try:
sock.sendto(payload, (host, port))
print(f"サイズ {size} のパケット送信成功")
except OSError as e:
# MTU超過でエラーが発生すると「Message too long」が返ってくる
print(f"サイズ {size} でエラー発生: {e}")
finally:
sock.close()
# 1472バイトが境界線であることを確認するテスト
check_mtu("8.8.8.8", 53, 1472)
—
4. 現場のシニアからの一言:トラブルシューティングの極意
多くの若手が陥る罠があります。それは、「pingが通るから通信は問題ないはずだ」と思い込むことです。
実は、ping(ICMP)は通るのに、TCP 通信だけがタイムアウトするケースが多々あります。原因の多くは「Path MTU Discovery (PMTUD)」がどこかでブロックされていることによる「ブラックホールルーター」問題です。
- ファイアウォールの設定: セキュリティポリシーでICMPの
Fragmentation Neededパケットを全て破棄していないか? - MSS Clamping: ルーターの設定で
ip tcp adjust-mssが適切に設定されているか?
トラブルに直面したとき、パケットのサイズだけを見るのではなく、「そのパケットがどんなヘッダー情報を持って旅立ち、途中でどの門前払いを食らったのか」を想像してください。
ネットワークは「魔術」ではなく、ただの「物理法則と論理」の積み重ねです。DFフラグを操り、MTUという見えない壁を可視化できるようになれば、あなたはもう一人前のトラブルシューターです。さあ、次はどのパケットを追いかけに行きましょうか?
コメント