Skip to main content
  1. Index/

eBPF (Extended Berkeley Packet Filter)

eBPF (Extended Berkeley Packet Filter) is a Linux kernel subsystem, its modern form dating to kernel 3.18 (2014), that allows user-authored programs to run inside the kernel with near-native performance, subject to safety guarantees enforced at load time by a verifier. The name is historical: the original BPF (Berkeley Packet Filter, 1992) was a narrow packet filtering mechanism for tools like tcpdump. eBPF extended the instruction set, registers, and capabilities far beyond packet filtering into a general-purpose in-kernel programmability platform. The central design constraint is that eBPF programs must be provably safe: they cannot crash the kernel, loop infinitely, or access memory out of bounds. The verifier statically analyses every program at load time, checking that all memory accesses are bounds-checked, all loops are bounded or unrolled, and all pointer dereferences are preceded by null checks. Only programs that pass verification are accepted; once accepted, the kernel JIT-compiles the eBPF bytecode to native machine code for the host architecture — x86-64, ARM64, RISC-V — so eBPF programs run at the same speed as compiled kernel code, not as an interpreter.

An eBPF program is defined by its program type, which determines where it attaches and what context it receives. Tracing programs attach to kprobes (dynamic hooks on arbitrary kernel functions, potentially unstable across kernel versions), kretprobes (function return), tracepoints (stable, curated kernel events with a documented ABI), fentry/fexit (BTF-enabled hooks on function entry/exit with lower overhead than kprobes), and uprobes/uretprobes (user-space function entry/return, allowing kernel-side instrumentation of user-space libraries without modifying application code). Networking programs attach to XDP (eXpress Data Path, in the NIC driver’s receive path before the kernel allocates a socket buffer — the earliest possible intervention point, capable of line-rate packet processing for load balancing, DDoS mitigation, and firewall), TC (Traffic Control, deeper in the kernel’s networking stack, for both ingress and egress), and socket operations hooks. Security programs attach to LSM hooks (BPF LSM, kernel 5.7) to implement custom MAC policy stacked alongside SELinux or AppArmor. cgroup programs attach to cgroup ingress/egress and socket creation hooks. State is shared between eBPF programs and with user space through eBPF maps — typed, kernel-managed key-value stores (hash maps, arrays, LRU maps, ring buffers, per-CPU maps) that persist across program invocations. CO-RE (Compile Once, Run Everywhere), enabled by BTF (BPF Type Format) metadata embedded in the kernel, allows eBPF programs compiled against one kernel version to run on different kernels without recompilation, resolving the portability problem that plagued earlier eBPF tooling.

The ecosystem built on eBPF covers all three layers of the infrastructure stack in this glossary. In networking, Cilium uses eBPF to implement Kubernetes network policy, service load balancing, and pod-to-pod encryption at XDP/TC, replacing iptables with a fully programmable data plane that scales to tens of thousands of pods without iptables rule explosion; Hubble builds on Cilium’s eBPF programs to provide network flow observability with pod and namespace context. In observability, tools like Pixie, Parca, Falco, and bpftrace use kprobes, uprobes, and tracepoints to collect system call traces, CPU profiles, memory allocations, and application-layer HTTP/gRPC/SQL events without any application instrumentation, with sub-microsecond overhead. In security, BPF LSM programs provide per-workload policy enforced inside the kernel, the Security Profiles Operator uses eBPF to record syscall profiles for seccomp generation, and Falco’s kernel driver uses eBPF to detect anomalous behaviour (unexpected execve, network connections from unexpected processes, privilege escalation attempts) with full container and pod context. The relationship to seccomp is complementary: seccomp operates on a static allowlist evaluated per-syscall with no kernel-side logic; eBPF LSM and tracing programs can express dynamic, context-aware policy (allow open() only for files under /app/data, deny connect() to IP ranges outside the pod’s expected endpoints) that seccomp’s BPF dialect cannot. The cost of this power is the CAP_BPF or CAP_SYS_ADMIN capability required to load eBPF programs — which is why the default container seccomp profile blocks the bpf() syscall, and why the decision to grant a workload eBPF loading privileges is a significant trust decision.

Related

WireGuard

WireGuard is a VPN protocol and implementation designed by Jason Donenfeld, merged into the Linux kernel in 5.6 (2020) and subsequently ported to Windows, macOS, iOS, Android, and BSD. Its defining characteristic is radical simplicity: the reference Linux kernel implementation is approximately 4,000 lines of code, compared to tens of thousands for IPsec’s XFRM subsystem and hundreds of thousands for OpenVPN. This simplicity is a deliberate security property — a smaller codebase has a smaller attack surface, is easier to audit, and is less likely to contain implementation vulnerabilities. WireGuard achieves this by making every design decision that allows optionality to be eliminated: there is no algorithm negotiation, no handshake negotiation, no cipher suite selection. The cryptographic suite is fixed: X25519 for key exchange, ChaCha20-Poly1305 for authenticated encryption, BLAKE2s for hashing and key derivation (via a custom HKDF-like construction), and Curve25519 for the static key pairs that identify peers. Peers are identified exclusively by their 32-byte Curve25519 public key, making WireGuard a public-key routed VPN: there are no usernames, passwords, certificates, or CAs; access control is entirely a function of which public keys are listed in each peer’s configuration.

iptables

iptables is the user-space command-line interface to the Linux kernel’s Netfilter packet filtering framework, the dominant firewall tool on Linux from its introduction in 2001 until nftables began replacing it in the mid-2010s. Netfilter inserts hook points at five positions in the kernel’s IPv4 (and separately IPv6, via ip6tables) packet processing path: PREROUTING (immediately after a packet arrives, before routing), INPUT (packets destined for the local host), FORWARD (packets being routed through the host), OUTPUT (packets generated by local processes), and POSTROUTING (after routing, before transmission). At each hook point, Netfilter calls into the active tables, each of which contains ordered chains of rules. A rule is a match condition (source IP, destination port, protocol, connection state, interface, packet mark, and many more via match extensions) paired with a target — the action to take if the rule matches: ACCEPT, DROP, REJECT, LOG, MASQUERADE, DNAT, SNAT, or a jump to a user-defined chain. Rules are evaluated in order; the first matching rule’s target is applied and evaluation stops (unless the target is LOG or another non-terminating target). If no rule matches, the chain’s policy (the default target) applies.

nftables

nftables is the successor to iptables within the Linux Netfilter framework, merged into the mainline kernel in 3.13 (2014) and now the default firewall backend on all major distributions — Debian 10+, Ubuntu 20.04+, RHEL 8+, Fedora 32+. It replaces not just iptables but the entire family of legacy Netfilter frontends: ip6tables (IPv6), arptables (ARP), and ebtables (Ethernet bridging) are all unified under a single nft command and a single kernel subsystem. The kernel component is a generic, protocol-independent packet classification engine; the protocol-specific logic (IPv4, IPv6, ARP, bridging) is expressed in user-space rule syntax rather than hardcoded in separate kernel modules. This unification eliminates the fragmented ruleset management of the iptables era, where a firewall with consistent IPv4/IPv6 and bridging policy required coordinating four separate tools with four separate rulesets and four separate persistence mechanisms.