BPF also known as Berkeley Packet Filters was introduced first in 1992 by Steven McCanne and Van Jacobson in their paper The BSD packet filter: A New architecture for user-level packet capture.
In this paper, they presented about BPF architecture and how it interfaced with the rest of the system and a new mechanism for filtering. If you want to read the paper you can find it here.
In the paper, they talked about a new virtual machine designed to work with register-based CPUs. Also the usage of per application buffers that could filter packets without copying all of packet information.
I will not go deep into the paper as it contains a lot of information that goes above my head. Here we will talk about how BPF is extended in today’s world and how you can write BPF programs.
What is eBPF?
In 2014. Alexei Starovoitov introduced the extended BPF implementation. BPF is an advanced VM, running in an isolated environment. It runs the piece of code that you write as a BPF program. You can consider it the same as JVM.
Components of eBPF
This is a code that you write to be loaded into the kernel. You can write it in C code and compilers that support BPF can convert it into BPF instructions.
This according to me is the most important part of this extended BPF. What it does is it verifies your program for various bugs like loops, make sure each code path reaches the end, etc to verify that you don’t crash your kernel or end up putting in an infinite loop. If keeps it safe and gives you trust to write the BPF program without thinking a lot about kernels.
The kernel also has a just in time compiler that transforms the BPF bytecode to machine code after the program is verified.
Since you are loading your program into kernel don’t think you need to restart the kernel. This is done while your system is running.
Components of BPF Code.
Now, what all your program actually contains. Your BPF program mainly has 3 components. The first part is the execution part of the kernel code. These execution points are predefined and you can use any of these to execute your program. For example, you can put the execution point to be a particular system. In this scenario whenever that particular system call is executed your BPF program will be executed.
Second is how you will share data between kernel and user-space. This can be done by using the BPF map. With these, you can share data in both directions. Whenever you create a BPF program you can create a BPF map for data sharing.
The third is your program what it actually does. Most of the times your use cases will fall in performance or troubleshooting categories.
So in short with BPF, you can execute your piece of code at any execution point in the kernel and with that code, you can measure the performance of system, filter network packets and many more such tasks.
In the coming articles I will try to write about how to write a BPF program and execute one. Since I am also a beginner in this space I am trying to explore and will keep you updated.
Why is it a superpower?
It gives you options to execute your code at multiple places in kernel. Earlier it was very tough and you generally need to compile a new kernel every time.
Its performance is much better than the traditional perf tools(Performance Counters) that we use for performance metering.
With these capabilities and its capability to chain multiple BPF programs gives you a powerful tool to write system tools.
That is why it can be used as a superpower.
If you like the article please share.