前の話で、TTL を逆手に取って各ホップから Time Exceeded を集めれば経路の長さが測れる、という発想転換を獲得した。応答が「廃棄通知」から「道しるべ」に変わった。
ところが、前の話の末尾には、ある1行が置かれていた。
「現実には、各ホップが律儀に名乗ってくれるとは限らない」。
あの発想を実際にコマンドにしたものが traceroute だ。出力には各ホップの IP と応答時間、そしてときどき * * * が並ぶ。
これまで、「コマンドが経路を探した結果」として読んできた。けれど、本当にそうだろうか。* は何を意味しているのか。出力された経路は、行きと戻りの両方を見せているのか、それとも片方だけなのか。
考えてみよう: traceroute を打つと、各ホップの IP が並んで出てくる。あの出力は、コマンドが経路を「探しに行った」結果なのだろうか。それとも、別の何かが起きているのだろうか。30秒だけ、自分で考えてみてほしい。前の話で TTL を逆手に取って各ホップから Time Exceeded を集めると経路の長さが測れる、という発想を獲得した。あの発想を実際にコマンドにすると、どう動くだろうか。コマンドは「探している」のか、それとも別のことをしているのか。
traceroute は経路を「探している」のではない。各ホップが TTL exceeded で返した Time Exceeded の送信元 IP を、送信側が並べて表示しているだけ。だから * は「壊れている」とは限らない -- 応答しない設定 / rate-limit / filter の3つの可能性がある。さらに、見えるのは行き経路だけ。戻り経路はこのコマンドでは見えない。


前の話で残った渇き — 各ホップが律儀に名乗ってくれるとは限らない
サーバへの経路確認、トラブル切り分け、構築後の疎通確認 — traceroute はいろいろな場面で使う。出力は、各ホップの IP と応答時間、そしてときどき混じる * * * で構成されている。
* を見て、判断が定まらなかった経験はないだろうか。ある時は「障害だ」と判断したくなり、ある時は「よくあることだ」と通り過ぎる。出力された経路をそのまま信じ、* には目をつぶってきたかもしれない。
判断軸が定まらないのは、traceroute が裏で何をしているのかを正確に把握していないからだ。コマンドの動きが分かると、* の読み方と出力経路の意味が変わる。
traceroute の正体 — 探したのではなく、教えてもらった結果を集めただけ
traceroute コマンドが裏でやっているのは、次の3ステップだ。
ステップ1。TTL=1 のパケットを送り出す。規格に従って、最初のホップのルータが TTL を 0 にして廃棄し、送信元へ Time Exceeded を返す。返ってきた応答の送信元 IP が、出力の1行目になる。
ステップ2。TTL=2 のパケットを送り出す。最初のルータは TTL を 1 にして転送し、2番目のルータが TTL を 0 にして廃棄、Time Exceeded を返す。出力の2行目に、その応答の送信元 IP が並ぶ。
ステップ3。TTL=3, 4, 5, ... と順に増やしていく。3番目、4番目、5番目のルータが順に Time Exceeded を返す。これを宛先まで届く(または規定回数まで)繰り返す。
送信側がやっているのは、TTL を 1 から段階的に増やしてパケットを送ることと、各ホップから返ってきた Time Exceeded の送信元 IP を集め、TTL の値が小さい順にターミナルへ表示すること。これだけだ。
コマンドは経路を能動的に探しに行ったのではない。経路にいる各ルータが、TTL=0 で廃棄する際に規格に従って Time Exceeded を返し、「自分はここにいる」と名乗っている。送信側はその応答を受け取って並べているだけだ。探しているのはコマンド側ではなく、教えてくれているのは経路の各ホップ。これが traceroute の正体だ。


