スマートフォン解析 top

TOP > タイガーチームセキュリティレポート > Berkeley Packet Filterの基礎と応用 - Part 1

タイガーチームセキュリティレポート

Berkeley Packet Filterの基礎と応用 - Part 1

今回からBPF(Berkekey Packet Filter)の基礎と応用について紹介していきます。


-BPFの概要-

BPFは、広い意味ではネットワークのdata link層の生データに対しuser spaceからアクセスするためのinterfaceを指す言葉です。例えばBSD系のシステムでdata link層の生データにアクセスするには/dev/bpf*というcharacter device fileを開いて読み書きすることで実現します。

狭い意味では、BPFはその名前が表しているように、パケットをフィルタするための仕組みを指すこともあります。その仕組みは一種のvirtual machineと専用の命令セットからなり、実装には大きく分けてuser spaceの実装とkernel spaceの実装があります。

この分類はvirtual machineが実行される空間(user spaceかkernel spaceか)による分類ですが、user spaceの実装の場合kernelが受け取ったデータをuser spaceにコピーした後にフィルタされるため、データをコピーしないkernel spaceの実装と比べパフォーマンス面で不利になります。

BPFは、tcpdumpやWiresharkに代表されるnetwork snifferがパケットをフィルタするために使われていますが、その他にもsocketを使ったnetwork programmingでsocketが受け取るデータをフィルタするためのファイアウォールとして利用することもできます。

以後のコラムでは、BPFをパケットフィルタの仕組みを指す言葉として使うことにします。


-BPFの設計-

BPFの基本的な設計思想は約20年前に発表された論文に述べられています。[2]論文ではBPF virtual machineに以下の設計上の制約を設定しています。

1. 使用されているプロトコルに関して独立であること。
2. 未知の使用方法に耐えうるように汎用的であること。
3. パケットデータへの参照の回数を最小限にすること。
4. 命令のデコードはC言語の1個のswitch文で行うこと。
5. 仮想マシンのレジスタには物理レジスタを使うこと。

BPFはパフォーマンスに重点をおいて設計されましたが、同時に汎用性も考慮されていることがわかります。
後の回ではネットワーク以外での応用の例を紹介する予定です。

ではBPF virtual machineの仕様を具体的に見ていきます。
BPF virtual machineはA(Accumulator)、X(indeX)、PC(Program Counter)の3つのレジスタと一時的なメモリ、および処理対象のデータに対する参照を持ちます。
virtual machineの命令セットは以下の6種類に分類されます。

1. LOAD
2. STORE
3. ALU
4. BRANCH
5. RETURN
6. その他

BPF virtual machineの命令フォーマットは以下のような固定長64ビットのフォーマットです。


opcodeは命令の種類とアドレッシングモード、jtとjfはTRUE or FALSEの分岐命令でjumpする先の(相対)アドレスです。kは命令ごとの用途をもつ汎用データです(例えばパケットの特定のフィールドからAレジスタにデータをloadする際のoffsetを指定するなど)。

完全な命令セットのリストは論文の7~8ページを見てください。
BPF virtual machineの命令を使ったフィルタの例を下に示します。IPパケットだけをacceptする例です。


命令を上から読んでいくと、

1行目: Ethernet frameの先頭から12バイト目にある2バイト(Ethertype field)をAレジスタにloadする。
2行目: Aレジスタの値がIP(0x0800)と比較し同じならL1にjump(accept)、違ったらL2にjump(drop)する。

という感じです。アセンブリ言語の経験があれば親しみを感じるかもしれません。:)


-フィルタの実例-

上で紹介したフィルタは抽象的なBFP virtual machine向けの命令で書かれていました。
LinuxやFreeBSD上での実際のプログラムでは、命令を表す構造体の配列としてフィルタを表現します。
FreeBSDのmanにBSD向けのBPF virtual machineの各命令の解説とフィルタの実例があります。[3]

以下はReverse ARPのみをacceptするフィルタの例です。


実際のプログラムではこのようにマクロを使ってある程度は直感的にフィルタを記述することができます。
BPF virtual machineの機械語をハンドアセンブルする必要はありません。:)

また、あまり知られていないかもしれませんが、Wiresharkにはtcpdumpと同じsyntaxで書いたフィルタをBPFのフィルタにcompileする機能があります。
例えば下の図は"src port 80"というフィルタをDebian WheezyのWiresharkでcompileした場合の例です。

"src port 80"のコンパイル結果

"Edit Interface Setting"の"Compile BPF"ボタンを押すと別ウィンドウに結果が表示されます。
Wiresharkがどのようにパケットを扱っているかわかります。
詳細は割愛しますが、上から読んでいくとdata link層から上位層に向かう順にヘッダを解析していることが
なんとなくわかると思います。ipヘッダは可変長なので、そのあたりの処理が少し面倒です。

Wiresharkがキャプチャしたパケットをフィルタする際に内部でこのような「機械語」が使われていることは知っておいて損はないと思います。


-まとめ-

今回はBFPの原理的な話とアプリケーションから見たBPFについて書きました。
BPFはtcpdumpやWiresharkのようなlibpcap/WinPcapを使うアプリケーションが効率的にパケットを処理する上で欠かせない仕組みです。


参考情報:
[1] Berkeley Packet Filter - Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Berkeley_Packet_Filter
[2] The BSD Packet Filter: A New Architecture for User -level Packet Capture
http://www.tcpdump.org/papers/bpf-usenix93.pdf
[3] FreeBSD Man Pages
http://www.freebsd.org/cgi/man.cgi?query=bpf
[4] Wireshark User's Guide
http://www.wireshark.org/docs/wsug_html_chunked/ChCapCaptureOptions.html


次回は、Linux KernelからBPFを見ていきたいと思います。


タイガーチームメンバー 塚本 泰三