猫型エンジニアのブログ

プログラム/ネットワーク系の技術関連をまとめたページです 

RAWソケットプログラミング(イーサフレーム基本編)

ソケットプログラミング

 通常ソケットプログラミングにおいて、送信元MACアドレスや送信元IPアドレスの設定などはカーネルが処理を行います。しかしソケットプログラミングでは文字通り無制約で任意のパケットを作成することが可能です。

 この特徴を用いてL2からL7までパケットを1バイトづつ作成してみます。普段意識しないプロトコルの詳細まで設定できるので大変勉強になります!使い方次第ではMACアドレスIPアドレスの偽装となるため、注意してください。

イーサフレーム

 イーサフレームのフォーマットとして4種類ほどあるそうですが、今回はもっとも普及しているイーサネットver2(DXI規格)を扱います。構造としては以下のようになります。

  
                                    1
            0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  |       DESTINATION_ADDR        |
                  |          (6 bytes)            |
                  |                               |
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  |         SOURCE_ADDR           |
                  |          (6 bytes)            |
                  |                               |
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  |    ETHER_TYPE  (2 bytes)      |
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  ~                               ~
                  ~           payload             ~
                  ~                               ~
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  |           CHECKSUM            |
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 送信元MACアドレス,送信元MACアドレス、イーサタイプ、ペイロードからなります。

イーサタイプ

 代表的なイーサタイプ番号としては以下のようになります。ここでイーサフレームの上位のペイロードプロトコルを指定します。

RAWソケットでのプログラミング例

 Pythonでのプログラミングは非常に簡単です。以下のようにrawSocket.sendにバイト列を直接書き込みます。非常に直観的でわかりやすいです。

 ここでは送信元MACアドレスを11:11:11:11:11:11、宛先MACアドレスを22:22:22:22:22:22というイーサフレームのみのパケットを作成してみました。

#!/usr/bin/env python
from socket import *
rawSocket = socket(AF_PACKET, SOCK_RAW)
rawSocket.bind(("eth1", 0))

class ether_flame:
     def __init__(self,src_mac_addr, dst_mac_addr, ethertype):
           self.src_mac_addr = src_mac_addr
           self.dst_mac_addr = dst_mac_addr
           self.ethertype = ethertype

     def string(self):
           return self.dst_mac_addr + self.src_mac_addr + self.ethertype

SRC_MAC_ADDR = "\x11\x11\x11\x11\x11\x11"
DST_MAC_ADDR = "\x22\x22\x22\x22\x22\x22"
ETHER_TYPE = "\x08\x00"

flame = ether_flame(SRC_MAC_ADDR, DST_MAC_ADDR, ETHER_TYPE)

rawSocket.send(flame.string())

実行結果

 tcpdumpコマンドでキャプチャすると、確かに設定した通りになっています。

13:47:59.002437 11:11:11:11:11:11 (oui Unknown) > 22:22:22:22:22:22 (oui Unknown), ethertype IPv4 (0x0800), length 14: IP0 [|ip]