Unit Testing eBPF Programs
who.ldelossa.isThis past summer one of the Trail of Bits interns worked on a project to test BPF programs from userspace, independent of kernel version: https://blog.trailofbits.com/2023/01/19/ebpf-verifier-harnes...
It is still very much a proof of concept but could be a starting point to make future BPF testing easier.
In my experience, the hardest part of developing with eBPF is dealing with the multiple kernel versions and configurations that the target machines may have. It's a challenge not only because eBPF features were added gradually but because the internal data structures are not stable. While CO-RE finally makes it possible to be offset agnostic and allows for dealing with things like missing struct members, it's still very much a game of finding out when the code is deployed in the wild. Unit testing is important but I long for a way to easily test across a large matrix of kernel versions/configs (here at EdgeBit, we would be happy to pay for such a service).
BPF_PROG_RUN is great but unfortunately depends on the running kernel version. To that end, I wrote `vmtest` (https://dxuuu.xyz/vmtest.html) which is designed for the BPF_PROG_RUN use case.
Thank you for bpftrace! It was a vital aid for kernel spelunking. Very excited to see vmtest. I did a similar tool in the past [1] but never achieved this level of polish.
Beyond the content, kudos to the author for crisply and clearly laying out the intent of the article and prerequisites, referring to prior art for larger questions. Really enjoy the style.
Pardon my ignorance, is clang needed to build eBPF programs like it says in the article?
It's a C compiler with an eBPF backend, but if you wanted to compile from a different language, or had a different C compiler with an eBPF backend (like GCC), you could do that. `libpcap` contains a surprisingly sophisticated compiler for filter expressions (to cBPF, which is JIT'd to eBPF; compiling to cBPF is in some ways more impressive given its limitations).
One could always write the eBPF assembly by hand. I ended up doing that a few years ago when clang didn't yet have eBPF support. Would not recommend.
If your unit test needs root privileges, then it's not really a unit test in all but very few exceptional cases. It means you are interacting with the actually running kernel, which means you are integration testing, not unit testing.
Who cares?
Those who think they are unit testing but are actually integration testing.
This is not just about terminology. If you don't have actual unit tests then your testing is much more complicated and heavyweight than it needs to be with costly consequences for maintaining your software. You can run thousands of unit tests in a second if you just run some functions and verify their output. If you need to set up some hardness to interact with the kernel, virtualizing and mocking tons of facilities around it, then your testing speed goes down by orders of magnitude.