夜中の3時、PagerDutyのけたたましいアラート音で叩き起こされた経験はないだろうか。「特定の外部Web APIへの接続がタイムアウトするようになった。しかし、通常の ping は通る」。
この瞬間、NOCのベテランなら誰もが苦い笑みを浮かべる。犯人は大抵、途中のどこかにそびえ立つお堅いファイアウォールや、気まぐれなステートフル・インスペクションだ。ICMPなんてものは、セキュリティポリシーが厳格な環境では真っ先にドロップされるか、最優先で後回しにされる「お飾り」にすぎない。
経路上のL4(レイヤー4)の現実を暴き出すには、ICMPではなく「本番トラフィックと同じ装いをしたTCPパケット」を放つ必要がある。今回は、標準的な traceroute の限界を突破し、ファイアウォールの裏側を丸裸にするTCPベースのtracerouteと、その実務的な活用法について、現場の泥臭い知見を交えて徹底的に解説しよう。
—
なぜICMPベースのtracerouteでは「見えない壁」に阻まれるのか?
皆さんがよく使う標準的な traceroute(Linuxのiputils版など)は、デフォルトでUDPパケット、あるいはICMPエコーリクエストを使用する。
しかし、現代のクラウドアーキテクチャやエンタープライズネットワークにおいて、これらは全くあてにならない。多くのステートフル・ファイアウォールや次世代FW(NGFW)は、UDPのハイポートあてパケットやICMPを「怪しい通信」とみなして容赦なくドロップする。結果として、ターミナル画面には延々と * * * が並び、途中のホップ数が全く分からないという「ブラックボックス地獄」に陥る。
ここで登場するのが、TCPベースのtracerouteだ。
TCPベースのtracerouteが最強である理由
Web APIやHTTPS通信の本質はTCP(主にポート 443 や 80)である。ファイアウォールやロードバランサー(LB)は、当然ながらこれらのポートを通すように設定されている。
TCPベースのtracerouteは、宛先のWebサーバーの 443 番ポートなどに対して、あえてコネクション確立前の TCP SYN パケットを投げる。このパケットのIPヘッダーにある TTL (Time To Live) を 1、2、3……とインクリメントしながら送信することで、経路上のルーターから返ってくる ICMP Time Exceeded をキャッチし、正確なL4の経路を描き出すのだ。
さらに素晴らしいことに、宛先サーバーに到達した際、もしポートが開いていれば TCP SYN-ACK が、閉じているかファイアウォールで明示的に拒否されていれば TCP RST が返ってくる。これにより、「単にルーターが応答しないのか、それとも本当に宛先まで届いてポートで弾かれているのか」を完璧に切り分けることができる。
—
現場で使える! tcptraceroute と nmap の実践コマンド
Linux環境であれば、tcptraceroute コマンドや、スイスアーミーナイフである nmap を使うのが手っ取り早い。インフラエンジニアなら、踏み台サーバーのツールボックスに必ず入れておくべきコマンドだ。
1. tcptraceroute を使った基本的な経路探索
特定のAPIエンドポイント(例: api.example.com)の 443 番ポートに対して、TCPベースのtracerouteを実行してみよう。
# api.example.comの443番ポート(HTTPS)へTCP SYNパケットを用いたtracerouteを実行
sudo tcptraceroute -n -p 443 api.example.com
--n: DNSの逆引きをスキップして高速化する(現場では名前解決のタイムアウトでイライラしたくないので必須)。-p 443: 宛先のポート番号を指定。APIの仕様に合わせて80や独自のポート番号(例:8443)に変更可能。
2. nmap を使った高度なTCP Traceroute
tcptraceroute が入っていない軽量なコンテナ環境や、より詳細なパケット制御を行いたい場合は nmap が重宝する。
# nmapのTCP SYN traceroute機能を利用し、途中のL4デバイスの挙動を暴く
sudo nmap --traceroute -p 443 --send-ip api.example.com
--traceroute オプションをつけるだけで、nmapは通常のポートスキャンと同時に、パケットのTTLを操作した経路探索をバックグラウンドで実行してくれる。ファイアウォールが途中でパケットを食っていないか、どのホップでレイターがレイヤー4のヘッダーを見てブロックしているかが一目瞭然だ。
—
通信フロー(シーケンス)の裏側を覗く
言葉だけではイメージしにくい読者のために、TCPベースのtracerouteがネットワーク上でどのようにパケットをやり取りしているのか、その内部シーケンスを整理しておこう。
[Client (NOC端末)] [Router A (TTL=1)] [Firewall/LB] [Target API Server]
| | | |
|--- (1) TCP SYN (TTL=1) ------------>| | |
| | | |
|<-- (2) ICMP Time Exceeded ----------| | |
| (ルーターAがTTL切れを通知) | | |
| | | |
|--- (3) TCP SYN (TTL=2) ------------------------------------->| |
| | | (ファイアウォール通過) |
|<-- (4) ICMP Time Exceeded -----------------------------------| |
| (FW/LBがTTL切れを通知) | | |
| | | |
|--- (5) TCP SYN (TTL=3) -------------------------------------------------------------->|
| | | (ポートオープン)
|<-- (6) TCP SYN-ACK -------------------------------------------------------------------|
1. クライアントは TTL=1 を設定した TCP SYN パケットを送信する。
2. 最初のルーター(Router A)がパケットを受け取り、TTLをデクリメントした結果 0 になったため破棄し、クライアントへ ICMP Time Exceeded を返す。これで1ホップ目が判明する。
3. 次に TTL=2 で送信し、今度は途中のファイアウォールやロードバランサーまで到達させる。
4. 最終的に TTL が十分に大きくなり、ターゲットのWeb APIサーバーまでパケットが到達する。
5. サーバー側が生きていれば TCP SYN-ACK(拒否なら TCP RST)を返すため、「ここまでは完全にL4の疎通が取れている」と確信できる。
—
アプリケーション開発者・インフラ担当者が知るべき実務での活用とトラブルシューティング
この手法がなぜWeb API設計やインフラ運用において強力なのか。それは「アプリケーション層のエラーと、ネットワーク層のエラーを完全に切り分けられるから」だ。
例えば、自社のマイクロサービスから外部の決済APIを叩く際、以下のようなPythonスクリプト(requests や低レイヤーのソケット)や、開発環境からの curl でエラーが出たとしよう。
import socket
import sys
def verify_tcp_route(target_host, target_port):
print(f"[*] ターゲット {target_host}:{target_port} へのTCP到達性を検証します...")
# ソケットの作成 (IPv4, TCP)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5.0) # 5秒でタイムアウト
try:
# 実際にTCP 3wayハンドシェイクを試みる
s.connect((target_host, target_port))
print(f"[+] 成功: {target_host}:{target_port} へのTCP接続が確立できました。L4層は健全です。")
except socket.timeout:
print("[-] タイムアウトエラー: 途中のファイアウォールでドロップされているか、ルーティングに問題があります。")
except ConnectionRefusedError:
print("[!] 接続拒否: ホストには到達しましたが、ポートが閉じているか、サービスが停止しています。")
except Exception as e:
print(f"[X] 予期せぬエラーが発生しました: {e}")
finally:
s.close()
if __name__ == "__main__":
# 例として外部APIのエンドポイントを指定
verify_tcp_route("api.example.com", 443)
このPythonスクリプトや、お馴染みの curl -v https://api.example.com でタイムアウトが発生した際、本稿で紹介したTCPベースのtracerouteを組み合わせることで、次のような切り分けが瞬時に完了する。
- ケースA:途中のホップ(例: ホップ数 5)でピタッと応答が途絶える
- $\rightarrow$ 自社側のネットワーク、あるいはプロバイダ、あるいは宛先手前の巨大なセキュリティゲートウェイで特定のトラフィックがフィルタリングされている。ネットワークチームへのエスカレーション材料が揃う。
- ケースB:最終ホップまで
ICMP Time Exceededが返り、最後に宛先からTCP RSTが返ってくる - $\rightarrow$ パケットは物理的・論理的に完璧に到達している。原因はネットワークではなく、宛先サーバー側のWAF(Web Application Firewall)がアプリケーション層(HTTPヘッダーやIPレピュテーション)でアクセスをブロックしている。APIキーやホワイトリストの確認が必要。
—
シニアエンジニアからの教訓
ネットワークのトラブルシューティングにおいて、「なんとなく ping を打って通らないからおしまい」というアプローチは、もはやプロの仕事とは言えない。クラウド全盛の時代、ネットワークは複雑なカオスの上に成り立っており、表面的に見えている挙動と内部の実態は異なることが多い。
「どのレイヤーでパケットがどのように扱われているか」を常に意識し、アプリケーションが依存しているL4の文脈(今回の場合はTCPポートとSYNパケット)に合わせた診断ツールを使いこなすこと。これこそが、障害切り戻しの時間を劇的に短縮し、チームからの厚い信頼を勝ち取るための最短ルートである。
さあ、次の夜間障害が起きたときには、ただの ping ではなく、誇りを持って tcptraceroute のコマンドを叩いてほしい。パケットは、いつだって真実を語っているのだから。
コメント