libpcap

399円で dual display

結局、399円でレンタルCD屋からHDMIなケーブル(2M)を仕入れてきた。これ1種類しか無いので迷う事無し。ホームセンターには1500円平均ぐらいのやつが何種類か置いてあった。どれを選んでいいやらである。HDMIはどこかの特許になってて、ケーブルにも特許料が課せられる。お墨付きって言うみかじめ代だ。

399円品は、どこかからの輸入品。価格から言って中華だろう。無事に映ったよ。画面を横に並べるより、縦にする方が、カーソルの移動が楽。カーソルが移動していって、画面を超えると、別画面ににゅーと出て来るのは、慣れが必要だな。

本体のLCDとTVの解像度は同一。TVの方でWindows Terminalを使ってる。Ctl+-, Ctl+= で、動的にフォントサイズを変更出来るのが便利。Rows 50 ぐらいまで、目に負担なく使えるので、なかなか良い。

ちょいと贅沢な悩み。2画面あると目移りしちゃう。思い切ってTV画面だけにしちゃって、TVに集中するか(Win+pで切り替え)。そして、Windows Terminalを全面に拡げる。複数の画面を作って、TABで切り替え(Ctl+Alt+1,2,3..)。

emacsを立ち上げる。Ctl-x 2 なり、Ctl-x 3 で、emacs画面を分割。画面間の移動はどうする? そんなの先人の知恵を借りればOk。 Emacsの分割ウィンドウを←↓↑→で移動

TVの前に、どっかりと胡坐をかいた状態だと、腰痛に襲われそう。さて、どうする? それと、女房とのTV取り合い戦争は、どうやって回避する? こういう問題が難しいんだよな。

調子こいて Thinkpad SL510のdebianで試そうと思ったら、何故かHDMIコネクターの口が合わない。マニュアル調べたら、HDMIかDisplay Portですって書かれていた。ひょっとしてHDMIとは違うの? なんでもDisplay Portの方は、任意団体が規格化したもののようで、みかじめ代は不要らしい。

ああ、思い出した。仙台の銘酒と言えば、浦霞。でも、昔仙台市民だった時は、 一ノ蔵、無監査二級酒なんてのを愛飲してた。国税庁の監査を受けると酒税が高くなる(可能性がある)。そこで、あえて監査を受けずに、二級酒として安く販売するんだそうな。その心意気気に入った、ってんで、ずっとこればかりでしたね。懐かしいな、たまには飲んでみたいぞ。

nmapのあれ

nmap方面で、OSの検出はどうなってるか、確認してみる。

OS DETECTION
       One of Nmap's best-known features is remote OS detection using TCP/IP
       stack fingerprinting. Nmap sends a series of TCP and UDP packets to the
       remote host and examines practically every bit in the responses. After
       performing dozens of tests such as TCP ISN sampling, TCP options
       support and ordering, IP ID sampling, and the initial window size
       check, Nmap compares the results to its nmap-os-db database of more
       than 2,600 known OS fingerprints and prints out the OS details if there
       is a match. 

       -O (Enable OS detection)
           Enables OS detection, as discussed above. Alternatively, you can
           use -A to enable OS detection along with other things.

10万行を超えるnma-os-dbってファイルの一部

# OpenBSD 6.1 GENERIC.MP#20 amd64
Fingerprint OpenBSD 6.1
Class OpenBSD | OpenBSD | 6.X | general purpose
CPE cpe:/o:openbsd:openbsd:6.1 auto
SEQ(SP=F7-101%GCD=1-6%ISR=FD-115%TI=RD%CI=RI%II=RI%TS=20|21)
OPS(O1=M5B4NNSNW6NNT11%O2=M5B4NNSNW6NNT11%O3=M5B4NW6NNT11%O4=M5B4NNSNW6NNT11%O5\
=M5B4NNSNW6NNT11%O6=M5B4NNSNNT11)
 :

見ていてもしょうがないので、検証開始。

