【実務・中級編】 tcpdumpのBpfフィルター式によるトラフィック絞り込み – トラブルシューティング&ネットワーク運用監視実践ガイド

夜間帯のデータセンター。冷え切ったサーバー室のファン音を聞きながら、モニタの前に陣取る。
「おい、APIのレスポンスが時々タイムアウトするらしい。上流のロードバランサーとバックエンドのコンテナ間で何が起きているか、パケットキャプチャで引っこ抜いてくれ」

インフラの現場でトラブルシューティングに直面したとき、私たちが最後に頼る真実のソース(Single Source of Truth)は、常にワイヤー上の生データ、すなわちパケットだ。
しかし、現代のハイパフォーマンスなデータセンターにおいて、フルパケットをそのままキャプチャするなど愚の骨頂である。10Gbpsや40Gbpsのリンク上でそれをやったら、ストレージは一瞬で埋まり、CPUは悲鳴を上げてパケットをドロップ(Kernel Drop)するだろう。

ここで必要になるのが、BPF(Berkeley Packet Filter)を用いた的確なトラフィックの絞り込みだ。
今回は、tcpdumpのBFPフィルター式を駆使し、広大なネットワークの海から「真犯人のパケット」だけをピンポイントで釣り上げる技術を、実戦のノウハウを交えて解説しよう。

—

1. なぜBPF(Berkeley Packet Filter)なのか?

パケットキャプチャツールとして広く愛用されているtcpdumpだが、その真価はカーネル空間で動作するBPFエンジンによる強力なフィルタリング能力にある。

多くの初心者は、キャプチャした後に Wireshark のディスプレイフィルターでパケットを探そうとする。しかし、それは「ゴミの山の中から針を探す」ようなものだ。
BPFは、OSのカーネルレベル(ネットワークスタックの極めて浅い層)で不要なパケットをバッファにすら載せず即座に破棄する。これにより、ディスクI/OやCPU負荷を最小限に抑えつつ、必要なデータだけを効率よくメモリ上にキャプチャできるのだ。

BPFの基本構文と3つの修飾子(Qualifiers)

BPFのフィルター式は、極めて直感的でありながら強力な論理演算をサポートしている。基本構造は以下の3つの修飾子の組み合わせで成り立っている。

1. Type(種類): host, net, port, portrange (例: host 192.168.1.10, port 443)
2. Dir(方向): src, dst, src or dst, src and dst (例: src host 10.0.0.1)
3. Proto(プロトコル): ether, ip, ip6, arp, tcp, udp, icmp (例: tcp, udp)

これらを and (&&), or (||), not (!) の論理演算子で結んでいく。

—

2. 実務で即座に使える!BPFフィルター実践レシピ

ここからは、Web APIの設計・運用現場で実際に遭遇するシーンを想定し、私が日々使っている具体的なtcpdumpのレシピを紹介しよう。

レシピA: 特定のWeb APIエンドポイントへの疎通・ペイロード確認

社内のマイクロサービス(Python製)から、外部の決済API(api.example.com)に対してHTTPSリクエストを送っているが、SSL/TLSのハンドシェイクあたりでコケている疑いがあるケース。

ホスト名ではなく、あらかじめdigなどで引いたIPアドレス、あるいはポート443を指定してキャプチャする。

# 宛先IPが 203.0.113.50 で、かつ ポート443(HTTPS)のトラフィックをキャプチャ
# ローカルのインターフェース(ここでは eth0)を指定
sudo tcpdump -i eth0 -nnvvS "host 203.0.113.50 and port 443"
  • Tips: -nn オプションをつけることで、IPアドレスやポート番号の名前解決(DNS逆引き等)を行わず、即座に数値で表示させる。これがリアルタイムの解析では命を救う。パケット解析中にDNSの逆引きでtcpdumpがフリーズするなんて失態は、プロとして避けたいところだ。
  • -vv は冗長出力(Verbose)。TTLやTCPウィンドウサイズ、フラグの詳細まで丸裸にする。

レシピB: 特定のクライアントからのHTTP/JSONリクエストを捉える

APIゲートウェイ(NginxやEnvoy)の前段で、特定のクライアントIP(例: 198.51.100.45)が送信してくるPOSTリクエストのボディやヘッダーを覗き見たいとき。

