Choosing a Linux Packet Filter: eBPF Socket Filters, Classic BPF, or nftables
Compare eBPF socket filters, classic BPF, and nftables for Linux packet filtering, then load and validate an eBPF program.
17 Apr 2026, 01:53 UTC

The decision and your constraints
You need to inspect or drop packets on a Linux host without forcing every packet through the full kernel network stack. The decision is which kernel packet filtering mechanism to use: classic BPF (cBPF), extended BPF (eBPF) socket filters, or nftables.
Your constraints determine the answer. You must know the kernel version, the privileges available to the process loading the filter, whether you need stateful inspection, and how much operational complexity your team can absorb. A filter that blocks the wrong port can also interrupt SSH or application traffic, so validation matters as much as selection.
Supported options compared
| Mechanism | Kernel support | Custom logic | Stateful maps | Typical privilege | Best fit |
|---|---|---|---|---|---|
| Classic BPF (cBPF) | Legacy; present on older kernels | Limited to a small fixed instruction count | No | CAP_NET_ADMIN or root | Legacy socket filters and seccomp only |
| eBPF socket filters | Linux 4.1 and newer | Yes, substantially larger programs than cBPF | Yes, via BPF maps | CAP_BPF or root | Low-overhead inspection, dropping, and tracing |
| nftables | Linux 3.13 and newer | No; rule set only | Userspace sets and maps | CAP_NET_ADMIN or root | Administrator-friendly firewall policies |
Classic BPF is the original packet filter language. It is effectively deprecated for new packet-filtering work because programs are small, cannot use maps for state, and lack just-in-time compilation on many paths. eBPF is the modern replacement: it runs a verifier-checked, JIT-compiled program inside the kernel and can access BPF maps to keep counters or blocklists across packets. nftables is the userspace rule engine behind iptables replacements; it is easier to read and audit, but it cannot execute custom code you write in C.
Trade-offs to weigh before you choose
Choose eBPF socket filters when you need low-latency, programmable inspection—for example, dropping packets to a specific port based on a dynamic list, or sampling traffic for observability. The cost is that your program must pass the kernel verifier, which rejects unsafe memory access or unbounded loops. You also need kernel headers and a compiler toolchain to build the object file, and a kernel upgrade can change the eBPF helper interface, requiring recompilation.
Choose nftables when your policy is static and your operators prefer declarative rules over custom code. It is easier to review in version control, but every packet still traverses more of the network stack than an eBPF program attached early in the receive path.
Choose classic BPF only if you are maintaining legacy software that already loads cBPF filters, or if you are working inside seccomp sandboxing where cBPF remains the supported filter format. Do not start new packet-filtering projects with cBPF.
Concrete implementation: load and validate an eBPF socket filter
This example loads an eBPF socket filter that drops TCP packets destined for port 80. It assumes you have compiled a program object named drop_port80.o from C source using clang and the kernel eBPF headers. The program attaches to a network interface and runs before the kernel delivers the packet to the socket layer.
Run these commands on the Linux host where you want the filter active. You need root privileges or the CAP_BPF and CAP_NET_ADMIN capabilities.
Confirm the kernel supports eBPF syscalls and JIT:
uname -r grep -E 'CONFIG_BPF_SYSCALL|CONFIG_BPF_JIT' /boot/config-$(uname -r)Expected: kernel release 4.1 or newer, and both config options set to
yorm. If they are missing, eBPF socket filters are not available in this kernel build.Load the program into the kernel:
bpftool prog load /path/to/drop_port80.o /sys/fs/bpf/drop_port80 type socket-filterExpected: exit code 0 and a program ID printed by
bpftool. If the verifier rejects the program, the command fails and you should inspect/sys/kernel/debug/tracing/tracefor the verifier log.Attach the program to an interface for ingress traffic:
tc filter add dev ingress bpf da obj /path/to/drop_port80.o sec from-networkReplace
with your interface name, such aseth0. Expected:tcreturns without error. Risk: if the filter drops SSH traffic or your management port, you can lose remote access. Test with a non-critical interface first, or attach through a local console session.Validate that packets to port 80 no longer appear:
tcpdump -i -nn port 80In another session, generate traffic to port 80:
hping3 -S -p 80Expected:
tcpdumpshows no SYN packets to port 80 whilehping3runs. If packets still appear, the program may not be attached, may not match the correct protocol, or may not be returning the drop verdict.
Limitations and practical verification
eBPF socket filters operate at the socket layer, so they do not see packets as early as XDP programs attached at the NIC driver. They also cannot modify arbitrary packet fields as freely as XDP; a socket filter primarily decides whether to accept or drop the packet. Programs are tied to the kernel version they were compiled against, so plan to rebuild and redeploy after major kernel upgrades.
Check the result with bpftool prog list to confirm the program is loaded, and tc filter show dev ingress to confirm the attachment. If traffic is not dropping as expected, check the verifier log and confirm the program section name matches the sec value used in the tc command.
Rollback: detach and remove the filter
Because loading and attaching an eBPF program changes kernel state, you can reverse it without rebooting:
tc filter del dev ingress bpf da obj /path/to/drop_port80.o sec from-network
bpftool prog detach id
bpftool prog delete id
rm /sys/fs/bpf/drop_port80
Replace with the ID shown when you loaded the program. After detachment, verify with tcpdump that port 80 traffic reappears.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.