【実務・中級編】 tcpdumpの基本構文とネットワークインターフェース指定の挙動 – トラブルシューティング&ネットワーク運用監視実践ガイド

夜中の3時、ピジェリの空き箱が転がるNOCの静寂を切り裂くアラート音。
「APIのレイテンシが跳ね上がっている、データベースの応答がおかしいのか、それともロードバランサのフォワード先でパケットが迷子になっているのか……」

こういう修羅場で、若いエンジニアが最初にやりがちなのが、とりあえず画面をぼんやり眺めることだ。違う。我々インフラエンジニアやWeb APIの設計・運用に携わる者が信じられるのは、目の前の「パケット」という名の絶対的な事実だけだ。嘘をつかないログ、それがパケットキャプチャである。

そして、そのパケット解析の羅針盤となるのが tcpdump だ。今回は、この tcpdump の基本中の基本でありながら、現場のトラブルシューティングで生死を分ける -i オプション(インターフェース指定)と、全知全能のようで実はクセモノである any インターフェースの挙動について、実戦の泥臭い知見を交えて徹底的に解説しよう。

—

なぜ tcpdump のインターフェース指定でハマるのか?

パケットキャプチャを取ろうとして、とりあえず以下のように叩いたことはないだろうか。

# インターフェースを指定せずに実行する(OSのデフォルトに依存するため危険)
sudo tcpdump -n

大規模なクラウド環境や、複数のVLAN、コンテナの仮想ブリッジ(docker0 や cni0 など)が複雑に入り組んだモダンなLinuxサーバーにおいて、インターフェースの指定を怠ることは、目隠しをして高速道路の真ん中に立つようなものだ。

tcpdump は、指定されたネットワークインターフェース(NIC)のデバイスドライバを経由して、カーネル空間からユーザー空間へとコピーされてくる生パケットをキャプチャする。つまり、「どの扉(インターフェース)の前に陣取るか」によって、見える世界が全く変わってくるのだ。

—

-i オプションの基本:どの扉を監視すべきか

まずは、標準的なインターフェースの指定方法をおさらいしよう。

# 特定の物理インターフェース(例: eth0)のパケットをキャプチャ
sudo tcpdump -i eth0 -n

ここで -n オプションを忘れないこと。名前解決(DNS逆引き)を行わないことで、大量のトラフィックが流れる高負荷な環境でも tcpdump 自体がボトルネックになってパケットをドロップするのを防げる。これはシニアの常識だ。

通信フロー(シーケンス)で理解するインターフェースの立ち位置

例えば、Web APIサーバー(例: 192.168.1.10)に対して、クライアントから curl でリクエストを投げたとしよう。

# クライアント側からAPIサーバーへリクエスト送信
curl http://192.168.1.10/api/v1/status

この時、APIサーバー側の eth0 でキャプチャを取ると、次のような美しい(あるいは悲惨な)スリーウェイハンドシェイクとHTTPリクエストのパケットが流れるのが見える。

[Client]                            [API Server (eth0)]
   |                                         |
   | -------- SYN (Port 80) ---------------> |  <- tcpdump -i eth0 でキャプチャ可能
   | <------- SYN-ACK ---------------------- |  <- 自信を持って送信されたパケット
   | -------- ACK -------------------------> |
   | -------- GET /api/v1/status ---------> |  <- APIのペイロードが剥き出しに

しかし、これがリバースプロキシ(Nginxなど)の背後にあるアプリケーションサーバー(GunicornやUvicornなど)で、ローカルのループバック(lo)経由で通信している場合、eth0 を指定してもリクエストは一文字もキャプチャされない。なぜなら、パケットはOSの内部ループバックを通って完結しているからだ。この場合は -i lo または後述の any を使わなければならない。

—

すべてを見通す魔力:any インターフェースの仕様と罠

「どのインターフェースを通るか分からないから、とりあえず全部キャプチャしたい!」
そんな時に便利なのが、any という特殊な疑似インターフェース指定だ。

# すべてのインターフェースのトラフィックをキャプチャする
sudo tcpdump -i any -n

この any を指定すると、Linuxカーネルの cooked socket(SLL ヘッダー)を利用して、システム上のあらゆるインターフェース(eth0, lo, docker0 など)を通過するパケットを一網打尽にキャプチャできる。Web APIのコンテナ環境やマイクロサービスが乱立するKubernetesノードなどで、パケットのルーティング経路を追う際にはまさに神のようなオプションだ。

