【実務・中級編】 tracerouteにおけるAS経路情報の推定と逆引きDNS(PTRレコード)の活用 – トラブルシューティング&ネットワーク運用監視実践ガイド

「おい、なんかAWSの東京リージョン向けの通信が詰まってるぞ!」

深夜2時、静まり返ったNOC(ネットワークオペレーションセンター)にアラートの警告音が響き渡る。ダッシュボードのパケットロス率を示すグラフが、牙を剥くように急上昇していた。

隣席の若手エンジニアが慌ててキーボードを叩き、ping を打ち始める。「応答がありません!相手のWebサーバーがダウンしたか、パブリッククラウド側で障害が起きてます!」

私は手元のマグカップからコーヒーを一口すすり、彼の肩に手を置いた。

「落ち着け。相手のサーバーのせいにするのは、パケットが『どこで立ち往生しているか』を見極めてからだ。ping だけでは『死んでいる』ことしか分からない。我々ネットワーク屋には、パケットが経由したルーターの一人ひとりに『お前は誰だ?』と問いかける武器があるだろう?」

そう、それが traceroute だ。

今回は、単に「traceroute を実行して終わり」という教科書レベルの話ではない。現場のトラブルシューティングにおいて、出力されたIPアドレスから「PTRレコード(逆引きDNS)」と「AS(Autonomous System)番号」を動的に組み合わせ、障害の『真犯人』がどこのキャリアの、どのルーターなのかを秒速で特定する実践的なデバッグ技術を伝授しよう。

—

1. パケットの生存期間(TTL)を利用した traceroute の通信フロー

まずは基本に立ち返ろう。traceroute がどうやってパケットの旅路を可視化しているのか、その通信シーケンスを頭に叩き込んでほしい。キーとなるのは、IPヘッダーに存在する TTL(Time To Live:生存期間)フィールドだ。

TTLの減少と ICMP Time Exceeded (Type 11)

ルーターはIPパケットをルーティング(中継)する際、パケットの TTL を必ず 1 減らす。もし TTL が 0 になった場合、そのルーターはパケットを破棄し、送信元に対して「これ以上パケットを運べませんでした」というエラーメッセージを返す。これが、RFC 792 で定義されている ICMP Time Exceeded(Type 11, Code 0)だ。

traceroute はこの仕様をハックしている。

1. 送信元は、まず TTL = 1 に設定したパケットを送信する。
2. 最初のルーター(Hop 1)に到達すると、TTL が 0 になり、パケットが破棄される。ルーターは ICMP Time Exceeded を送り返す。これで「1番目のルーターのIPアドレス」が判明する。
3. 次に送信元は TTL = 2 に設定して送信する。Hop 1 は TTL を 1 に減らして通過させ、次のルーター(Hop 2)で TTL が 0 になる。これで「2番目のルーターのIPアドレス」が判明する。
4. これをターゲット(最終目的地)に到達するか、最大ホップ数(デフォルトでは 30)に達するまで繰り返す。

Windows と Linux/UNIX での挙動の違い

ここで新人がよく引っかかる罠がある。OSによるプロトコルの違いだ。

  • Linux/macOS (traceroute): デフォルトでは UDPパケット(宛先ポート 33434 から開始し、ホップごとにインクリメント)を使用する。
  • Windows (tracert): デフォルトでは ICMP Echo Request(いわゆる ping パケット)を使用する。
[送信元 (Client)]                           [中継ルーター (Hop 1)]                      [宛先 (Server)]
       |                                           |                                           |
       |--- 1. UDP/ICMP (TTL=1) ------------------>|                                           |
       |                                           | (TTLが0になりパケット破棄)                 |
       |<-- 2. ICMP Time Exceeded (Type 11) -------|                                           |
       |                                           |                                           |
       |--- 3. UDP/ICMP (TTL=2) -------------------------------------------------------------->|
       |                                           |                                           |
       |                                           |--- 4. (TTL=1に減衰して転送) ------------->|
       |                                           |                                           | (宛先に到達)
       |<-- 5. ICMP Echo Reply / Port Unreachable <--------------------------------------------|

ターゲットに到達した際、UDPを使用している場合は、宛先ポートが通常閉じているため、ターゲットから ICMP Port Unreachable (Type 3, Code 3) が返ってくる。これで「目的地に到着した」と判断する仕組みだ。

—

2. 逆引きDNS(PTRレコード)の行間を読む技術

traceroute の結果に、単なるIPアドレスではなくホスト名(PTRレコード)が表示されることがある。実は、キャリアや大手ISPのエンジニアは、このPTRレコード(RFC 1035 / RFC 1912)の命名規則に極めて重要なインフラの構成情報を埋め込んでいる。

これを見抜けるようになると、パケットの出力結果が「生きた路線図」に見えてくる。

PTRレコードに隠された3つのシグナル

例えば、ある中継ホップで以下のようなホスト名が返ってきたとする。

ae5.cr1.tyo1.ap.equinix.com

この文字列から、我々NOCエンジニアは瞬時に以下の情報をデコードする。

1. インターフェース種別と帯域 (ae5)

  • ae は「Aggregated Ethernet」(リンクアグリゲーション:LACP等で束ねられた高帯域回線)を指す。
  • もしここが et-0/0/0 であれば Juniper製ルーターの 100Gbps(or 40Gbps)ポート、xe- であれば 10Gbpsポート、ge- であれば 1Gbpsポートである可能性が高い。

2. ルーターの役割 (cr1)

  • cr は「Core Router(コアルーター)」を意味する。バックボーンの背骨を担う超高速ルーターだ。
  • 他にも ar (Access Router)、er or edge (Edge Router)、gw (Gateway) などの表記が一般的だ。

3. 地理的ロケーション (tyo1)

  • tyo は言わずと知れた東京(Tokyo / 羽田空港コード)だ。
  • グローバルキャリアは、IATA空港コード(nrt: 成田, lax: ロサンゼルス, sin: シンガポール, lhr: ロンドン)を拠点コードに使うのが鉄則となっている。

PTRがないノード、あるいはプライベートIPの謎

たまに 10.0.0.0/8 や 192.168.0.0/16、あるいは 172.16.0.0/12 といったプライベートIPアドレス(RFC 1918)が traceroute の途中に現れることがある。

「インターネットの途中にプライベートIPなんてあり得るのか?」と若手は驚くが、これはISP内部の相互接続セグメントや、MPLS(Multi-Protocol Label Switching)ネットワーク内部のトンネルをパケットが通っている証拠だ。ルーターが ICMP Time Exceeded を返す際、自身のインターフェースに割り当てられた内部用IPをそのままソースIPとして使うため、表に出てきてしまう。バグではないので、慌てず「キャリア内部のクローズドな網を通っているな」と判断すればいい。

—

3. AS(Autonomous System)経路情報の結合

障害対応において、PTRレコードと同様に重要なのが「AS番号」だ。

インターネットは、AS(自律システム)と呼ばれる巨大なネットワークの集合体だ。自社が契約しているISP(AS-A)から、クラウド事業者(AS-B)にパケットが届くまでに、どこのTransit(仲介キャリア:Tier 1/Tier 2)を経由しているか。それを特定しなければ、障害が発生したときに「誰に連絡してねじ込むべきか」が分からない。

WHOISデータベースと IP-to-ASN マッピング

通常、IPアドレスからAS番号を特定するにはWHOISデータベースを検索するが、traceroute の結果一行一行に対して手動でWHOISを引くのは非現実的だ。

ここで裏技を紹介しよう。セキュリティ研究やネットワーク運用で愛用されている Team Cymru の DNS-based IP-to-ASN マッピングサービスを利用するのだ。これは、DNSの TXT レコード問い合わせを使って、高速かつ軽量にIPアドレスからAS番号を逆引きできる仕組みだ。

例えば、IP 8.8.8.8 のAS情報を調べたい場合、IPのオクテットを逆順にし、8.8.8.8.origin.asn.cymru.com に対して TXT レコードを問い合わせる。

# digコマンドを使って、DNS経由で高速にAS番号を調べる
$ dig +short TXT 8.8.8.8.origin.asn.cymru.com

応答は以下のように返ってくる。

"15169 | 8.8.8.0/24 | US | arin | 2001-04-17"

この "15169" こそが、GoogleのAS番号(AS15169)だ。DNSベースなので非常に高速で、スクリプトへの組み込みも容易だ。

—

4. 実践:PythonでPTRとAS経路情報を動的に解決するデバッグスクリプト

