Python3で Pingを実装してみる。¶
初めに¶
こんばんは、突然Pythonの記事を書き始めようと思い、
たまたま2011 アドベントカレンダーを発見し無謀にも参加表明。
実はPython3を触るのは今回は始めて。かなり苦戦しました。
苦戦した点は後にまとめるとして、まず何をつくったのか。
Warning
※今回のソースは、Python3限定です! Python2ですと所々エラーが出るので、各自悩んで直してくださいw
Pingとは、何か¶
Pingとは、サーバーが生きているかの確認や、今ネットワークと繋がっているかを確認するためによく使うコマンド。
その実態は、ICMPと言うプロトコルを使い、PING通信を行っている。
ICMPは、IPなど処理を行うネットワーク層に存在し、よく聞くHTTPやFTPなどのアプリケーション層よりもずっと下の階層に存在する。
階層が下にあると言う事は、本来は知らなくよい話ではあるが、ここは一つマニアックに話を進めようと思う。
なお、この先、ビットの話やバイトの話が飛び交うので、今まで文字列しか触ってない人には厳しい話かもしれない。
ICMPプロトコルの作り方¶
ICMPは、EtherNetヘッダ と IPヘッダ と ICMPヘッダ の三種類のヘッダで構成されており、
EtherNetヘッダ と IPヘッダ はSocket関数が自動的に処理を行ってくれるため、
今回はICMPヘッダの作成を行いたいと思う。
細かなICMPのプロトコルは参照文献に詳しく乗っているが、ここでは簡単に説明する。
ICMPには以下の値を使い通信を行う。
1、タイプ(8ビット):ICMPメッセージのタイプを決めます。0x08ならPING要求 0x00ならPING応答
2、コード(8ビット):1のタイプにより変化。得に到着不能時の処理に使う
3、チェックサム(16ビット):メッセージ全体のチェックサムを行う
4、識別子(16ビット): よくわからないが、得に重要じゃない気がする。
5、シーケンスナンバー(16ビット): よくわからないが、得に(ry
6、データ(任意):エコーを行うデータ
と、こんな感じのデータを順番に送信していけばOKなはず。
Pythonソースコード¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #!/usr/bin/env python
#-*- coding:utf-8 -*-
import socket
#=============================================#
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
#=============================================#
IPaddr = "192.168.0.1"
ICM_Type = (0x08)
ICM_Code = (0x00)
ICM_ID = (0x01)
ICM_Seq = (0x01)
#=============================================#
ICM_LIST = [0]*6
ICM_LIST[0] = ICM_Type
ICM_LIST[1] = ICM_Code
ICM_LIST[2] = 0#ICM_ChekeSum1
ICM_LIST[3] = 0#ICM_ChekeSum2
ICM_LIST[4] = ICM_ID
ICM_LIST[5] = ICM_Seq
#=============================================#
ICM_DATA = b"abcdefghijklmnopqrstuvwxyz"
ICM_LIST.extend(ICM_DATA)
#=============================================#
csum = 0
for i in range(int(len(ICM_LIST)/2)):
csum += (ICM_LIST[i*2]<<8) | (ICM_LIST[i*2+1])
csum = (csum&0xffff) + (csum>>16)
csum = 0xffff-(csum)
print("ChekeSum:"+hex(csum))
#=============================================#
ICM_LIST[2] = (csum&0xFF00)>>8 #ICM_ChekeSum2
ICM_LIST[3] = csum&0x00FF #ICM_ChekeSum1
#=============================================#
print(bytes(ICM_LIST))
sock.sendto(bytes(ICM_LIST), (IPaddr, 0))
#=============================================#
data = sock.recv(255)
for i in range(20,len(data)):
print ("%r" % hex(data[i])),
#=============================================#
|
Warning
※Python3で実行してください。
Warning
※Linux(Ubuntu)で動作確認してます。実行する際に sudo python が必要でした。なぜか。
解説¶
そんな感じで、サクッと実装してみた。
- まず、今回のPINGには Socketを使います。
http://docs.python.org/py3k/library/socket.html?highlight=socket#socket
- socketをSOCK_RAWとIPPROTO_ICMPの設定で開く
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
※なぜここで、SOCK_RAWとIPPROTO_ICMPを選択するかは、C言語のSocket関数を調べると凄く分かるのでお勧めw
3. PING送信先のIPを設定し、ICMヘッダへ代入する値(ここでは決め打ち)を代入する。
IPaddr = "192.168.0.1"
ICM_Type = (0x08)
ICM_Code = (0x00)
ICM_ID = (0x01)
ICM_Seq = (0x01)
4.ICMヘッダのリストを作成する。
ICM_LIST = [0]*6
ICM_LIST[0] = ICM_Type
ICM_LIST[1] = ICM_Code
ICM_LIST[2] = 0#ICM_ChekeSum1
ICM_LIST[3] = 0#ICM_ChekeSum2
ICM_LIST[4] = ICM_ID
ICM_LIST[5] = ICM_Seq
5. エコを行うデータをリストの最後に追加する。
ICM_DATA = b"abcdefghijklmnopqrstuvwxyz"
ICM_LIST.extend(ICM_DATA)
6.ヘッダ全体のチェックサムを行う。
csum = 0
for i in range(int(len(ICM_LIST)/2)):
csum += (ICM_LIST[i*2]<<8) | (ICM_LIST[i*2+1])
csum = (csum&0xffff) + (csum>>16)
csum = 0xffff-(csum)
print("ChekeSum:"+hex(csum))
ICM_LIST[2] = (csum&0xFF00)>>8 #ICM_ChekeSum2
ICM_LIST[3] = csum&0x00FF #ICM_ChekeSum1
7.作ったリストの中身を表示し、Socketのsendtoで送信する。この際ポートは0を設定する。
print(bytes(ICM_LIST))
sock.sendto(bytes(ICM_LIST), (IPaddr, 0))
8.帰ってきたデータを受け取り、一つずつ表示する。
data = sock.recv(255)
for i in range(20,len(data)):
print ("%r" % hex(data[i]))
この際、受信したリスト20番より先にICMPのエコー内容が記載されている。 20番よりも前には受信先のIPなどが入っている。好きな人は解析してみるとよいかも。
終わりに¶
以上でPING送信ができます。
ものすごくザックリですみません。
ほんと、今回のPING実装は結構難しかった!
Python3のByteの扱い方がちょっと複雑で、しょっちゅう型が違う!って叱られました。
まぁ、なれてくると、エラーメッセージが何を怒っているか分かって来たのでよかったですが、
慣れるまで厳しそうですね。得に変換関数に関しては一度まとめたいと思います。
今回、こんな形でPython3アドベントカレンダーの13日めに参加出来てよかったです。
こんなPINGを送るだけの関数は得に需要が無いんじゃないかなと・・・
ネタバレをしてしまうと、得にPythonで実現しなくても、OSのコマンドを叩けばすむ話でw
そこをなぜ実装してしまったかは、まぁ勉強と思ってw
気持ち、もう少しこのPING関数を作り込めば、それなりな高性能PINGマシンが出来上がるんじゃないかなと思ったりもします。
と、言うことでw
Python3アドベントカレンダー13日目はギリギリ終了です!
次は14日目 @Surgoさんです!