Raw socketでVLANタグ付きフレームを受信する

この記事はビール飲みながら書きました。アルコール入ってるので間違ってたり変なこと言ってたらごめんなさい。。おびーるおいちい!!!!

Qiitaデビュー兼ねてこの記事の微修正入れたりしつつ投稿しました。
技術的な記事はもうそっちに寄せようかなー(このブログの存在意義がなくなる)

Linux Raw Socketを使って遊んでいたのですが、VLANタグ付きフレームを受信してもraw socketではタグが消されてしまうことがわかった。
結論から言うと結構弄めんどくさかったのでそのメモ。

まずはこんな感じのコードを書きました。
単純にraw socketでフレームを受信するだけ。
(hexdump()のコードはここからコピペさせて頂きました)

ビルド

このコードでVLANタグ付きフレームを受信した場合(MACアドレスは念のため書き換えてます)

一方tcpdumpで同じフレームを見た場合

raw socketでは802.1Qタグが綺麗に取り除かれていますね。。
どうしてもタグが必要だったので色々調べたのですが、日本語情報もなく、英語情報を見ても「libpcap使え」と出てきます。
「じゃあそのlibpcapはどう実装されてるのさ!!」というツッコミは置いといて。。
raw socketで単純にread()しても、VLANタグは早い段階で取り除かれ、ユーザーランドに上がってくる頃にはタグなしフレームになってるそうです。

ところでソフトウェアスイッチに、Open vSwitchLagopus switchというOSSがあります。
どちらもraw socket実装もあるそうなので、Lagopusの実装を見てみましょう。
「lagopus/src/dataplane/mgr/sock_io.c」がsocketとかの処理っぽいです。

まずは、socket作成部分。

ただのソケットの作成なので、僕が書いたコードとやってることはそんなに変わらない。
(Lagopusで扱うために色々書かれてますがその部分を無視すると)
その中で、
「if (setsockopt(fd, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on)) != 0)」
というのが何かしてそうです。
調べてみると、ソケットオプションを設定する機能らしい。
Man page of SOCKET
Man page of PACKET
Man page of CMSG
Man page of RECV

SOL_PACKETで受信したパケットすべてに対して適用、PACKET_AUXDATAをセットすることで補助データを受信できるようです。
recvmsg()でパケットデータとこの補助データを受信して、補助データについてはcmsgを使って読むそうです。

とはいえ、実際のコードがないと使い方もよくわからないので、続いてLagopusの受信部分を読んでみます。

recvmsgでデータを受信したあと、何かしらやってますね。
これを参考に802.1Qタグ付きフレームを受信するコード書いてみました。

何をやってるかはコード中のコメント参照。
要約すると、recvmsg()で受信したとき、802.1Qタグ情報は補助データとして受信フレームとは別で与えられるので、
補助データからタグに入れるべき内容を読んできて、フレームの適切な位置に挿入してるだけです。

結構めんどくさい。。。

ビルドしてコードを動かして最初と同じようにタグ付きフレームを受信してみましょう。

できました。
お疲れ様でした。

(今回は単一インターフェースでの受信だけだったので面白みもないループで回しましたが、
複数のinterfaceを扱うときはpoll()とかepoll()とか使ってノンブロッキングにしたほうが良いです。)

ググっても日本語情報どころか、他言語でも有力な情報がなく、結局OSSのコードリーディングをすることに。。
(LagopusがRaw Socket対応してるという話は知ってたので、今回参考にできました。)
OSSの偉大さを実感できました。。

余談ですが、職業がネットワーク屋さんということで普段はルータとかスイッチばっかり触ってます。
昔からプログラミングは趣味でやっているのですが、最近ネットワーク知識も加わったので、Raw Socketを使ったL2SWみたいなのを作ってます。
salacia-forwarder
(pull requestとかはあまり受け付ける気はないです、ごめんなさい)
最初はCで実装してたのですが、だんだんしんどくなってC++に。(C++初めてなので勉強しながら書いてます)
パケット送受信はLinuxシステムコールに依存するので仕方ないですが、
極力標準ライブラリに頼らず(iostreamとかcstdintくらいは使ってる)、スマートポインタも使わないつもりで作っていこうと思ってます。
(趣味です。メモリ管理とかも含めて学びたいので、生産性とかよりお勉強優先です。)

今回、L2SWを実装するにあたり、Raw socketのVLANの扱いでハマったので、この記事を書きました。
たったこれだけの内容で、ぼくのお休み2日が潰れました。
参考になれば幸いです。。

コメントを残す

Your email address will not be published / Required fields are marked *