Quick and dirty BPF map updates with bpftool
Thu Jan 23 '20
The bpftool
utility makes easy work of reading and updating BPF maps. This
post will start with a really simple BPF program that uses a map to determine
whether a message should be printed on each packet that goes through a BPF
program running on a route.
Dependencies
You’ll need to start by installing dependencies. Assuming you’re already set up for bpf development[1], you may just need the bpftool package.
zypper in bpftool
The BPF program
boring
Here is a minimal[2] BPF program that has some maps we can use for
demonstration. This example is meant to attach to a route using
BPF_PROG_TYPE_LWT_OUT
. But really, it’s not picky. It will attach to anything
that is foolish enough to give it a skb
and accept payment in the form of
BPF_OK
in return.
Note this is not using the new-style BTF maps; updating this example to use them is an exercise left to the reader.
// lwt_out.c
#include <linux/bpf.h>
#include "include/bpf_helpers.h"
struct bpf_map_def SEC("maps") printk_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 1
};
SEC("lwt_out")
int prog_lwt_out(struct __sk_buff *skb)
{
const char fmt[] = "Print about this skb boi\n";
int *value, key = 0;
value = bpf_map_lookup_elem(&printk_map, &key);
if (value && !*value)
bpf_trace_printk(fmt, sizeof(fmt));
return BPF_OK;
}
char _license[] SEC("license") = "GPL";
This blog may contain nuts.
In a nutshell[3], unless otherwise told by the value in the
BPF_MAP_TYPE_ARRAY
, this program will print on every single packet that leaves
via the route it is attached to. Compile the program and install it to a route:
clang -O2 -g -Wall -Werror -target bpf -emit-llvm -c lwt_out.c -o - | \
llc -march=bpf -filetype=obj -o lwt_out.o
pahole -J lwt_out.o
ip link add dum-dum type dummy
ip link set dum-dum up
ip route add 192.0.2.0/32 dev dum-dum encap bpf out obj lwt_out.o section lwt_out
Note pahole
is optional but converts the DWARF format into BTF format for
better debugging.
At this point, you should be able to ping 192.0.2.0
and see the printk output:
bpftool prog tracelog
ping-24366 [007] .... 18601.103214: 0: Print about this skb boi
ping-24366 [007] .... 18602.125616: 0: Print about this skb boi
Enter the maps
Okay. Obviously this blue part here is the land…
Now that we have our super useful and not-at-all demonstrative program running, we need a way to turn logging on and off using the map we created in lwt_out.c above.
Lets first get an idea of what our map looks like. The map_ids
field shows a
list of maps attached to our program.
bpftool prog show id 85 | grep lwt_out -A2
85: lwt_out tag 671a86d804002c58 gpl
loaded_at 2020-01-21T21:56:21-0800 uid 0
xlated 264B jited 164B memlock 4096B map_ids 25
Knowing the map ID, we can show it.
bpftool map show id 25
25: array flags 0x0
key 4B value 4B max_entries 1 memlock 4096B
There’s a --json
flag to output as JSON. There’s a --pretty
flag you can
use instead of piping to jq
, but I find the latter prettier.
bpftool map dump id 25 -j | jq
[
{
"key": [
"0x00",
"0x00",
"0x00",
"0x00"
],
"value": [
"0x00",
"0x00",
"0x00",
"0x00"
]
}
]
Finally, updating the map is trivial with bpftool. The printk output should stop immediately upon entering this command.
bpftool map update id 25 key 0 0 0 0 value 1 0 0 0
Easy! Note that this will parse the byte sequence as decimal, but you can prefix
it with 0x
for hexadecimal, or 0
for octal. Alternatively, if you get sick
of typing 0x
in front of everything, just use the hex
keyword in front of
the byte sequence.
For technically-accurate and better-wrtten content on bpftool
’s abilities
around maps, check out man bpftool-map
.
Now that you know how easy it is, get out there, and be creative!∎