HappyNote3966’s blog

備忘録、作業記録的なことを書きます。低レベル注意。ご指摘等ございましたらやんわりとお願いします(´;ω;`)

LinuxのRAWソケットからイーサネットフレームを取得してみる

パケットの中身を覗きたくなったので作ってみた。ネットワークの理解をするために今後も続けていきたい。

とりあえず作ってみる

これまでの知識やWiresharkでキャプチャした結果を頼りに作ってみる。

作ったリポジトリと該当するコードはこちら。

github.com

このコードは以下のサイトを参考にして作成している。RAWソケットの取得方法だけ知りたかったので、全部読んでいない。 (閲覧すると文字化けしていたが、ある意味最低限の情報を知るにはちょうどいいと思ってそのままコードだけ読んだ)

http://www.is.noda.tus.ac.jp/~t-matsu/NetworkProgramming/

重要な部分は以下に示すコード。

// in main()
soc = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
// in captureFrame()
len = read(fd,&buf[len],malloc_usable_size(buf));

ここでは、RAWソケットからパケットを取得するための初期設定と読み出しを行っている。 最初にsocket関数を使ってRAWソケットのファイルディスクリプタを取得する。 次に、そのファイルディスクリプタからデータを読み取る。

読み取られたデータは、以下の関数でMACアドレスとタイプを表示するのに使われる。

void printMACAddress(u_char *frame){
    int type;
    printf("[!] Dst : %02x,%02x,%02x,%02x,%02x,%02x\n",frame[0],frame[1],frame[2],frame[3],frame[4],frame[5]);
    printf("[!] Src : %02x,%02x,%02x,%02x,%02x,%02x\n",frame[6],frame[7],frame[8],frame[9],frame[10],frame[11]);
    type = data2value16(&frame[12]);
    printf("[!] Type : %04x\n",type);
    switch(type){
        case 0x0800:
            printf("[=>] IPv4\n");
            break;
        case 0x0806:
            printf("[=>] ARP\n");
            break;
    }
} 

イーサネットフレームは先頭6バイトが宛先MACアドレス、次の6バイトが送信元MACアドレス、次の2バイトがイーサネットフレーム内の中にあるデータの種別を表している。

|Destination MAC Address (6byte) | Source MAC Address (6byte) | Type (2byte) | Data |

Wiresharkを使って適当にキャプチャしたパケットのうち、ARPIPv4だけその種別を表示させるようにした。

先人から学ぶ

あまりコードが美しくないように感じるので、tcpdumpリポジトリを参考にしながら色々と改造してみる。

github.com

閲覧したのはtcpdumpリポジトリ内のethertype.hというファイル。

まずイーサネットフレームの長さを覚えていなかった。最初は65536バイトの領域を確保できるようにしていたが、実際には1500バイトの領域だけで事足りるようだった。 また、フレームタイプにIPv6(0x86dd)とRARP(0x8035)を追加した。他にもバナーを表示してみたり、関数名や表示そのものを見やすくしたりするなど、色々と変更を加えた。

変更後のコードはこちら。

github.com

感想

ネットワークのデータを分解するのは想像していたよりも手間がかかることがわかった。 また、コードを書いていると「汚いなこれ」と思うようになったり、「もうちょっと機能を追加したいな」とか考えるようにもなった。コードを書くのは色んな発見ができて大切だと思う。

おまけ

今回はVirtualBox上で動作するUbuntu 16.04のVM内で作業したが、パケットキャプチャをする際にWireshark側でインタフェースをanyにしていた。 そのせいで、イーサネットフレームを取得したくても、Linux cooked-mode caputureなる形式に変わるらしく、思い通りにいかなかった。ちゃんとインタフェースを指定しないとだめだと学んだ。

対処するのに参考にしたサイトはこれ。

d.hatena.ne.jp

ちゃんと形式を変換するためのものも用意されているらしい。知らなかった。