eBPF XDP Hooks
Student: Ankur Kothiwal <ankur AT SPAMFREE freebsd DOT org>
Mentors: Ryan Stone <rstone AT SPAMFREE freebsd DOT org>
Hiroki Sato <hrs AT SPAMFREE freebsd DOT org>
Implement eBPF XDP support to FreeBSD
The eBPF eXpress Data Path (XDP) allows eBPF programs to be run to filter received packets as early as possible, avoiding unnecessary processing overhead before the filter is run. The goal of this project is to extend an existing FreeBSD network driver (a virtual NIC like a VirtIO if_vtnet ) to be able to call into an eBPF program when processing a newly received packet. As in XDP the driver must PASS (accept and process normally), DROP, TX or REDIRECT the packet as specified by the program. eBPF helper functions and maps for aiding in packet filtering will also be implemented.
eBPF or extended Berkeley Packet filter is a virtual machine construct in the kernel (currently in Linux) to execute bytecode at various hook points in a safe manner. It is used in many Linux kernel subsystems, most prominently networking, tracing and security.
eXpress Data Path (XDP)
XDP is a framework to run eBPF programs in the Network Interface driver so that to return an output at the earliest time possible in the networking stack (works at L2-L3 layer). XDP is an eBPF program hook point where its attachment point is at the lowest level of the network stack. The eBPF eXpress Data Path (XDP) allows eBPF programs to be run to filter received packets as early as possible, avoiding unnecessary processing overhead before the filter is run. The goal of this project is to extend an existing FreeBSD network driver (a virtual NIC like a VirtIO if_vtnet ) to be able to call into an eBPF program when processing a newly received packet. As in XDP the driver must PASS (accept and process normally), DROP, TX or REDIRECT the packet as specified by the program. eBPF helper functions and maps for aiding in packet filtering will also be implemented The project to implement eBPF for FreeBSD is still under development. The aim of this project is to add new probe sites into the kernel that will call eBPF functions.
- XDP is a framework for running BPF programs in driver’s RX(receive) path. It basically:
- Ensures packets are linear, read/writable
- Is an atomic replacement of BPF programs during runtime
- Post-processing of 5 verdicts from the BPF program, they are pass, drop, tx, redirect and aborted
Is tailored for high-performance
Hooks run at the earliest possible point of definition when there is no allocation of mbuf.
1. Register a eBPF probe when a interface is registered with pfil
2. Activating eBPF probe
3. Create hooks and link them to the pfil head when the eBPF XDP probe is activated and successfully list the XDP probes
4. Create a xdp_rx function which will pass the received packets to the eBPF program where the packets can be further processed. This function will return XDP actions: DROP and PASS.
5. Register the xdp hook and link it to the head
6. Write an eBPF program to process (currently drop and pass) ICMP traffic - This is to test that the hook is working properly
7. Write a loader function to load the ICMP filter program to the kernel
Implemented XDP hook to pass and drop packets
Created a loader program to attach the eBPF program to the kernel
A test program to DROP ICMP filter
Approach for the project and the Implementation details
At first we need to register an interface when a program is attached to the eBPF probe. Packet filtering points are registered with pfil_head_register function. We used the pfil_head_register function to monitor if any interface is registered with pfil and call the ebpf_probe register function (inside pfil_head_register function in sys/net/pfil.c) to register ebpf_probe when the interface is registered.
When a program is attached to the probe (ebpf_probe) then add a hook to the pfil head for the same interface which was registered with pfil (by adding the hook in struct pfil_head sys/net/pfil.c).
To register a struct ebpf_probe when an interface is registered with pfil(9), and when a program is attached to the eBPF probe, we added a hook to the pfil head for the interface using ebpf_activate_probe(). But previously it was very specific to syscall probes, so for the activating probe we modified the ebpf_activate_probe to be of general use and moved the syscall related active probe to a new function ebpf_active_syscall_probe().
When initializing syscall probes, the activate pointer points to the ebpf_active_sycall_probe() function and when the XDP probe is registered, the function pointer can be set to a XDP related function. To register with pfil_add_hook, we used the ebpf_probe_fire() as a wrapper function which can be used as a general function and for syscall related callback we created a new function ebpf_syscall_probe_fire().
Now to activate the eBPF probe we wrote a xdp_activate() function under “pfil.c”.
What this function did is create hooks and link them to heads when an XDP probe is activated.This function will be called to start forwarding received packets to an eBPF program.
In brief the function did this:
i) Allocate a structure to hold the state that we create below. It needs fields to store the hook, ebpf_probe and state.
ii) pfil_add_hook() to create our hook.
iii) pfil_link() to link the hook to the head.
Add a struct ebpf_probe instance to struct pfil_head.
The hook created in Step 1. ii) needs a pointer to a pfil_func_t function. Create one called xdp_rx.
xdp_rx just has to pass the data in the format expected by an XDP program to ebpf_probe_fire() and return XDP actions - pass or fail.
Now to test the probe we wrote a simple program that blocks ICMP traffic and it can be tested by just sending ping traffic. We created a “load_program.c” to load the program into the kernel and attach the program to the probe.
Is the code merged upstream?
The eBPF implementation for FreeBSD is still a work in progress and FreeBSD doesn’t support eBPF yet. But the basic implementation for eBPF was a GSoC’18 project (SummerOfCode2018Projects/eBPF) which is still under development. This project is based on that implementation so XDP implementation for FreeBSD can only be merged into the FreeBSD source code once it supports eBPF.
So currently this code is merged to Ryan Stone’s branch which supports the basic eBPF implementation ( https://github.com/rysto32/freebsd/tree/ebpf-import )
Currently we can only attach the XDP hook to PASS and DROP the packets. The work on detaching the hook is left.
The XDP action to “TX” and “REDIRECT” the packets
Original repo: https://github.com/rysto32/freebsd/tree/ebpf-import
Repo containing all the changes: https://github.com/Ankurk99/freebsd/tree/ebpf-import
Week 1: https://docs.freebsd.org/cgi/getmsg.cgi?fetch=17347+0+archive/2020/soc-status/20200614.soc-status
Week 2: https://docs.freebsd.org/cgi/getmsg.cgi?fetch=11388+0+archive/2020/soc-status/20200621.soc-status
Week 3: https://docs.freebsd.org/cgi/getmsg.cgi?fetch=5448+0+archive/2020/soc-status/20200628.soc-status
Week 4: https://docs.freebsd.org/cgi/getmsg.cgi?fetch=5448+0+archive/2020/soc-status/20200628.soc-status
Week 5: https://docs.freebsd.org/cgi/getmsg.cgi?fetch=5304+0+archive/2020/soc-status/20200712.soc-status
Week 6: https://docs.freebsd.org/cgi/getmsg.cgi?fetch=4891+0+archive/2020/soc-status/20200719.soc-status
Week 7: https://docs.freebsd.org/cgi/getmsg.cgi?fetch=0+0+archive/2020/soc-status/20200802.soc-status
Week 8 and Week 9: https://docs.freebsd.org/cgi/getmsg.cgi?fetch=6048+0+archive/2020/soc-status/20200816.soc-status
Final Report SummerOfCode2020Projects/eBPFXDPHooks
Additional useful links
https://www.linuxplumbersconf.org/event/2/contributions/71/attachments/17/9/presentation-lpc2018-xdp-tutorial.pdf - This was used as a reference to write the ICMP filter program
https://speakerdeck.com/tuxology/the-bsd-packet-filter - The BSD packet filter by Steven McCanne and Van Jacobson ( The BSD Packet Filter )
https://raw.githubusercontent.com/tohojo/xdp-paper/master/xdp-the-express-data-path.pdf- The eXpress Data Path: Fast Programmable Packet Processing in the Operating System Kernel