その時々

その時々で違うんです。特に決まっていないんです。

pythonで簡単なpingを作ってみる

pythonで簡単なpingを作成しました。


RFC792にICMPの仕様が書いてあります。
RFCによるとpingはEcho MessageとEcho Reply Messageの2つを使用しているっぽいです。

このようなパケットの構成になっていて、タイプに8をセットすると
Echo Message、0をセットするとEcho Reply Messageになるようです。

Cのサンプルソースや色々インターネットに出回っていますが、
ここではシンプルに手で作成してみます。

まず、タイプは0x08か0x00です。
コードは使用していないようなので0x00です。
チェックサムRFCを読んだりしていても、いまいち理解できず、
ここを参考にしました。

http://d.hatena.ne.jp/kingkunikuni/20100116/1263631169

ICMPパケット全体を16bitずつで積算して、桁上りが発生したら、
溢れた分を再度足す。最後にbitを反転さして出来上がり。
ちなみにチェックサムの計算中はチェックサムの部分は0として計算する。
具体例は、後ほどパケットを作成するときに書きます。

識別子とシーケンス番号はパケットをEchoとEcho Replyを結び付けやすくするための番号みたいなもので、ゼロでもいいみたいです。

データですが、これまたなんでもよさそうな感じです。
通常のpingを打ってみると、
abcdefghijk...とか、asciiコードにすると61626364のようになっていたり
(これはwindowsからのping
ubuntuからだとasciiコードで10111213というようになっています。
pingのサイズによってこれがずらーっとつらなっているわけです。

これらを踏まえてping用のパケットを作ってみたいと思います。

0800 0000 0101 0102

まずは、Echo Messageを相手に送るため08
コードは00
チェックサムは最初は0000
にしておきます。
IDとSeqNoは両方とも1にして0101
データは0102とします。

それではチェックサムを計算してみます。
以下は16進数で計算しています。

0800 + 0000 + 0101 + 0102 = A03

NOT A03はF5FCです。


桁上りがある場合は、こんな感じになります。
0800 + 0000 + 0101 + AFAF + CDCD = 1867D

1溢れるので

867D + 1 = 867E

NOT 867E = 7981


さてこれでpingのパケットは完成です。

0800 F5FC 0101 0102

これだけIPヘッダにくっ付けて送ってやればいいのです。

sping.py

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
sock.sendto('\x08\x00\xf5\xfc\x01\x01\x01\x02', ('150.13.16.184', 0))

とりあえず送ってみます。



rootでターミナルをひとつ別に立ちあげて、パケットを拾ってみます。

# tcpdump -vvX proto ICMP

この状態で別のターミナルからrootで先程のpythonスクリプトを実行します。

# python sping.py

ちなみにpingはrootでないと打てません。
普通のpingコマンドが通常ユーザーで実行できるのはsetuid権限が設定されているからです。

tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
15:10:00.685187 IP (tos 0x0, ttl 128, id 21524, offset 0, flags [none], proto ICMP (1), length 28) hoge.c
o.jp > hoge.local: ICMP echo request, id 257, seq 258, length 8
        0x0000:  4500 001c 5414 0000 8001 992d 960d 10cd  E...T......-....
        0x0010:  960d 10b8 0800 f5fc 0101 0102 0000 0000  ................
        0x0020:  0000 0000 0000 0000 0000 0000 0000       ..............
15:10:00.685234 IP (tos 0x0, ttl 64, id 50147, offset 0, flags [none], proto ICMP (1), length 28) hoge.local > hoge.
co.jp: ICMP echo reply, id 257, seq 258, length 8
        0x0000:  4500 001c c3e3 0000 4001 695e 960d 10b8  E.......@.i^....
        0x0010:  960d 10cd 0000 fdfc 0101 0102            ............

0800 f5fc 0101 0102
というデータが届きました。
その後Echo Replyを返しています。

次のようにスクリプトを変更してEcho Replyも受信します。

sping.py

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
sock.sendto('\x08\x00\xf5\xfc\x01\x01\x01\x02', ('150.13.16.184', 0))

data = sock.recv(255)

print 'recv Data'

for i in data:
    print '%r' % hex(ord(i)),

動かしてみます。

# python sping.py
recv Data
'0x45' '0x0' '0x0' '0x1c' '0xc3' '0xe3' '0x0' '0x0' '0x40' '0x1' '0x69' '0x5e' '0x96' '0xd' '0x10' '0xb8' '0x96' '0xd' '
0x10' '0xcd' '0x0' '0x0' '0xfd' '0xfc' '0x1' '0x1' '0x1' '0x2'

こんな感じでpingが作れました。