tcpdump: Linuxコマンドラインからのネットワークパケット解析

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

クイックスタート:5分で最初のパケットをキャプチャ

深夜2時にWiresharkへのアクセスもなく、不安定なマイクロサービスをデバッグしなければならない経験があれば、tcpdumpがなぜ重要なのか、もうわかっているはずだ。ネットワークで何かおかしいことが起きているとき、まず手を伸ばすのはこのツールだ。コマンドをいくつか覚えてしまえば、構文を気にせずに使いこなせるようになる。

まず、tcpdumpがインストールされているか確認する:

tcpdump --version

インストールされていない場合:

# Debian/Ubuntu
sudo apt install tcpdump

# RHEL/CentOS/Rocky
sudo dnf install tcpdump

では、メインインターフェースでライブトラフィックをキャプチャしてみよう:

sudo tcpdump -i eth0

すぐにパケットが流れ始める。Ctrl+Cで停止できる。これだけでトラフィックのスニッフィングができている。生の出力だけでは使いにくいので、もっと実用的にしていこう。

日常的によく使うコマンドは、パケット数を制限してファイルに保存するものだ:

sudo tcpdump -i eth0 -c 100 -w capture.pcap

正確に100パケットをキャプチャして.pcapファイルに保存する。後で分析できるし、より詳細なプロトコル解析が必要なときはWiresharkで開くこともできる。

詳細解説:フィルターと出力を理解する

正しいインターフェースを選ぶ

キャプチャを始める前に、利用可能なインターフェースを一覧表示しよう:

sudo tcpdump -D
# または
ip link show

Dockerが動いているサーバーでは、docker0eth0lo、そしていくつかのvethペアが見える。間違ったインターフェースを選ぶと、目的のトラフィックを完全に見逃してしまう。

BPFフィルター:真の力

tcpdumpはBerkeley Packet Filter(BPF)構文を使用している。これを使いこなせれば、必要なトラフィックだけを正確に絞り込める。本番環境で実際に使うフィルターを紹介する:

ホストでフィルタリング:

sudo tcpdump -i eth0 host 192.168.1.50

ポートでフィルタリング:

sudo tcpdump -i eth0 port 443
sudo tcpdump -i eth0 port 80 or port 443

プロトコルでフィルタリング:

sudo tcpdump -i eth0 tcp
sudo tcpdump -i eth0 udp
sudo tcpdump -i eth0 icmp

フィルターの組み合わせ(送受信元+ポート):

# 特定のホストからポート3306(MySQL)へのトラフィック
sudo tcpdump -i eth0 src host 10.0.0.5 and port 3306

# アプリサーバーのポート8080へのトラフィック
sudo tcpdump -i eth0 dst host 10.0.0.10 and port 8080

ノイズを減らす — SSHを除外して自分のターミナルを溢れさせない:

sudo tcpdump -i eth0 not port 22

出力を読む

tcpdumpの典型的な出力行はこのようになっている:

14:32:01.123456 IP 10.0.0.5.54321 > 10.0.0.10.80: Flags [S], seq 1234567890, win 65535, length 0
  • 14:32:01.123456 — マイクロ秒精度のタイムスタンプ
  • IP 10.0.0.5.54321 > 10.0.0.10.80 — 送信元IP:ポート → 宛先IP:ポート
  • Flags [S]TCPフラグ:S=SYN、A=ACK、F=FIN、R=RST、P=PSH
  • length 0 — ペイロードサイズ(SYNのような制御パケットは0)

-nでDNS解決をスキップ(出力が速くなり、ノイズが減る)し、-nnでポート名の解決もスキップできる:

sudo tcpdump -i eth0 -nn port 443

応用編:実際の本番シナリオ

遅いAPIのデバッグ

APIエンドポイントが遅く感じるが、ネットワークの問題なのかアプリの問題なのか判断できないとき、TCPハンドシェイクをキャプチャして接続レイテンシを直接測定する:

# 接続確立時間を測定するためにSYNとSYN-ACKパケットのみをキャプチャ
sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-ack) != 0' and port 8080

SYNとSYN-ACKの間隔が長ければ、サーバーが接続を受け付けるのに苦労している。SYN-ACKはすぐに返ってくるのにレスポンスが遅い?ボトルネックはネットワークではなくアプリケーション側にある。

DNSの名前解決をリアルタイムで監視

sudo tcpdump -i eth0 -nn udp port 53

これには何度も助けられた。設定ミスのコンテナが内部リゾルバーに毎秒約8,000件のDNSリクエストを投げ続け、クラッシュ寸前になっていたケースを発見したこともある。ライブパケット検査がなければ、こういう問題は曖昧なレイテンシメトリクスやタイムアウトの陰に隠れてしまう。

HTTPヘッダーのキャプチャ(暗号化されていないトラフィック)

