tcpdumpでプロトコルを解析してみよう

前へ << telnet の際の注意点 TCP/IP と OSI 参照モデル >> 次へ

もっと楽にプロトコルを理解したい

RFC を読むのはめんどくさいです。って、 そんなこと言っちゃうと怒られるかもしれませんが、 特にプロトコルのおおまかな流れをわかっていない場合、 RFC を読んでも「なんのこっちゃ?」となりやすいです。 日本語訳があればまだしも、英文を読むのは非常に苦痛です。

「Netscape Navigator は今どういう HTTP プロトコルを送っているのかな」 とか「POP3 サーバはどういうレスポンスを返しているのだろうか」 と思ったことはありませんか? 「それさえわかれば楽にプロトコルを理解できるのに…」。 そういうときは tcpdump を使って調べることができます。

tcpdumpとは?

tcpdump というのは、ネットワーク上を流れるデータを モニタリングするツールで、TCP、UDP、IP、ICMPなどの各種 プロトコルに対応しています。localhost のどのポートから どのホストのどのポートへ接続が行われたか、そのパケットの 内容は、など詳細なデータを取ることができます。

以下の説明の一部(カーネル再構築など)は FreeBSD に特化した 内容になっていますが、オプション類などは他の UNIX でも 参考になるでしょう。

カーネルの再構築

この部分は FreeBSD に特化した内容になっています。 他のOSの場合は、環境に合ったインストール方法を調べてください。

tcpdump は bpfilter という疑似デバイスを使って パケットの内容を表示します。GENERIC カーネルには bpfilter は含まれていないので、カーネルの再構築が必要です。 ここでは細かい説明はしませんが、カーネルコンフィギュレーションに

pseudo-device  bpfilter  4
を加えて、カーネルの再構築をしてください。例えば以下のような感じになります。
# cd /usr/src/sys/i386/conf/ (カーネルコンフィグのディレクトリに移動)
# cp GENERIC mykernel (新しいカーネルコンフィグを作成)
# echo "pseudo-device bpfilter 4" >> mykernel
# config mykernel (コンフィグファイルにあったMakefileを作成)
# cd ../../mykernel (コンパイルディレクトリに移動)
# make depend (依存関係チェック)
# make (カーネル再構築)
# make install (/kernelの置き換え)
カーネルの更新ができたら、リブートします。

さっそく覗き見してみよう

まず telnet で echo サーバに接続します。
% telnet localhost echo
Trying 127.0.0.1...
flushoutput character is 'off'.
Connected to localhost.
Escape character is '^]'.
一方、別ウィンドウで root になって、
# tcpdump -l -s 1600 -x -i lo0
と tcpdump を実行します。では先程の telnet で何か 適当に文字を打ってください。すると tcpdump は
09:51:42.562095 localhost.miteksys-lm > localhost.echo: P 1:7(6) ack 1 win 57344  (DF) [tos 0x10]
            4510 0042 5c5a 4000 4006 e049 7f00 0001
            7f00 0001 05ca 0007 0248 8030 0249 a535
            a018 e000 85de 0000 0101 080a 0001 5ffd
            0001 5fea 0101 0b06 0000 0229 7465 7374
            0d0a
09:51:42.562533 localhost.echo > localhost.miteksys-lm: P 1:23(22) ack 7 win 57344  (DF)
            4500 0052 5c5b 4000 4006 e048 7f00 0001
            7f00 0001 0007 05ca 0249 a535 0248 8036
            a018 e000 b4ad 0000 0101 080a 0001 5ffd
            0001 5ffd 0101 0b06 0000 022a 6f72 6967
            696e 616c 2065 6368 6f64 3a20 7465 7374
            0d0a
09:51:42.568522 localhost.miteksys-lm > localhost.echo: . ack 23 win 57322  (DF) [tos 0x10]
            4510 003c 5c5c 4000 4006 e04d 7f00 0001
            7f00 0001 05ca 0007 0248 8036 0249 a54b
            a010 dfea 7ab7 0000 0101 080a 0001 5ffd
            0001 5ffd 0101 0b06 0000 0229
と表示します。これがパケットの情報と内容です。

localhost の miteksys-lm ポートから localhost の echo ポートへ、 それに対するレスポンス、さらにもう一度 echo ポートへ、という 3つのパケットが流れたことがわかります。なぜ miteksys-lm と 表示されているのかというと、/etc/services に