any を使うときの重大な注意点(実務の罠)

しかし、百戦錬磨のエンジニアから警告しておこう。any には「諸刃の剣」としての仕様上の特性がある。

1. 送信方向(Direction)のフィルターが直感と異なる
通常、-i eth0 でキャプチャする場合、inbound(受信)か outbound(送信)かを in / out 修飾子でフィルタリングできる。しかし、any インターフェースではカーネルの仕様上、パケットが「入ってきたのか、出て行ったのか」の判定が曖昧になる、あるいは意図通りにフィルタが機能しないケースがある。
2. パフォーマンスとパケットドロップ
全インターフェースのトラフィックを集約するため、高スループットな環境では tcpdump 自体が CPU を食い潰し、肝心のパケットをロロップ(Kernel drops)し始める。本番環境のピークタイムに安易に any を回すのは、障害調査のつもりが新たな障害を引き起こす原因になるので厳禁だ。

—

実務で役立つ!言語別・ツール別のパケット検証フロー

ここで、Web APIの開発やインフラ検証でよく使われるツール(curl, Python requests, Node.js fetch)からリクエストを飛ばし、それを tcpdump でどう捉えるかの実例を示そう。

1. Python (requests) によるAPIリクエストのキャプチャ

以下のPythonスクリプトでAPIサーバーにJSONをPOSTしたとする。

# api_client.py
import requests

url = "http://192.168.1.10/api/v1/users"
headers = {"Content-Type": "application/json", "X-Request-Source": "NOC-Debug"}
payload = {"username": "network_ninja", "role": "admin"}

# APIへPOSTリクエストを送信
response = requests.post(url, json=payload, headers=headers)
print(f"Status Code: {response.status_code}")
print(f"Response: {response.json()}")

このスクリプトを実行した瞬間、サーバー側で以下の tcpdump コマンドを構えておく。

# ポート80番を指定し、ペイロード(ASCII)までしっかりと確認する (-A オプション)
sudo tcpdump -i eth0 -nn -A 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'

このコマンドにより、TCPのハンドシェイクや純粋なACKパケット(データを含まないもの)をノイズとして除外し、Pythonから送信された {"username": "network_ninja"...} というJSONのボディを生の状態で画面に引きずり出すことができる。APIのバリデーションエラーや、リバースプロキシでのヘッダー書き換えトラブルの切り分けにおいて、これほど頼りになる手法はない。

2. Node.js (Fetch API) のデバッグ

現代のモダンなバックエンドやBFF(Backend for Frontend)で主流の Fetch API(Node.js 18以降標準)を使った通信でも同様だ。

// fetch_test.js
async function callApi() {
    try {
        const response = await fetch('http://192.168.1.10/api/v1/health', {
            method: 'GET',
            headers: { 'Authorization': 'Bearer secret-token-123' }
        });
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('API Error:', error);
    }
}

callApi();

もし、この Authorization ヘッダーがバックエンドのマイクロサービスに正しく伝わっていない疑いがある場合、コンテナ間のブリッジネットワークも含めてキャプチャするために、以下のように -i any と特定のホストIPを組み合わせて調査する。

# anyインターフェースを使いつつ、特定の宛先IPとポートに絞ってキャプチャ
sudo tcpdump -i any -nn -s 0 -v 'host 192.168.1.10 and port 80'

(※ -s 0 はパケットの切り捨て(snaplen)を行わず、パケット全体を丸ごとキャプチャするための必須オプションだ)

—

シニアエンジニアからの教訓

ネットワークのトラブルシューティングにおいて、勘や推測は百害あって一利なしだ。「設定は合っているはずだ」「コードは動くはずだ」という思い込みを、冷徹に打ち砕いてくれるのが tcpdump であり、その第一歩が適切なインターフェース選定である。

  • どのNICにパケットが流れているのか(物理か、ループバックか、仮想ブリッジか)を見極め、適切な -i を選択すること。
  • 全体像をつかむために any を使うのは強力だが、負荷とフィルタの特性を理解して慎重に扱うこと。
  • -n や -s 0 などの実務的なオプションを指が覚えるまで叩き込むこと。

障害の夜、パケットの嵐の中に真実を見出した時のカタルシスこそ、我々インフラ・ネットワークエンジニアの醍醐味だ。さあ、ターミナルを開き、パケットの声に耳を傾けよう。

コメント

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