tcpdumpチートシート
[履歴] [最終更新] (2018/09/02 16:23:43)
プログラミング/IoT の関連商品 (Amazonのアソシエイトとして、当メディアは適格販売により収入を得ています。)
最近の投稿
注目の記事

概要

pcap (packet capture) というパケットキャプチャ用APIの実装には、UNIX系のlibpcapとWindowsのWinPcapがあります。これらのライブラリを利用したアプリケーションとしては、UNIX系のtcpdumpとWindowsのWiresharkが有名です。また、スクリプト言語にはlibpcap/WinPcapのラッパーが存在しており、パケットキャプチャアプリケーションの開発時に利用できます。例えばPerlにはNet::Pcapというラッパーがあります。

以下にtcpdumpコマンドを用いたパケットキャプチャのチートシートを記載します。キャプチャしたものの解析に必要となるヘッダ情報の意味自体は「TCPDUMPの出力を見てみよう」などを参照してください。

キャプチャするパケットの絞り込み

インターフェースを指定

tcpdumpを実行するPCに複数のネットワークインターフェースが存在する場合に、それらのうちどれでパケットキャプチャするかを指定します。-i オプションを指定しないと up 状態のインタフェースの中で番号が一番小さいものが自動的に選択されることに注意します。システムによっては lo (ループバックインタフェース) を含むすべてのインタフェースに関してキャプチャを行う -i any がサポートされています。

$ tcpdump -i eth0

ポート番号で絞り込む

あるネットワークインターフェースには様々なTCP/UDPパケットが到着します。それらのうち、TCP/UDPパケットのヘッダにおける「送信元ポート番号」または「宛先ポート番号」が特定の番号のもののみに絞り込むことができます。

$ tcpdump -i eth0 port 80
$ tcpdump -i eth0 src port 80 ← 「送信元ポート番号」が80番のもののみ
$ tcpdump -i eth0 dst port 80 ← 「宛先ポート番号」が80番のもののみ
$ tcpdump -i eth0 dst port 80 or dst port 443 ← 条件は 'or' で緩められます

IPアドレスで絞り込む

あるネットワークインターフェースには様々なIPパケットが到着します。それらのうち、IPパケットのヘッダにおける「送信元IPアドレス」または「宛先IPアドレス」が特定の番号のもののみに絞り込むことができます。

$ tcpdump -i eth0 host 10.0.2.15
$ tcpdump -i eth0 src host 10.0.2.15 ← 「送信元IPアドレス」で絞り込み
$ tcpdump -i eth0 dst host 10.0.2.15 ← 「宛先IPアドレス」で絞り込み

サブネットマスクを使用して、複数のIPアドレスを指定することもできます。

$ tcpdump -i eth0 net 10.0.2.15 mask 255.255.255.255 ← "host 10.0.2.15" と同義
$ tcpdump -i eth0 net 10.0.2.0 mask 255.255.255.0 ← "10.0.2.0",...,"10.0.2.255"

自分宛のパケットのみをキャプチャ

tcpdumpコマンドを実行するマシンの、指定したネットワークインターフェース宛てではないパケットを無視するためには、ネットワークカードのプロミスキャスモードを '-p' オプションを付与して無効にします。

$ tcpdump -i eth0 port 80 -p

パケットを丸ごとキャプチャ

パケットのペイロードのサイズがある既定値を越えると、パケットキャプチャ時に超過した部分が切り捨てられ、tcpdumpの処理上無視されます。この既定値を無限にするためには '-s0' オプションを付与します。

$ tcpdump -i eth0 port 80 -s0

キャプチャしたパケットの情報表示

出力を詳細にする

'-vvv' オプションを付与すると詳細な情報が出力されます。

$ tcpdump -i eth0 port 80 -vvv

時間情報を表示しない

$ tcpdump -i eth0 port 80 -t

ポート番号を文字列に変換して表示しない

'http' ではなく '80' と表示されるようにするには '-nn' オプションを付与します。

$ tcpdump -i eth0 port 80 -nn

ペイロード部分をアスキーで表示

通常実行時はヘッダ情報だけが表示されます。それに加えて、ペイロード情報をアスキー表示するためには '-A' または '-X' オプションを付与します。前述の '-s0' オプションと併用するとよいです。