それでは、現場で即座に使えるPythonスクリプトを紹介しよう。

このスクリプトは、traceroute の生ログ(IPアドレスのリスト)を入力すると、自動的に各ホップの「PTRレコード(逆引きホスト名)」と、Team CymruのDNSサービスを利用した「AS番号およびAS名」を並列で高速に解決し、綺麗な経路マップを出力するものだ。

事前の準備として、DNS解決ライブラリである dnspython をインストールしておいてほしい。

# 必要なライブラリのインストール
pip install dnspython

ネットワーク経路分析スクリプト (trace_analyzer.py)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import socket
import sys
from dns import resolver

def get_ptr_record(ip_address):
    """
    IPアドレスからPTRレコード(逆引きホスト名)を解決する
    """
    try:
        # socketの標準機能で逆引きを実行
        hostname, _, _ = socket.gethostbyaddr(ip_address)
        return hostname
    except socket.herror:
        # 逆引きレコードが存在しない場合
        return "No-PTR-Record"
    except Exception as e:
        return f"Error({str(e)})"

def get_as_info(ip_address):
    """
    Team Cymru の DNS-based mapping を使用して
    IPアドレスからAS番号とプレフィックス情報を取得する
    """
    try:
        # IPアドレスのオクテットを逆転させる (例: 8.8.8.8 -> 8.8.8.8)
        octets = ip_address.split('.')
        if len(octets) != 4:
            return "N/A", "N/A"
        
        reversed_ip = ".".join(reversed(octets))
        query_domain = f"{reversed_ip}.origin.asn.cymru.com"
        
        # DNS TXTレコードを問い合わせ
        answers = resolver.resolve(query_domain, 'TXT')
        for rdata in answers:
            # 応答例: "15169 | 8.8.8.0/24 | US | arin | 2001-04-17"
            txt_data = rdata.strings[0].decode('utf-8')
            parts = [p.strip() for p in txt_data.split('|')]
            if len(parts) >= 2:
                as_number = f"AS{parts[0]}"
                prefix = parts[1]
                return as_number, prefix
    except Exception:
        # プライベートIPやDNSエラーの場合はN/Aを返す
        pass
    
    return "N/A", "N/A"

def get_as_name(as_number):
    """
    AS番号からASの組織名を取得する
    """
    if as_number == "N/A":
        return "Private/Unknown"
    
    try:
        # AS名解決用のクエリドメイン (例: AS15169.asn.cymru.com)
        query_domain = f"{as_number}.asn.cymru.com"
        answers = resolver.resolve(query_domain, 'TXT')
        for rdata in answers:
            # 応答例: "15169 | US | arin | 2000-03-30 | GOOGLE, US"
            txt_data = rdata.strings[0].decode('utf-8')
            parts = [p.strip() for p in txt_data.split('|')]
            if len(parts) >= 5:
                return parts[4]
    except Exception:
        pass
    return "Unknown AS Name"

def analyze_route(ip_list):
    """
    IPアドレスのリストを受け取り、詳細な経路情報を出力する
    """
    print(f"{'Hop':<4} | {'IP Address':<15} | {'AS Number':<8} | {'AS Name / PTR Record':<50}")
    print("-" * 90)
    
    for idx, ip in enumerate(ip_list, start=1):
        # 宛先がタイムアウト (*) の場合はスキップ
        if ip == "*":
            print(f"{idx:<4} | {'*':<15} | {'N/A':<8} | Request Timed Out")
            continue
            
        # AS情報とPTRレコードを並行して解決
        as_num, prefix = get_as_info(ip)
        as_name = get_as_name(as_num)
        ptr = get_ptr_record(ip)
        
        # 表示を見やすく整形
        info_string = f"[{as_name}] {ptr}" if as_num != "N/A" else f"{ptr}"
        print(f"{idx:<4} | {ip:<15} | {as_num:<8} | {info_string[:60]}")

if __name__ == "__main__":
    # サンプルのtraceroute結果(ホップ順のIPリスト)
    # 実際の運用では、tracerouteコマンドの出力をパースしてこのリストに流し込む
    sample_ips = [
        "192.168.1.1",     # ローカルルーター
        "210.130.133.1",   # IIJ 中継ルーター例
        "210.130.143.130", # IIJ コアルーター例
        "8.8.8.8"          # ターゲット (Google DNS)
    ]
    
    print("Starting Route Path Analysis...\n")
    analyze_route(sample_ips)