debian:~$ sudo nmap -O localhost
Starting Nmap 7.80 ( https://nmap.org ) at 2021-06-14 06:32 JST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000026s latency).
Other addresses for localhost (not scanned): ::1
Not shown: 999 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.80%E=4%D=6/14%OT=22%CT=1%CU=34197%PV=N%DS=0%DC=L%G=Y%TM=60C6797
OS:6%P=i686-pc-linux-gnu)SEQ(SP=107%GCD=1%ISR=10B%TI=Z%CI=Z%II=I%TS=A)OPS(O
   :

知らないんで、下記のデータと共に、投稿してよとお願いされたよ。そんじゃ、推測してみてくれって指示。

debian:~$ sudo nmap -O --osscan-guess localhost
  :
Aggressive OS guesses: Linux 2.6.32 (96%), Linux 3.8 - 4.14 (95%), Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), Linux 3.7 - 3.11 (93%), Linux 3.10 (93%), Linux 3.8 - 3.9 (93%), Linux 3.7 - 3.10 (93%), ASUS RT-N56U WAP (Linux 3.4) (93%)

確率論的な結果を返してきましたなあ。ネットワークカメラも、最近はリナの範疇なのか。NetBSDは、こういう分野得意だったのに、お株を奪われつつあるのね。

libpcap

libpcapを使っているアプリとして真っ先に思い付くのはtcpdumpそれに、上でやったnmapとかもそうだ。libpcapはしっかりと組み込まれてしまっていて、どんな風に動いているか知るのは、容易ではない。みんなこう思っているみたいで、偉大な先人達は、libpcapを裸で使おうと努力なされている。

プログラミング with libpcap

libpcapでパケットキャプチャをやってみる

パケットキャプチャツールのつくり方【C言語】 〜IPヘッダー解析〜

リアルタイム圧縮によるパケットキャプチャの高速化

これらが、その例だ。コピペして動かしてgdbしてみるって簡単コースだな。OpenBSDなら、libpcapに分け入って行くのも自然だし。。。こういう事は誰もやってないみたい。

車のエンジンを解体屋から買ってきて、自分仕様の車を作ってみるって事だ。でも、エンジンがどう動いているかまでは踏み込んでいないって事だ。まあ、エンジン(libpcap)は、部品として扱えば良い訳で、深入りしても意味ないよって所だろう。でも、あえてそこに踏み込むもの一興かと。そんな訳で、libpcapを解剖台に載せるべく、頑張ってみる。

http.server

で、tcpdumpの雛型なんだけど、パケット発生をさせないと、どうにもならない。今まではsshした時のパケットを観察した。それにも秋田ので、今流行りのWebでやってみるか。

ええ、どこでもpythonで、どこでもWebサーバーです。オイラーのpythonの使い道と言ったら、これぐらいしかありません(きっぱり)。

alias server='python3 -m http.server 8080'

早速起動して、w3mからアクセス。

sakae@pen:/tmp$ server
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
aa.bb.cc.129 - - [15/Jun/2021 07:27:17] "GET / HTTP/1.0" 200 -

それを、キャプチャ出来るか、本物で確認。

sakae@pen:/tmp$ sudo tcpdump port 8080
[sudo] password for sakae:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes

あろう事か、tcpdumpは無反応。でも、serverは、リクエストをちゃんと受け付けているし、w3mもちゃんと表示した。変なの、理解に苦しむぞ。気を取り直して、loなデバイスを監視。

sakae@pen:/tmp$ sudo tcpdump -i lo port 8080
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
  :
07:58:02.771377 IP aa.bb.cc.129.36762 > aa.bb.cc.129.http-alt: Flags [.],
ack 1, win 512, options [nop,nop,TS val 1440373251 ecr 1440373251], length 0
07:58:02.772029 IP aa.bb.cc.129.36762 > aa.bb.cc.129.http-alt: Flags [P.], seq 1:289, ack 1, win 512, options [nop,nop,TS val 1440373251 ecr 1440373251],
length 288: HTTP: GET / HTTP/1.0
07:58:02.772067 IP aa.bb.cc.129.http-alt > aa.bb.cc.129.36762: Flags [.],
ack 289, win 510, options [nop,nop,TS val 1440373251 ecr 1440373251], length 0
07:58:02.773226 IP aa.bb.cc.129.http-alt > aa.bb.cc.129.36762: Flags [P.], seq 1:156, ack 289, win 512, options [nop,nop,TS val 1440373253 ecr 1440373251], length 155: HTTP: HTTP/1.0 200 OK

127.0.0.1なIPにも、グローバルなIPパケットが流れるのか? ちょいとオイラーのの理解の範疇を越えているな。まあ、兎も角、今後はlocalhostでアクセスする事にしよう。

原型なtcpdump

今度は、コードを寄せ集めて、loなデバイスでport 8080をキャプチャするもどきを作ってみた(先人に感謝です)。設計図は、巻末に掲載。

sakae@pen:/tmp$ sudo ./a.out
device = lo
packet size = 94
 00 00 00 00 00 00 00 00 00 00 00 00 86 dd 60 00| ..............`.
  :
packet size = 348
 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00| ..............E.
 01 4e 1a 8f 40 00 40 06 21 19 7f 00 00 01 7f 00| .N..@.@.!.......
 00 01 80 76 1f 90 28 4d f3 1b eb cf 6d 7a 80 18| ...v..(M....mz..
 02 00 ff 42 00 00 01 01 08 0a 34 fb ba b2 34 fb| ...B......4...4.
 ba b2 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 30| ..GET / HTTP/1.0
 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 77 33| ..User-Agent: w3
 6d 2f 30 2e 35 2e 33 2b 67 69 74 32 30 31 39 30| m/0.5.3+git20190
 31 30 35 0d 0a 41 63 63 65 70 74 3a 20 74 65 78| 105..Accept: tex
  :
packet size = 221
 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00| ..............E.
 00 cf 6b 77 40 00 40 06 d0 af 7f 00 00 01 7f 00| ..kw@.@.........
 00 01 1f 90 80 76 eb cf 6d 7a 28 4d f4 35 80 18| .....v..mz(M.5..
 02 00 fe c3 00 00 01 01 08 0a 34 fb ba b3 34 fb| ..........4...4.
 ba b2 48 54 54 50 2f 31 2e 30 20 32 30 30 20 4f| ..HTTP/1.0 200 O
 4b 0d 0a 53 65 72 76 65 72 3a 20 53 69 6d 70 6c| K..Server: Simpl
 65 48 54 54 50 2f 30 2e 36 20 50 79 74 68 6f 6e| eHTTP/0.6 Python
 2f 33 2e 37 2e 33 0d 0a 44 61 74 65 3a 20 4d 6f| /3.7.3..Date: Mo
  :
packet size = 1181
 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00| ..............E.
 04 8f 6b 78 40 00 40 06 cc ee 7f 00 00 01 7f 00| ..kx@.@.........
 00 01 1f 90 80 76 eb cf 6e 15 28 4d f4 35 80 18| .....v..n.(M.5..
 02 00 02 84 00 00 01 01 08 0a 34 fb ba b4 34 fb| ..........4...4.
 ba b3 3c 21 44 4f 43 54 59 50 45 20 48 54 4d 4c| ..<!DOCTYPE HTML
 20 50 55 42 4c 49 43 20 22 2d 2f 2f 57 33 43 2f|  PUBLIC "-//W3C/
 2f 44 54 44 20 48 54 4d 4c 20 34 2e 30 31 2f 2f| /DTD HTML 4.01//
 45 4e 22 20 22 68 74 74 70 3a 2f 2f 77 77 77 2e| EN" "http://www.
 77 33 2e 6f 72 67 2f 54 52 2f 68 74 6d 6c 34 2f| w3.org/TR/html4/
 73 74 72 69 63 74 2e 64 74 64 22 3e 0a 3c 68 74| strict.dtd">.<ht
 6d 6c 3e 0a 3c 68 65 61 64 3e 0a 3c 6d 65 74 61| ml>.<head>.<meta
  :

リクエストが出て、返事が有って、やがてコンテンツが流れてきた。以上構築終了。

at OpenBSD

Debianではpcapの中へ潜っていけないので(自前で何とかするのめんどい)、OpenBSDでやってみる。loなデバイスなんてOpenBSDには無いので、忘れずにlo0に変更。

vbox$ cc -g mypc.c -lpcap
vbox$ doas ./a.out
doas (sakae@vbox.local.jp) password:
device = lo0
packet size = 44
Segmentation fault (core dumped)

なんと、まあ、行くてを阻むのはセグフォ也。何でさ? こういう予期せぬトラップが仕掛けられているんだな。

vbox# gdb -q a.out a.out.core
Reading symbols from a.out...done.
[New process 263199]

warning: Unexpected size of section `.reg2/263199' in core file.
#0  0x1860688f in dump (data_buffer=0x0, length=44) at mypc.c:10
10              byte = data_buffer[i];

