eBPF: the Technology Rewiring Cloud-Native Infrastructure
How eBPF lets you run verified, JIT-compiled programs inside the Linux kernel — the verifier, maps, and hook points that make it safe, plus the ecosystem of Cilium, Tetragon, bpftrace, and Hubble built on top.
Published on • November 15, 2026
AI Assistant

For two decades, changing kernel behavior meant filing a patch, rebuilding a module, or waiting for a distribution update. eBPF dissolved that bottleneck. You can now load small, sandboxed programs into a running Linux kernel and have them execute at hook points ranging from the network card’s ingress path to a syscall entry — without rebooting, without patching, and without risking a kernel panic. That single capability is why Cilium replaced iptables in Kubernetes clusters, why Cloudflare filters DDoS traffic below the syscall boundary, and why Netflix can ask questions about production traffic it never anticipated when the kernel was compiled.
This post walks through the mechanics: what an eBPF program actually is, the verifier that makes in-kernel execution safe, the maps that move data in and out, the hook landscape, and the projects that turned a kernel feature into an industry.
From Packet Filter to Kernel-Resident VM
eBPF’s ancestry is mundane: the classic Berkeley Packet Filter (BPF), described by McCanne and Jacobson in 1993, existed to filter packets for tcpdump with minimal copying. The extended BPF arrived in Linux 3.18 (2014) and quietly changed the contract. Instead of a fixed predicate language for packets, it became a general-purpose, register-based virtual machine: 11 64-bit registers, a bounded stack, a restricted set of helper functions, and a flat instruction set of loads, stores, arithmetic, and jumps.
Programs are written in C (or Rust, via projects like Aya) and compiled with clang -target bpf into BPF bytecode. At load time the kernel checks the bytecode, and if it passes, a JIT compiler translates it into native machine code for the host architecture — x86-64, arm64, and others — so hot-path execution is essentially native speed, with the interpreter only as a fallback.
// A minimal XDP program: drop packets arriving on TCP port 9000
SEC("xdp")
int drop_port(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end)
return XDP_PASS;
if (eth->h_proto != htons(ETH_P_IP))
return XDP_PASS;
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end)
return XDP_PASS;
if (ip->protocol != IPPROTO_TCP)
return XDP_PASS;
struct tcphdr *tcp = (void *)ip + (ip->ihl * 4);
if ((void *)(tcp + 1) > data_end)
return XDP_PASS;
if (ntohs(tcp->dest) == 9000)
return XDP_DROP;
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
Note what is absent: no malloc, no unbounded loops, no arbitrary pointer arithmetic, no calls to arbitrary kernel functions. That is not a style choice — it is the verifier’s contract.
The Verifier: Why Running Code in the Kernel Is Safe
The kernel must never be crashed or hung by a loaded program, so every program is statically analyzed before it is allowed to attach. The verifier explores the program’s control-flow graph and proves, before a single instruction runs, that:
- Every path terminates. Loops must have a statically provable bound (bounded loops became legal in Linux 5.3); the verifier caps total explored instructions (1 million since Linux 5.2) and rejects anything it cannot fully explore.
- Every memory access is in bounds. Stack slots, packet pointers, and map value pointers are tracked with precise ranges; out-of-bounds reads and writes are rejected at load time.
- Every call is legal. Programs can only invoke helper functions whitelisted for their program type, with argument types checked. A socket program cannot call a tracing helper, and nothing can call
printkat will. - Every register holds a known type. The verifier tracks value types (scalar, packet pointer, map value) so a pointer cannot be smuggled in as an integer.
If any check fails, the program is rejected with a load error and never runs. Programs also execute with a strict privilege model: since Linux 5.8, loading most program types requires CAP_BPF (and CAP_PERFMON for tracing), and unprivileged BPF is disabled by default on most distributions. In practice, eBPF is a privileged capability — the safety story is about protecting the kernel from buggy or hostile authorized code, not about sandboxing untrusted tenants.
Maps: The Only Way In and Out
eBPF programs cannot access user memory directly. All state lives in maps — kernel data structures with a defined key/value layout that both the kernel program and user space can read and write:
| Map type | Shape | Typical use |
|---|---|---|
BPF_MAP_TYPE_HASH | key → value | connection tables, custom counters |
BPF_MAP_TYPE_ARRAY | fixed index → value | configuration, per-CPU statistics |
BPF_MAP_TYPE_RINGBUF (5.8+) | shared ring buffer | high-volume event streaming to user space |
BPF_MAP_TYPE_LPM_TRIE | prefix → value | CIDR routing tables |
BPF_MAP_TYPE_STACK_TRACE | id → stack | profiling, flame graphs |
Maps are the reason eBPF tooling can aggregate in the kernel: a tracing program increments a histogram bucket inside the kernel, and user space reads the final histogram once a second instead of shipping millions of samples across the syscall boundary. The ring buffer, introduced in Linux 5.8, largely replaced the older perf buffer for event streaming with lower overhead and per-record reservation semantics.
Where You Can Hook
A program’s power comes from its attachment point. The major families:
- XDP (eXpress Data Path): the earliest possible point in the NIC receive path, running before sk_buff allocation. Used for DDoS mitigation and load balancing — Meta’s Katran balances every packet entering its data centers at this layer. Works in native, generic, and offloaded modes.
- tc (traffic control): ingress/egress packet processing with the ability to drop, redirect, or encapsulate — the sweet spot for Cilium’s policy enforcement.
- kprobes / kretprobes: dynamic probes on essentially any kernel function, used by tracing tools; superseded in new code by fentry/fexit (Linux 5.5), which use BPF trampolines for lower overhead.
- tracepoints / raw_tracepoints: static, stable instrumentation sites compiled into the kernel (syscalls, scheduler, block I/O). A tracepoint is a contract; a kprobe on an internal function is not.
- uprobes: the kprobe equivalent for user-space binaries — attach to a symbol in
libc,openssl, or a JVM and observe arguments without rebuilding anything. - cgroup and socket hooks: per-cgroup ingress/egress,
sock_opsfor connection lifecycle,setsockopt/getsockoptinterception. - LSM hooks (Linux 5.7): BPF programs on the Linux Security Module interface, enabling policy enforcement (
security_file_open,security_task_kill) with the kernel’s own authorization framework — this is Tetragon’s enforcement backbone. - perf events: sampling-based profiling feeding tools like
perf-compatible stack collection.
The Ecosystem Built on eBPF
The kernel feature became an industry once the user-space toolchain matured. The load-bearing pieces:
- libbpf + CO-RE: the official loader library, plus Compile Once – Run Everywhere — BTF (BPF Type Format) type metadata embedded in binaries lets one compiled object run across kernel versions without a compiler on the target host.
- BCC and bpftrace: BCC (2016) pioneered Python-driven on-the-fly compilation; bpftrace added a DTrace-like one-liner language that remains the fastest way to answer a production question:
# Which processes are opening files, and with what path?
bpftrace -e 'tracepoint:syscalls:sys_enter_openat
{ printf("%s -> %s\n", comm, str(args->filename)); }'
- Cilium: eBPF-based CNI, network policy, and service mesh for Kubernetes — now the default dataplane in GKE Dataplane V2 and a CNCF graduated project.
- Hubble: Cilium’s observability layer, turning kernel-level flow data into service maps, DNS-aware policy debugging, and Prometheus metrics.
- Tetragon: runtime security and observability built on kprobes and LSM hooks — process execution, file access, and socket events filtered by Kubernetes identity.
- Katran (Meta) and Pixie: respectively, an XDP load balancer and a Kubernetes observability platform that both push computation into the kernel.
Google, Netflix, Android, Cloudflare, and S&P Global all run eBPF in production for tasks that previously required custom kernel modules or appliance hardware.
Why It Stuck
Three properties reinforce each other. Performance: JIT-compiled code on the XDP path processes packets in nanoseconds, not microseconds. Safety: the verifier means operators can load custom code on a Tuesday without scheduling a maintenance window. And operability: programs are versioned artifacts with a defined lifecycle — attach, observe via maps, detach — that fits GitOps and Kubernetes mental models better than kernel patches ever did.
That combination is what rewired cloud-native infrastructure: the kernel became programmable by the people who operate it, and the observability, networking, and security tooling of the last five years is the result.
References
- eBPF.io — What is eBPF: https://ebpf.io/
- eBPF Documentation (libbpf, verifier, maps, program types): https://docs.ebpf.io/
- Linux kernel BPF documentation: https://www.kernel.org/doc/html/latest/bpf/
- McCanne & Jacobson, “The BSD Packet Filter: A New Architecture for User-level Packet Capture” (1993): https://www.tcpdump.org/publications/bpf.pdf
- Isovalent: Tetragon runtime security: https://tetragon.io/