# -A でパケットペイロードをASCIIで表示
sudo tcpdump -i eth0 -A -nn port 80 | grep -E 'GET|POST|Host:|Content-Type:'

これはプレーンHTTPにのみ有効だ。HTTPSにはmitmproxyやSSLキーロギングが必要になる。とはいえ、内部サービス、レガシーAPI、プライベートネットワーク上のコンテナ間トラフィックには非常に便利だ。

キャプチャの保存と読み込み

# キャプチャを保存
sudo tcpdump -i eth0 -w /tmp/debug-$(date +%Y%m%d-%H%M%S).pcap not port 22

# 後で読み込む(読み込みにはroot権限不要)
tcpdump -r /tmp/debug-20260401-143200.pcap

# 保存済みファイルにフィルターを適用して読み込む
tcpdump -r /tmp/debug-20260401-143200.pcap port 5432

ファイル名にタイムスタンプを入れる習慣は、深夜のインシデント対応中に5つのキャプチャファイルを混同してしまった経験から身についた。小さなことだが、すでにストレスがかかっているときの混乱を防いでくれる。

長時間分析のためのキャプチャローテーション

# 100MBごとにファイル分割、60秒ごとにローテート、10ファイルを保持
sudo tcpdump -i eth0 -w /tmp/capture-%Y%m%d-%H%M%S.pcap \
  -C 100 -G 60 -W 10 not port 22

断続的な問題に対してtcpdumpを一晩中動かしておくときに重宝する。ディスクを埋めることなく、直近10分間のトラフィックをローリングで保持できる。

現場からの実践的なヒント

ヒント1:インタラクティブキャプチャ時は必ずSSHを除外する

これを忘れると、自分のSSHトラフィックでターミナルが溢れかえる。本当に必要なパケットが埋もれてしまう:

sudo tcpdump -i eth0 not port 22 and not port 2222

ヒント2:詳細表示には -v、-vv、-vvv を使う

# -v: TTL、合計長、IPオプション
# -vv: NFS/SMB/DNSのより詳細な情報
# -vvv: 可能な限り完全なデコード
sudo tcpdump -i eth0 -vv icmp

ヒント3:コンテナトラフィックにはDockerブリッジでキャプチャ

# DockerブリッジインターフェースをFind
ip link show | grep docker

# コンテナ間トラフィックをキャプチャ
sudo tcpdump -i docker0 -nn not port 22

ホストの外部インターフェースに触れずにコンテナ間トラフィックを検査できる。問題が起きているDocker Composeスタックをデバッグするときにまさに必要なものだ。

ヒント4:tcpdump + grep でクイックパターンマッチング

# 接続リセットをリアルタイムで監視
sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0' 2>/dev/null

# 送信元IPごとのパケット数をカウント(sortにパイプ)
sudo tcpdump -i eth0 -nn -c 1000 2>/dev/null | \
  awk '{print $3}' | cut -d'.' -f1-4 | sort | uniq -c | sort -rn | head -20

ヒント5:スクリプト解析にはtsharkと組み合わせる

tsharkはWiresharkのCLI版で、.pcapファイルをより豊富なプロトコル解析で読み込める。tcpdumpでキャプチャして、tsharkで解析する:

# キャプチャ
sudo tcpdump -i eth0 -w capture.pcap port 5432

# tsharkでPostgreSQLトラフィックを解析
tshark -r capture.pcap -Y 'pgsql' -T fields -e frame.time -e ip.src -e ip.dst

このコンボを使って、ネットワークホップをまたぐ遅いPostgreSQLクエリを追跡したことがある。pcapを見れば、どのクエリがコネクションプールを圧迫しているか一目瞭然だった。アプリケーションログからは何の手がかりも得られなかったのに。

クイックリファレンスチートシート

  • tcpdump -i eth0 — インターフェースでキャプチャ
  • tcpdump -i any — 全インターフェースでキャプチャ
  • tcpdump -nn — DNS/ポート名解決を無効化
  • tcpdump -c 100 — 100パケットで停止
  • tcpdump -w file.pcap — ファイルに書き込む
  • tcpdump -r file.pcap — ファイルから読み込む
  • tcpdump -A — ASCIIペイロードを表示
  • tcpdump -X — 16進数+ASCIIペイロードを表示
  • tcpdump -e — Ethernet/MACヘッダーを表示
  • tcpdump host X.X.X.X — ホストでフィルタリング
  • tcpdump port 443 — ポートでフィルタリング
  • tcpdump net 10.0.0.0/24 — サブネットでフィルタリング

tcpdumpは使えば使うほど上達する。まずは開発環境やステージング環境で試してみよう。プレッシャーも、リスクもない。出力を読み慣れることが大切だ。深夜3時に本番インシデントが発生したとき、自然と手が伸びて、数時間ではなく数分で答えが出せるようになる。

Share: