DDoS Protection

UDP Amplification: Why Kernel-Level Rate Limiting Changes Everything

October 4, 2026 · 8 min read
Illustration immersive du sujet : UDP amplification kernel

UDP amplification kernel protection is the most efficient way to stop reflection attacks before they saturate your network stack. By leveraging XDP (eXpress Data Path) and eBPF programs directly at the network interface, you can drop malicious packets in microseconds—long before the kernel's TCP/IP stack even sees them. Traditional user-space firewalls and even conventional iptables rules operate too late in the packet lifecycle, allowing attackers to consume CPU cycles and memory buffers with spoofed DNS, NTP, or memcached requests that can amplify bandwidth 50× to 1000×.

This article explains how UDP amplification attacks work at the protocol level, why kernel-layer filtering is essential, and how to configure XDP and nftables rules to block or rate-limit suspicious patterns without breaking legitimate services. You will see real command-line examples and learn the technical trade-offs between stateless XDP maps and stateful connection tracking.



How UDP Amplification Exploits Work

UDP amplification is a class of DDoS attack that abuses the connectionless, stateless nature of UDP. An attacker sends a small request to a public service (DNS resolver, NTP server, SSDP endpoint, memcached instance) with a forged source IP address—the victim's IP. The service replies with a much larger payload to the spoofed address, amplifying both packet count and byte volume.

Common amplification vectors include:

  • DNS: A 60-byte query (ANY record) can trigger a 3000-byte response (50× amplification).
  • NTP monlist: A 234-byte request returns up to 100 packets totaling 48 kB (206× amplification).
  • Memcached: A single UDP GET can produce responses exceeding 1 MB (up to 51,000× amplification if the cache holds large values).
  • SSDP: Universal Plug and Play discovery messages can yield 30× amplification.
  • CharGen: The legacy character-generator service responds indefinitely with random data.

Because UDP requires no handshake, the reflector has no way to verify that the source IP is authentic. The victim receives a flood of unsolicited responses, saturating their ingress link or exhausting connection-tracking tables in stateful firewalls.



Why Kernel-Layer Filtering Beats User-Space Solutions

A user-space firewall daemon—whether implemented in Python, Go, or even C—must wait for the kernel to receive the packet, copy it into a socket buffer, traverse the full network stack (L2 → L3 → L4), and then deliver it to the application via a system call. By the time your daemon reads the packet and decides to drop it, the CPU has already spent hundreds of clock cycles on context switches, memory allocations, and protocol parsing.

XDP hooks execute immediately after the NIC driver places the packet in RAM, before any `skb` (socket buffer) allocation. An eBPF program attached to the XDP hook can inspect Ethernet, IP, and UDP headers in a few dozen instructions and return XDP_DROP, discarding the frame with sub-microsecond latency. This approach scales to several million packets per second on commodity hardware, because:

  • No system calls or context switches occur.
  • No `skb` allocation—XDP operates on raw DMA buffers.
  • No connection-tracking overhead for stateless policies.
  • Parallel execution on multi-queue NICs (RSS distributes flows across CPU cores).

For stateful policies—tracking connection state, TCP flags, or per-flow rate limits—nftables with conntrack remains the best tool. The key is to combine XDP for high-volume, stateless drops with nftables for nuanced, stateful filtering. PAKKT.io orchestrates both layers from a single control plane, synchronizing BPF maps and nft rulesets in real time.



Building an Anti-Amplification Ruleset

Step 1: Identify Reflection-Prone Ports

Start by listing UDP services on your server that should not be publicly exposed:

ss -ulnp | grep -E ':(53|123|11211|1900|19|137)' 

If you see DNS (53), NTP (123), memcached (11211), SSDP (1900), CharGen (19), or NetBIOS (137) bound to 0.0.0.0, you have a potential reflector. Best practice: bind these services to 127.0.0.1 or a private management IP. If public exposure is required (e.g., authoritative DNS), proceed to rate-limiting.

Step 2: XDP Stateless Port Block

An XDP program can drop all inbound UDP traffic to a given port in a single BPF map lookup. Here is the conceptual flow:

