logo

eBPF vs LKM

The comparison between eBPF (extended Berkeley Packet Filter) and LKM (Loadable Kernel Module) is often described as the difference between JavaScript running in a web browser vs. C++ code running natively on your OS.

Loadable Kernel Modules (LKM)

An LKM is essentially a piece of the kernel that is loaded dynamically. Once loaded, it has full, unrestricted access to the entire kernel.

  • How it works: You write C code, compile it against the kernel headers, and load it using insmod. It becomes part of the kernel's address space.
  • Safety: Dangerous. If your LKM has a bug (like a null pointer dereference), the entire system will crash (Kernel Panic). There is no "sandbox."
  • Capabilities: Infinite. It can do anything the kernel can do: modify any memory, handle any interrupt, and implement new filesystems or complex hardware drivers.
  • Lifecycle: Requires recompilation almost every time you update your kernel version, as internal kernel APIs change.

eBPF (The Modern Alternative)

eBPF allows you to run custom code inside the kernel, but that code runs inside a restricted Virtual Machine (VM).

  • How it works: You write code (usually in C-style), which is compiled into eBPF bytecode. When you load it, the kernel runs it through a Verifier.
  • Safety: Very High. The Verifier ensures your code:
    1. Doesn't have infinite loops (it must terminate).
    2. Doesn't access invalid memory.
    3. Doesn't crash the kernel. If the code is unsafe, the kernel refuses to load it.
  • Capabilities: Restricted. eBPF programs cannot access arbitrary kernel memory. They can only interact with the kernel through "Helper Functions" and "Maps" (key-value stores).
  • Lifecycle: CO-RE (Compile Once, Run Everywhere). Thanks to a technology called BTF (BPF Type Format), an eBPF program can often run on different kernel versions without being recompiled.

Key Comparison Table

Feature LKM (Kernel Module) eBPF Program
Safety Low (Can crash the system) High (Guaranteed by Verifier)
Privilege Full Kernel Access Restricted (Helper functions only)
Speed Native (Fastest) Near-native (JIT Compiled)
Primary Use Hardware Drivers, Filesystems Monitoring, Networking, Security
Crash Risk High Zero (Kernel rejects bad code)
Complexity Hard (Kernel internals knowledge) Moderate (Easier toolchains like BCC)
Updating Must recompile for new kernels Portable across versions (CO-RE)

When to use which?

Use an LKM when:

  • You are writing a Hardware Driver (e.g., a new GPU or WiFi card).
  • You are implementing a new Filesystem (e.g., a new version of ZFS or Btrfs).
  • You need to modify the core behavior of the scheduler or memory manager that eBPF doesn't expose.

Use eBPF when:

  • Observability/Monitoring: You want to see how many times a specific function is called or trace disk I/O latency (e.g., tools like strace or bpftrace).
  • Networking: You want to filter packets at extreme speeds (XDP - Express Data Path) before they even reach the networking stack.
  • Security: You want to monitor system calls and block suspicious activity in real-time (e.g., Tetragon or Falco).