bpfc8
A berkeley packet filter assembler and compiler
Usage
bpfc {[options] | [source-file]}
Element Description
A 32
bit
wide
accumulator
X 32
bit
wide
X
register
M []
16
x
32
bit
wide
misc
registers
aka
“scratch
memory
store”,
addressable
from
0
to
15
Instruction Addressing
mode
Description
ld 1,
2,
3,
4,
10
Load
word
into
A
ldi 4
Load
word
into
A
ldh 1,
2
Load
half-word
into
A
ldb 1,
2
Load
byte
into
A
ldx 3,
4,
5,
10
Load
word
into
X
ldxi 4
Load
word
into
X
ldxb 5
Load
byte
into
X
st 3
Copy
A
into
M
[]
stx 3
Copy
X
into
M
[]
jmp 6
Jump
to
label
ja 6
Jump
to
label
jeq 7,
8
Jump
on
k
==
A
jneq 8
Jump
on
k
!=
A
jne 8
Jump
on
k
!=
A
jlt 8
Jump
on
k
<
A
jle 8
Jump
on
k
<=
A
jgt 7,
8
Jump
on
k
>
A
jge 7,
8
Jump
on
k
>=
A
jset 7,
8
Jump
on
k
&
A
add 0,
4
A
+
<x>
sub 0,
4
A
-
<x>
mul 0,
4
A
*
<x>
div 0,
4
A
/
<x>
mod 0,
4
A
%
<x>
neg 0,
4
!A
and 0,
4
A
&
<x>
or {0,
4
A | <x>}
xor 0,
4
A
^
<x>
lsh 0,
4
A
<<
<x>
rsh 0,
4
A
>>
<x>
tax Copy
A
into
X
txa Copy
X
into
A
ret 4,
9
Return
Addressing mode
Syntax
Description
0 x/%x
Register
X
1 [k]
BHW
at
byte
offset
k
in
the
packet
2 [x
+
k]
BHW
at
the
offset
X
+
k
in
the
packet
3 M
[k]
Word
at
offset
k
in
M
[]
4 #k
Literal
value
stored
in
k
5 4*(
[k]
&0xf)
Lower
nibble
*
4
at
byte
offset
k
in
the
packet
6 L
Jump
label
L
8 #k,Lt
Jump
to
Lt
if
predicate
is
true
9 a/%a
Accumulator
A
10 extension
BPF
extension
(see
next
table)
Extension (and
alias)
Description
Further extension
details
(**)
Value
1 -
to
all
/
broadcast
2 -
to
group
/
multicast
3 -
to
others
(promiscuous
mode)
4 -
outgoing
of
any
type
8 -
APPLEtalk
19 -
ATM
24 -
IEEE
1394
IPv4
-
RFC
2734
32 -
InfiniBand
768 -
IPIP
tunnel
769 -
IP6IP6
tunnel
772 -
Loopback
device
778 -
GRE
over
IP
783 -
Linux-IrDA
801 -
IEEE
802.11
802 -
IEEE
802.11
+
Prism2
header
803 -
IEEE
802.11
+
radiotap
header
823 -
GRE
over
IP6
824 -
Netlink
[]
See
include/uapi/linux/if_arp.h
There are
two
types
of
comments
in
bpfc
source-files:
1. Multi-line
C-style
comments:
/*
put
comment
here
*/
2. Single-line
ASM-style
comments:
;
put
comment
here
Used Abbreviations:
Overview
bpfc is a small Berkeley Packet Filter assembler and compiler which is able to translate BPF assembler-like mnemonics into a numerical or C-like format, that can be read by tools such as netsniff-ng, iptables (xt_bpf) and many others. BPF is the one and only upstream filtering construct that is used in combination with packet(7) sockets, but also seccomp-BPF for system call sandboxing.
The Linux kernel and also BSD kernels implement "virtual machine" like constructs and JIT compilers that mimic a small register-based machine in BPF architecture and execute filter code that is, for example, composed by bpfc on a data buffer that is given by network packets. The purpose of this is to shift computation in time, so that the kernel can drop or truncate incoming packets as early as possible without having to push them to user space for further analysis first. Meanwhile, BPF constructs also find application in other areas such as in the communication between user and kernel space like system call sand-boxing.
At the time of writing this man page, the only other available BPF compiler is part of the pcap(3) library and accessible through a high-level filter language that might be familiar to many people as tcpdump-like filters.
However, it is quite often useful to bypass that compiler and write optimized code that cannot be produced by the pcap(3) compiler, or is wrongly optimized, or is defective on purpose in order to debug test kernel code. Also, a reason to use bpfc could be to try out some new BPF extensions that are not supported by other compilers. Furthermore, bpfc can be useful to verify JIT compiler behavior or to find possible bugs that need to be fixed.
bpfc is implemented with the help of flex(1) and bison(1), tokenizes the source file in the first stage and parses its content into an AST. In two code generation stages it emits target opcodes. bpfc furthermore supports Linux kernel BPF extensions. More about that can be found in the syntax section.
The Linux kernel BPF JIT compiler is automatically turned on if detected by netsniff-ng. However, it can also be manually turned on through the command ''echo "1" > /proc/sys/net/core/bpf_jit_enable'' (normal working mode) or ''echo "2" > /proc/sys/net/core/bpf_jit_enable'' (debug mode where emitted opcodes of the image are printed to the kernel log). An architecture agnostic BPF JIT image disassembler can be found in the kernel source tree under ''tools/net/bpf_jit_disasm.c'' or within the netsniff-ng Git repository.
Options
-
-i <source-file/->, --input <source-file/-> -
Read BPF assembly instruction from an input file or from stdin.
-
-p, --cpp -
Pass the bpf program through the C preprocessor before reading it in bpfc. This allows #define and #include directives (e.g. to include definitions from system headers) to be used in the bpf program.
-
-D <name>=<definition>, --define <name>=<definition> -
Add macro definition for the C preprocessor to use it within bpf file. This option is used in combination with the
-p/--cppoption.
-
-f <format>, --format <format> -
Specify a different output format than the default that is netsniff-ng compatible. The <format> specifier can be: C, netsniff-ng, xt_bpf, tcpdump.
-
-b, --bypass -
Bypass basic filter validation when emitting opcodes. This can be useful for explicitly creating malformed BPF expressions for injecting into the kernel, for example, for bug testing.
-
-V, --verbose -
Be more verbose and display some bpfc debugging information.
-
-d, --dump -
Dump all supported instructions to stdout.
-
-v, --version -
Show version information and exit.
-
-h, --help -
Show user help and exit.
Source examples
In this section, we give a couple of examples of bpfc source files, in other words, some small example filter programs:
- Only return packet headers (truncate packets):
ld poff
ret a
- Only allow ARP packets:
ldh [12]
jne #0x806, drop
ret #-1
drop: ret #0
- Only allow IPv4 TCP packets:
ldh [12]
jne #0x800, drop
ldb [23]
jneq #6, drop
ret #-1
drop: ret #0
- Only allow IPv4 TCP SSH traffic:
ldh [12]
jne #0x800, drop
ldb [23]
jneq #6, drop
ldh [20]
jset #0x1fff, drop
ldxb 4 * ([14] & 0xf)
ldh [x + 14]
jeq #0x16, pass
ldh [x + 16]
jne #0x16, drop
pass: ret #-1
drop: ret #0
- A loadable x86_64 seccomp-BPF filter to allow a given set of syscalls:
ld [4] /* offsetof(struct seccomp_data, arch) */
jne #0xc000003e, bad /* AUDIT_ARCH_X86_64 */
ld [0] /* offsetof(struct seccomp_data, nr) */
jeq #15, good /* __NR_rt_sigreturn */
jeq #231, good /* __NR_exit_group */
jeq #60, good /* __NR_exit */
jeq #0, good /* __NR_read */
jeq #1, good /* __NR_write */
jeq #5, good /* __NR_fstat */
jeq #9, good /* __NR_mmap */
jeq #14, good /* __NR_rt_sigprocmask */
jeq #13, good /* __NR_rt_sigaction */
jeq #35, good /* __NR_nanosleep */
bad: ret #0 /* SECCOMP_RET_KILL */
good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */
- Allow any (hardware accelerated) VLAN:
ld vlanp
jeq #0, drop
ret #-1
drop: ret #0
- Only allow traffic for (hardware accelerated) VLAN 10:
ld vlant
jneq #10, drop
ret #-1
drop: ret #0
- More pedantic check for the above VLAN example:
ld vlanp
jeq #0, drop
ld vlant
jneq #10, drop
ret #-1
drop: ret #0
- Filter rtnetlink messages:
ldh #proto /* A = skb->protocol */
jneq #0, skip /* check for NETLINK_ROUTE */
ldb [4] /* A = nlmsg_type */
jneq #0x10, skip /* check type == RTNL_NEWLINK */
ldx #16 /* X = offset(ifinfomsg) */
ldb [x + 4] /* offset(ifi_index) */
jneq #0x3, skip /* check ifindex == 3 */
ld #32 /* A = len(nlmsghdr) + len(ifinfomsg), payload offset */
ldx #16 /* X = IFLA_OPERSTATE */
ld #nla /* A = offset(IFLA_OPERSTATE) */
jeq #0, skip
tax
ldb [x + 4] /* A = value(IFLA_OPERSTATE) */
jneq #0x6, skip /* check oper state is UP */
ret #-1
skip: ret #0
Usage example
- bpfc fubar
-
Compile the source file ''fubar'' into BPF opcodes. Opcodes will be directed to stdout.
- bpfc -f xt_bpf -b -p -i fubar, resp. iptables -A INPUT -m bpf --bytecode `bpfc -f xt_bpf -i fubar` -j LOG
-
Compile the source file ''fubar'' into BPF opcodes, bypass basic filter validation and emit opcodes in netfilter's xt_bpf readable format. Note that the source file ''fubar'' is first passed to the C preprocessor for textual replacements before handing over to the bpfc compiler.
- cat fubar | bpfc -
-
Read bpfc instruction from stdin and emit opcodes to stdout.
- bpfc foo > bar && netsniff-ng -f bar ...
-
Compile filter instructions from file foo and redirect bpfc's output into the file bar, that can then be read by netsniff-ng(8) through option
-f.
- bpfc -f tcpdump -i fubar
-
Output opcodes from source file fubar in the same behavior as ''tcpdump -ddd''.
Legal
bpfc is licensed under the GNU GPL version 2.0.
Releases
8 releases since Debian 7 (Wheezy); the text changed 3 times.
--bypass since Debian 10 (Buster);
--cpp since Debian 10 (Buster);
--define since Debian 10 (Buster);
--dump since Debian 10 (Buster);
--format since Debian 10 (Buster);
--help since Debian 10 (Buster);
--input since Debian 10 (Buster);
--verbose since Debian 10 (Buster);
--version since Debian 10 (Buster);
-D since Debian 10 (Buster);
-V since Debian 10 (Buster);
-b since Debian 10 (Buster);
-d since Debian 10 (Buster);
-f since Debian 10 (Buster);
-h since Debian 10 (Buster);
-i since Debian 10 (Buster);
-p since Debian 10 (Buster);
-v since Debian 10 (Buster).