OS やツールごとに細部は違うが、「TTL を 1 から段階的に増やして、各ホップから返ってくる Time Exceeded を集める」という骨格の動作は共通している。
* の3つの意味 — 壊れているとは限らない
* * * を見て「そのホップが壊れている」と読んできたかもしれない。* が示すのは、そのホップから Time Exceeded が規定時間内に返ってこなかったという事実だけだ。それ以上のことは、* 単体では言えない。
返ってこなかった理由は、3つある。
1つ目は、応答が返ってこない実装・ポリシー。TTL=0 廃棄時の Time Exceeded が、観測側には返ってこない状態だ。多くの組織で、上位ルータや境界ルータで ICMP を絞る運用が行われる。セキュリティポリシーの一環、応答による負荷の軽減 — いろいろな経緯がある。観測側からは Time Exceeded が見えないが、それは「壊れている」のではなく「観測側に届いていない / 観測できない」だ。
2つ目は、rate-limit。ルータが ICMP 応答の頻度を絞っている設定。短時間に大量の Time Exceeded を返すと、ルータの処理負荷が上がる。それを防ぐために、応答の頻度に上限がかけられている。間引かれた応答は、規定時間内に送信側に届かないので * として現れる。これも「壊れている」のではなく「絞っている」状態だ。本来の機能は問題なく動いていて、応答だけが規定時間内に届かない。
3つ目は、filter。ファイアウォールや ACL で ICMP を遮断している設定。組織のネットワーク境界では、外向きの ICMP 応答を制限することが日常的に行われる。応答そのものが経路上で止められるので、送信側には届かず * になる。これも「壊れている」のではなく「遮断されている」状態だ。
どれが起きているかは、* 単体からは決められない。だから、* を見て即「そのホップが障害」と決めつけない。
「壊れている」を選ぶ前に、他のホップは応答しているのか、宛先までの疎通は別の方法で確認できるか、ログには何が記録されているか。組み合わせて切り分けると、* が障害のサインなのか、応答しない・絞られている・遮断されているサインなのかが見えてくる。
戻り経路の不可視 — 行きの各ホップしか見えない
送信側が集めているのは、行きのパケットが各ホップで TTL=0 になったときに、そのルータが返した Time Exceeded だ。ターミナルに並ぶのは、行き経路の各ホップの IP。
各ホップから返ってきた Time Exceeded は、どの経路を通って送信元へ戻ってきたのだろうか。traceroute の出力には、その情報は含まれていない。コマンドは応答パケットの送信元 IP を読み取って並べるが、応答パケットがどこを通って戻ってきたかは観測していない。
行き経路と戻り経路は、独立にルーティングされる。各ホップは、自分のルーティングテーブルに従って次のホップを決める。送信側から廃棄したルータへの行き経路と、廃棄したルータから送信側への戻り経路では、辿る経路が違うことが起こりうる。これが asymmetric routing(非対称ルーティング)だ。
BGP マルチホーム環境(複数の上位 ISP に接続している組織)では、行きと戻りが違う ISP を通ることがある。ECMP(同コストの複数経路を活用する設計)では、フローごとに違う経路を取ることがある。設計上の選択の結果として、意図的に非対称が成立している場合もある。
traceroute で行きの経路は綺麗に並び、* も出ず、応答時間も平常通りに見えても、実際のサービス通信(HTTP / DB アクセスなど)が成立しない場面に出会うことがある。
「traceroute は通っているから経路は問題ない」と判断したくなる。見えているのは行き経路だけだ。問題は戻り経路で起きている可能性がある。戻り経路上のルータで何かが詰まっている、戻り経路の途中で応答パケットだけが落とされる設定がある、戻り経路が想定と違うルートを取っていて ACL や Firewall がある — これらは traceroute では検出できない。


3動詞統合 — 分ける / 測る / 集めて疑う が手元に揃う
1つ目は、分ける。「届かない」を一言で済ませず、タイムアウト(沈黙)、unreachable(拒否の声)、部分応答(一部だけ返事)の3つに分けて読む。それぞれ壊れている層が違うので、次に確認する場所も変わる。
2つ目は、測る。TTL という安全装置を逆手に取り、1, 2, 3 ... と段階的に切らせて各ホップから Time Exceeded を集めれば、経路の長さが測れる。廃棄したルータが規格に従って返す応答が、経路の手がかりになる。
3つ目は、集めて疑う。traceroute は各ホップから返った Time Exceeded を並べただけだから、* は壊れているとは限らない。見えるのは行き経路だけで、戻り経路は別のルートを辿っている可能性がある。集まらないホップ(*)と、集めていない方向(戻り経路)を疑う判断軸が必要だ。
3つが揃うと、ping や traceroute の出力を「通る / 通らない」の二択で読まなくなる。ここから先は、ping、traceroute、機器ログ、経路表、フィルタ設定を突き合わせ、どの証拠が同じ現象を指しているかを見分ける段階に進む。
traceroute の限界 — 集めるコマンドだから、集まらないホップと見えない方向がある
traceroute の出力は、経路を探した結果ではなく、各ホップが TTL exceeded で返した Time Exceeded を送信側が並べたものだ。集めるコマンドとして読み直すと、限界が自然に見えてくる。集まらないホップ(*)は応答しない設定・rate-limit・filter のいずれかで応答が返ってこなかった事実であり、機器の障害とは限らない。集めていない方向、つまり戻り経路は、このコマンドでは見えない。
届かないを分け、TTL を逆手に取って測り、並べた結果を疑う — この3つの動詞が、運用の現場で次に見る場所を決める判断軸になる。返ってきた応答、返ってこない応答、見えていない方向を分けて読める。観測はそこから始まる。