High-Performance Packet Filtering with Linux XDP
Learn how Linux XDP (eXpress Data Path) allows high-performance packet filtering by executing eBPF programs at the NIC driver level, bypassing the expensive kernel network stack.
02 Jul 2025, 17:23 UTC

The Cost of the Kernel Network Stack
When a network packet hits a Linux server, the kernel typically performs a series of expensive operations before your application ever sees the data. It allocates a socket buffer (sk_buff or skb), parses headers, and passes the packet through the netfilter (iptables/nftables) hooks. For most applications, this is fine. However, if you are defending against a DDoS attack or building a high-throughput load balancer, the CPU overhead of allocating an skb for every single malicious packet can saturate your processor before the packets even reach your firewall rules.
The takeaway: To handle millions of packets per second without crashing your CPU, you need to process or drop packets before the kernel spends resources on them. This is where eXpress Data Path (XDP) comes in.
What is XDP?
XDP (eXpress Data Path) is a programmable data path in the Linux kernel that allows you to attach an eBPF (extended Berkeley Packet Filter) program directly to a network interface driver. Instead of waiting for the kernel to build a complex data structure for the packet, XDP executes your code the moment the DMA (Direct Memory Access) transfer from the NIC is complete.
An XDP program must return one of several verdicts:
XDP_DROP: Discard the packet immediately.XDP_PASS: Let the packet continue up the standard Linux networking stack.XDP_TX: Send the packet back out the same interface it arrived on (useful for load balancing).XDP_REDIRECT: Send the packet to a different network interface.
Deployment Modes and Performance
XDP performance varies wildly depending on the "mode" used. Choosing the wrong one can lead to a significant performance gap.
| Mode | Execution Point | Performance | Requirement |
|---|---|---|---|
| Native | NIC Driver | Highest | Driver must support XDP |
| Offloaded | NIC Hardware | Extreme | SmartNIC support |
| Generic | After skb allocation |
Lowest | Works on all drivers |
Comparing XDP to iptables and tc
While iptables (netfilter) and tc (Traffic Control) are powerful, they operate deeper in the stack. iptables requires a fully formed skb, meaning the kernel has already done the heavy lifting of memory allocation. tc (specifically clsact) is faster than iptables but still occurs after the XDP hook. XDP is the "first line of defense," making it orders of magnitude faster for simple drop/redirect operations.
Worked Example: Dropping Traffic by Source IP
To implement an XDP filter, you write a C program, compile it to eBPF bytecode using LLVM/Clang, and load it into the kernel. Below is a conceptual implementation of a packet dropper.
The C Code (xdp_drop.c)
#include <linux/bpf.h><br>#include <bpf/bpf_helpers.h><br>#include <linux/if_ether.h><br>#include <linux/ip.h><br>
SEC("xdp")
int xdp_drop_ip(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 != __constant_htons(ETH_P_IP)) return XDP_PASS;
struct iphdr *iph = (void *)(eth + 1);
if ((void *)(iph + 1) > data_end) return XDP_PASS;
// Drop packets from 1.2.3.4
if (iph ->saddr == __constant_htonl(0x01020304)) {
return XDP_DROP;
}
return XDP_PASS;
}
Loading the Program
Assuming you have compiled the code to xdp_drop.o using clang -target bpf, run the following as root on your target machine:
# Attach the program to interface eth0
# Permissions: Root/sudo required
# Risk: A bug in the program (e.g., incorrect return) can drop all traffic
# and lock you out of the server.
ip link set dev eth0 xdp obj xdp_drop.o sec xdp
Verification
To verify the filter is working, use tcpdump on the host. You should see that packets from 1.2.3.4 never appear in the capture, while other traffic flows normally. To remove the program:
ip link set dev eth0 xdp off
Limitations and Trade-offs
XDP is not a replacement for a full firewall. Because it runs so early, it lacks access to the full kernel networking state. For example, connection tracking (stateful inspection) is extremely difficult in XDP because the program cannot easily track TCP sequence numbers or handshake states without implementing its own complex map-based state machine.
Furthermore, the eBPF verifier strictly enforces safety. You cannot use arbitrary loops or access memory outside the packet boundaries, which is why the data_end checks in the example above are mandatory. If you omit them, the kernel will refuse to load the program.
Actionable Summary
If your server is struggling with high packet rates or DDoS mitigation, move your filtering logic from iptables to XDP. Start by verifying your NIC driver supports native XDP via ethtool -i [interface]. If you need stateful inspection, keep that logic in nftables, but use XDP to "pre-filter" the obvious noise.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.