BPF(4) | FreeBSD Kernel Interfaces Manual | BPF(4) |
名称
bpf — バークレイパケットフィルタ書式
device bpf解説
バークレイパケットフィルタは、プロトコルの独立の方法でデータリンク層 (レイヤ) に生のインタフェースを提供します。ネットワーク上のすべてのパケットは、たとえ他のホストに向けられたものでも、この機構を通してアクセスできます。パケットフィルタは、キャラクタ特殊デバイス /dev/bpf として現れます。デバイスをオープンした後に、ファイル記述子は、 BIOCSETIF ioctl で特定のネットワークインタフェースにバインドしなければなりません。あるインタフェースは、複数のリスナによって共有することができ、各記述子の基礎となるフィルタは、同じパケットのストリームを見ることになります。
別々のデバイスファイルは、それぞれのマイナデバイスが必要とされます。ファイルが使用中であるなら、オープンは、失敗し、 errno は、 EBUSY に設定されます。
オープンしている bpf ファイルのインスタンスそれぞれに関連づけられているのは、ユーザが設定可能なパケットフィルタです。インタフェースでパケットが受信されるときはいつでも、そのインタフェースで listen (接続を受け付け) してるすべてのファイル記述子は、それらのフィルタを適用します。パケットを受け付ける各記述子は、それ自身のコピーを受け付けます。
パケットフィルタは、固定長ヘッダを持っているどんなリンクレベルプロトコルもサポートします。これまでのところ、イーサネット、 SLIP および PPP ドライバだけが bpf と交信するように変更されています。
パケットデータは、ネットワークバイト順になっているので、アプリケーションは、マルチバイトの値を抽出するためには byteorder(3) マクロを使用するべきです。
bpf ファイル記述子に書き込むことによってネットワークにパケットを送出することができます。書き込みは、バッファリングされないので、1 回の書き込みにつき 1 つのパケットだけしか処理されません。現在イーサネットと SLIP リンクへの書き込みだけがサポートされています。
バッファモード
bpf デバイスは、アプリケーションによって提供されたメモリバッファを通してパケットデータをアプリケーションに配信します。バッファモードは、 BIOCSETBUFMODE ioctl を使用して設定され、 BIOCGETBUFMODE ioctl を使用して読み込まれます。バッファリングされた読み込みモード
デフォルトで、 bpf デバイスは、パケットデータが、 read(2) システムコールを使用してカーネルからユーザメモリへ明白にコピーされる、 BPF_BUFMODE_BUFFER モードで動作します。ユーザプロセスは、内部のバッファをサイズを決めるためと、ファイル上のすべての read(2) 操作の両方ために使用される、固定のバッファサイズを宣言します。このサイズは、 BIOCGBLEN ioctl を使用して問い会わされ、 BIOCSBLEN ioctl を使用して設定されます。バッファサイズより大きい個別のパケットは、必ずしも切り詰められないことに注意してください。0 コピーバッファ (zero-copy buffer) モード
また、 bpf デバイスは、パケットデータが、システムコールとコピーのオーバヘッドの両方を避けて、カーネルによって 2 つのユーザメモリバッファに直接書き込まれる、 BPF_BUFMODE_ZEROCOPY モードで動作します。バッファは、固定の (等しい) サイズ、ページで整列され、偶数で複数ページサイズです。最大の 0 コピーバッファサイズは、 BIOCGETZMAX ioctl によって返されます。バッファサイズより大きい個別のパケットは、必ずしも切り詰められないことに注意してください。ユーザプロセスは、引数として struct bpf_zbuf ポインタを受け付ける BIOCSETZBUF ioctl を使用して 2 つのメモリバッファを登録します:
struct bpf_zbuf { void *bz_bufa; void *bz_bufb; size_t bz_buflen; };
bz_bufa は、書き込まれる最初のバッファのユーザ空間のアドレスへのポインタで、 bz_bufb は、2 番目のバッファへのポインタです。 bpf は、バッファが満杯となり、認識されるとき、2 つのバッファの間を循環 (切り替え) します。
各バッファは、バッファのための同期とデータ長さの情報を保持するために、固定長のヘッダで始まります:
struct bpf_zbuf_header { volatile u_int bzh_kernel_gen; /* カーネル世代番号. */ volatile u_int bzh_kernel_len; /* バッファ内のデータ長. */ volatile u_int bzh_user_gen; /* ユーザ世代番号. */ /* ...将来の使用のための埋め草... */ };
すべての詰め物も含めて、各バッファのヘッダ構造は、それが BIOCSETZBUF を使用して設定される前に、0 クリアされるべきです。バッファ中の残りの空間は、バッファリングされる読み込みモードと同様に、パケットデータを格納し、同じ形式でレイアウトするために、カーネルによって使用されます。
カーネルとユーザプロセスは、バッファへのアクセスを同期するためにバッファヘッダを通して簡単な承認プロトコルに従います: ヘッダ世代番号、 bzh_kernel_gen と bzh_user_gen が同じ値を保持するとき、カーネルが、バッファを所有し、異なるとき、ユーザ空間が、バッファを所有しています。
カーネルが、バッファを所有している間、内容は、不安定であり、非同期に変化するかもしれません。ユーザプロセスが、バッファを所有している間、内容は、安定していて、バッファが認識されるまで、変更されません。
バッファを登録する前にバッファヘッダをすべての 0 に初期化することは、両方のバッファの最初の所有権をカーネルに割り当てるという効果があります。カーネルは、バッファが bzh_kernel_gen を修正することによってユーザ空間に割り当てられることで、シグナルを発生し、ユーザ空間がバッファを認識し、 bzh_user_gen の値を bzh_kernel_gen の値を設定することによって、それをカーネルに返します。
キャッシュとメモリ再順序付けの効果を避けるために、ユーザプロセスは、バッファをチェックして、認識するとき、不可分な操作とメモリバリアを使用しなければなりません:
#include <machine/atomic.h> /* * 再利用のためにバッファの所有権をカーネルに返す. */ static void buffer_acknowledge(struct bpf_zbuf_header *bzh) { atomic_store_rel_int(&bzh->bzh_user_gen, bzh->bzh_kernel_gen); } /* * バッファがカーネルによってユーザ空間に割り当てられているかどうか * チェックする. ユーザ空間がバッファを所有しているなら, 真を返し, * そうでなければ, 偽を返します. */ static int buffer_check(struct bpf_zbuf_header *bzh) { return (bzh->bzh_user_gen != atomic_load_acq_int(&bzh->bzh_kernel_gen)); }
ユーザプロセスは、任意のデータが、 BIOCROTZBUF ioctl を使用するユーザ空間で、保留中 (pending) であるなら、次のバッファの割り当てを強制します。これによって、ユーザプロセスは、タイムアウトに続くような、バッファが満杯になる前に、部分的に書き込まれたバッファのデータを検索することができます。プロセスは、データが存在していなかったなら、バッファがユーザ空間に割り当てられないときに、ヘッダの世代番号を使用してバッファの所有権を再チェックしなければなりません。
バッファリングされた読み込みモードで見られるように、 kqueue(2), poll(2) と select(2) は、完了したバッファの利用可能性を待ってスリープするために使用されます。それらは、次のバッファの所有権がユーザ空間に割り当てられるとき、読み込み可能なファイル記述子を返します。
現在の実装では、カーネルは、0、1 または両方のバッファをユーザプロセスに割り当てます。しかしながら、初期の実装は、多くても 1 つのバッファが、一度にユーザプロセスに割り当てられる、不変条件を保っていました。発展と高性能をともに確実にするために、ユーザプロセスは、できるだけはやく、再利用のためにそれを返して、別のバッファを保持している間に、2 番目のバッファでブロックのウェートしないで、完全に処理されたバッファを認識するべきです。
IOCTL
次の ioctl(2) コマンドコードは、 < net/bpf.h> で定義されています。すべてのコマンドは、次のインクルードを必要とします:
#include <sys/types.h> #include <sys/time.h> #include <sys/ioctl.h> #include <net/bpf.h>
さらに、 BIOCGETIF と BIOCSETIF は、 < sys/socket.h> と < net/if.h> を必要とします。
FIONREAD と SIOCGIFADDR に加えて、次のコマンドは、どんな bpf ファイルのオープンに適用されます。 ioctl(2) への (3 番目の) 引数は、指示されたタイプへのポインタであるべきです。
- BIOCGBLEN
-
(
u_int
) bpf ファイル上で読み込みのために必要なバッファ長を返します。 - BIOCSBLEN
-
(
u_int
) bpf ファイル上での読み込みのためのバッファ長を設定します。バッファは、ファイルが BIOCSETIF によってインタフェースにアタッチされる前に設定されなければなりません。要求されたバッファサイズの場所を適用することができないなら、許容できる最も近いサイズが設定され、引数に返されます。このサイズでないバッファが渡されるなら、read (読み込み) 呼び出しは、 EIO エラーとなります。 - BIOCGDLT
-
(
u_int
) アタッチされたインタフェースの基礎となるデータリンク層のタイプを返します。インタフェースが指定されていないなら、 EINVAL を返します。“DLT_
”が前に付いたデバイスタイプは、 < net/bpf.h> で定義されています。 - BIOCPROMISC
- 強制的にインタフェースを無差別 (promiscuous) モードにします。ローカルホストに向けられたものだけではなく、すべてのパケットが処理されます。 2 つ以上のファイルが与えられたインタフェースで listen (接続を受け付け) されるかもしれないので、インタフェースを無差別でないモードでオープンした listen する側は、無差別にパケットを受信することができます。この問題は、適切なフィルタで改善することができます。
- BIOCFLUSH
- 着信パケットのバッファをフラッシュし、BIOCGSTATS によって返される統計値をリセットします。
- BIOCGETIF
-
(
struct ifreq
) ファイルが listen (接続を受け付け) しているハードウェアインタフェースの名前を返します。名前は、ifreq
構造体の ifr_name フィールドに返されます。他のすべてのフィールドは、未定義です。 - BIOCSETIF
-
(
struct ifreq
) ファイルに関連するハードウェアインタフェースを設定します。このコマンドは、どんなパケットも読み込むことができるようになる前に実行されなければなりません。デバイスは、ifreq
構造体のifr_name
フィールドを使用する名前によって示されます。さらに BIOCFLUSH の動作を実行します。 - BIOCSRTIMEOUT
- BIOCGRTIMEOUT
-
(
struct timeval
) 読み込みタイムアウトパラメータを設定または取得します。引数は、読み込み要求でタイムアウトする前の待ち時間の長さを指定します。このパラメータは、 open(2) によって 0 に初期化され、タイムアウトしないことを示します。 - BIOCGSTATS
-
(
struct bpf_stat
) パケットの統計値の次の構造体を返します:struct bpf_stat { u_int bs_recv; /* 受信したパケット数 */ u_int bs_drop; /* 落としたパケット数 */ };
フィールドは、次の通りです:
-
bs_recv
- オープンまたはリセット以後にこの記述子によって受信したパケットの数 (最後の read (読み込み) 呼び出しのときからのバッファされているものを含みます)。
-
bs_drop
- フィルタで受け付けられましたが、バッファのオーバフローためにカーネルに落されたパケットの数 (すなわち、アプリケーションの読み込みは、パケットトラフィックについていっていません)。
-
- BIOCIMMEDIATE
-
(
u_int
) 引数の真偽値に基づいて“直接モード”を有効または無効にします。直接モードが有効にされるとき、read (読み込み) は、パケットの受信でただちに返ります。そうでなければ、 read は、カーネルバッファが満杯になるかタイムアウトが起こるまでブロックされます。これは、リアルタイムでメッセージに応答しなければならない rarpd(8) のようなプログラムで役に立ちます。新しいファイルに対するデフォルトは、オフです。 - BIOCSETF
- BIOCSETFNR
-
(
struct bpf_program
) 興味のないパケットを捨てるためにカーネルによって使用される読み込みフィルタプログラムを設定します。命令の配列とその長さは、次の構造体を使用して渡されます:struct bpf_program { int bf_len; struct bpf_insn *bf_insns; };
フィルタプログラムは、
bf_insns
フィールドで指されますが、‘struct bpf_insn
’のユニット中のフィルタプログラムの長さは、bf_len
フィールドによって与えられます。フィルタ言語の説明については、セクション フィルタマシン を参照してください。 BIOCSETF と BIOCSETFNR の唯一の違いは、 BIOCSETF が、 BIOCFLUSH の動作を実行しますが、 BIOCSETFNR は、実行しません。 - BIOCSETWF
-
(
struct bpf_program
) インタフェースへのどのようなタイプのパケットを書き込むことができるかを制御するカーネルによって使用される書き込みフィルタプログラムを設定します。 bpf フィルタプログラムに関する詳しい情報については、 BIOCSETF コマンドを参照してください。 - BIOCVERSION
-
(
struct bpf_version
) 現在カーネルで認識されているフィルタ言語のメジャーとマイナバージョン番号を返します。フィルタをインストールする前に、アプリケーションは、現在のバージョンが実行しているカーネルと互換性があるかどうかチェックしなければなりません。メジャー番号が一致し、アプリケーションのマイナ番号がカーネルのマイナ番号と等しいか小さいなら、バージョン番号は互換性があります。カーネルのバージョン番号は、次の構造体で返されます:struct bpf_version { u_short bv_major; u_short bv_minor; };
現在のバージョン番号は、 < net/bpf.h> の BPF_MAJOR_VERSION と BPF_MINOR_VERSION によって与えられます。互換性のないフィルタは、未定義の動作となるかもしれません (たぶん、 ioctl() によってエラーが返されるかまたは偶然にパケットが一致します)。
- BIOCSHDRCMPLT
- BIOCGHDRCMPLT
-
(
u_int
) “ヘッダ完全”フラグの状態を設定するか取得します。リンクレベルソースアドレスがインタフェース出力ルーチンによって自動的に書き込まれるなら、0 が設定されます。リンクレベルソースアドレスが書き込まれるなら、通信する配線に供給するように 1 に設定されます。このフラグは、デフォルトで 0 に初期化されます。 - BIOCSSEESENT
- BIOCGSEESENT
-
(
u_int
) これらのコマンドは、時代遅れですが、互換性のために残されています。代わりに BIOCSDIRECTION と BIOCGDIRECTION を使用します。インタフェースで局所的で生成されるパケットが BPF によって返されるべきであるかどうかを決定するフラグを設定または取得します。インタフェースで着信パケットだけを見るために 0 に設定します。インタフェースで局所的およびリモートに起因するパケットを見るために 1 に設定します。このフラグは、デフォルトで 1 に初期化されます。 - BIOCSDIRECTION
- BIOCGDIRECTION
-
(
u_int
) インタフェースの着信、発信、またはすべてのパケットが BPF によって返されるべきであるかどうか決定する設定を設定するか、取得します。インタフェースの着信パケットのみを見るためには、 BPF_D_IN に設定します。インタフェースのローカルとリモートから始まるパケットを見るためには BPF_D_INOUT に設定します。インタフェースの発信パケットのみを見るためには BPF_D_OUT に設定します。この設定は、デフォルトで BPF_D_INOUT に初期化されます。 - BIOCSTSTAMP
- BIOCGTSTAMP
-
(
u_int
) BPF によって返されるタイムスタンプの形式と解像度を設定するか、または取得します。 64 ビットの struct timeval 形式でタイムスタンプを取得するために BPF_T_MICROTIME, BPF_T_MICROTIME_FAST, BPF_T_MICROTIME_MONOTONIC または BPF_T_MICROTIME_MONOTONIC_FAST を設定します。 64 ビットの struct timespec 形式でタイムスタンプを取得するために BPF_T_NANOTIME, BPF_T_NANOTIME_FAST, BPF_T_NANOTIME_MONOTONIC または BPF_T_NANOTIME_MONOTONIC_FAST を設定します。 64 ビットの struct bintime 形式でタイムスタンプを取得するために BPF_T_BINTIME, BPF_T_BINTIME_FAST, BPF_T_NANOTIME_MONOTONIC または BPF_T_BINTIME_MONOTONIC_FAST を設定します。タイムスタンプを無視するために BPF_T_NONE を設定します。すべての 64 ビットのタイムスタンプの形式は、 struct bpf_ts でラップされます。 BPF_T_MICROTIME_FAST, BPF_T_NANOTIME_FAST, BPF_T_BINTIME_FAST, BPF_T_MICROTIME_MONOTONIC_FAST, BPF_T_NANOTIME_MONOTONIC_FAST と BPF_T_BINTIME_MONOTONIC_FAST は、_FAST 接尾辞のない対応する形式に似ていますが、フルタイムカウンタ問い合わせを実行しないので、それらの精度は、ワンタイマチック (one timer tick) です。 BPF_T_MICROTIME_MONOTONIC, BPF_T_NANOTIME_MONOTONIC, BPF_T_BINTIME_MONOTONIC, BPF_T_MICROTIME_MONOTONIC_FAST, BPF_T_NANOTIME_MONOTONIC_FAST と BPF_T_BINTIME_MONOTONIC_FAST は、カーネルのブート以来の経過時間を格納しています。この設定は、デフォルトで BPF_T_MICROTIME に初期化されています。 - BIOCFEEDBACK
-
(
u_int
) パケットフィードバックモードに設定します。これによって、インタフェースを通る出力が成功するとき、インタフェースへの入力のように、注入されたパケットをフィードバックすることができます。 BPF_D_INOUT 方向が設定されるとき、注入された発信パケットは、重複を避けるために BPF によって返されません。このフラグは、デフォルトで 0 に初期化されます。 - BIOCLOCK
- bpf 記述子でロックされたフラグを設定します。これは、デバイスの基本的な操作パラメータを変更することができるかもしれない ioctl コマンドの実行を防ぎます。
- BIOCGETBUFMODE
- BIOCSETBUFMODE
-
(
u_int
) 現在の bpf のバッファリングモードを取得か、または設定します。指定できる値は、バッファリングされた読み込みモードの BPF_BUFMODE_BUFFER と 0 コピーバッファモードの BPF_BUFMODE_ZBUF です。 - BIOCSETZBUF
-
(
struct bpf_zbuf
) 現在の 0 コピーバッファ位置を設定します。バッファ位置は、いったん 0 コピーバッファモードが選択されて、インタフェースにアタッチする前にだけ設定されます。バッファは、同じサイズ、ページで整列され、ページの整数の倍数のサイズでなければなりません。 3 つのフィールド bz_bufa, bz_bufb と bz_buflen は、書き込まなければなりません。バッファが、既にこのデバイスに設定されていたなら、 ioctl は、失敗します。 - BIOCGETZMAX
-
(
size_t
) 許された最も大きい個々の 0 コピーバッファサイズを取得します。 2 つのバッファが 0 コピーバッファモードで使用されるとき、(実際の) 制限は、返されたサイズの 2 倍です。 0 コピーバッファが、カーネルアドレス空間を消費するとき、特に 32 ビットのシステムで使用中の複数の bpf 記述子があるとき、バッファサイズの保守的な選択が勧められます。 - BIOCROTZBUF
- 任意のデータがバッファに存在するなら、次のバッファの所有権を強制的にユーザ空間に割り当てられます。データが在していないなら、バッファは、カーネルによって所有されていたままとなります。これによって、0 コピーバッファリングの消費者は、タイムアウトを実装することができ、部分的に満たされたバッファを検索することができます。データがバッファに存在しなくて、したがって、所有権が割り当てられない場合を扱うために、ユーザプロセスは、 bzh_kernel_gen に対して bzh_user_gen をチェックしなければなりません。
BPF ヘッダ
次の構造の 1 つは、 read(2) によって、または 0 コピーバッファを通して返された各パケットの先頭に追加されます:
struct bpf_xhdr { struct bpf_ts bh_tstamp; /* タイムスタンプ */ uint32_t bh_caplen; /* キャプチャされた部分の長さ */ uint32_t bh_datalen; /* パケットのオリジナルの長さ */ u_short bh_hdrlen; /* bpf ヘッダの長さ (この構造体 + 境界調整パディング) */ }; struct bpf_hdr { struct timeval bh_tstamp; /* タイムスタンプ */ uint32_t bh_caplen; /* キャプチャされた部分の長さ */ uint32_t bh_datalen; /* パケットのオリジナルの長さ */ u_short bh_hdrlen; /* bpf ヘッダの長さ (この構造体 + 境界調整パディング) */ };
ホスト順に格納された値のフィールドは、次の通りです:
-
bh_tstamp
- パケットがパケットフィルタによって処理された時刻。
-
bh_caplen
- パケットのキャプチャされている部分の長さ。これは、フィルタによって指定された切り詰められた量の最小限とパケットの長さです。
-
bh_datalen
- 通信する配線から離れたパケットの長さ。この値は、フィルタによって指定された切り詰められた量に依存しません。
-
bh_hdrlen
- sizeof( struct bpf_xhdr) または sizeof( struct bpf_hdr) と等しくないかもしれない、 bpf ヘッダの長さ。
bh_hdrlen
フィールドは、ヘッダとリンクレベルプロトコルの間のパディングとなるように存在しています。ここでの目的は、パケットデータ構造の適切な境界調整を保証することです。それは、境界調整に注意を要するアーキテクチャで必要であり、他の多くのアーキテクチャで性能を向上させます。パケットフィルタは、 bpf_xhdr, bpf_hdr とネットワーク層のヘッダがワード境界になることを保証します。現在、 bpf_hdr は、タイムスタンプが後方互換性の理由のために BPF_T_MICROTIME, BPF_T_MICROTIME_FAST, BPF_T_MICROTIME_MONOTONIC, BPF_T_MICROTIME_MONOTONIC_FAST または BPF_T_NONE に設定されるとき、使用されます。そうでなければ、 bpf_xhdr が使用されます。しかしながら、 bpf_hdr は、近い将来、廃止される可能性があります。境界調整が制限されたマシンのリンク層プロトコルのフィールドにアクセスするとき、適切な注意を払わなければなりません。 (これは、イーサネットに関する問題ではありません。なぜなら、タイプフィールドは、偶数オフセットに当たる short であり、アドレスは、おそらくバイト単位でアクセスされるからです)。
さらに、個々のパケットは、それぞれワード境界で始まるようにパディングされます。これは、アプリケーションがパケットから次のパケットをどのように取得するかに関する何らかの知識があることを必要とします。マクロ BPF_WORDALIGN は、この処理過程を容易にするために < net/bpf.h> で定義されます。その引数は、最も近いワード境界値 (ここで、ワードは、 BPF_ALIGNMENT バイト幅) に切り上げられます。
例えば‘ p
’がパケットの始まりを指すなら、次の式は、ポインタを次のパケットへ進めます:
p = (char *)p + BPF_WORDALIGN(p->bh_hdrlen + p->bh_caplen)
境界調整の機構が適切に動作するためには read(2) に渡されるバッファは、それ自体がワード境界になければなりません。 malloc(3) 関数は、常に境界調整されたバッファを返します。
フィルタマシン
フィルタプログラムは、すべての分岐が前方に方向づけられた return 命令で終わる、命令の配列です。各命令は、アキュムレータ、インデックスレジスタ、一時メモリ記憶、および内在するプログラムカウンタから成る疑似マシン状態で何らかの動作を実行します。次の構造体は、命令形式を定義します:
struct bpf_insn { u_short code; u_char jt; u_char jf; u_long k; };
k
フィールドは、さまざまな命令でさまざまな方法で使用され、 jt
と jf
フィールドは、分岐命令によってオフセットとして使用されます。オペコードは、準階層的な方法でコード化されます。次の 8 つのクラスの命令があります: BPF_LD, BPF_LDX, BPF_ST, BPF_STX, BPF_ALU, BPF_JMP, BPF_RET と BPF_MISC です。他の様々なモードと操作ビットは、実際の命令を与えるためにクラスに論理和 (or) されます。クラスとモードは、 < net/bpf.h> で定義されています。
以下は、定義されたそれぞれの bpf 命令のセマンティクス (意味) です。便宜的に A は、アキュムレータ、X は、インデックスレジスタ、 P[] は、パケットデータ、 M[] は、一時メモリ記憶であるとします。 P[i:n] は、パケット中のバイトオフセット“i”のデータを示し、ワード (n=4)、符号無しハーフワード (n=2) または符号無しバイト (n=1) と解釈されます。 M[i] は、ワード単位でのみアドレス指定される一時メモリ記憶で i 番目のワードを示します。メモリ記憶は、0 から BPF_MEMWORDS - 1 までインデックス付けされます。 k
, jt
と jf
は、命令定義の中で対応するフィールドです。“len”は、パケットの長さを参照します。
- BPF_LD
-
これらの命令は、値をアキュムレータにコピーします。ソースオペランドのタイプは、“アドレッシングモード”によって指定され、定数 (
BPF_IMM), 固定オフセットのパケットデータ (
BPF_ABS), 可変オフセットのパケットデータ (
BPF_IND), パケットの長さ (
BPF_LEN), または一時メモリ記憶内のワード (
BPF_MEM) になり得ます。
BPF_IND と
BPF_ABS に対しては、データサイズは、ワード (
BPF_W), ハーフワード (
BPF_H) またはバイト (
BPF_B) を指定しなければなりません。
BPF_LD 命令として認識されるすべてのセマンティクス (意味) は、次の通りです。
BPF_LD+BPF_W+BPF_ABS A <- P[k:4] BPF_LD+BPF_H+BPF_ABS A <- P[k:2] BPF_LD+BPF_B+BPF_ABS A <- P[k:1] BPF_LD+BPF_W+BPF_IND A <- P[X+k:4] BPF_LD+BPF_H+BPF_IND A <- P[X+k:2] BPF_LD+BPF_B+BPF_IND A <- P[X+k:1] BPF_LD+BPF_W+BPF_LEN A <- len BPF_LD+BPF_IMM A <- k BPF_LD+BPF_MEM A <- M[k]
- BPF_LDX
-
これらの命令は、値をインデックスレジスタにロードします。このアドレッシングモードは、アキュムレータのものをロードするよりもより制限されていますが、効率的に IP ヘッダ長をロードするためのハッキングである、
BPF_MSH, を含んでいることに注意してください。
BPF_LDX+BPF_W+BPF_IMM X <- k BPF_LDX+BPF_W+BPF_MEM X <- M[k] BPF_LDX+BPF_W+BPF_LEN X <- len BPF_LDX+BPF_B+BPF_MSH X <- 4*(P[k:1]&0xf)
- BPF_ST
-
この命令は、アキュムレータを一時メモリに格納します。宛先の可能性が 1 つしかないので、アドレッシングモードを必要としません。
BPF_ST M[k] <- A
- BPF_STX
-
この命令は、インデックスレジスタを一時メモリ記憶に格納します。
BPF_STX M[k] <- X
- BPF_ALU
-
alu 命令は、アキュムレータとインデックスレジスタまたは定数の間の操作を実行し、アキュムレータに結果を格納します。バイナリ操作のためには、ソースモードは、 (
BPF_K または
BPF_X) が必要です。
BPF_ALU+BPF_ADD+BPF_K A <- A + k BPF_ALU+BPF_SUB+BPF_K A <- A - k BPF_ALU+BPF_MUL+BPF_K A <- A * k BPF_ALU+BPF_DIV+BPF_K A <- A / k BPF_ALU+BPF_AND+BPF_K A <- A & k BPF_ALU+BPF_OR+BPF_K A <- A | k BPF_ALU+BPF_LSH+BPF_K A <- A << k BPF_ALU+BPF_RSH+BPF_K A <- A >> k BPF_ALU+BPF_ADD+BPF_X A <- A + X BPF_ALU+BPF_SUB+BPF_X A <- A - X BPF_ALU+BPF_MUL+BPF_X A <- A * X BPF_ALU+BPF_DIV+BPF_X A <- A / X BPF_ALU+BPF_AND+BPF_X A <- A & X BPF_ALU+BPF_OR+BPF_X A <- A | X BPF_ALU+BPF_LSH+BPF_X A <- A << X BPF_ALU+BPF_RSH+BPF_X A <- A >> X BPF_ALU+BPF_NEG A <- -A
- BPF_JMP
-
ジャンプ命令は、制御の流れを変更します。条件付きのジャンプは、アキュムレータと定数 (
BPF_K) またはインデックスレジスタ (
BPF_X) を比較します。結果が真 (または非 0) であるなら、真の分岐が解釈され、そうでなければ偽の分岐が解釈されます。ジャンプオフセットは、8 ビットでコード化されるので、最長のジャンプは、256 命令です。しかしながら、 (
BPF_JA) オペコードは、任意の距離がある行き先を許す、オフセットとして 32 ビットの
k
フィールドを使用して常にジャンプします。すべての条件文は、慣習として符号無しの比較を使用します。BPF_JMP+BPF_JA pc += k BPF_JMP+BPF_JGT+BPF_K pc += (A > k) ? jt : jf BPF_JMP+BPF_JGE+BPF_K pc += (A >= k) ? jt : jf BPF_JMP+BPF_JEQ+BPF_K pc += (A == k) ? jt : jf BPF_JMP+BPF_JSET+BPF_K pc += (A & k) ? jt : jf BPF_JMP+BPF_JGT+BPF_X pc += (A > X) ? jt : jf BPF_JMP+BPF_JGE+BPF_X pc += (A >= X) ? jt : jf BPF_JMP+BPF_JEQ+BPF_X pc += (A == X) ? jt : jf BPF_JMP+BPF_JSET+BPF_X pc += (A & X) ? jt : jf
- BPF_RET
-
リターン命令は、フィルタプログラムを終了し、受信するパケットの量を指定します (すなわち、それらは、切り詰め量を返します)。 0 の返り値は、パケットが無視されるべきであることを示しています。返り値は、定数 (
BPF_K) またはアキュムレータ (
BPF_A) のいずれかです。
BPF_RET+BPF_A A バイト受信 BPF_RET+BPF_K k バイト受信
- BPF_MISC
-
その他のカテゴリは、上記のクラスに適合しないもの、および追加する必要がある新しい命令のために作成されました。現在、これらは、インデックスレジスタをアキュムレータにコピーするまたはその逆を行うレジスタ転送命令です。
BPF_MISC+BPF_TAX X <- A BPF_MISC+BPF_TXA A <- X
bpf インタフェースは、配列の初期化を容易にするために次のマクロを提供しています: BPF_STMT( opcode, operand) と BPF_JUMP( opcode, operand, true_offset, false_offset) です。
SYSCTL 変数
sysctl(8) 変数の 1 組は、 bpf サブシステムの振る舞いを制御します:- net.bpf.optimize_writers: 0
- 様々なプログラムは、生のパケット (cdpd、lldpd、dhcpd、dhcp 中継などは、そのようなプログラムのよい例です) を送信するために (しかし受信ではなく) BPF を使用します。それらは、それらに送信されるための着信パケットを必要としません、プログラムが pcap_set_filter() によって明示的に読み込みフィルタを指定するまで、新しい BPF ユーザを書き込み専用のインタフェースリストにアタッチすることで、このオプションを調整します。これは、高速インタフェースのためのあらゆる性能劣化を排除します。
- net.bpf.stats:
- 一般的な統計を検索するためのバイナリインタフェース。
- net.bpf.zerocopy_enable: 0
- 0 のコピーがネットの BPF リーダで使用されることを可能にします。注意して使用してください。
- net.bpf.maxinsns: 512
- BPF プログラムが含むことができる指示の最大数。あらゆるフィルタのための指示の概数を決定するために tcpdump(1) -d オプションを使用します。
- net.bpf.maxbufsize: 524288
- パケットのバッファのために割り付ける最大のバッファサイズ。
- net.bpf.bufsize: 4096
- パケットのバッファに割り付けるデフォルトのバッファサイズ。
使用例
次のフィルタは、Reverse ARP デーモンから取得されます。それは、Reverse ARP 要求のみを受信します。
struct bpf_insn insns[] = { BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_REVARP, 0, 3), BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 20), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, REVARP_REQUEST, 0, 1), BPF_STMT(BPF_RET+BPF_K, sizeof(struct ether_arp) + sizeof(struct ether_header)), BPF_STMT(BPF_RET+BPF_K, 0), };
このフィルタは、ホスト 128.3.112.15 と 128.3.112.35 の間の IP パケットだけを受け付けます。
struct bpf_insn insns[] = { BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_IP, 0, 8), BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 26), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x8003700f, 0, 2), BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 30), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x80037023, 3, 4), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x80037023, 0, 3), BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 30), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x8003700f, 0, 1), BPF_STMT(BPF_RET+BPF_K, (u_int)-1), BPF_STMT(BPF_RET+BPF_K, 0), };
最後に、このフィルタは、TCP finger パケットだけを返します。私たちは、TCP ヘッダに達するために IP ヘッダを解析しなければなりません。 BPF_JSET 命令は、IP フラグメントオフセットが 0 であることをチェックし、それで、私たちは、TCP ヘッダがあることを確信します。
struct bpf_insn insns[] = { BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_IP, 0, 10), BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 23), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_TCP, 0, 8), BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 20), BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, 0x1fff, 6, 0), BPF_STMT(BPF_LDX+BPF_B+BPF_MSH, 14), BPF_STMT(BPF_LD+BPF_H+BPF_IND, 14), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 79, 2, 0), BPF_STMT(BPF_LD+BPF_H+BPF_IND, 16), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 79, 0, 1), BPF_STMT(BPF_RET+BPF_K, (u_int)-1), BPF_STMT(BPF_RET+BPF_K, 0), };
関連項目
tcpdump(1), ioctl(2), kqueue(2), poll(2), select(2), byteorder(3), ng_bpf(4), bpf(9) McCanne, S. and Jacobson V., An efficient, extensible, and portable network monitor.歴史
Enet パケットフィルタは、1980 年に Carnegie-Mellon 大学で Mike Accetta と Rick Rashid によって作成されました。 Jeffrey Mogul は、Stanford でコードを BSD に移植して、1983 年以降開発を続けました。それ以来、それは、 DEC の Ultrix Packet Filter、 SunOS 4.1 の STREAMS NIT モジュール、そして BPF へと発展しています。作者
Lawrence Berkeley 研究所の は、1990 年夏に BPF を実装しました。デザインの多くは、 のおかげです。0 コピーバッファのサポートは、Seccuris Inc. と契約して
によって追加されました。バグ
読み込みバッファは、 ( BIOCGBLEN ioctl によって返される) 固定サイズでなければなりません。無差別モードを要求しないファイルは、同じハードウェアインタフェースでこのモードを要求する別のファイルの副作用として受信パケットを無差別に受信するかもしれません。これは、オーバヘッドのある追加処理でカーネルで修正することができるかもしれません。しかしながら、私たちは、すべてのファイルがインタフェースが無差別であると仮定しなければならないようなモデルを好みます。そして、そうだとしたら必要に応じて、外部のパケットを拒否するためにフィルタを利用しなければなりません。
可変長ヘッダがあるデータリンクプロトコルは、現在サポートされていません。
SEESENT, DIRECTION と FEEDBACK の設定は、ソフトウェアループバックとポイントツーポイントインタフェースよりむしろハードウェアループバックがあるそれらを含んで、いくつかのインタフェースタイプで正しく動作しないことが観測されました。それらは、広範囲のイーサネットスタイルインタフェースで正しく機能するように思われます。
June 15, 2010 | FreeBSD |