.. PyDDG documentation master file, created by
sphinx-quickstart on Tue Aug 23 08:21:51 2011.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
=================================
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ソースコード
-----------------------------
.. literalinclude:: Ping_test.py
:language: ruby
:linenos:
.. warning::
※Python3で実行してください。
.. warning::
※Linux(Ubuntu)で動作確認してます。実行する際に sudo python が必要でした。なぜか。
解説
-----------------------------
そんな感じで、サクッと実装してみた。
1. まず、今回のPINGには Socketを使います。
http://docs.python.org/py3k/library/socket.html?highlight=socket#socket
2. socketをSOCK_RAWとIPPROTO_ICMPの設定で開く
.. code-block:: python
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
※なぜここで、SOCK_RAWとIPPROTO_ICMPを選択するかは、C言語のSocket関数を調べると凄く分かるのでお勧めw
3.
PING送信先のIPを設定し、ICMヘッダへ代入する値(ここでは決め打ち)を代入する。
.. code-block:: python
IPaddr = "192.168.0.1"
ICM_Type = (0x08)
ICM_Code = (0x00)
ICM_ID = (0x01)
ICM_Seq = (0x01)
4.ICMヘッダのリストを作成する。
.. code-block:: python
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.
エコを行うデータをリストの最後に追加する。
.. code-block:: python
ICM_DATA = b"abcdefghijklmnopqrstuvwxyz"
ICM_LIST.extend(ICM_DATA)
6.ヘッダ全体のチェックサムを行う。
.. code-block:: python
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を設定する。
.. code-block:: python
print(bytes(ICM_LIST))
sock.sendto(bytes(ICM_LIST), (IPaddr, 0))
8.帰ってきたデータを受け取り、一つずつ表示する。
.. code-block:: python
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さんです!
.. raw:: html

参考文献
-----------------------------
http://net-juku.org/tcpip/tcpip85.html
http://www.itbook.info/study/ping6.html
-----------------------------
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`