パケットキャプチャツールである tcpdump の使い方を整理しておきます。自分用メモ。

tcpdump の基本

まず実行してみる

$ sudo tcpdump
12:46:26.850710 IP 192.168.0.10.54181 > nrt19s02-in-f24.1e100.net.http: Flags [.], seq 3579718551:3579719953, ack 1130673671, win 8192, options [nop,nop,TS val 750304771 ecr 3577990075], length 1402
12:46:26.850712 IP 192.168.0.10.54181 > nrt19s02-in-f24.1e100.net.http: Flags [P.], seq 1402:1461, ack 1, win 8192, options [nop,nop,TS val 750304771 ecr 3577990075], length 59
12:46:26.901212 IP 192.168.0.10.54181 > nrt19s02-in-f24.1e100.net.http: Flags [.], ack 802, win 8141, options [nop,nop,TS val 750304820 ecr 3578009852], length 0
12:46:26.906398 IP 192.168.0.10.54139 > nrt19s02-in-f23.1e100.net.https: Flags [P.], seq 1468315812:1468315849, ack 1338670021, win 8192, options [nop,nop,TS val 750304825 ecr 3578007435], length 37
12:46:26.906514 IP 192.168.0.10.54139 > nrt19s02-in-f23.1e100.net.https: Flags [.], seq 37:1439, ack 1, win 8192, options [nop,nop,TS val 750304825 ecr 3578007435], length 1402
12:46:26.906515 IP 192.168.0.10.54139 > nrt19s02-in-f23.1e100.net.https: Flags [P.], seq 1439:1681, ack 1, win 8192, options [nop,nop,TS val 750304825 ecr 3578007435], length 242
12:46:26.915643 IP 192.168.0.10.54139 > nrt19s02-in-f23.1e100.net.https: Flags [.], ack 38, win 8189, options [nop,nop,TS val 750304834 ecr 3578026165], length 0

パケットのヘッダ情報が垂れ流されます。

いつ どこから(IPおよびポート) > どこへ(IPおよびポート): どんなパケットか

が表示されています。たとえば

21:48:36.476894 IP 192.168.24.73.4697 > 192.168.24.62.http: S 1751526227:1751526227(0) win 65535 
21:48:36.477798 IP 192.168.24.62.http > 192.168.24.73.4697: S 1906616760:1906616760(0) ack 1751526228 win 5840 
21:48:36.476970 IP 192.168.24.73.4697 > 192.168.24.62.http: . ack 1 win 65535

こんなかんじだったりすると、これがいわゆる TCP の3ウェイハンドシェイクになっていて、SYN 送信(S) -> SYN/ACK 受信(S/ack) -> ACK 送信(ack)になっているわけです。

見方については TCPDUMPの出力を見てみよう のサイトが詳しいのでそちらに譲ります。

フィルタをかける

ポートで絞る

なにもフィルタをかけないと出力が多すぎてわけがわからないので、例えば http (80番) ポートに絞る場合は次のようにします。

$ sudo tcpdump port 80

発信または受信だけに絞りたい場合は、それぞれ dst, src を使います。

$ sudo tcpdump dst port 80 # 発信
$ sudo tcpdump src port 80 # 受信

http および https に絞り込みたい場合は、or を使う事ができます。

$ sudo tcpdump src port 80 or src port 443

ホストで絞る

host を使うと IP アドレスで絞り込めます。

$ sudo tcpdump host 192.168.1.1
$ sudo tcpdump dst host 192.168.1.1
$ sudo tcpdump src host 192.168.1.1

ネットワークアドレスで絞る

net を使うとネットワークアドレスで絞れます。

$ tcpdump net 192.168.2.0 mask 255.255.255.0

192.168.2.0/24ネットワークとのパケットだけを表示します。

パケットの中身も表示したい

オプション -X を使用するとヘッダだけではなくパケットの中身も表示できます。16進とASCIIで表示してくれます。

