Writing eBPF code is somewhat new and not for the feint of heart. It's typically done in a low level subset of C with a myriad of gotchas and edge cases. If you follow me, you'll probably guess Rust is my goto language, but writing eBPF code in Rust is even trickier. Luckily, a collection of crates exist to make this job easier, but in using them I found a few areas that could use some improvement.
This series goes over the where the ecosystem stands right now, and ultimately the details of changes I'm proposing to make it even better.
I believe Rust can be the goto eBPF language with a little work.
$DAYJOB we've been experimenting with a new(ish) Linux kernel technology called eBPF. Before I dive into the specifics of eBPF, let me explain the simple use case we had so that we can keep it in the back of our mind as a concrete example.
We have a set of servers that accept traffic on a particular port and then aggregate and display this data to the users. This traffic was coming from a single source and contained data about a single entity.
The server application was designed to handle multiple external sources, where each source comes in over a different port.
Before we go any further I should (I want to) explain that we did not design or build this server application or architecture. We don't have control over how or why it operates this way.
-- (´ ͡༎ຶ ͜ʖ ͡༎ຶ )
One day we get notified that we will be receiving multiple entities from a single external source over a single port. This is a problem for the server application which has no knowledge of the "entities" and only differentiates
The boss says we need a solution to trick the server application into thinking all these entities came from different ports. Why can't we just actually use different ports, you ask? As mentioned, we don't control those external sources.
...hence the "external"
-- ᕕ(⌐■_■)ᕗ ♪♬
Luckily, the packets as the come across the wire have byte tags in the payload that differentiate entities.
I can hear you now, "Easy! Just use
-m string to match the bytes and clone the packets with
-j TEE to another port via the
Slow down buckaroo. It turns out I didn't give you all the details in order to keep this simple enough for a blog series. We also have some strict validation requirements along with this new multiple-entities-from-a-single-source
"upgrade." For the sake of this series we'll just use a narrow window for payload size narrow payload size (in addition to this entity:port mapping).
Just to be clear, we're in a situation where we don't control the server application, don't control the source of the packets, are getting new requirements piled on, and are expected to produce some magic to keep things operating as if nothing changed.
The solution should:
- Validate the packet
- Inspect packet payload to determine entity
- Apply some kind of mangle with an entity:port mapping
- Ideally we'd like something that is easily extendable to meet any surprise follow on requirements as well.
Oh, and we could be receiving a high volume of these packets so performance is key (i.e. a userspace implementation will probably not fly).
So there are probably many ways to solve this particular issue.
Having heard of eBPF and specifically XDP, I wanted to dig in and see how difficult this would be for us to implement. In theory, it'd be a quick program
that could be implemented in almost no time, not require any modifications to either the server application or external source system, and not impact
performance. Win, win, win.
This did, and didn't play out.
eBPF can absolutely solve this problem well. It isn't that difficult, and is extremely performant.
You feel a "but" coming on, don't you?...me to
-- ( ͡° ͜ʖ ͡°)
BUT, it required a lot C.
Here (and always) "C" also stands for confusion
(jokes, just jokes)
Just getting into eBPF was more of a challenge than I'd like. Because it's such a low level technology, and there are "competing" ecosystems I found the whole space confusing at first. Of course once I navigated the waters, everything makes sense and I can see why things are the way they are. Call it survivors bias, perhaps.
So the teaser is that it works, but that getting into the ecosystem was difficult. And even though the C wasn't too bad, I'd much rather be writing Rust. As it turns out, there is quite an excellent set of eBPF crates for Rust.
But this is still part 1, so lets first back up and go over the high level wave tops of what eBPF actually is.