As volumetric Distributed Denial of Service (DDoS) attacks increasingly exceed terabits per second, traditional software firewalls like iptables or nftables are fundamentally incapable of mitigating the threat. These legacy firewalls operate too high up in the Linux networking stack; by the time a malicious packet reaches netfilter hooks, the Linux kernel has already allocated an sk_buff data structure, exhausting critical CPU and memory resources. To achieve wire-speed packet dropping without hardware offloading, Linux systems engineers must deploy eBPF (Extended Berkeley Packet Filter) XDP (eXpress Data Path) programs. XDP allows custom C code to execute securely inside the kernel device driver, intercepting and dropping malicious packets before they ever reach the Linux network stack.
The Architecture of XDP and eBPF
eBPF is a revolutionary technology that allows safe, sandboxed programs to execute within the Linux kernel without requiring custom kernel modules or system reboots. XDP is a specific networking hook for eBPF programs, positioned at the lowest possible point in the software stack—directly inside the network interface controller (NIC) driver, immediately after an interrupt is received from the hardware.
When an XDP program inspects a packet, it can return one of several action codes:
XDP_PASS: Allow the packet to proceed normally into the Linux network stack.XDP_DROP: Silently discard the packet immediately, utilizing almost zero CPU cycles.XDP_TX: Bounce the packet back out the same network interface (useful for load balancers).
Writing an XDP Packet Dropper in C
To mitigate a specific DDoS attack (for example, an aggressive flood of UDP packets hitting a specific port), you must write a low-level C program that parses the raw Ethernet, IP, and UDP headers.
Below is a minimal eBPF C program designed to indiscriminately drop all incoming UDP packets destined for port 53 (frequently targeted in DNS amplification attacks):
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
SEC("xdp")
int xdp_drop_dns(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
// 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;
// Parse IPv4 header
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end)
return XDP_PASS;
if (ip->protocol != IPPROTO_UDP)
return XDP_PASS;
// Parse UDP header
struct udphdr *udp = (void *)ip + (ip->ihl * 4);
if ((void *)(udp + 1) > data_end)
return XDP_PASS;
// If destination port is 53, drop the packet at the NIC level
if (udp->dest == __constant_htons(53))
return XDP_DROP;
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
Compiling and Loading the eBPF Object
Because the Linux kernel requires eBPF bytecode, the C source code must be compiled using the LLVM/Clang compiler toolchain.
Install the necessary build dependencies on your Linux server (e.g., Ubuntu/Debian):
sudo apt-get install clang llvm libbpf-dev bpfcc-tools
Compile the C code into an ELF object file containing the eBPF bytecode:
clang -O2 -g -Wall -target bpf -c xdp_filter.c -o xdp_filter.o
Once compiled, the iproute2 toolkit provides the standard mechanism to inject the XDP program into the kernel and attach it to a specific physical network interface (e.g., eth0).
sudo ip link set dev eth0 xdp obj xdp_filter.o sec xdp
Verifying and Monitoring XDP Execution
To verify that the XDP program has successfully attached to the NIC driver, inspect the network link status:
ip link show dev eth0
You should see the xdp flag active on the interface.
During a live DDoS attack, because the packets are dropped before reaching the Linux networking stack, tools like tcpdump (which bind via AF_PACKET sockets higher up the stack) will not see the dropped traffic. To monitor the mitigation efficacy, engineers must utilize eBPF maps to pass high-speed drop counters from kernel space to user space, allowing Prometheus or Grafana to visualize the wire-speed defense mechanisms in real-time.