BPF Filter Pseudo-Machine
Instruction Set

 

WinDis™ 32 V4.12.07.19 release and later includes code ported from the LBL BPF codebase. This page includes extracts from the BPF.4 Unix manual which describe the syntax and usage of the BPF filter pseudo-machine. Accordingly, the following statement is made about this WinDis 32 help file page:

This document describes software developed by the University of California, Berkeley and its contributors.
The BPF filter code and it's documentation is copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
The Regents of the University of California. All rights reserved.

Table Of Contents

General

A filter program is an array of instructions, with all branches forwardly directed, terminated by a return instruction. Each instruction performs some action on the pseudo-machine state, which consists of an accumulator, index register, scratch memory store, and implicit program counter.

The following structure defines the instruction format:

struct bpf_insn {
u_short	code;
u_char	jt;
u_char	jf;
long k;
};  

The \fIk\fP field is used in different ways by different instructions, and the \fIjt\fP and \fIjf\fP fields are used as offsets by the branch instructions.

The opcodes are encoded in a semi-hierarchical fashion. There are eight classes of instructions: BPF_LD, BPF_LDX, BPF_ST, BPF_STX, BPF_ALU, BPF_JMP, BPF_RET, and BPF_MISC. Various other mode and operator bits are or'd into the class to give the actual instructions.

The classes and modes are defined in <bpf.h>.

Below are the semantics for each defined BPF instruction. We use the convention that A is the accumulator, X is the index register, P[] packet data, and M[] scratch memory store.

P[i:n] gives the data at byte offset ``i'' in the packet, interpreted as a word (n=4), unsigned halfword (n=2), or unsigned byte (n=1). M[i] gives the i'th word in the scratch memory store, which is only addressed in word units. The memory store is indexed from 0 to BPF_MEMWORDS-1. \fIk\fP, \fIjt\fP, and \fIjf\fP are the corresponding fields in the instruction definition. ``len'' refers to the length of the packet.

BPF_LD

These instructions copy a value into the accumulator. The type of the source operand is specified by an ``addressing mode'' and can be a constant (\fBBPF_IMM\fP), packet data at a fixed offset (\fBBPF_ABS\fP), packet data at a variable offset (\fBBPF_IND\fP), the packet length (\fBBPF_LEN\fP), or a word in the scratch memory store (\fBBPF_MEM\fP). For \fBBPF_IND\fP and \fBBPF_ABS\fP, the data size must be specified as a word (\fBBPF_W\fP), halfword (\fBBPF_H\fP), or byte (\fBBPF_B\fP).

The semantics of all the recognized BPF_LD instructions follow.

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

These instructions load a value into the index register. Note that the addressing modes are more restricted than those of the accumulator loads, but they include BPF_MSH, a hack for efficiently loading the IP header length.

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

This instruction stores the accumulator into the scratch memory. We do not need an addressing mode since there is only one possibility for the destination.

BPF_ST
M[k] <- A

BPF_STX

This instruction stores the index register in the scratch memory store.

BPF_STX
M[k] <- X

BPF_ALU

The alu instructions perform operations between the accumulator and index register or constant, and store the result back in the accumulator. For binary operations, a source mode is required (\fBBPF_K\fP or \fBBPF_X\fP).

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

The jump instructions alter flow of control. Conditional jumps compare the accumulator against a constant (\fBBPF_K\fP) or the index register (\fBBPF_X\fP). If the result is true (or non-zero), the true branch is taken, otherwise the false branch is taken. Jump offsets are encoded in 8 bits so the longest jump is 256 instructions. However, the jump always (\fBBPF_JA\fP) opcode uses the 32 bit \fIk\fP field as the offset, allowing arbitrarily distant destinations. All conditionals use unsigned comparison conventions.

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

The return instructions terminate the filter program and specify the amount of packet to accept (i.e., they return the truncation amount). A return value of zero indicates that the packet should be ignored. The return value is either a constant (\fBBPF_K\fP) or the accumulator (\fBBPF_A\fP).

BPF_RET+BPF_A
accept A bytes

BPF_RET+BPF_K
accept k bytes

BPF_MISC

The miscellaneous category was created for anything that doesn't fit into the above classes, and for any new instructions that might need to be added. Currently, these are the register transfer instructions that copy the index register to the accumulator or vice versa.

BPF_MISC+BPF_TAX
X <- A

BPF_MISC+BPF_TXA
A <- X

The BPF interface provides the following macros to facilitate array initializers:
.RS
\fBBPF_STMT\fI(opcode, operand)\fR
.br
and
.br
\fBBPF_JUMP\fI(opcode, operand, true_offset, false_offset)\fR
.RE
.PP

Filter Program Examples

The following filter is taken from the Reverse ARP Daemon. It accepts only Reverse ARP requests.

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),
};

This filter accepts only IP packets between host 128.3.112.15 and 128.3.112.35.

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),
};

Finally, this filter returns only TCP finger packets. We must parse the IP header to reach the TCP header. The \fBBPF_JSET\fP instruction checks that the IP fragment offset is 0 so we are sure that we have a TCP header.

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),
};

SEE ALSO

McCanne, S. and Jacobson V. "The BSD Packet Filter: A New Architecture for User-level Packet Capture" . Proceedings of the 1993 Winter USENIX Technical Conference, San Diego, CA.

 

HISTORY

The Enet packet filter was created in 1980 by Mike Accetta and Rick Rashid at Carnegie-Mellon University. Jeffrey Mogul, at Stanford, ported the code to BSD and continued its development from 1983 on. Since then, it has evolved into the Ultrix Packet Filter at DEC, a STREAMS NIT module under SunOS 4.1, and BPF.

Thomas F. Divine of Printing Communications Assoc., Inc. ported the BPF pseudo-machine to the Microsoft Windows platform for use in PCA's Win32 NDIS Framework (WinDis 32) in 1997.

 

AUTHORS

Steven McCanne, of Lawrence Berkeley Laboratory, implemented BPF in Summer 1990. The design was in collaboration with Van Jacobson, also of Lawrence Berkeley Laboratory.

 

Mailing Lists  · PCAUSA Newsletter · PCAUSA Discussion List
·
Privacy Statement · 
WinDis 32 is a trademark of Printing Communications Assoc., Inc. (PCAUSA).
Rawether for Windows and Rawether .NET are trademarks of Printing Communications Assoc., Inc. (PCAUSA).
Microsoft, MS, Windows, Windows 95, Windows 98, Windows Millennium, Windows 2000, Windows XP, and Win32 are registered trademarks and Visual C++ and Windows NT are trademarks of the Microsoft Corporation.
Send mail to rawether-webmaster@pcausa.com with questions or comments about this web site.
Copyright © 1996-2008 Printing Communications Assoc., Inc. (PCAUSA).
All rights reserved.
Last modified: December 31, 2007