What is Layer 7 DDoS and Why Is It Harder to Stop?
A Layer 7 DDoS attack (also called an application-layer DDoS or HTTP flood) overwhelms a web server by sending enormous volumes of seemingly legitimate HTTP requests rather than raw network packets.
Unlike Layer 3/4 floods (UDP amplification, SYN floods), Layer 7 attacks:
Complete a full TCP three-way handshake — making them indistinguishable from real users at the network level
Bypass standard iptables rate-limiting rules without exhausting CPU resources first
Exploit expensive server-side operations, such as database queries triggered by specific URL paths
Modern botnets frequently use this technique because traditional firewalls cannot inspect HTTP headers without terminating the TCP connection — an operation that is far too expensive at high traffic volumes.
Why XDP + eBPF Is the Right Tool
eBPF (Extended Berkeley Packet Filter) allows you to run sandboxed programs inside the Linux kernel without modifying 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.
| Mitigation Method | Where It Runs | CPU Cost at 10Gbps | L7 Inspection |
|---|---|---|---|
| iptables / nftables | Netfilter (post-kernel) | High | No |
| eBPF + TC (Traffic Control) | After kernel ingress | Medium | Yes |
| eBPF + XDP (this guide) | Inside NIC driver | Near-zero | Yes |
| Cloud WAF | Remote scrubbing centre | Zero (offloaded) | Yes |
XDP is the fastest software-based mitigation available on Linux. On a 10Gbps unmetered bare metal server, an XDP program can process and drop packets faster than the OS can schedule a user-space process to even acknowledge them.
The Challenge: Parsing All Four Network Layers in eBPF
XDP operates at the lowest level of the Linux networking stack. To reach Layer 7 (HTTP data), your eBPF program must manually walk the entire packet structure from the ground up:
[ Ethernet Header ]→ 14 bytes[ IPv4 Header ]→ 20 bytes (variable with options)[ TCP Header ]→ 20 bytes (variable with options, doff field)[ TCP Payload ]→ HTTP data starts here
Critical constraint: The Linux kernel eBPF verifier performs strict bounds checking on every memory access. Any attempt to read beyond data_end will cause the program to be rejected at load time — not at runtime. Every pointer dereference must be guarded.
Prerequisites
This guide is tested on Ubuntu 24.04 LTS on an eServers bare metal instance. Install the required build tools:
sudo apt update
sudo apt install clang llvm libbpf-dev linux-headers-$(uname -r) gcc-multilib build-essential
Verify your kernel version supports XDP (Linux 4.8+ required, 5.x+ recommended):
uname -r
Step 1: Write the eBPF C Program
Create a file named l7_firewall.c. This program inspects each incoming packet. When it detects a malicious HTTP signature — in this example, a botnet repeatedly hitting GET /attack-target — it returns XDP_DROP, discarding the packet instantly at the NIC level.
#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) {
// Define pointers to the start and end of the packet data
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;
// Only inspect IPv4 packets
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;
// Only inspect TCP packets
if (ip->protocol != IPPROTO_TCP)
return XDP_PASS;
// 3. Parse TCP Header (ihl = IP header length in 32-bit words)
struct tcphdr *tcp = (void *)ip + (ip->ihl * 4);
if ((void *)(tcp + 1) > data_end)
return XDP_PASS;
// 4. Calculate TCP Payload Offset
// doff = TCP data offset in 32-bit words
unsigned char *payload = (unsigned char *)tcp + (tcp->doff * 4);
// Bounds check: ensure at least 14 bytes of payload exist
// ("GET /attack-tar" = 14 characters)
if ((void *)(payload + 14) > data_end)
return XDP_PASS;
// 5. Inspect the HTTP Payload for malicious signature
// Target: "GET /attack" prefix
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') {
// Malicious signature detected — drop at the NIC, zero CPU cost
return XDP_DROP;
}
// Allow all other traffic
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
What to customise: Replace GET /attack with your actual attack signature. Common targets include /wp-login.php, /xmlrpc.php, or any endpoint your logs show being flooded.
Step 2: Compile to eBPF Bytecode
Compile the C program into an eBPF object file using Clang, targeting the BPF architecture:
clang -O2 -g -Wall -target bpf -c l7_firewall.c -o l7_firewall.o
Flag explanations:
-O2— optimisation required; unoptimised eBPF often fails the kernel verifier-target bpf— compile for the eBPF virtual machine, not your host CPU architecture-g— include debug symbols (useful for bpftool inspection workflows)
On success, this produces l7_firewall.o. The kernel verifier will validate this bytecode for memory safety before loading it.
Step 3: Attach the Firewall to Your NIC
Find your primary network interface name:
ip a
Common names: eth0, eno1, enp3s0. Attach the eBPF program using ip link:
sudo ip link set dev eth0 xdp obj l7_firewall.o sec xdp
Your bare metal server is now inspecting TCP payloads and dropping matching Layer 7 traffic directly at the NIC — before the Linux kernel network stack ever sees the packet.
Step 4: Verify and Monitor
Confirm the XDP program is successfully attached:
ip link show dev eth0
Look for xdp in the output interface flags. To inspect drop statistics using bpftool:
sudo bpftool prog show
sudo bpftool prog dump xlated id <ID>
Monitor dropped packets across interface statistics in real time:
watch -n1 'cat /proc/net/dev | grep eth0'
Remove the Filter
To detach the XDP program and restore normal standard packet processing routes:
sudo ip link set dev eth0 xdp off
Why Bare Metal Matters for eBPF Performance
eBPF and XDP can be loaded on any Linux system, but the performance benefit is only real on physical hardware.
On cloud VPS instances, the host hypervisor processes every packet before it reaches your virtual NIC interface layer. Your XDP program fires after the hypervisor — meaning the CPU structural overhead cost has already been paid, and the "near-zero infrastructure penalty" guarantee no longer holds true.
For genuine line-rate DDoS mitigation profiles, you need direct hardware access to a physical NIC device.
eServers Dedicated Bare Metal — Built for This Use Case
UK businesses running high-traffic workloads choose eServers dedicated bare metal servers for eBPF-based DDoS mitigation configurations because:
Direct NIC access: XDP runs at the raw hardware level, not inside an abstract hypervisor architecture
10Gbps unmetered bandwidth: Absorb volumetric floods cleanly without cloud egress network fees or traffic throttling policies
15–30 minute hardware SLA: On-site UK engineers across London, Coventry, and Manchester replace edge deployment components efficiently
Ubuntu 24.04 LTS pre-installed: Deploy this exact kernel tutorial from the first automated boot sequence
Conclusion
Building edge defense layers via eBPF structures allows platforms to handle high request volumes safely inside network pathways. Deploy raw power arrays with zero virtualization taxes to keep applications secure.
Upgrade your filtering architecture today with our ultra-fast, high-throughput dedicated servers.
Discover eServers Dedicated Server Locations
eServers provides reliable dedicated servers across multiple global regions. Whether you need low latency, regional compliance, or proximity to your audience, our wide geographic coverage ensures the perfect hosting environment for your project.
Frequently Asked Questions (FAQ)
Can eBPF inspect HTTPS (TLS-encrypted) traffic?
No. XDP operates before TLS termination. For encrypted traffic, you need a reverse proxy (e.g., Nginx or HAProxy) with eBPF applied downstream, or a dedicated TLS-aware WAF.
Does this work on Ubuntu 22.04 as well as 24.04?
Yes. The libbpf API and XDP support used here have been stable since Ubuntu 20.04. Ensure your kernel is 5.4 or later (uname -r).
What is the maximum throughput of an XDP firewall?
On a 10Gbps NIC with a simple XDP_DROP program, modern systems can sustain wire-rate drops of 14–24 million packets per second with negligible CPU usage. L7 inspection adds minimal overhead because the program exits early on non-matching traffic.
Can I block multiple attack signatures at once?
Yes. Extend the payload inspection block with additional if conditions, or use an eBPF hash map (BPF_MAP_TYPE_HASH) to store a dynamic blocklist of URL patterns updatable without reloading the program.
Is a kernel module required?
No. eBPF programs load entirely from user space using the bpf() syscall or tools like ip link and bpftool. No kernel recompilation or module signing is needed.
How is this different from iptables -m string?
iptables string matching runs inside Netfilter, after the kernel has fully processed the packet through multiple hooks. XDP drops the packet inside the driver, before Netfilter, skbuff allocation, or any kernel TCP processing — making it 5–10× more CPU-efficient at high packet rates.