0番地から読みだそうとしたら、怒られるわな。

u_char *
pcap_next(pcap_t *p, struct pcap_pkthdr *h);

manにはこう定義されてた。この関数からの返値をそのまま使うなって事かな。パケットが到着しなくてタイムアウトした場合も、この関数が終了するんだろうから、その場合は無効って判断を、ユーザーの責任でしろ、だろうね。

vbox# gdb -q a.out
Reading symbols from a.out...done.
(gdb) b pcap_next
Function "pcap_next" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (pcap_next) pending.
(gdb) r
Starting program: /tmp/a.out
device = lo0

Breakpoint 1, pcap_next (p=0x5090ae00, h=0xcf7c40e8)
    at /usr/src/lib/libpcap/pcap.c:108
108             s.hdr = h;
(gdb) l
103     const u_char *
104     pcap_next(pcap_t *p, struct pcap_pkthdr *h)
105     {
106             struct singleton s;
107
108             s.hdr = h;
109             if (pcap_dispatch(p, 1, pcap_oneshot, (u_char*)&s) <= 0)
110                     return (0);
111             return (s.pkt);
112     }

if文を満たしてしまっているんだな。ユーザープログラムで対処するか。元のfor文の所を下記のように改造。

i = 10;
while (i>0) {
    packet = pcap_next(pcap_handle, &header);
    if (packet == NULL)
           continue;
    printf("packet size = %d\n", header.len);
    dump(packet, header.len);
    i--;
}

10パケで十分か? 接続時のネゴシエーションの3パケ、それからgetが来て、okが来てって具合になるんで、まあ、頭の方は取れるだろう。

