Modern Linux networking is no longer limited to iptables, nftables, or traditional firewalls. With eBPF and XDP, we can inspect, filter, and drop packets before they even enter the kernel networking stack.
This allows us to build:
- High-performance firewalls
- DDoS mitigation
- IP blocking at line rate
- Packet statistics and monitoring
- Load balancers and custom network logic
All of this without kernel modules and without touching the kernel source code.
In this article, we’ll cover:
- What eBPF is
- What XDP is and why it’s insanely fast
- How packets normally flow vs XDP path
- Writing a simple XDP program to block IP addresses
- Using XDP as a DDoS protection layer
What is eBPF?
eBPF (extended Berkeley Packet Filter) is a virtual machine inside the Linux kernel.
You can load small programs into the kernel safely, and they can:
- Inspect packets
- Collect metrics
- Modify behavior
- Make decisions at runtime
eBPF programs are:
- Verified by the kernel (safe, no crashes)
- JIT compiled (very fast)
- Attached to hooks (network, tracepoints, syscalls, etc.)
What is XDP?
XDP (eXpress Data Path) is a hook in the earliest point of the Linux network stack — inside the network driver.
That means packets are processed:
Before iptables
Before conntrack
Before the kernel networking stack
Before Kubernetes, Docker, anything
Normal packet path
NIC → Driver → Kernel Stack → Netfilter → UserspaceWith XDP
NIC → XDP → (DROP or PASS)This is why XDP can handle millions of packets per second per core.
XDP Actions
An XDP program returns one of these:
| Action | Meaning |
|---|---|
| XDP_PASS | Let packet continue |
| XDP_DROP | Drop packet immediately |
| XDP_TX | Send packet back out |
| XDP_REDIRECT | Send to another interface/CPU |
For firewall/DDoS protection, we mostly use:
XDP_DROPExample 1 – Block traffic by IP using XDP
To compile, load, and attach XDP programs (and use the tools we’ve been using) on Linux, you need these packages.
Debian / Ubuntu
sudo apt-get update
sudo apt-get install -y \
clang \
llvm \
libbpf-dev \
make \
linux-headers-$(uname -r)We’ll write a minimal XDP program that drops packets from a specific IP.
xdp_block_ip.c
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <bpf/bpf_helpers.h>
SEC("xdp")
int xdp_block_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 *ip = data + sizeof(struct ethhdr);
if ((void *)(ip + 1) > data_end)
return XDP_PASS;
// Block this IP: 1.2.3.4
if (ip->saddr == __constant_htonl(0x01020304))
return XDP_DROP;
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";Compile the program
clang -O2 -target bpf -c xdp_block_ip.c -o xdp_block_ip.oAttach to interface
ip link set dev eth0 xdp obj xdp_block_ip.o sec xdpCheck:
ip -details link show eth0Now all packets from 1.2.3.4 are dropped before reaching iptables.
Why this is much faster than iptables?
iptables works after:
- Packet allocation
- Conntrack
- Routing decision
- Kernel networking stack
XDP drops packets at driver level – almost zero CPU cost.
Example 2 – Dynamic IP Block List with BPF Map
Hardcoding IPs is useless for real life. Instead, we use a BPF map.
xdp_ddos_protect.c
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, __u32);
__type(value, __u8);
} blocked_ips SEC(".maps");
SEC("xdp")
int xdp_filter(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 *ip = data + sizeof(struct ethhdr);
if ((void *)(ip + 1) > data_end)
return XDP_PASS;
__u8 *blocked = bpf_map_lookup_elem(&blocked_ips, &ip->saddr);
if (blocked)
return XDP_DROP;
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";Load program
ip link set dev eth0 xdp obj xdp_ddos_protect.o sec xdpAdd IPs to block list dynamically
Find map id:
bpftool map listAdd IP:
bpftool map update id <MAP_ID> key 0x01020304 value 0x1Now you can block IPs in real time without reloading the program.
Using XDP for DDoS protection
With XDP you can implement:
1. SYN flood protection
Drop excessive SYN packets per IP.
2. Rate limiting per IP
Use a map to count packets and drop if threshold exceeded.
3. Drop malformed packets
Check headers and drop garbage traffic.
4. Block entire subnets
Mask IP and drop ranges.
5. Early packet validation
Drop non-TCP/UDP/ICMP traffic instantly.
This is exactly how Cloudflare, Facebook, and Cilium protect their infrastructure.
Example logic for simple rate limiting (concept)
- Map: IP → packet counter
- If counter > threshold in short time → XDP_DROP
This stops:
- UDP floods
- SYN floods
- Bot traffic
Before kernel even sees it.
Performance
Typical numbers on modern NIC:
| Method | Packets/sec/core |
|---|---|
| iptables | ~200k |
| nftables | ~400k |
| XDP | 5–20 million |
That’s why XDP is used in production at hyperscale.
Where XDP shines in Kubernetes
- Protect Kubernetes nodes from DDoS
- Protect Ingress/LoadBalancer
- Replace iptables rules with eBPF logic
- Used internally by Cilium CNI
Detach XDP
ip link set dev eth0 xdp offConclusion
eBPF + XDP turns Linux into a high-performance programmable firewall.
You can:
- Block IPs at line rate
- Mitigate DDoS
- Inspect traffic without performance loss
- Build advanced networking logic safely
And all of this runs inside the kernel with no kernel modules.
If you work with Kubernetes, high-traffic APIs, or public services – learning XDP is a game changer.
Next step ideas
- XDP rate limiter implementation
- XDP + Prometheus metrics
- XDP load balancer
- How Cilium uses XDP internally
