ネットワークの「今」を透視する:tracerouteが教えてくれる、パケットの泥臭い旅路
深夜3時、データセンターの片隅でアラートが鳴り響く。監視画面に表示されたのは、特定のリージョンへのレイテンシ急増。原因はどこだ? サーバーのNICか、隣接ルーターか、あるいはISPのバックボーンか。
そんな時、我々エンジニアが真っ先に手に取る武器が traceroute だ。しかし、ただコマンドを叩いて結果を眺めるだけでは不十分だ。「なぜそこに時間がかかるのか」「パケットは本当に意図した経路を通っているのか」。その裏側にあるロジックを知らなければ、真の障害解決には辿り着けない。
今日は、ネットワークの深淵を覗く traceroute の仕組みと、実務で使えるTipsを掘り下げていこう。
—
1. TTLが引き起こす「パケットの自爆」という演出
traceroute の本質は、ルーターの「律儀な性格」を利用した巧妙なハックだ。IPヘッダーには TTL (Time To Live) というフィールドがある。これはパケットがネットワーク内を彷徨い続けないように、経由するルーターごとに値を1ずつ減らし、0になった瞬間にパケットを破棄するための「寿命」だ。
traceroute はこの仕組みを逆手に取る。
1. TTL=1 のパケットを送る:最初のルーターでTTLが0になり、ルーターは「寿命切れ」を告げる ICMP Time Exceeded を送り返してくる。これで1ホップ目が判明する。
2. TTL=2, 3… とインクリメント:これを繰り返すことで、各ホップのルーターから律儀にエラー通知を集め、経路を可視化する。
これがネットワーク診断における「手紙の追跡」の正体だ。
—
2. UDPとICMP:OSによる「作法の違い」を理解する
実務で必ず直面するのが、「環境によってtracerouteの挙動が違う」という問題だ。
- Linux/macOS系 (
traceroute): デフォルトでは UDP を使用する。33434番ポートから始まる高いポート番号を狙い撃ちし、到達先で「そんなポートは開いていない」というICMP Port Unreachableが返ってきたら「ゴール到達」とみなす。 - Windows (
tracert): 伝統的に ICMP Echo Request を使用する。よりシンプルだが、ファイアウォールでICMPが遮断されている環境では途中で止まってしまうことが多い。
実務でのトラブルシューティング:mtr のススメ
最近の現場では、静的な traceroute よりも、リアルタイムで統計を取れる mtr (My Traceroute) を使うのが鉄板だ。
# 1秒間隔でパケットを投げ続け、ジッターやパケットロス率を可視化する
# --report-wide は詳細な情報を出すためのオプション
mtr --report-wide google.com
—
3. Webエンジニアのための「隠れた経路」の可視化
インフラ運用だけでなく、Web APIのレスポンスが遅い際、パケットがどこで滞留しているかを知ることは不可欠だ。もしアプリケーションから外部APIを叩いていて遅延が発生しているなら、curl の --trace-time や verbose オプションを併用して確認するのも手だが、経路自体が怪しい場合は以下のコマンドが役に立つ。
Pythonで簡易的な経路診断スクリプトを書くなら
ライブラリに頼らず、生のソケットでTTLを操作する感覚を掴むためのシンプルな実装例だ。
import socket
import struct
# TTLを操作して経路を確認する概念コード
def check_route(dest_ip, ttl):
# ICMPソケットを作成
recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
send_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
# TTLを設定
send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl)
# ゴールに向かってパケットを投げる(ポートは適当な高い番号)
send_socket.sendto(b"", (dest_ip, 33434))
# 応答を待つ(簡易実装)
recv_socket.settimeout(2)
try:
data, addr = recv_socket.recvfrom(1024)
print(f"TTL={ttl}: 応答元ホスト -> {addr[0]}")
except socket.timeout:
print(f"TTL={ttl}: タイムアウト(ファイアウォールで遮断?)")
finally:
send_socket.close()
recv_socket.close()
# 指定したホストの1ホップ目を確認
check_route("8.8.8.8", 1)
—
4. 現場のシニアからのアドバイス:諦めるな、その先の景色を読め
現場で traceroute を実行して、途中で * * * と表示され、星が並ぶことは珍しくない。これは多くの場合、以下のいずれかだ。
1. ICMPレートリミット: ルーターが「traceroute用のパケットばかり処理してられるか」と、ICMP応答をわざと無視している。
2. セキュリティポリシー: 中継ルーターのセキュリティ設定で、ICMP Time Exceeded の送出が禁止されている。
もし、ある地点から先が全て星になり、かつ通信が成立しているなら、それは「隠れているだけ」だ。しかし、星の先で通信が断絶しているなら、そこがボトルネックである可能性が極めて高い。
最後に一つだけ覚えておいてほしい。
コマンドの結果はあくまで「その瞬間のパケットの運命」に過ぎない。特にクラウド環境では、動的ルーティングによりパケットごとに経路が変わることもザラだ。一つの結果を鵜呑みにせず、数分間観察し、パケットの流れを「線」ではなく「波」として捉える感覚を養ってほしい。
障害対応は、パケットの声を聞くことだ。教科書よりも、そのコマンドが叩き出している生のレスポンスにこそ、真実が隠されている。さあ、ターミナルを開いて、現場の景色を読み解いてみよう。
コメント