Berkeley Packet Filter(BPF)は、特定のコンピュータのオペレーティングシステム上で特にネットワークトラフィックの解析に必要なプログラムで使われる技術である。

概要 BPFはデータリンク層へのrawインタフェースを提供し、生のリンク層パケットの送信と受信を可能にする。

それに加えて、ネットワークインタフェース向けのドライバーがプロミスキャス・モードをサポートしている場合には、インタフェースをそのモードに切り替えて、他のホスト向けのパケットも含むネットワーク上のすべてのパケットを受信できるようにすることができる。

BPFはパケットのフィルタリングをサポートしているため、のプロセスは受信したいパケットを指定するフィルタープログラムを実装できる。たとえば、tcpdumpプロセスは、TCP接続を開始するパケットのみを受信する必要があることがある。BPFはプロセスが提供するフィルターを通過したパケットのみを返せるため、この機能を利用することで、オペレーティングシステムのカーネルからプロセスへの不要なパケットのコピーが回避され、パフォーマンスが大幅に向上する。

BPFは、インタフェース全体ではなく、BPFのフィルタリングメカニズムだけを指す場合がある。LinuxやTru64 UNIXなどの一部のシステムは、BPFのrawインタフェース以外にデータリンク層へのrawインタフェースを提供するが、そのrawインタフェースにはBPFのフィルタリングメカニズムを使用する。

また、eBPFはLinuxカーネル内で実行される拡張された(extended)BPFのJITの仮想マシンである。

BPFはほとんどのUnix系オペレーティングシステムで利用でき、eBPFにはLinuxおよびWindows向けのものがある。

rawインタフェース BPFは、ネットワークインタフェースにバインドできる疑似デバイスを提供する。疑似デバイスからの読み取りは、ネットワークインタフェースで受信したパケットが保持されたバッファーの読み取りになり、疑似デバイスへの書き込みは、ネットワークインタフェースへのパケットの挿入になる。

2007年、 Robert WatsonとChristian Peronは、FreeBSDのBPF実装に拡張を追加した。これにより、デバイスドライバの割り込みハンドラーのカーネルパケットキャプチャが、要件を回避するためにユーザープロセスメモリに直接書き込むことができるようになった。これは、BPFデバイスを介して受信したすべてのパケットデータに対して2つのコピーが必要になるのを回避するためである。1つのコピーがユーザープロセスの受信パスに残るが、これにより、さまざまなBPFデバイスコンシューマーの独立性が維持され、完全なパケットデータをコピーするのではなく、ヘッダーをBPFバッファーにパッキングできるようになる。

フィルタリング BPFのフィルタリング機能は、BPF仮想マシン向けの機械語のインタプリタとして実装されている。BPF仮想マシンは32ビットのマシンであり、固定長の命令、1つのアキュムレータ、1つのを備えている。この言語で書かれたプログラムでは、パケットからのデータのフェッチ、パケットのデータに対する算術演算の実行、結果に対する定数・パケット内のデータ・結果内のテストビットとの比較を行い、その結果に基づいてパケットをacceptまたはrejectすることができる。

BPFは、しばしばロード(ld)とストア(str)命令を「オーバーロード」することで拡張される。

伝統的なUnix系のBPF実装はカーネル空間用に記述されているが、ユーザー空間で使用できる。この仕組みは、プリプロセッサの条件分岐を使用して実現されている。

拡張と最適化 一部のプロジェクトでは、オリジナルとは異なるBPF命令セットや実行手法を使用している。

FreeBSD、NetBSD、WinPcapなどの一部のプラットフォームでは、パフォーマンスを向上させるために、JITコンパイラを使用してBPF命令をネイティブコードに変換している。LinuxにはBPF JITコンパイラが含まれているが、デフォルトでは無効になっている。

同じ仮想マシン言語のカーネルモードのインタプリタは、Tru64 Unixなどの他のオペレーティングシステムの生のデータリンク層の内部や、Linuxカーネルのソケットフィルター、WinPcapやNpcapのパケットキャプチャの内部で使用される。

バージョン3.18以降のLinuxカーネルには、extended BPF(eBPF)と呼ばれる10個の64ビットレジスタを備えたeBPFの仮想マシンが含まれている。eBPFプログラムをさまざまなTracepointにアタッチする用途など、ネットワーク以外の目的でも使用できる。

カーネルバージョン3.19以降では、eBPFフィルターはにアタッチすることができ、カーネルバージョン4.1以降は、ingressおよびegressネットワークデータパスの分類器にアタッチできる。元の古いバージョンは、遡及的にclassic BPF(cBPF)に名前が変更された。現在、LinuxカーネルはeBPFのみを実行しており、ロードされたcBPFのバイトコードは、プログラムの実行前にカーネル内で透過的にeBPF表現に変換されるようになっている。DoS攻撃を防ぐために、実行前にすべてのバイトコードが検証される。Linux 5.3までは、verifierはループの使用を禁止していた。

BPFのユーザーモードのインタプリタは、pcap APIのlibpcap/WinPcap/Npcapの各実装で提供されるため、特定のフィルタリングメカニズムに対するカーネルモードのサポートがないシステムでパケットをキャプチャする場合、パケットをユーザーモードでフィルタリングできる。ただし、pcap APIを使用するコードは両方のタイプのシステムで機能するが、ユーザーモードでフィルタリングが行われるシステムでは、フィルターで除外されるものを含むすべてのパケットがカーネルからユーザー空間にコピーされる。このインタプリタは、pcapを使用してキャプチャされたパケットを含むファイルを読み取るときにも使用できえる。

もう1つのユーザーモードインタプリタは、JITとeBPFをサポートするuBPFである。そのコードは、Linux以外のシステムでeBPFサポートを提供するために再利用されている。マイクロソフトの「eBPF on Windows」は、uBPFとPREVAIL形式の検証ツールを基に構築されている。

歴史 オリジナルの論文は、1992年にローレンス・バークレー国立研究所に所属していたとヴァン・ジェイコブソンによって書かれた。

2003年8月、SCO Groupは、Linuxカーネルが所有するUnixコードを侵害していると公に主張した。プログラマーはすぐに、挙げられた例の1つがBerkeley Packet Filterであることに気付いた。実際にはSCOはBPFを所有したことない。SCOはその間違いを弁明することなく訴訟を続け、SCO・Linux論争を引き起こした。

セキュリティ上の懸念 Spectre攻撃により、LinuxカーネルのeBPF JITコンパイラを利用して、他のカーネルプロセスからデータを抽出し、ユーザー空間でそのデータを読み取れるようになる可能性がある。

関連項目 データリンク層

脚注

参考文献

外部リンク –従来の BPF の例 eBPF.io - イントロダクション、チュートリアル、コミュニティ リソース bpfc、Berkeley Packet Filter コンパイラ、Linux BPF JIT逆アセンブラ (netsniff-ng の一部) BPF ドキュメント、Linux カーネル用 Linux フィルターのドキュメント、cBPF および eBPF バイトコード形式の両方