Stage 3 · Build
Observability, Tracing & Security
eBPF Fundamentals
Verifier, maps, helpers, and writing your first eBPF program — programmable kernel without the risk.
What Is eBPF?
eBPF (extended Berkeley Packet Filter) is a virtual machine built into the Linux kernel that runs sandboxed programs at kernel level. It lets you hook into almost every kernel function and syscall without modifying kernel code or loading modules. eBPF powers modern networking, observability, and security tools.
- Networking — XDP, traffic control, load balancing (Cilium, Katran)
- Observability — Tracing, profiling, metrics (bpftrace, Pixie)
- Security — Syscall filtering, runtime enforcement (Falco, Tracee)
- Storage — I/O optimization, caching (bcc, bpftrace)
# Check kernel eBPF support
bpftool feature probe | head -20
# bpf_probe_read: yes
# bpf_probe_read_kernel: yes
# bpf_trace_printk: yes
# bpf_perf_event_output: yes
# Check available eBPF program types
bpftool feature probe | grep "prog_type"
# Kernel config
zgrep BPF /proc/config.gz
# CONFIG_BPF=y
# CONFIG_BPF_SYSCALL=y
# CONFIG_BPF_JIT=yeBPF requires kernel 4.1+ for basic features, 4.15+ for tracing, and 5.x+ for advanced features like bpf_ringbuf and BTF.
The Verifier
The eBPF verifier analyzes every program before loading it into the kernel. It ensures the program always terminates, accesses memory safely, and does not crash the kernel. The verifier is the key safety mechanism that makes eBPF safe for production.
# When a program fails verification, you see detailed errors
sudo bpftool prog load program.o /sys/fs/bpf/test
# libbpf: prog 'my_func': BPF program is too large: processed 100001, max 1000000
# Check loaded program stats
sudo bpftool prog show
# id 1 type tracing tag abc123...
# loaded_at 2024-01-15T10:30:00+0000 uid 0
# xlated 256B jited 128B memlock 4096B
# run_cnt 1234 run_ns 567890The verifier rejects programs that could hang the kernel, access invalid memory, or have unbounded loops. It transforms eBPF from a potential security risk into a safe, sandboxed execution environment.
eBPF Maps
Maps are eBPF's data structures for sharing data between the kernel program and userspace. They are used to store statistics, histograms, configuration, and event data. Maps are created in userspace and shared with the kernel program.
# List all maps
sudo bpftool map list
# id 1 name events type ringbuf ...
# id 2 name stats type array ...
# Dump a map
sudo bpftool map dump id 2
# key: 00 00 00 00 value: 34 12 00 00
# key: 01 00 00 00 value: 56 34 00 00
# Common map types
# hash — Key-value storage
# array — Fixed-size indexed array
# percpu_hash — Per-CPU hash (no locking needed)
# ringbuf — Event streaming from kernel to userspace
# stack_trace — Kernel stack tracesPer-CPU maps avoid lock contention by giving each CPU its own copy. Use them for high-frequency counters and statistics.
Helper Functions
Helper functions are the API between eBPF programs and the kernel. They provide access to kernel data, event output, timer management, and more. Each program type has a specific set of allowed helpers.
# bpf_probe_read — Read kernel memory safely
# bpf_probe_read_user — Read userspace memory
# bpf_ktime_get_ns — Get current time in nanoseconds
# bpf_get_current_pid_tgid — Get current PID/TID
# bpf_get_current_comm — Get current process name
# bpf_perf_event_output — Write event to userspace
# bpf_map_update_elem — Update a map entry
# bpf_map_lookup_elem — Look up a map entry
# bpf_ringbuf_submit — Submit event to ring buffer
# Check available helpers
bpftool feature probe | grep helperHelper functions change between kernel versions. Use bpftool feature probe to check which helpers are available on your kernel.
Writing eBPF Programs
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
SEC("tracepoint/syscalls/sys_enter_openat")
int handle_openat(void *ctx)
{
__u32 pid = bpf_get_current_pid_tgid() >> 32;
char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));
bpf_printk("pid=%d comm=%s opening file", pid, comm);
return 0;
}
char LICENSE[] SEC("license") = "GPL";This program traces openat() syscalls and prints the PID and process name. The SEC macro places the program in the correct ELF section. The LICENSE macro is required for GPL-only helpers.
# Compile with clang
clang -O2 -target bpf -c program.c -o program.o
# Load into kernel
sudo bpftool prog load program.o /sys/fs/bpf/trace_openat
# View output
sudo cat /sys/kernel/debug/tracing/trace_pipe
# <...>-12345 [001] d... 1234.567: bpf_printk: pid=12345 comm=bash opening file
# Attach to tracepoint
sudo bpftool prog attach pinned /sys/fs/bpf/trace_openat tracepoint /sys/kernel/events/syscalls/sys_enter_openatbpftool prog load compiles and loads the program. The program is pinned to bpffs (/sys/fs/bpf/) for persistence across reboots.
Program Types and Attach Points
| Program Type | Attach Point | Use Case |
|---|---|---|
| tracepoint | Tracepoint events | Syscall tracing, scheduling |
| kprobe | Any kernel function | Dynamic function tracing |
| XDP | NIC driver | Wire-speed packet processing |
| tc | Traffic control | Packet modification |
| cgroup_skb | Cgroup network | Container network policy |
| LSM | LSM hooks | Security enforcement |
eBPF is replacing kernel modules for many use cases. It provides kernel-level capabilities with userspace-level safety. Learn eBPF to build the next generation of observability, networking, and security tools.
The verifier rejects programs that could run forever. Always ensure loops have a bounded iteration count. Use bpf_loop() helper for safe loops in newer kernels.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.