【実務・中級編】 tracerouteコマンドにおけるTTL制御とUDP/ICMPパケットの使い分け – トラブルシューティング&ネットワーク運用監視実践ガイド

ネットワークの「今」を透視する: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 の送出が禁止されている。

もし、ある地点から先が全て星になり、かつ通信が成立しているなら、それは「隠れているだけ」だ。しかし、星の先で通信が断絶しているなら、そこがボトルネックである可能性が極めて高い。

最後に一つだけ覚えておいてほしい。
コマンドの結果はあくまで「その瞬間のパケットの運命」に過ぎない。特にクラウド環境では、動的ルーティングによりパケットごとに経路が変わることもザラだ。一つの結果を鵜呑みにせず、数分間観察し、パケットの流れを「線」ではなく「波」として捉える感覚を養ってほしい。

障害対応は、パケットの声を聞くことだ。教科書よりも、そのコマンドが叩き出している生のレスポンスにこそ、真実が隠されている。さあ、ターミナルを開いて、現場の景色を読み解いてみよう。

コメント

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