スポンサーリンク

BPF(4) FreeBSD カーネルインタフェースマニュアル BPF(4)

名称

bpf − バークレイパケットフィルタ

書式

pseudo-device bpfilter

解説

バークレイパケットフィルタは、データリンク層においてプロトコル非依存の生 の形のインタフェースを提供します。ネットワーク上のパケットは、たとえ他の ホストに向けられたものであっても、すべてこの機構を通してアクセスすること ができます。

このパケットフィルタは /dev/bpf0, /dev/bpf1 などのキャラクタ型特殊デバイ スに見えます。このデバイスをオープンした後、ファイル記述子は BIOCSETIF ioctl によって、特定のネットワークインタフェースに結びつけなければなりま せん。指示されたインタフェースは複数の監視者で共有することができ、各記述 子の下にあるフィルタは、同じパケットの流れを見ることになります。オープン できるファイルの上限は、カーネルの設定で与えられた値に制限されます。上の 書式で与えられた例では、制限は 16 になっています。

それぞれのマイナデバイスには、別々のデバイスファイルが必要です。デバイス ファイルが使用中であるならば、オープンは失敗し、 errno には EBUSY がセッ トされます。

オープンされた bpf ファイルの実体それぞれに関連づけられているのが、ユーザ が設定可能なパケットフィルタです。あるインタフェースでパケットを受信した ときはいつでも、そのインタフェースを監視しているファイル記述子はすべて自 身のフィルタを適用します。パケットを受け取る各記述子は、自分用のコピーを 受け取ります。

それぞれのファイルからの入力は、フィルタでマッチしたパケットの次のグルー プを返します。性能を上げるために、read に渡すバッファは bpf が内部で使用 するバッファと同じサイズでなければなりません。このサイズは、 BIOCGBLEN ioctl (下記を参照) で得られ、 BIOCSBLEN で設定できます。このサイズより大 きい個々のパケットは、必然的に切り詰められてしまうことに注意して下さい。

このパケットフィルタは、固定長ヘッダであればどのリンクレベルプロトコルで もサポートします。今のところ、イーサネットと SLIP と PPP ドライバだけが bpf と協調して動作するように修正されています。

パケットデータはネットワークバイトオーダになっているので、アプリケーショ ンが複数バイトの値を引き出すためには byteorder(3) マクロを使わなければな りません。

bpf ファイル記述子に書き込むことでネットワークにパケットを送出することが できます。書き込みはバッファリングされないので、1 回の書き込みにつき 1 つ のパケットだけしか処理されません。現在イーサネットと SLIP リンクへの書き 込みだけがサポートされています。

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 によってインタ フェースに接続される前に設定されなければなりません。要求さ れたバッファサイズが適用できなかった場合は、許容できるサイ ズで最も近いものに設定され、それが引数の中に返されます。こ のサイズでないパケットがバッファを通ったときには、読み込み コールは EIO の値で終了します。

BIOCGDLT
(u_int) 接続されたインタフェースの下にあるデータリンク層の 型を返します。インタフェースが指定されていなかった場合 EINVAL が返されます。デバイスの型は、 ‘‘DLT_’’ が前に付い た形であり、 ⟨net/bpf.h⟩ で定義されています。

BIOCPROMISC
強制的にインタフェースを無差別 (promiscuous) モードにしま す。ローカルホストに向けられたもののみならずすべてのパケッ トが処理されます。複数のファイルが与えられたインタフェース を監視することができるので、そのインタフェースを無差別でな いモードでオープンした監視者でもパケットを無差別に受信する ことができてしまいます。この問題は適当なフィルタで矯正する ことができます。

BIOCFLUSH
到着パケットのバッファをフラッシュし、BIOCGSTATS で返され る集計値をリセットします。

BIOCGETIF
(struct ifreq) ファイルが監視しているハードウェアインタ フェースの名前を返します。名前は 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 オープンまたはリセット後に、この記述子によって受信したパケット数 (最後の読み込みコールからバッファされているものを含みます)。

bs_dropフィルタが受け取りはしたが、バッファのオー バフローによりカーネルが落としたパケットの数 (つまり、アプリケーションの読み込みがパケッ トの流量に追いついていないということです)。

