Volumetric Distributed Denial of Service (DDoS) attacks can overwhelm even the most robust infrastructure. When under attack, many administrators try to block the malicious traffic using standard firewalls like iptables, UFW, or nftables. However, during a massive volumetric flood (like DNS or NTP amplification), the server's CPU quickly reaches 100% exhaustion just trying to process and drop the packets, taking the server offline anyway.
To survive high-capacity attacks, enterprise networks use dedicated "scrubbing centers." In this tutorial, you will learn how to build your own scrubbing gateway using a high-bandwidth bare metal server from EPY Host. By combining FRRouting (FRR) to steer traffic and XDP/eBPF to drop malicious packets directly at the network card driver level, you can process millions of packets per second without breaking a sweat.
What You'll Learn
The Reality of Traditional Firewall Bottlenecks
Prerequisites and Architecture Overview
Installing and Configuring FRRouting (BGP)
Understanding eBPF and XDP Filtering
Writing the eBPF C Program for Mitigation
Compiling and Attaching the XDP Program
Routing Clean Traffic via GRE Tunnels
Monitoring and Post-Mitigation Cleanup
The Reality of Traditional Firewall Bottlenecks
When a packet reaches a Linux server, the Network Interface Card (NIC) hardware receives it and triggers an interrupt. The Linux kernel then allocates a data structure called an sk_buff (socket buffer) to manage the packet as it travels up the network stack through the routing layer, netfilter (iptables/nftables), and finally to the user-space application.
Allocating millions of sk_buff structures per second requires immense CPU overhead. If you use iptables to block an attack of 10 million packets per second (Mpps), your CPU will bottleneck on memory allocation and context switching long before it even reads your firewall rule.
The Solution: eXpress Data Path (XDP) paired with eBPF. XDP allows you to attach custom code directly to the NIC driver. The code inspects the packet the millisecond it hits the hardware and can issue a XDP_DROP command before the kernel allocates an sk_buff. This allows a standard EPY Host dedicated server to drop millions of packets per second with virtually zero CPU load.
Prerequisites and Architecture Overview
To follow this tutorial, you will need:
A Dedicated Server: A high-bandwidth EPY Host bare metal server acting as the "Scrubber".
Operating System: Ubuntu 24.04 LTS or AlmaLinux 9 (this guide uses Ubuntu).
A Backend Server: The actual application server you are protecting.
Network Control: Your own ASN and IP prefix (e.g., a /24 block) to announce via BGP, or routing control from your upstream provider to steer targeted IPs to your scrubber.
The Architecture:
Your Scrubber server announces the targeted IP space via BGP using FRR.
Attack traffic flows to the Scrubber.
The XDP/eBPF program instantly drops malicious packets (e.g., UDP port 53 amplification).
The remaining "clean" traffic is forwarded to your Backend Server via a GRE (Generic Routing Encapsulation) tunnel.
Installing and Configuring FRRouting (BGP)
FRRouting (FRR) is a powerful, open-source routing suite. We will use it to establish a BGP session with your upstream provider so that traffic destined for your application flows through this scrubbing gateway first.
1. Install FRR:
sudo apt update
sudo apt install frr frr-pythontools -y
2. Enable the BGP Daemon:
Edit the daemon configuration file to enable BGP:
sudo nano /etc/frr/daemons
Find the line bgpd=no and change it to bgpd=yes. Save and restart the service:
sudo systemctl restart frr
3. Configure BGP Peering:
Enter the FRR integrated shell (vtysh) to configure your routing:
sudo vtysh
Run the following commands (replace the ASNs and IP addresses with your actual network data):
conf t
router bgp 65000
no bgp ebgp-requires-policy
neighbor 192.168.1.1 remote-as 64512
neighbor 192.168.1.1 description Upstream-Provider
address-family ipv4 unicast
network 203.0.113.0/24
exit-address-family
exit
write memory
exit
Your scrubbing server is now announcing the 203.0.113.0/24 network to the world. All traffic for those IPs will hit this server.
Understanding eBPF and XDP Filtering
eBPF (Extended Berkeley Packet Filter) allows you to run sandboxed programs safely inside the Linux kernel without changing kernel source code or loading standard modules.
When you write an eBPF program for XDP, you are instructing the network card on how to handle raw network frames. The program parses the Ethernet header, then the IP header, and finally the TCP/UDP header. If the packet matches your attack signature (for instance, massive inbound UDP traffic on a specific port), the program returns XDP_DROP. If the packet is legitimate, it returns XDP_PASS, allowing it to proceed to the standard Linux networking stack.
To compile these programs, you need the appropriate development tools:
sudo apt install clang llvm libbpf-dev linux-headers-$(uname -r) build-essential -y
Writing the eBPF C Program for Mitigation
Let us write a program to mitigate a UDP amplification attack. In this example, we will drop all incoming UDP packets originating from source port 53 (DNS) and port 123 (NTP).
Create a new file named xdp_ddos_drop.c:
nano xdp_ddos_drop.c
Add the following C code. This code carefully parses the packet headers from the raw memory pointers provided by the XDP framework.
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <arpa/inet.h>
#include <bpf/bpf_helpers.h>
SEC("xdp")
int xdp_drop_udp_amp(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;
// Parse the 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;
// Parse the IP header
struct iphdr *iph = (void *)(eth + 1);
if ((void *)(iph + 1) > data_end)
return XDP_PASS;
// Check if the protocol is UDP (Protocol 17)
if (iph->protocol == IPPROTO_UDP) {
// Parse the UDP header
struct udphdr *udp = (void *)iph + (iph->ihl * 4);
if ((void *)(udp + 1) > data_end)
return XDP_PASS;
// Drop DNS (53) and NTP (123) amplification traffic
if (udp->source == __constant_htons(53) || udp->source == __constant_htons(123)) {
return XDP_DROP; // Packet dropped at wire speed!
}
}
// Allow all other traffic to pass to the OS
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
-
Note: Security code running in the kernel must adhere strictly to boundaries; notice how we check if the headers exceed
data_endbefore reading them. If you skip this, the eBPF verifier will reject the program to prevent kernel panics.
Compiling and Attaching the XDP Program
Now, compile the C code into an eBPF object file using the clang compiler:
clang -O2 -g -Wall -target bpf -c xdp_ddos_drop.c -o xdp_ddos_drop.o
You should now have an xdp_ddos_drop.o file in your directory. To attach this to your network interface (e.g., eth0 or eno1), use the ip command.
First, identify your primary public network interface:
ip link show
Assume your interface is eno1. Attach the compiled XDP program:
sudo ip link set dev eno1 xdp obj xdp_ddos_drop.o sec xdp
If successful, running ip link show eno1 will now display xdp in the interface description. Your EPY Host bare metal server is now actively dropping malicious UDP amplification traffic before the Linux kernel even allocates memory for it.
To remove the filter later, you can run:
sudo ip link set dev eno1 xdp off
Routing Clean Traffic via GRE Tunnels
Now that the attack traffic is being dropped at wire speed, the surviving legitimate traffic is passed up to the Linux networking stack of your scrubbing server. However, this server doesn't host your website or application—it just cleans the traffic.
We need to forward the clean traffic to your actual backend server without altering the original source IPs (so your application still logs the real visitors). We do this using a GRE tunnel.
On the Scrubber Server:
Run these commands to create a tunnel to the backend server (Assuming Scrubber IP is 10.0.0.1 and Backend IP is 10.0.0.2):
# Enable IP forwarding
sudo sysctl -w net.ipv4.ip_forward=1
# Create the GRE tunnel
sudo ip tunnel add gre1 mode gre remote 10.0.0.2 local 10.0.0.1 ttl 255
sudo ip link set gre1 up
sudo ip addr add 192.168.100.1/30 dev gre1
# Route the protected prefix traffic through the tunnel
sudo ip route add 203.0.113.0/24 dev gre1
On the Backend Application Server:
Set up the other side of the tunnel to receive the traffic and route responses back through the scrubber.
sudo ip tunnel add gre1 mode gre remote 10.0.0.1 local 10.0.0.2 ttl 255
sudo ip link set gre1 up
sudo ip addr add 192.168.100.2/30 dev gre1
# Ensure return traffic goes back out the tunnel
sudo ip route add default via 192.168.100.1 table 100
sudo ip rule add from 203.0.113.0/24 table 100
With this setup, the clean packet arrives at the scrubber, enters the GRE tunnel, and emerges at your application server with its original source IP intact.
Monitoring and Post-Mitigation Cleanup
Once your eBPF program is running, standard tools like tcpdump won't see the dropped traffic on the interface because XDP eliminates it before tcpdump (which relies on sk_buff structures) gets a chance to look.
To monitor how many packets your program is dropping, you can use the bpftool utility.
Install the tool:
sudo apt install linux-tools-common linux-tools-generic -y
List the loaded BPF programs:
sudo bpftool prog show
You will see your xdp_drop_udp_amp program listed with an ID. While building advanced statistics tracking into eBPF requires a concept called "BPF Maps," simply watching your system's overall bandwidth charts in your EPY Host control panel will reveal the massive ingress traffic, while your backend server will only show the clean, scrubbed traffic.
Final Thoughts
Building a custom scrubbing center with eBPF and FRRouting gives you unparalleled control over DDoS mitigation. Instead of relying on expensive third-party scrubbing subscriptions, you can leverage the raw hardware power of an EPY Host dedicated server to defeat high-volume attacks right at the network edge.