$ tcpdump -i eth0 port 80 -s0 -A  ← アスキー表示
$ tcpdump -i eth0 port 80 -s0 -X  ← アスキーだけでなく16進数でも表示

ファイル入出力

ファイルに出力

'-w' オプションを付与すると標準出力ではなくファイル出力ができます。pcapの生データでありエディタで開いてもその意味はよく分かりませんが、tcpdumpやWiresharkで読み込むことができます。

$ tcpdump -i eth0 port 80 -w http.cap

ファイルから読み込む

$ tcpdump -r http.cap
$ tcpdump -r http.cap -t ← オプション指定が可能

分割してファイル書き込み

一つの巨大なファイルが生成されることを避け、指定秒数毎にファイル分割を行うためには '-G' オプションを付与します。なお、パケットが発生しなかった場合はファイル作成自体がなされません。

$ tcpdump -i eth0 port 80 -s0 -G 3600 -w http_%Y%m%d_%H%M.cap

YahooサイトへのHTTPアクセスのパケットキャプチャ例

バックグラウンドでパケットキャプチャ用にtcpdumpコマンドを実行しておきます。

$ tcpdump -i eth0 port 80 -p -s0 -w http.cap &

Yahooサイトへアクセスします。

$ curl http://www.yahoo.co.jp/

tcpdumpコマンドをフォアグラウンドに戻して終了させます。

$ fg
(Ctrl-C)

ファイルから読み込んで一連のやりとりをアスキー表示してみます。

$ tcpdump -r http.cap -Atvvv | less

表示された内容を見ると、TCPの教科書に記載されている通りの通信がなされていることが確認できます。

  1. 「3ウェイ・ハンドシェイク」によるコネクション確立
  2. ウィンドウサイズ内でのデータ転送 (シーケンス番号とAck番号に注目)
  3. "FIN" を送信してのコネクション終了

ping と tcpdump でネットワークの疎通確認のヒントを得る

ping と併用するとネットワークの疎通確認時などに便利です。icmp と指定することで TCP および UDP パケットを無視できます。また、前述の通り -i オプションを指定しないと up 状態のインタフェースの中で番号が一番小さいものが自動的に選択されることに注意します。

$ ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.046 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.051 ms
...

$ sudo tcpdump -i lo icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
14:41:15.458338 IP localhost > localhost: ICMP echo request, id 47689, seq 1, length 64
14:41:15.458360 IP localhost > localhost: ICMP echo reply, id 47689, seq 1, length 64
14:41:16.457353 IP localhost > localhost: ICMP echo request, id 47689, seq 2, length 64
14:41:16.457365 IP localhost > localhost: ICMP echo reply, id 47689, seq 2, length 64
...

パケット解析の例

インタフェースの確認

$ traceroute www.example.com
traceroute to www.example.com (93.184.216.34), 30 hops max, 60 byte packets
 1  gateway (10.0.2.2)  0.195 ms  0.093 ms  0.135 ms
...

$ ip a | grep -B2 10.0.2
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:08:17:39 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3

パケットキャプチャ

sudo tcpdump -ienp0s3 -p -s0 -X -t -vvv -nn port 80
curl www.example.com

ローカルホストから外部サーバへの HTTP リクエストが含まれる IP パケット

IP (tos 0x0, ttl 64, id 3530, offset 0, flags [DF], proto TCP (6), length 119)
    10.0.2.15.44370 > 93.184.216.34.80: Flags [P.], cksum 0x4253 (incorrect -> 0x2a00), seq 1:80, ack 1, win 29200, length 79: HTTP, length: 79
        GET / HTTP/1.1
        Host: www.example.com
        User-Agent: curl/7.52.1
        Accept: */*

        0x0000:  4500 0077 0dca 4000 4006 eacd 0a00 020f  E..w..@.@.......
        0x0010:  5db8 d822 ad52 0050 6834 dd99 2c93 2202  ]..".R.Ph4..,.".
        0x0020:  5018 7210 4253 0000 4745 5420 2f20 4854  P.r.BS..GET./.HT
        0x0030:  5450 2f31 2e31 0d0a 486f 7374 3a20 7777  TP/1.1..Host:.ww
        0x0040:  772e 6578 616d 706c 652e 636f 6d0d 0a55  w.example.com..U
        0x0050:  7365 722d 4167 656e 743a 2063 7572 6c2f  ser-Agent:.curl/
        0x0060:  372e 3532 2e31 0d0a 4163 6365 7074 3a20  7.52.1..Accept:.
        0x0070:  2a2f 2a0d 0a0d 0a                        */*....