BIOCIMMEDIATE
(u_int) 引数の真偽値に基づいて ‘‘直接モード’’ を有効または 無効にします。直接モードが有効なとき、パケットを受け取ると 読み込みはただちに返されます。無効なとき、入力はカーネル バッファがいっぱいになるか、またはタイムアウトが起こるまで ブロックされます。これは、リアルタイムにメッセージに応答し なければならない rarpd(8) のようなプログラムには便利です。 新しいファイルに対しては、デフォルトではオフになります。

BIOCSETF
(struct bpf_program) 興味のないパケットを捨てるためにカー ネルが使うフィルタプログラムを設定します。次の構造体を通し て命令を並べた配列とその長さが渡されます:

struct bpf_program {

int bf_len;

struct bpf_insn *bf_insns;

};

フィルタプログラムは bf_insns フィールドで指定され、 ‘struct bpf_insn’ の構造体中におけるプログラムの長さが bf_len フィールドで与えられます。そして、 BIOCFLUSH の動作 が実行されます。フィルタ言語の説明については フィルタマシ ンのセクションを見て下さい。

       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() によってエラーが返されるか、または 偶然にパケットが一致することです)。

BPF ヘッダ

read(2) によって返される各パケットの先頭には、次のような構造体がつけられ ています:

struct bpf_hdr {
        struct timeval bh_tstamp;     /* タイムスタンプ */
        u_long bh_caplen;             /* キャプチャされた部分の長さ */
        u_long bh_datalen;            /* パケットのオリジナルの長さ */
        u_short bh_hdrlen;            /* bpf ヘッダの長さ (この構造体

+ 境界調整パディング) */

};

フィールドはホスト順で保存されており、次のようになります:

       bh_tstamp

パケットフィルタによって、そのパケットが処理された時刻
bh_caplen
パケットのキャプチャされた部分の長さ。これは切り詰め量の最小 値で、フィルタとパケット長によって指定されています。
bh_datalen
経路から離れたパケット長。この値はフィルタで指定された切り詰 め量には依存しません。
bh_hdrlen
bpf
ヘッダの長さ。これは sizeof(struct bpf_hdr) に等しいとは 限りません。

bh_hdrlen フィールドはヘッダとリンクレベルプロトコル間のパディングのため に存在します。この目的は、パケットデータ構造の適切な境界調整を保証するこ とです。これは、境界調整に厳しいアーキテクチャが必要とすることであり、ま た、これによって他の多くのアーキテクチャにおける性能が向上します。パケッ トフィルタは 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
この命令はアキュムレータを一時メモリに退避します。行き先の可能 性が一つしかないのでアドレッシングモードは不要です。

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)

使用例

以下のフィルタが RARP デーモンから取得されます。フィルタは、 RARP 要求の みを受信します。

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), byteorder(3)

       McCanne, S.  and                          Jacobson V.,                                         An efficient, extensible, andportable network monitor.

関連ファイル

       /dev/bpfn

パケットフィルタデバイス

バグ

読み込みバッファは固定長 ( BIOCGBLEN ioctl で返される値) でなければなりま せん。

無差別モードを要求しないファイルは、同じハードウェアインタフェース上でこ のモードを要求する他のファイルの副作用として、無差別にパケットを受信する かもしれません。これは、オーバヘッドのある処理を追加すれば、カーネル内で 修正できるでしょう。しかし、インタフェースは無差別であるとすべてのファイ ルがみなさなければならないようなモデルの方が好まれます。必要なら、外部の パケットをはじくためのフィルタを利用しなければなりません。

可変長ヘッダのデータリンクプロトコルは現在サポートされていません。

歴史

Enet パケットフィルタは、 1980 年にカーネギーメロン大学の Mike Accetta と Rick Rashid により作成されました。スタンフォードの Jeffrey Mogul がコード を BSD に移植して 1983 年以降発展させました。その後 DEC の Ultrix パケッ トフィルタ、 SunOS 4.1 の STREAMS NIT モジュール、 BPF へと進化しました。

著者

Lawrence Berkeley 研究所の Steven McCanne が 1990 年夏に BPF をインプリメ ントしました。多くは Van Jacobson によってデザインされました。

4.4BSD January 16, 1996 4.4BSD

スポンサーリンク