.. 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`