1. Parse Ethernet → check EtherType == 0x0800 (IPv4)
2. Parse IP → check protocol == 17 (UDP)
3. Parse UDP → extract dport
4. Lookup dport in BPF_MAP_TYPE_HASH (pakkt_rules)
5. If rule_type == BLOCK → return XDP_DROP
6. Else → return XDP_PASS

PAKKT's engine supports up to 256 simultaneous rules per interface, keyed by protocol + port range. To block UDP/11211 globally:

# Conceptual PAKKT API call (actual syntax via dashboard or REST API)
POST /api/v1/agents/{agent_id}/xdp-rules
{
  "protocol": "udp",
  "port_start": 11211,
  "port_end": 11211,
  "rule_type": "block"
}

The agent compiles this into a BPF map entry; the running XDP program picks it up within milliseconds.

Step 3: nftables Stateful Rate-Limiting

For services you must expose (authoritative DNS, game-server query ports), XDP alone cannot distinguish legitimate bursts from attack traffic. Use nftables meters to track per-source-IP request rates:

nft add table inet pakkt
nft add chain inet pakkt input { type filter hook input priority filter\; policy accept\; }

# Allow established/related (stateful shortcut)
nft add rule inet pakkt input ct state established,related accept

# Rate-limit new UDP/53 to 20 pps per source IP
nft add rule inet pakkt input udp dport 53 \
  meter dns_ratelimit { ip saddr limit rate over 20/second burst 5 packets } \
  drop

# Log dropped packets (optional, watch overhead)
nft add rule inet pakkt input udp dport 53 \
  meter dns_ratelimit { ip saddr limit rate over 20/second } \
  log prefix \"DNS_AMPLIFICATION \" drop

This rule creates a dynamic map (meter) that drops any source exceeding 20 packets/sec with a burst allowance of 5. Adjust thresholds based on your query baseline—authoritative nameservers may need 100+ pps; recursive resolvers restricted to known clients can go lower.

Step 4: Packet-Size Filtering in XDP

Many amplification attacks send minimum-size requests. An NTP monlist query is exactly 234 bytes; a DNS ANY query is typically 60–90 bytes. Conversely, legitimate application traffic (game state updates, VoIP) often exceeds 200 bytes. PAKKT's XDP engine supports min/max packet-size rules:

# Block UDP packets smaller than 100 bytes on port 123 (NTP)
POST /api/v1/agents/{agent_id}/xdp-rules
{
  "protocol": "udp",
  "port_start": 123,
  "port_end": 123,
  "min_packet_size": 0,
  "max_packet_size": 99,
  "rule_type": "block"
}

This prevents monlist requests while allowing ntpdate and chrony clients (which send larger, well-formed packets) to function normally.

Step 5: GeoIP Blacklist for Reflection Sources

If your logs show amplification replies originating from ASNs known for hosting open resolvers or misconfigured NTP servers, you can blacklist those IP ranges at the XDP layer. PAKKT's dual-layer IP blacklist synchronizes a BPF LPM_TRIE map (XDP) with an nftables ip saddr @blacklist drop rule. Add ranges via the dashboard or API:

POST /api/v1/agents/{agent_id}/blacklist
{
  "cidr": "203.0.113.0/24",
  "comment": "Known NTP reflector range"
}

The agent updates both the XDP map and the nftables set within one heartbeat cycle (≤30 seconds).



Measuring and Tuning Performance

After deploying your ruleset, verify drop counts and CPU impact:

# Check XDP statistics (requires bpftool or PAKKT agent metrics)
bpftool prog show
bpftool map dump name pakkt_stats

# Check nftables counters
nft list ruleset inet pakkt

# Monitor CPU usage
mpstat -P ALL 1