このスクリプトを実行すると、コンソールには以下のような極めて視認性の高いレポートが出力される。

Starting Route Path Analysis...

Hop  | IP Address      | AS Number | AS Name / PTR Record
------------------------------------------------------------------------------------------
1    | 192.168.1.1     | N/A      | router.local
2    | 210.130.133.1   | AS2497   | [IIJ-AS Internet Initiative Japan, JP] tyo001bb00.iij.ad.jp
3    | 210.130.143.130 | AS2497   | [IIJ-AS Internet Initiative Japan, JP] tyo001bf01.iij.ad.jp
4    | 8.8.8.8         | AS15169  | [GOOGLE, US] dns.google

どうだ? これを見れば、パケットが自宅のルーター(Hop 1)を出て、日本を代表するISPであるIIJ(AS2497)の東京拠点を経由し、Google(AS15169)のDNSサーバーへ吸い込まれていく様子が、手に取るように分かるだろう。

—

5. 現場のトラブルシューティングにおける「3つの罠」

この強力な武器を手に入れた君に、百戦錬磨のシニアエンジニアとして、実際の障害対応で何度も痛い目を見てきた「罠」を共有しておこう。これを知らないと、誤った解析で無駄な時間を過ごすことになる。

罠1:ICMPのレートリミット(途中ノードの * * *)

traceroute を実行した際、特定のホップだけが * * *(タイムアウト)となり、その後のホップでは正常に応答が返ってくることがある。

「あ! Hop 5 のルーターが死んでます!」と叫ぶ新人がいるが、これは間違いだ。
もし本当に Hop 5 のルーターがダウンしていれば、それ以降(Hop 6 や 7)のルーターにパケットが届くはずがない。

これは多くの場合、中継ルーターが「ICMP Rate Limiting(生成制限)」を行っているために発生する。ルーターのCPU保護のため、時間あたりの ICMP Time Exceeded の送信数が制限されているのだ。応答がなくても、パケットは正常にフォワーディングされているため、無視して先に進んで問題ない。

罠2:非対称ルーティング(Asymmetric Routing)の影

インターネットの基本原則として、「行きと帰りの経路は必ずしも同じではない」。

traceroute で見えるのは、あくまで「送信元から宛先への片道の経路(行きのパケット)」だけだ。宛先から送信元へ戻ってくるパケット(帰りのパケット)は、全く異なるAS、全く異なる海底ケーブルを通っている可能性がある。

「行きの経路は綺麗なのに、通信遅延(RTT)が300msを超えている」という場合、往路ではなく復路のどこかのキャリアで鬱血(パケットロスや輻輳)が起きている可能性を疑うべきだ。この場合は、宛先側のサーバーからも逆方向に traceroute を実行してもらう(双方向測定)以外に真実は見えてこない。

罠3:Anycast IP のマジック

Google Public DNS (8.8.8.8) や Cloudflare (1.1.1.1) などは、世界中に同じIPアドレスを分散配置する IP Anycast という技術を使っている。

東京から traceroute 8.8.8.8 を叩けば東京近郊のサーバーに届くが、ロンドンから叩けばロンドンのサーバーに届く。PTRレコードに現れるホスト名が、自分が想定している物理的なロケーションと一致しているか、常に疑いの目を持つことだ。

—

終わりのないパケットの旅路を楽しもう

ネットワークの障害対応は、一見すると地味で泥臭い作業の連続だ。しかし、traceroute の出力結果の行間に隠された「PTRの命名規則」や「AS間の関係性」を読み解くパズルだと捉えれば、これほどエキサイティングな仕事もない。

次に見慣れないIPアドレスに出会ったら、すかさず逆引きを引き、AS番号を調べてみることだ。その1ホップ1ホップが、世界中のエンジニアたちが血と汗を流して創り上げたインターネットという巨大な神経網の一部なのだから。

よし、夜食のピザが届いたようだ。これを食べたら、もう一度、さっきのAWS向け経路のパケットロスが「どこのトランジットキャリアのコアルーター」で起きているか、作成したスクリプトで丸裸にしてみようじゃないか。

コメント

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