「おい、なんか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)、eroredge(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向け経路のパケットロスが「どこのトランジットキャリアのコアルーター」で起きているか、作成したスクリプトで丸裸にしてみようじゃないか。
コメント