Healthy XDP performance on a 10 Gbps NIC should handle several million packets per second with <1% CPU per core. If you observe packet loss (ip -s link show eth0 reports rx_dropped), consider:

  • NIC offloads: Ensure XDP native mode (not generic). Check ethtool -i eth0 for driver support.
  • Queue count: Match RSS queues to CPU cores (ethtool -l eth0).
  • Map size: BPF hash maps have fixed capacity; PAKKT's default is 256 rules—sufficient for most scenarios.
  • Stateful overhead: If conntrack table fills (cat /proc/sys/net/netfilter/nf_conntrack_count), raise nf_conntrack_max or tighten timeouts.

PAKKT's dashboard aggregates per-port and per-rule metrics into TimescaleDB, giving you historical PPS/BPS graphs and GeoIP heatmaps. Use the world map view to spot geographic clusters of amplification sources and refine your blacklist. PAKKT Integrations (Pterodactyl, public API) let you automate these adjustments from your existing management stack.



Complementary Upstream Scrubbing

Kernel-layer filtering is on-path: packets must reach your server's NIC before XDP can drop them. If an attacker saturates your upstream link (1 Gbps inbound, 10 Gbps attack), even perfect XDP filtering won't help—the pipe is full. In such cases, pair PAKKT with cloud-based scrubbing:

  • Cloudflare Spectrum or Magic Transit (Anycast, GRE tunnel).
  • OVH VAC (Vacuum, automated BGP diversion).
  • Path.net, Arbor Networks, or similar enterprise DDoS mitigation.

The scrubbing service advertises your IP via BGP, absorbs the flood, filters malicious traffic in their data centers, and forwards clean packets to your origin. PAKKT then provides the second line of defense on the server itself, catching any residual spoofed traffic, application-layer attacks, or zero-day exploits that slip through the scrubber's heuristics. This layered approach is standard in high-availability gaming, SaaS, and financial-services environments.

For transparency: PAKKT does not offer upstream scrubbing or BGP services. We focus exclusively on kernel-level protection. Choose your preferred transit provider or cloud scrubber based on latency, cost, and peering—then deploy PAKKT on top for granular, real-time control. Further reading on BGP and traffic diversion is available in RFC 7999 (BLACKHOLE community).



Conclusion

Stopping UDP amplification at the kernel with XDP and nftables delivers unmatched speed and efficiency. By parsing packets before the network stack allocates memory and applying stateless drops or stateful rate-limits in microseconds, you protect your services without sacrificing CPU or adding perceptible latency. Combine port-based blocking, packet-size filters, per-IP meters, and synchronized blacklists to build a defense that scales with your infrastructure. Measure, tune, and iterate using real metrics—and remember that kernel protection works best as part of a layered strategy alongside upstream scrubbing when link saturation is a risk.



FAQ

Can XDP rate-limit by bytes per second, or only packets per second?

XDP programs are stateless and operate on individual packets without tracking flow history. Byte-rate limiting requires per-connection state, which belongs in nftables (using meters with limit rate in bytes) or in a stateful eBPF program attached to tc (traffic control). PAKKT's XDP engine enforces packet-per-second limits; for byte-rate policies, use the complementary nftables layer or tc hooks.

How do I prevent blocking legitimate DNS resolvers when rate-limiting UDP/53?

Whitelist known recursive-resolver IPs (Google 8.8.8.8, Cloudflare 1.1.1.1, your ISP's resolvers) in both the XDP whitelist map and an nftables set with higher priority. For example, nft add rule inet pakkt input ip saddr @dns_whitelist udp dport 53 accept placed before the rate-limit rule will bypass metering for trusted sources. Tune your per-IP threshold based on query patterns—10–20 pps is safe for most authoritative zones.

What happens if my NIC driver does not support native XDP mode?

The kernel falls back to generic XDP, which attaches the eBPF program later in the receive path (after skb allocation). Performance drops significantly—typically 10× slower than native mode—but the program still executes and drops packets before they reach user space. Check your driver with ethtool -i eth0 and consult the kernel documentation for XDP-capable drivers (Intel ixgbe/i40e, Mellanox mlx5, Netronome nfp). Upgrading the NIC or kernel version may unlock native mode.

Protect your servers

Deploy PAKKT in 30 seconds

Dual-layer kernel protection. XDP + nftables. Driven from a central panel. 7-day free trial.