上記は TCP セグメントを含む IP パケットです。

TCP データ部は 41 バイト目 4745 5420 2f20 4854 から始まります。今回は以下のコマンドで数値を ASCII コードに変換して確認できます。マルチバイト文字やバイナリデータの場合は適宜処理します

$ echo -e '\x47\x45\x54\x20\x2f\x20\x48\x54'
GET / HT
$ echo 'print chr(0x47)' | python
G
$ echo 'print hex(ord("G"))' | python
0x47

送信元IPアドレス 32bit 0a00 020f、宛先IPアドレス 32bit 5450 2f31

$ echo '
> import socket, struct
> print socket.inet_ntoa(struct.pack("!I", 0x0a00020f))' | python
10.0.2.15
$ mysql -uroot -e "select inet_ntoa(`echo 'obase=10; ibase=16; 0A00020F' | bc`)"
+----------------------+
| inet_ntoa(167772687) |
+----------------------+
| 10.0.2.15            |
+----------------------+

送信元ポート番号 16bit + 宛先ポート番号 16bit ad52 0050

$ echo 'print 0xad52' | python
44370
$ echo 'print 0x0050' | python
80

TCPヘッダ長 4bit + 将来の機能拡張用の予約 6bit + コードビット 6bit 5018

`5` * 1ワード(32ビット(4バイト)) = 20 バイト

パケット解析の例2 (UDP)

インタフェースの確認 (Google Public DNS)

traceroute 8.8.8.8
ip r

パケットキャプチャ

sudo tcpdump -ienp0s3 -p -s0 -X -t -vvv -nn udp and port 53

DNS クエリ (id: 43521)

$ dig @8.8.8.8 www.example.com

; <<>> DiG 9.10.3-P4-Debian <<>> @8.8.8.8 www.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43521  <-- ID は 43521 です。
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;www.example.com.               IN      A

;; ANSWER SECTION:
www.example.com.        6354    IN      A       93.184.216.34

;; Query time: 87 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Sun Sep 02 16:05:08 JST 2018
;; MSG SIZE  rcvd: 60

DNS からの応答 IP パケット

IP (tos 0x0, ttl 64, id 24556, offset 0, flags [none], proto UDP (17), length 88)
    8.8.8.8.53 > 10.0.2.15.36621: [udp sum ok] 43521$ q: A? www.example.com. 1/0/1 www.example.com. [1h45m54s] A 93.184.216.34 ar: . OPT UDPsize=512 (60)
        0x0000:  4500 0058 5fec 0000 4011 fe8a 0808 0808  E..X_...@.......
        0x0010:  0a00 020f 0035 8f0d 0044 1ccd aa01 81a0  .....5...D......
        0x0020:  0001 0001 0000 0001 0377 7777 0765 7861  .........www.exa
        0x0030:  6d70 6c65 0363 6f6d 0000 0100 01c0 0c00  mple.com........
        0x0040:  0100 0100 0018 d200 045d b8d8 2200 0029  .........].."..)
        0x0050:  0200 0000 0000 0000                      ........

送信元ポート番号 16bit + 宛先ポート番号 16bit 0035 8f0d

$ echo 'print 0x0035' | python
53
$ echo 'print 0x8f0d' | python
36621

UDP データの開始 aa01 81a0

$ echo 'print 0xaa01' | python
43521

ID が取得できました。

関連ページ
    コマンドのエイリアスを登録する (update-alternatives) mybin という名前のコマンドを登録 sudo update-alternatives --install /usr/local/bin/mybin mybin /usr/bin/echo 10 sudo update-alternatives --install /usr/local/bin/mybin mybin