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およびMicrosoft 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サポートを提供するために再利用されている。Microsoftの「eBPF on Windows」は、uBPFとPREVAIL形式の検証ツールを基に構築されている。

## 歴史

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

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

## セキュリティ上の懸念

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