Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

eBPF

Overview

eBPF (extended Berkeley Packet Filter) is a Linux kernel technology that runs sandboxed, event-driven programs inside the kernel, attached to hooks such as system calls, network events, and function entry points — with no kernel modules and no application changes. Merged into Linux 3.15 in 2014 (the bpf() syscall that lets user space load programs landed in 3.18), it powers today’s observability, networking, and security tooling.

It evolved from the classic BPF packet filter (tcpdump’s filter language, 1992) into a general-purpose in-kernel VM with maps, JIT compilation, and a safety verifier.

How It Works

graph LR
    SRC["C / Rust source"] --> LLVM["clang/LLVM → eBPF bytecode"]
    LLVM --> LOAD["bpf() syscall"]
    LOAD --> VER["Verifier<br/>(safety checks)"]
    VER -->|"pass"| JIT["JIT compile to native code"]
    JIT --> ATTACH["Attach to hook<br/>(kprobe, tracepoint, XDP, ...)"]
    ATTACH --> EV["Event fires"]
    EV --> RUN["Program runs in kernel"]
    RUN --> MAP["Shared data structures (maps)"]
    MAP --> USR["User-space reads maps"]
  1. Write a small program in C/Rust; compile to eBPF bytecode with clang/LLVM.
  2. Load via the bpf() syscall. The kernel verifier statically checks the program: bounded loops (guaranteed termination), no arbitrary memory access, no unsafe pointer arithmetic, and correct types.
  3. JIT-compile to native machine code for the host CPU.
  4. Attach to a hook; run on every event, writing results into maps (hash maps, arrays, ring buffers, LPM tries) that user space reads.

Hooks (Attachment Points)

HookFires onUse case
kprobe / kretprobeKernel function entry/exitTrace any kernel function (e.g., do_sys_open)
tracepointStatic kernel tracepointsStable tracing (scheduler, block I/O, network events)
uprobe / uretprobeUser-space function entry/exitTrace app functions (e.g., SSL_read) without code changes
fentry / fexitBTF-typed kernel functionsLow-overhead function tracing
XDPPacket arrival at the NIC driverLine-rate packet filtering, DDoS mitigation, load balancing
tcTraffic-control layerPacket classification, shaping, policy
socketSocket operationsSocket filters, per-connection events
cgroupCgroup eventsContainer network isolation
LSMLinux Security Module hooksRuntime security policy (Falco, Tetragon)
perf_eventHardware/software countersContinuous profiling (stack sampling)

CO-RE and BTF

  • BTF (BPF Type Format) — kernel and program type metadata shipped with the kernel, enabling type-aware tracing and fentry/fexit.
  • CO-RE (Compile Once, Run Everywhere) — programs use relocations so one compiled binary adapts to different kernel versions/struct layouts; pairs with libbpf. This eliminated the old “compile per kernel” deployment burden.

Major Use Cases

AreaExamplesWhat it enables
ObservabilityBCC, bpftrace, Pixie, Parca, Cilium HubbleZero-code tracing of any language (HTTP/gRPC latency, syscalls, CPU profiling), including compiled Go/Rust binaries
NetworkingCilium (Kubernetes CNI + service mesh dataplane), XDP DDoS filteringIn-kernel packet processing at line rate (Cloudflare filters 10M+ pps), without kernel stack overhead
SecurityFalco, TetragonRuntime detection of execs, file opens, privilege changes, container escapes

eBPF vs Kernel Modules

eBPFKernel module
SafetyVerifier guarantees memory safety + terminationNo such guarantee; any bug can panic the kernel
DeploymentLoad from user space, no rebootinsmod/modprobe, kernel API lock-in
API surfaceRestricted helper setFull kernel API
Use forObserving/filtering kernel events, fast networkingNew drivers, custom file systems, features needing kernel APIs eBPF can’t express

Rule of thumb: observe or filter → eBPF; extend with new hardware/FS → module. For cloud-native infra (observability, networking, security), eBPF is the standard choice.

Interview Questions

Q: How does the eBPF verifier make programs safe?

It performs static analysis on the bytecode before loading: it walks every path to prove bounded loops (termination), verifies memory accesses are within bounds and correctly typed, rejects arbitrary pointer arithmetic, and ensures the program can’t write kernel memory it doesn’t own. Unsafe programs are rejected at load time — that’s what lets untrusted code run in the kernel.

Q: What are eBPF maps used for?

Maps are shared kernel↔user data structures (hash maps, arrays, ring buffers, LRU maps, LPM tries). The kernel-side program writes events/state into a map; user space reads and processes them (or configures the program by writing to the map). Maps are the communication channel between the event-driven kernel code and the userspace agent.

Q: How does XDP differ from processing packets in the kernel stack?

XDP runs before the kernel networking stack — at the NIC driver, on the raw packet. It can drop/forward/modify packets at line rate with minimal overhead, which makes it ideal for DDoS filtering and load balancing. The trade-off: it works on raw packets (no sockets), so TCP/IP stack features are unavailable at that point.

Q: Why is eBPF important for observability?

It can trace any process on the host without code changes or restarts: syscalls, function calls, HTTP/gRPC request latency, CPU stacks. Because programs run in-kernel and only aggregate into maps, overhead is low, and it works uniformly across languages including compiled binaries. This is why tools like Cilium, Pixie, and Tetragon are built on it.

References

  • eBPF Foundation / documentation — https://ebpf.io/
  • Linux kernel documentation: BPF — https://docs.kernel.org/bpf/
  • BPF and XDP Reference Guide (Cilium) — https://docs.cilium.io/en/stable/bpf/
  • Brendan Gregg’s BPF resources — https://www.brendangregg.com/ebpf.html
  • The eBPF verifier (kernel source, kernel/bpf/verifier.c) — https://docs.kernel.org/bpf/verifier.html