$ sudo tcpdump -X dst port 80
13:11:43.075524 IP 176.32.121.17.http > 192.168.0.10.54636: Flags [F.], seq 1, ack 2, win 127, length 0
     0x0000:  4500 0028 430d 4000 ee06 5fde b020 7911  E..(C.@..._...y.
     0x0010:  c0a8 000a 0050 d56c 3f8f 3bf0 937c ba55  .....P.l?.;..|.U
     0x0020:  5011 007f 2662 0000 797f e526 fa34       P...&b..y..&.4
13:11:48.822171 IP nrt19s17-in-f31.1e100.net.http > 192.168.0.10.54659: Flags [S.], seq 1649666050, ack 1051684197, win 14180, options [mss 1414,sackOK,TS val 3520905570 ecr 751819267,nop,wscale 6], length 0
     0x0000:  4500 003c c703 0000 3606 28e5 adc2 265f  E..<....6.(...&_
     0x0010:  c0a8 000a 0050 d583 6253 e802 3eaf 6d65  .....P..bS..>.me
     0x0020:  a012 3764 1596 0000 0204 0586 0402 080a  ..7d............
     0x0030:  d1dc c162 2ccf da03 0103 0306            ...b,.......
13:11:48.826251 IP nrt19s17-in-f31.1e100.net.http > 192.168.0.10.54655: Flags [.], ack 2923, win 311, options [nop,nop,TS val 3520905574 ecr 751819271], length 0
     0x0000:  4500 0034 c704 0000 3706 27ec adc2 265f  E..4....7.'...&_
     0x0010:  c0a8 000a 0050 d57f 51ad 1e62 e596 78a4  .....P..Q..b..x.
     0x0020:  8010 0137 a27d 0000 0101 080a d1dc c166  ...7.}.........f
     0x0030:  2ccf da07                                ,...
13:11:48.883209 IP nrt19s17-in-f31.1e100.net.http > 192.168.0.10.54655: Flags [P.], seq 802:1603, ack 2923, win 311, options [nop,nop,TS val 3520905630 ecr 751819271], length 801
     0x0000:  4500 0355 c705 0000 3706 24ca adc2 265f  E..U....7.$...&_
     0x0010:  c0a8 000a 0050 d57f 51ad 1e62 e596 78a4  .....P..Q..b..x.
     0x0020:  8018 0137 8dbe 0000 0101 080a d1dc c19e  ...7............
     0x0030:  2ccf da07 4854 5450 2f31 2e31 2033 3032  ,...HTTP/1.1.302
     0x0040:  2046 6f75 6e64 0d0a 4c6f 6361 7469 6f6e  .Found..Location
     0x0050:  3a20 6874 7470 733a 2f2f 7777 772e 676f  :.https://www.go
     0x0060:  6f67 6c65 2e63 6f2e 6a70 2f73 6561 7263  ogle.co.jp/searc
     0x0070:  683f 713d 686f 6765 2673 7567 6578 703d  h?q=hoge&sugexp=
     0x0080:  6368 726f 6d65 2c6d 6f64 3d35 2673 6f75  chrome,mod=5&sou
     0x0090:  7263 6569 643d 6368 726f 6d65 2669 653d  rceid=chrome&ie=
     0x00a0:  5554 462d 380d 0a43 6163 6865 2d43 6f6e  UTF-8..Cache-Con
     0x00b0:  7472 6f6c 3a20 7072 6976 6174 650d 0a43  trol:.private..C

google への GET がみえてますね。-X ではなく-A を使うとパケットの中身を ASCII だけで表示してくれます。

$ sudo tcpdump -s0 -A dst port 80
00:21:17.219392 IP 192.168.0.10.49030 > kix01s02-in-f23.1e100.net.http: P 1:161(160) ack 1 win 46E...4.@.@..[!1.J}.....P2..=..IFP.......GET / HTTP/1.1User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5Host: www.google.co.jpAccept: */*00:21:17.223807 IP kix01s02-in-f23.1e100.net.http > 192.168.0.10.49030: . ack 161 win 668E..(-...6...J}..!1..P....IF2...P.............00:21:17.277463 IP kix01s02-in-f23.1e100.net.http > 192.168.0.10.49030: . 1:1431(1430) ack 161 win 668E...-...6..yJ}..!1..P....IF2...P....^..HTTP/1.1 200 OKDate: Thu, 17 Oct 2013 15:21:17 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=Shift_JIS
Set-Cookie: PREF=ID=1154a5c406e9b205:FF=0:TM=1382023277:LM=1382023277:S=e6t3mjXS1VIJsfyK; expires=Sat, 17-Oct-2015 15:21:17 GMT; path=/; domain=.google.co.jp
Set-Cookie: NID=67=AETVOuLZ1B9FjJqqBGQ-ZsSaWX4FEjcuOKf4vgPMqAGGwKH0zzt-s2BnADI6EoUv0AIeDKIdR4U4wCB7Bh8WNlCf27M89Y1MnMXfr3lGkeJFgWCMseb_FhhSm_am0lae; expires=Fri, 18-Apr-2014 15:21:17 GMT; path=/; domain=.google.co.jp; HttpOnly
P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Alternate-Protocol: 80:quic
Transfer-Encoding: chunked

4979
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage"><head><meta content="...E.................................c.[...................B...............@.\.....p.....A...T...........................B" name="description"><meta content="noodp" name="robots"
><meta itemprop="image" content="/images/google_favicon_128.png"><title>Google</title><script>(function(){
window.google={kEI:"bQBgUrfnDseGkQWniIGgAw",getEI:function(a){for(var b;a&&(!a.getAttribute||!(b=a.getAttribute("eid")));)a=a.parentNode;return b||google.kEI},https:function(){return"https:"==window.location.protocol},kEXPI:"17259,146147,600395,400011
00:21:17.277480 IP g

ただ、デフォルトではパケットの中身は途中で切られてしまうので(CentOS 6 ならデフォルト 65535 バイト、CentOS 5 だとデフォルトたったの 96 バイト)、もっとみたい場合は -s オプションでバイト長を指定します。0 を指定すると制限がなくなります。

良く使いそうなオプション

よく使いそうなオプションをリストアップしておきます。

-i キャプチャするインターフェースを指定。
-n アドレスやポート番号を名前に変換しない。DNS lookup の時間短縮
-nn ポート番号をプロトコル名に変換(80 を http など)しない
-p プロミスキャスモードにしない。自分宛のパケットのみキャプチャ wikipedia:プロミスキャス・モード
-s キャプチャするサイズを指定。0 だと全て
-w 生データをそのままファイルに書き込む。WireSharkやEtherealで開くこともできる
-r -w で保存したファイルを開く
-c 指定した個数のパケットを受信したら終了。ディスク圧迫しないように使う
-X 16進とASCII文字で表示を行う。
-A ASCII文字で表示を行う。
-G ファイル書き込みを指定時間間隔で rotate する。-w のファイル名に時間フォーマットを指定
-W -G での rotate の回数を制御する。-W の回数に達すると終了

よく使いそうなテンプレ

よく使いそうなテンプレをまとめておきます。

1) いつも使う基本形 -s0 -A

$ sudo tcpdump -s0 -A port 80 and host www.google.co.jp

2) ファイル吐き出しをする場合に、ファイルが巨大に1つになってしまうと追いづらいので、指定時間間隔で rotate させる。-G オプションを使う

$ sudo tcpdump -s0 -A port 80 -G <rotate_seconds> -w /tmp/tcpdump_%Y%m%d_%H%M.cap

3) crontab に仕込んで毎日 12:00 - 13:00 だけパケットキャプチャしたい場合

オプション -G 60 で毎分 rotate させ、-W 60 で 60回 rotate したら、つまり1時間たったら、tcpdump 終了。ただし、一切書き込みがない場合、ファイル rotate が発生せず、tcpdump が終了しない挙動となるため、念のために kill もしている。

0 12 * * * sudo /usr/sbin/tcpdump -i bond0 -s 512 -A -n -nn -p "tcp port 80" -G 60 -W 60 -w /tmp/tcpdump_%Y%m%d_%H%M.cap >/dev/null 2>&1 & echo $! > /tmp/tcpdump.pid
0 13 * * * sudo kill $(cat /tmp/tcpdump.pid)

4) localhost 間の通信をみる

loopback interface を指定すればOK

linux)

$ sudo tcpdump -i lo

mac)

$ sudo tcpdump -i lo0

この場合、なぜか port xxxx ではダメで、tcp port xxxx とのように tcp まで指定しないと port 番号で絞れない。

参考

謝辞