packet size = 255
 00 00 00 02 45 00 00 fb 68 0f 40 00 40 06 d3 eb| ....E...h.@.@...
 7f 00 00 01 7f 00 00 01 19 c2 1f 90 70 20 fe a6| ............p ..
 d0 68 ce 54 80 18 01 00 7a 54 00 00 01 01 08 0a| .h.T....zT......
 73 2a 2d c9 14 7b a9 81 47 45 54 20 2f 20 48 54| s*-..{..GET / HT
 54 50 2f 31 2e 30 0d 0a 55 73 65 72 2d 41 67 65| TP/1.0..User-Age
 6e 74 3a 20 77 33 6d 2f 30 2e 35 2e 33 2b 67 69| nt: w3m/0.5.3+gi
  :
packet size = 210
 00 00 00 02 45 00 00 ce d4 92 40 00 40 06 67 95| ....E.....@.@.g.
 7f 00 00 01 7f 00 00 01 1f 90 19 c2 d0 68 ce 54| .............h.T
 70 20 ff 6d 80 18 01 00 76 ac 00 00 01 01 08 0a| p .m....v.......
 14 7b a9 81 73 2a 2d c9 48 54 54 50 2f 31 2e 30| .{..s*-.HTTP/1.0
 20 32 30 30 20 4f 4b 0d 0a 53 65 72 76 65 72 3a|  200 OK..Server:
 20 53 69 6d 70 6c 65 48 54 54 50 2f 30 2e 36 20|  SimpleHTTP/0.6
 50 79 74 68 6f 6e 2f 33 2e 38 2e 38 0d 0a 44 61| Python/3.8.8..Da
  :

ちゃんとキャプチャ出来てる。物はついでなんで、dump関数を既存のhexdumpに任せてみるかな。 ソースの冒頭に #include <unistd.h> を追加。元のdump呼び出しを write(1, packet, header.len); に変更。システムコールですよ。

vbox# ./a.out | hexdump -C
00000000  00 00 00 02 45 00 00 40  c7 aa 40 00 40 06 75 0b  |....E..@..@.@.u.|
00000010  7f 00 00 01 7f 00 00 01  10 61 1f 90 f4 2b 70 df  |.........a...+p.|
00000020  00 00 00 00 b0 02 40 00  d3 19 00 00 02 04 7f d8  |......@.........|
00000030  01 01 04 02 01 03 03 06  01 01 08 0a 59 a8 bc 15  |............Y...|
00000040  00 00 00 00 00 00 00 02  45 00 00 40 68 f3 40 00  |........E..@h.@.|
00000050  40 06 d3 c2 7f 00 00 01  7f 00 00 01 1f 90 10 61  |@..............a|
00000060  1d 56 f4 77 f4 2b 70 e0  b0 12 40 00 6e bd 00 00  |.V.w.+p...@.n...|
  :

もろにバイナリーデータが出てきて、それをhexdumpに喰わせてみた。これが、unix流のプログラミングだよ。まて、そんなバイナリーデータは始末に困るぞ。そんな事もあろうかと、erlangが控えているのさ。バイナリーも得意だそうですから。

それにしても、ひょひょいとコピペしたコードが何故リナでは普通に動いてしまったのだろう? そうか、リナはスクリプト・キディに優しい仕様になってるんだな。正統派ならBSDを使えって事だ(多分)。

at linux

やっぱり気になるよう。OpenBSDでは、改造しなければ動かなかったやつが、リナでは普通に動いちゃうのが。ひょっとしてlibpcapの仕様が違うのではなかろうか? スクリプト・キディ用になってる疑惑。疑惑を正すのが刑事の務めです。

まずは、自前でlibpcapをインストールだな。取ってきて、入れた。configure –prefix=/home/sakae/MINE って指定。コンパイルの報告を見てたら、-g -O2 だったから佳しとしよう。

次は、これをリンクだな。

debian:tmp$ ls -l /home/sakae/MINE/lib
-rw-r--r--  1 sakae sakae 1447800 Jun 18 07:37 libpcap.a 
lrwxrwxrwx  1 sakae sakae      12 Jun 18 07:37 libpcap.so -> libpcap.so.1*
lrwxrwxrwx  1 sakae sakae      17 Jun 18 07:37 libpcap.so.1 -> libpcap.so.1.10.1*
-rwxr-xr-x  1 sakae sakae  987080 Jun 18 07:37 libpcap.so.1.10.1*
drwxr-xr-x  2 sakae sakae    4096 Jun 18 07:37 pkgconfig/
debian:tmp$ cc -g mypc.c -L/home/sakae/MINE/lib -lpcap
debian:tmp$ ldd a.out
        linux-gate.so.1 (0xb7fbe000)
        libpcap.so.1 => not found
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7da8000)
        /lib/ld-linux.so.2 (0xb7fc0000)

新たなlibpcapをリンクするように指示。んが、見つからんぞ。

debian:tmp$ export LD_LIBRARY_PATH=/home/sakae/MINE/lib
debian:tmp$ ldd a.out
        linux-gate.so.1 (0xb7f59000)
        libpcap.so.1 => /home/sakae/MINE/lib/libpcap.so.1 (0xb7f03000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7cf7000)
        libibverbs.so.1 => /usr/lib/i386-linux-gnu/libibverbs.so.1 (0xb7cdb000)
        libdbus-1.so.3 => /lib/i386-linux-gnu/libdbus-1.so.3 (0xb7c7c000)
        /lib/ld-linux.so.2 (0xb7f5b000)
        libnl-route-3.so.200 => /usr/lib/i386-linux-gnu/libnl-route-3.so.200 (0xb7bfa000)
        libnl-3.so.200 => /lib/i386-linux-gnu/libnl-3.so.200 (0xb7bd6000)
        libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb7bb5000)
        libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb7baf000)
        libsystemd.so.0 => /lib/i386-linux-gnu/libsystemd.so.0 (0xb7b04000)
        librt.so.1 => /lib/i386-linux-gnu/librt.so.1 (0xb7af9000)
        liblzma.so.5 => /lib/i386-linux-gnu/liblzma.so.5 (0xb7acb000)
        liblz4.so.1 => /usr/lib/i386-linux-gnu/liblz4.so.1 (0xb7aab000)
        libgcrypt.so.20 => /lib/i386-linux-gnu/libgcrypt.so.20 (0xb79c8000)
        libgpg-error.so.0 => /lib/i386-linux-gnu/libgpg-error.so.0 (0xb79a3000)

