There is a layer of software that has changed, over the last few years, the way Linux servers are observed and protected, and it almost never makes the headline: eBPF. Its starting idea is easy to state and hard to deliver: letting user-written code run inside the kernel, at the exact moment an event happens, without recompiling the kernel, without loading a module and without rebooting the machine.
From a packet filter to a virtual machine inside the kernel
The name comes from the Berkeley Packet Filter, the mechanism that tools such as tcpdump have used since 1992 to discard packets inside the kernel before copying them to user space. The «e» (for extended) arrived in 2014 with a deep redesign: instead of an ad-hoc filter for network capture, the kernel began exposing its own instruction set —a 64-bit RISC bytecode, today with eleven general-purpose registers, a bounded stack and limited memory access— that can be attached to many different points in the system.
That bytecode is the contract. The program is written in C (or in Rust, or directly in BPF assembly), compiled with Clang into an ELF object holding the instructions plus a section table that declares where it wants to attach. The libbpf library reads that object, and a single system call, bpf(2), hands it to the kernel for loading.
The verifier: nothing gets in without proving it is safe
This is the piece that makes everything else viable. Before a single instruction runs, the kernel’s verifier walks the bytecode and tries to prove, statically, that the program cannot do harm: that it does not follow arbitrary pointers, that it does not read memory outside declared bounds, that every jump lands on a valid path, that there are no infinite loops and that the program always finishes —it cannot hang inside an atomic kernel context.
To do so it tracks, instruction by instruction, the type state of each register: whether a pointer comes from a socket context, a map or a helper function, the verifier knows which operations it accepts. Any path it cannot justify is rejected with an error message and the program simply never loads. The limits are explicit: up to one million verified instructions, and bounded loops (loops with a provable bound have been allowed since kernel 5.3; before that they were banned outright).
Once validated, the program is compiled on the fly by the JIT: the bytecode is translated into native instructions for the architecture —x86-64, ARM64— and runs at kernel-code speed, with no interpreter in between. The price of safety is verification time at load, not latency on every event.
Where an eBPF program attaches
The kernel offers a broad catalogue of attachment points (hooks), and choosing the right one is half the design of any tool:
- kprobes and uprobes: inserted dynamically at the start of any kernel function or user binary without modifying it. They are the backbone of debugging instrumentation.
- tracepoints: stable points the kernel itself declares; narrower in coverage, far more reliable across versions.
- XDP (eXpress Data Path): the program runs in the network card driver, right after a packet arrives and before the
sk_buffstructure has even been built. It is the fastest hook and the basis of volumetric flood defences: an XDP program can returnXDP_DROPand discard malicious traffic at millions of packets per second per core. - tc (traffic control) and cgroup: traffic classification and network or resource policies applied to a group of processes.
- LSM: the kernel’s security modules can be complemented with eBPF rules, doing away with the need to compile a custom module.
Maps: shared state without copying memory
An eBPF program cannot allocate arbitrary memory or talk to files directly. Its state and its output channel are maps: key-value structures created by the user and managed by the kernel, such as hash tables, arrays, per-CPU counters (per-CPU, which avoid contention between cores) or ring buffers that push events to user space with very little copying. Programs talk to each other and to the application that controls them through those maps, and helper functions are the only door to the rest of the kernel: reading the clock, generating a random number, redirecting a packet, updating a map.
Portability: BTF and CO-RE
The historical problem was fragility: a program that read a field of a kernel structure broke whenever that structure changed between versions. The fix arrived with BTF (BPF Type Format), a description of types embedded in the kernel itself, and CO-RE (Compile Once – Run Everywhere): the compiler records field access paths in the object, and the loader resolves them against the real types of the machine where it runs. A single binary thus works on a 5.15 kernel and on a 6.11 one.
Who is allowed to use it
Loading eBPF is a privileged capability: the code runs with kernel privileges. Since version 5.8 there are the narrower capabilities CAP_BPF and CAP_PERFMON, which allow observability to be delegated without handing over full root, and the verifier itself carries mitigations against Spectre-style side-channel attacks, as well as masking kernel reads. On most distributions, unprivileged use is disabled by default through kernel.unprivileged_bpf_disabled. Recent versions add BPF tokens to authorise specific loads inside containers without opening the whole door.
Where it shows up in production
The practical consequence is a different model from that of traditional libraries. In observability, tools such as bpftrace or kernel metric collectors make it possible to see system call latencies, disk failures and CPU contention at a cost in the low single-digit percentages. In networking, Cilium replaces much of the iptables rule set with eBPF programs on sockets and XDP, and load balancers such as Katran build traffic-spike resilience on XDP. In security, runtime container detection leans on eBPF hooks to watch processes and system calls without instrumenting every application.
The common pattern is clear: instead of exporting ever more telemetry to user space so the decision can be made there, filtering, deciding and dropping happen where the data is born. The price is a program format of its own, a strict verifier and a privilege model that must be managed carefully. The payoff is a programmable control layer running inside the core of the operating system.