miteksys-lm     1482/tcp    #Miteksys License Manager
と書いてあるからです。echo クライアント (telnet) はたまたま ポート 1482 番を割り当てられたのですが、tcpdump が /etc/services を 参照して miteksys-lm という表示をしたのでしょう。
tcpdump に -n オプションを付けることで、IP アドレスや ポートを数字のまま表示させることもできます。

tcpdump の出力をフィルタで整形 (1)

しかしパケットの内容は16進でダンプされているので、 内容がよくわかりません。腕に自信がある人は、 出力を整形するスクリプトを書いてみるといいでしょう。 僕は腕に自信がなかったので(^^;、FreeBSD-users-jp メーリングリストで かわい@きやのん さんに教えてもらいました。

tcpdump-filter.pl

    1: #!/usr/local/bin/perl
    2: 
    3: # $Id: tcpdump-filter.pl,v 1.2 2002/02/17 11:07:28 68user Exp $
    4: 
    5: while(<STDIN>){
    6:     if(m/^[ \t]/){
    7:         $store .= $_
    8:     } else {
    9:         s/^[^ ]* //;
   10:         s/:[^:]*$//;
   11:         print "$_\n";
   12:         $_ = $store;
   13:         s/\s//g;
   14:         s/^.{120}//;
   15:         s/[0-9A-Fa-f][0-9A-Fa-f]/pack("C", hex $&)/eg;
   16:         s/[\x00-\x08\x0a-\x1f\x7f-\xff]/ /g;
   17:         print "$_\n";
   18:         $store = "";
   19:     }
   20: }
このスクリプトを使うと、
# tcpdump -l -s 1600 -x -i lo0 | tcpdump-filter.pl
localhost.miteksys-lm > localhost.echo: P 38305852

localhost.echo > localhost.miteksys-lm: P 1
data  
localhost.miteksys-lm > localhost.echo
my echod: data  
という見やすい形式に変換されます。

tcpdump の出力をフィルタで整形 (2)

記憶があいまいなのですが、↑の文章を書いた時代には tcpdump にデータを表示する機能がなかったような気がします。

しかし少なくとも tcpdump-3.8 にはデータを ASCII 表示する -X オプションがありますので、 まずはこれを試してみるのがよいでしょう。例えば web サーバに

GET / HTTP/1.0
というデータを送信したときの出力は以下のようになります。
# tcpdump -s 1600 -X -i lo0
07:09:41.140676 localhost.49372 > localhost.http: P 1:17(16) ack 1 win 35840 (略)
0x0000   4510 0044 4ba3 4000 4006 f0fe 7f00 0001        E..DK.@.@.......
0x0010   7f00 0001 c0dc 0050 6205 e642 4076 0ccc        .......Pb..B@v..
0x0020   8018 8c00 f622 0000 0101 080a 00ad 6734        ....."........g4
0x0030   00ad 65a4 4745 5420 2f20 4854 5450 2f31        ..e.GET./.HTTP/1
0x0040   2e30 0d0a                                      .0..
IP データグラム部分も表示されてしまうので慣れないうちはわかりづらいですが、 これで十分な場合も多いでしょう。

いろんな使い方

もし HTTP プロトコルだけを見たいなら、
# tcpdump -l -s 1600 -x -i lo0 port http | tcpdump-filter.pl
また、SMTP クライアントの送るデータだけを見たいなら (SMTP サーバのレスポンスは必要ないなら)、
# tcpdump -l -s 1600 -x -i lo0 dst port smtp | tcpdump-filter.pl
逆に SMTP サーバのレスポンスだけを見たいなら、
# tcpdump -l -s 1600 -x -i lo0 src port smtp | tcpdump-filter.pl
などと色々指定できます。オプションの意味は以下の通り。
  • -l は tcpdump の出力をパイプで他プログラムに渡す場合に必要
  • -x はパケットの内容を出力するオプション
  • -s はパケットから取り出すデータの長さ。-s 1600 なら1600バイト
  • -i はモニタリングするインタフェースを指定。-i ed0 や -i ppp などなど。
より詳しい説明は こちら

tcpdump の危険性

同じように、telnet で localhost にログインするところを覗いて見てください。 キーストロークが全てエコーバックされているところ、パスワードも そのまま流れていること、ls などのコマンドの出力やエディタで開いた ファイルの内容全てがネットワーク上を流れていることがわかります。

tcpdump は、使い方次第では非常に危険なツールになります。 真面目な目的でモニタリングするつもりでも、 他人の見てはいけないデータを誤って見てしまうこともありえます。 慎重に使うようにしてください。

前へ << telnet の際の注意点 TCP/IP と OSI 参照モデル >> 次へ

ご意見・ご指摘は Twitter: @68user までお願いします。