# クライアントIPからの往復トラフィックのうち、HTTP(S)やAPI用ポート(例: 8080)を絞り込む
sudo tcpdump -i eth0 -A -s 1500 "src host 198.51.100.45 and tcp port 8080"
  • -A オプション: パケットのペイロードをASCII(テキスト)でダンプする。JSON形式のペイロードやHTTPヘッダー(AuthorizationやContent-Typeなど)のやり取りを生で確認したいときに必須となる。
  • -s 1500 オプション: スナップショット長(Snaplen)。デフォルトではパケットの先頭数バイトしかキャプチャしないことがあるため、イーサネットのMTUサイズ付近(1500バイト)を指定してペイロード全体を切り取る。

—

3. アプリケーション層とパケットの挙動をリンクさせる

インフラエンジニアたるもの、ただパケットを眺めるだけでなく、それが上位のアプリケーション(PythonやNode.js、あるいはWebブラウザからのFetch API)のどの挙動に起因しているかを脳内でブリッジできなければならない。

例えば、Pythonのrequestsやhttpx、あるいはNode.jsのfetchからAPIを叩いたときの通信フローを考えてみよう。

デバッグ対象のPythonコード例

以下のよなシンプルなAPIリクエストスクリプトがあるとしよう。

import requests

# デバッグ対象のAPIエンドポイント
API_URL = "https://api.internal.net/v1/users"

try:
    # タイムアウトを3秒に設定してリクエスト送信
    response = requests.get(API_URL, timeout=3)
    response.raise_for_status()
    print("レスポンスデータ:", response.json())
except requests.exceptions.Timeout:
    print("【警告】APIサーバーからの応答がタイムアウトしました。")
except requests.exceptions.RequestException as e:
    print(f"通信エラーが発生しました: {e}")

このスクリプトを実行した際、「タイムアウト」が発生したとする。ネットワーク層では何が起きているのか?
それを突き止めるために、以下のBPFフィルターを仕掛けた状態で先ほどのPythonスクリプトを走らせる。

# SYNパケット(接続要求)や再送(Retransmission)を検出するためのフィルター
sudo tcpdump -i any -nn "tcp[tcpflags] & (tcp-syn|tcp-rst) != 0"

ここでBPFの真骨頂であるバイトオフセット指定が登場する。tcp[tcpflags]を使うことで、TCPヘッダーのフラグフィールドを直接覗き見ることができる。
もしここで [S] (SYN)が飛んでいるにもかかわらず、相手からの [S+R] (SYN-ACK)が返ってこない(あるいは何回もSYNが再送されている)場合、それはアプリケーションのバグではなく、ファイアウォールのドロップ、あるいはバックエンドの過負荷による接続拒否(Connection Refused)であると即座に切り分けられるのだ。

—

4. 現場で役立つ実践Tipsとパフォーマンスの注意点

最後に、現場で数々の障害を踏んできた私から、BPFフィルターを使う上での実用的なTipsをいくつか授けておこう。

1. ダブルクォーテーション(””)でフィルター式を囲む
シェル(Bashなど)によっては、アスタリスクや括弧(*, (, ))を特殊文字として解釈してしまうことがある。BPFフィルター式は必ずダブルクォーテーションで囲む癖をつけよう。
2. any インターフェースの罠
-i any を使うとシステム全体のトラフィックを拾えて便利だが、Linuxの「Cooked Linux capture」ヘッダーが付与されるため、一部のイーサネットヘッダーを前提とした高度なBPF式(リンク層を指定するフィルタなど)が意図通りに動かないことがある。特定のインターフェース(eth0やens192など)を明示するのが無難だ。
3. Pcapファイルへの出力とWiresharkへの連携
コンソールに流れるテキストを眺めるだけでなく、後からじっくり解析するためにファイルへ保存する習慣をつけよう。

sudo tcpdump -i eth0 -w incident_202311.pcap "host 10.100.50.20 and not port 22"
  • -w でバイナリとして保存しておけば、SSHのセッション自体のトラフィック(not port 22)をキレイに除外した上で、手元のPCのWiresharkでグラフィカルに深掘りできる。

—

ネットワークは嘘をつかない。パケットの往来を正しく捉える能力は、インフラエンジニアにとっての「聴診器」であり、最大の武器だ。
場当たり的な勘に頼るのではなく、BPFでノイズを綺麗に削ぎ落とし、通信の本質をクリアに捉える——このスキルを自分のものにした瞬間から、どんな難解な障害も「解けるパズル」に見えてくるはずだ。さあ、次のトラブルシューティングに備えよう。

コメント

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