昔のSolaris時代を思い出して、伝家の宝刀をを抜く。そしたら、余計なものまで見えてきた。 けど、気にしない。気にしなくちゃいけないのは、このアプリはrootでの実行になるんで、rootにも伝家の宝刀を抜いて貰うと言う、危ない橋を渡ってもらうって事。

root@debian:/tmp# gdb -q a.out
Reading symbols from a.out...
(gdb) b pcap_next
Breakpoint 1 at 0x10e0
(gdb) r
Starting program: /tmp/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
device = lo

Breakpoint 1, pcap_next (p=0x4059a0, h=0xbffff694) at ./pcap.c:593
593             s.hdr = h;
(gdb) l
588     pcap_next(pcap_t *p, struct pcap_pkthdr *h)
589     {
590             struct oneshot_userdata s;
591             const u_char *pkt;
592
593             s.hdr = h;
594             s.pkt = &pkt;
595             s.pd = p;
596             if (pcap_dispatch(p, 1, p->oneshot_callback, (u_char *)&s) <= 0)
597                     return (0);
(gdb) n
594             s.pkt = &pkt;
(gdb)
595             s.pd = p;
(gdb)
596             if (pcap_dispatch(p, 1, p->oneshot_callback, (u_char *)&s) <= 0)
(gdb)

ざっと見、OpenBSDのそれと同一のコードっぽい。が、決定的に違うのは、パケットが到着するまで。 pcap_dispatch で、ブロック(待たされて)しまうと言う事。

(gdb)
main () at mypc.c:67
67              printf("packet size = %d\n", header.len);
(gdb) n
packet size = 94
68              dump(packet, header.len);
(gdb) s
dump (data_buffer=0x405c30 "", length=94) at mypc.c:9
9           for (i = 0; i < length; i++) {

w3m http://localhost:8080 で、パケットを発生させると、ブロックが解除されて、メインに復帰してくる。その時は、バッファーに有効なパケットが充填されてる。 だから、メイン側で気を回す必要無し。

じゃ、パケットが到着するまでのタイムアウトうんぬんはどうなった。それは無視ですよ。便利なら、それでいいじゃない、何か文句ある? 世の中の仕組みを捻じ曲げてる、矛盾した仕様は、混乱の元。だからリナ系ときたら、、、、、、これ以上は言うまえ。

なお、FreeBSDはちゃんと、矛盾なく実装されてたよ。

(gdb) bt
#0  0xb7fd4d31 in __kernel_vsyscall ()
#1  0xb7e66b37 in poll () from /lib/i386-linux-gnu/libc.so.6
#2  0xb7f87caf in pcap_wait_for_frames_mmap (handle=handle@entry=0x4059a0)
    at ./pcap-linux.c:3493
#3  0xb7f88827 in pcap_read_linux_mmap_v3 (handle=<optimized out>,
    max_packets=<optimized out>, callback=<optimized out>,
    user=<optimized out>) at ./pcap-linux.c:4085
#4  0xb7f90778 in pcap_next (p=0x4059a0, h=0xbffff694) at ./pcap.c:596
#5  0x00401544 in main () at mypc.c:66

リナは、特別扱いされてるな。コードをちょい見したら、kernelの特殊性を吸収するため、リングバッファを導入してるよーーなんて事が、解説されてた。深入りはよしときましょ。

sniffex.c

tcpdumpな本家が、libpcapを使った見本を提供してる。これは、有無を言わさず見て桶。 sniffex.c

vbox$ doas ./a.out lo0
doas (sakae@vbox.local.jp) password:
sniffex - Sniffer example using libpcap
Copyright (c) 2005 The Tcpdump Group
THERE IS ABSOLUTELY NO WARRANTY FOR THIS PROGRAM.

Device: lo0
Number of packets: 10
Filter expression: tcp port 8080
lo0 is not an Ethernet

loなデバイスはEthernetとは認められないのか。ならば、

root@debian:/tmp# ./a.out
  :
Device: wlp5s0
Number of packets: 10
Filter expression: tcp port 8080
 :
Packet number 4:
       From: aa.bb.cc.5
         To: aa.bb.cc.6
   Protocol: TCP
   Src port: 59556
   Dst port: 8080
   Payload (194 bytes):
00000   47 45 54 20 2f 20 48 54  54 50 2f 31 2e 30 0d 0a    GET / HTTP/1.0..
00016   55 73 65 72 2d 41 67 65  6e 74 3a 20 77 33 6d 2f    User-Agent: w3m/
  :

code

これがdebianで動いた、tcpdumpの原型。コンパイルは、

cc mypc.c -lpcap

mypc.c

#include <stdlib.h>
#include<pcap.h>

void
dump(const unsigned char *data_buffer, const unsigned int length)
{
    unsigned char   byte;
    unsigned int    i, j;
    for (i = 0; i < length; i++) {
        byte = data_buffer[i];
        printf(" %02x", data_buffer[i]);
        if ((i % 16 == 15) || (i == length - 1)) {
            for (j = 0; j < 15 - (i % 16); j++) {
                printf("   ");
            }
            printf("| ");
            for (j = (i - (i % 16)); j <= i; j++) {
                byte = data_buffer[j];
                if ((byte > 31) && (byte < 127))
                    printf("%c", byte);
                else
                    printf(".");
            }
            printf("\n");
        }
    }
}

int
main()
{
    struct pcap_pkthdr header;
    const u_char   *packet;
    char            errbuf[PCAP_ERRBUF_SIZE];
    pcap_t         *pcap_handle;
    int             i;
    char            device[] = "lo";
    struct bpf_program fp;
    char            filter_exp[] = "port 8080";
    bpf_u_int32     mask;
    bpf_u_int32     net;

    printf("device = %s\n", device);

    if (pcap_lookupnet(device, &net, &mask, errbuf) == -1) {
        fprintf(stderr, "dev: %s netmask: %s\n", device, errbuf);
        net = 0;
        mask = 0;
    }
    pcap_handle = pcap_open_live(device, 4096, 1, 1000, errbuf);
    if (pcap_handle == NULL) {
        printf("error\n");
        return 1;
    }
    if (pcap_compile(pcap_handle, &fp, filter_exp, 0, net) == -1) {
        fprintf(stderr, "%s can not analyze: %s\n",
                filter_exp, pcap_geterr(pcap_handle));
        exit(1);
    }
    if (pcap_setfilter(pcap_handle, &fp) == -1) {
        fprintf(stderr, "%s can not inst: %s\n",
                filter_exp, pcap_geterr(pcap_handle));
        exit(1);
    }
    for (int i = 0; i < 20; i++) {
        packet = pcap_next(pcap_handle, &header);
        printf("packet size = %d\n", header.len);
        dump(packet, header.len);
    }

    pcap_close(pcap_handle);

    return 0;
}

This year's Index

Home