Network Engineering: Mitigating Layer 7 DDoS with eBPF and XDP

A Layer 7 DDoS attack (also called an HTTP flood) overwhelms a web server by sending enormous volumes of seemingly legitimate HTTP requests rather than raw network packets. Because these attacks complete a full TCP three-way handshake, they effectively bypass standard iptables rate-limiting rules without exhausting CPU resources first.
Traditional firewalls cannot inspect HTTP headers without terminating the TCP connection, an operation that is far too expensive at high traffic volumes.
eBPF and XDP Architecture
eBPF (Extended Berkeley Packet Filter) allows systems administrators to run sandboxed programs inside the Linux kernel without modifying the kernel source code. Paired with XDP (eXpress Data Path), these programs execute directly inside the NIC driver — the earliest possible point in the networking stack.
XDP is the fastest software-based mitigation available on Linux. An XDP program can process and drop packets faster than the OS can schedule a user-space process to even acknowledge them.
The Code: Parsing Network Layers in C
XDP operates at the lowest level of the Linux networking stack. To reach Layer 7 (the HTTP data), the program must manually walk the packet structure from the ground up: Ethernet Header (14 bytes), IPv4 Header (20 bytes), and TCP Header (20 bytes).
The Linux kernel eBPF verifier performs strict bounds checking; any attempt to read beyond data_end will cause the program to be rejected at load time.
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
SEC("xdp")
int xdp_l7_filter(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
// 1. Parse Ethernet Header
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;
// 2. Parse IP Header
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end) return XDP_PASS;
if (ip->protocol != IPPROTO_TCP) return XDP_PASS;
// 3. Parse TCP Header
struct tcphdr *tcp = (void *)ip + (ip->ihl * 4);
if ((void *)(tcp + 1) > data_end) return XDP_PASS;
// 4. Calculate Payload Offset
unsigned char *payload = (unsigned char *)tcp + (tcp->doff * 4);
if ((void *)(payload + 14) > data_end) return XDP_PASS;
// 5. Detect malicious "GET /attack" signature
if (payload[0] == 'G' && payload[1] == 'E' && payload[2] == 'T' &&
payload[3] == ' ' && payload[4] == '/' && payload[5] == 'a' &&
payload[6] == 't' && payload[7] == 't' && payload[8] == 'a' &&
payload[9] == 'c' && payload[10] == 'k') {
return XDP_DROP; // Discard at the NIC driver
}
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
Step 2: Compile to eBPF Bytecode
Compile the C program into an eBPF object file using Clang. Note that optimization (-O2) is strictly required; unoptimised eBPF often fails the kernel verifier.
Bash
clang -O2 -g -Wall -target bpf -c l7_firewall.c -o l7_firewall.o
Step 3: Attach the Firewall to Your NIC
Find your primary network interface (e.g., eth0) and attach the eBPF program:
Bash
sudo ip link set dev eth0 xdp obj l7_firewall.o sec xdp
Step 4: Verify and Monitor
Confirm the XDP program is successfully attached:
Bash
ip link show dev eth0
To monitor dropped packets across interface statistics in real time:
Bash
watch -n1 'cat /proc/net/dev | grep eth0'
Hardware vs. Virtualization
eBPF and XDP can be loaded on any Linux system, but the performance benefit is only real on physical bare-metal hardware. On cloud VPS instances, the host hypervisor processes every packet before it reaches your virtual NIC interface layer. This means the CPU structural overhead cost has already been paid, completely removing the "near-zero infrastructure penalty" guarantee. For genuine line-rate DDoS mitigation, direct access to a physical NIC device is mandatory.



