0x00C - eBPF 🐝

0x00C - eBPF 🐝

Extended BPF (Berkeley Packet Filter).

Hi, y’all! Welcome to unzip.dev, a newsletter dedicated to unpacking trending developer concepts. My name is Agam More, and I’m a developer generalist. I’ll be posting every few weeks, so if you haven’t subscribed yet, go for it!

I've put some effort in re-designing unzip.dev's landing page - reply directly here if you have any suggestions because I'm preparing for a Product Hunt launch soon, any help is appreciated 🤩

TL;DR:

  • Problem: Extending the kernel safely and easily is hard.
  • Solution: eBPF lets you safely execute bytecode in the Linux kernel without changing the kernel source code or creating a kernel module.
  • In Sum: eBPF adds JavaScript-like capabilities to the Kernel - resulting in a safe, performant, and fast development cycle.

How does it work? 💡

With eBPF you can interact with the Kernel from User space in a way that keeps security and stability without patching the Kernel.

An abstraction over eBPF could be bpftrace, but if you want to create an eBPF program from scratch you’d need to:

  1. Write your eBPF program (C/Rust while user-space code can be written via high-level language bindings like python) including a kernel hook (eBPF programs are event-driven) using helpers.
  2. Compile your eBPF program into bytecode the Kernel can run.
  3. Then your eBPF is loaded and verified (important step, as eBPF exposes a path for unprivileged users to execute in ring0).
  4. Attaching the program to its hook - eBPF programs execute when they get an event.
  5. Interact back with user-space programs via eBPF maps - the main way to communicate back with user-space.

Originally, BPF (no “e”, sometimes called cBPF) was used for networking-related tasks (used for tcpdump). eBPF extended this functionality by extending out from the networking subsystem allowing you to attach programs to a tracepoint or a kprobe, uprobes, and more… - which opened the door to many other use cases other than networking.

The traditional way of achieving many of the eBPF use cases was using an LKM (a kernel module). The drawbacks of an LKM are plenty: Kernel releases often break your module. New Kernel versions mean you need to rewrite your LKM. Lastly, there is a good chance you’ll crash the kernel - there are no safeties in place. The only other option is to get code into the Linux kernel directly, which could take a few years and might not be approved.

Use cases ✅

  • Observability, profiling, and tracing - monitor OS events.
  • Security tools.
  • Networking tools - fast packet processing.
    • Create new custom network parsers?
  • Debugging tools.
  • Many use cases traditionally for a Loadable Kernel Module.

Why? 🤔

  • Robustness: If you were ever worried about crashing, hanging, or interfering with the kernel negatively, eBPF saves you from that because it’s sandboxed and verified.
  • Speed: You gain performance improvements by running directly in the kernel compared to a user space program. Couple that with a built-in JIT compiler, and you get some speed increases (note that you’re still running in an emulator, so an LKM might still be faster).
  • Ease of development: You have fewer ways to shoot yourself in the foot compared to an LKM. Plus, using high-level languages adds more developer convenience. Mitigating crashes helps with testability.

Why not? 🙅

  • Limitations: because eBPF programs need to be secured, they are verified and limited.
    • Feature-limits: eBPF programs are mostly used for observability (other than filtering network packets). Kernel modules are still more versatile if you need to act and manipulate the OS (like the filesystem).
    • Complex programs: loops, advanced pointer manipulation, large program size and more are restricted.
  • CPU intensive: eBPF programs consume CPU cycles, which can increase cloud provider costs.
  • Attack vector: eBPF can expose critical kernel systems to an attacker in some scenarios. LKMs are at least signed (at the moment, there are some strides forward).
  • Old Linux Kernel needed: To make use of eBPF fully, you’d need Linux 4.4 or above.

Tools & players 🛠️

  • BCC - a toolkit to simplify eBPF program creation with python and Lua frontends.
  • Falco - cloud-native (k8s) runtime security tool via eBPF.
  • Cilium - network observability and security platform using eBPF.
  • facebookincubator/katran - an eBPF-based L4 Load-balancer by Facebook.
  • teleport - easily access your infrastructure, a replacement to ssh (uses eBPF for recording user’s network actions).
  • bpftrace - as @Matan Cohen said “it’s like SQL for eBPF”, making eBPF easy without the need for a fully-fledged eBPF program from scratch.
  • tetragon - a really cool tool by the Cilium team to enforce security policies.
  • Aya - write eBPF programs with Rust.
  • SDK’s: Go, Rust.
🤠
My opinion:  If you thought about a program that needs to be a Kernel module, I would check eBPF first. I’d probably check BCC.

Forecast 🧞

Examples ⚗️

Try it out yourself?

Extra ✨

Some extra information that is related to the subject matter:

Thanks 🙏

I wanted to thank @Matan Cohen (an eBPF guru with some helpful insights), @Alon Zivony (security researcher at Aqua Nautilus, focused on kernel exploits and eBPF), @Valeri Pliskin (creating some cool eBPF solutions at Datadog and runs the eBPF Israel community), @Michel Heily (performance researcher at Granulate (Acquired by Intel) working on eBPF open source profiler), Bill Mulligan (🐝 pollinating the Cilium and eBPF open source communities), and last but not least @TomGranot (the best DevRel I know).

EOF

(Where I tend to share unrelated things).

I really enjoyed this talk about Programming’s Greatest Mistakes by Mark Rendle - some great history and funny anecdotes.


Any questions, feedback, or suggestions are welcome 🙏

Simply reply to this e-mail or tweet at me @agammore - I promise to respond!