XDP / eBPF

XDP explained simply: why does Linux filter before the kernel in 2026?

May 10, 2026 · 11 min read
Illustration immersive du sujet : XDP filter before kernel

Understanding why XDP filter before kernel processes network traffic is fundamental to deploying high-performance, DDoS-resistant infrastructure in 2026. XDP (eXpress Data Path) operates at the earliest possible point in the Linux networking stack—immediately after the network interface card (NIC) driver receives a frame, before any kernel subsystem allocates resources for it. This positioning enables sub-microsecond per-packet decisions, dropping malicious traffic with minimal CPU overhead and preventing kernel memory exhaustion during volumetric attacks. For system administrators managing publicly exposed services—game servers, APIs, or web infrastructure—this architectural choice translates directly into survivability under multi-gigabit floods.

The kernel's traditional packet processing path involves numerous allocation steps: socket buffers (sk_buff structures), conntrack table lookups, routing decisions, and protocol stack traversal. Each of these operations consumes CPU cycles and memory. When a server receives a 10 Mpps (million packets per second) DDoS attack, the kernel can exhaust available memory in seconds, triggering out-of-memory kills of critical services. XDP short-circuits this problem by evaluating packets in a restricted eBPF program context before sk_buff allocation occurs. Malicious packets identified at this stage are dropped with an XDP_DROP verdict, returning the frame descriptor to the NIC's receive ring without ever entering the kernel's networking stack.

This guide examines the technical rationale behind XDP's pre-kernel positioning, explores the performance implications measured in real-world deployments, and demonstrates how platforms like PAKKT.io leverage this architecture to deliver centrally managed, multi-rule XDP filtering alongside stateful nftables firewalls—all without conflicting with existing Docker, fail2ban, or iptables-persistent configurations.



The Linux network stack: why traditional filtering comes too late

To appreciate why XDP filter before kernel operations matter, we must first understand the conventional packet journey. When a frame arrives at a NIC, the hardware uses Direct Memory Access (DMA) to write the packet data into a pre-allocated ring buffer in system RAM. The driver then raises a hardware interrupt (or uses NAPI polling), prompting the kernel to begin processing.

At this point, the kernel allocates an sk_buff—a complex structure holding packet metadata, pointers to data fragments, and routing information. This allocation alone involves multiple memory operations and can consume 2–4 KB per packet when accounting for metadata overhead. For a 5 Mpps attack, that translates to 10–20 GB/s of memory bandwidth just for buffer allocation, independent of actual packet content processing.

Next, the packet traverses Netfilter hooks (the framework underlying iptables and nftables). The PREROUTING chain evaluates rules in sequence: connection tracking lookups check if the packet belongs to an existing flow, NAT rules are consulted, and finally filter rules determine acceptance or rejection. Connection tracking (conntrack) maintains a hash table of all active flows, which must be locked and searched for every packet. Under high packet rates, conntrack table contention becomes a severe bottleneck, with lock contention alone capable of saturating CPU cores.

Only after these stages does the packet reach the transport layer (TCP/UDP processing) or application. If the packet is malicious—say, a SYN flood or a UDP amplification reflection—the damage is already done: memory allocated, CPU cycles burned, cache lines polluted, and conntrack entries potentially created. Even with stateful firewalling dropping the packet at the filter table, the kernel has already paid the full processing cost.


Detailed cutaway diagram of a Linux server rack in a modern datacenter, fiber optic cables glowing cyan, with an overlay of translucent packet flow arrows—some packets stopped at the NIC layer (labeled XDP) before entering the mainboard, others penetrating deeper into a visualization of kernel memory structures, dramatic blue-tinted lighting, photorealistic 3D render

XDP inverts this model. An XDP program attached to the NIC's receive queue evaluates the raw Ethernet frame before sk_buff allocation. The eBPF bytecode runs in a sandboxed, verified environment with access to a limited set of helper functions and BPF maps (hash tables, arrays, LRU caches). Typical operations include:

  • Parsing Ethernet, IP, and transport headers to extract source IP, destination port, and protocol.
  • Looking up the source IP in a BPF hash map (blacklist/whitelist).
  • Checking per-port rate-limit counters stored in BPF per-CPU arrays.
  • Matching against a ruleset stored in a BPF array (up to 256 rules in PAKKT.io deployments).
  • Returning a verdict: XDP_DROP, XDP_PASS, XDP_TX (bounce back), or XDP_REDIRECT.

Because XDP operates on the raw frame and uses per-CPU data structures with no locking, it scales linearly with core count. Measurements on commodity 10 GbE hardware demonstrate XDP dropping minimal-size (64-byte) packets at line rate—14.88 Mpps—with per-packet processing time under 100 nanoseconds. Contrast this with iptables conntrack, which typically handles 1–2 Mpps per core before lock contention dominates.



XDP filter before kernel: architectural advantages in DDoS mitigation

When a volumetric DDoS attack targets a server, the attacker's goal is resource exhaustion: CPU, memory bandwidth, conntrack table slots, or application worker threads. XDP's pre-kernel positioning directly counters each vector.

Memory bandwidth conservation

Modern servers equipped with 25 GbE or 100 GbE NICs can receive tens of millions of packets per second. If each packet triggers a 2 KB sk_buff allocation, memory bandwidth saturates rapidly. XDP programs access packet data via a direct pointer to the DMA ring buffer—no allocation, no copy. Dropped packets never leave the NIC driver's memory space, freeing DRAM bandwidth for legitimate traffic and application workloads.

CPU cache efficiency

The Linux networking stack's layered design means a single packet touches dozens of cache lines: sk_buff metadata, conntrack hash buckets, routing tables, Netfilter hook lists. XDP programs are typically a few hundred eBPF instructions operating on a compact working set—the packet headers and a handful of BPF maps. This tight loop keeps L1/L2 caches hot, minimizing expensive DRAM accesses. In PAKKT deployments, the XDP engine's CPU footprint remains below 1% during normal traffic and scales predictably under attack.

Stateless rate-limiting at scale

Traditional stateful rate-limiting (e.g., nftables limit or meter) requires maintaining per-flow or per-source state, which introduces memory overhead and lookup latency. XDP can implement stateless or lightweight stateful rate-limits using per-CPU counters. For example, PAKKT's max_port_pps parameter enforces a global packet-per-second ceiling for a given destination port, updated atomically in a BPF per-CPU array. This approach handles millions of unique source IPs without table exhaustion.

For attacks requiring per-source rate-limiting (e.g., limiting each /32 to 1000 pps), BPF hash maps with LRU eviction provide bounded memory usage. The eBPF verifier ensures the program cannot loop indefinitely or access out-of-bounds memory, guaranteeing safety even under hostile input.


Close-up photorealistic view of a Linux terminal screen displaying real-time XDP statistics, green monospace text on black background showing packet counters, drop rates, and BPF map entries, with a blurred background of server hardware and blinking network LEDs in cyan and amber, shallow depth of field

Early discard of malformed packets

Many DDoS attacks exploit edge cases in protocol parsers—fragments with overlapping offsets, invalid TCP flag combinations, or undersized packets. XDP programs can enforce strict validation: reject packets below a minimum size, drop fragments (which legitimate traffic rarely uses for UDP/TCP), or enforce TCP flag sanity. These checks occur before the kernel's protocol parsers run, preventing potential parser vulnerabilities from being exercised.

// Pseudocode: XDP logic rejecting packets smaller than 64 bytes
if (data_end - data < 64) {
    return XDP_DROP;
}

PAKKT rules support configurable min_packet_size and max_packet_size parameters, automatically compiled into the XDP program. This prevents both runt packet floods and oversized packet attacks without kernel involvement.



Dual-layer defense: XDP and nftables in complementary roles

While XDP excels at high-speed, stateless filtering, not all threats fit this model. Application-layer attacks—HTTP floods, game protocol abuse, SSH brute-force—require stateful inspection, connection tracking, and per-flow rate-limits. This is where nftables, operating later in the kernel stack, remains indispensable.

PAKKT.io deployments configure both layers in a non-conflicting architecture:

  • XDP layer (PAKKT Engine): Attached to the network interface with a single compiled eBPF object. Enforces up to 256 simultaneous rules driven by BPF maps: global PPS caps, per-port PPS limits, IP blacklists/whitelists, protocol filters (TCP/UDP/ICMP/any), and packet size bounds. Stateless, sub-microsecond per-packet overhead.
  • nftables layer (inet pakkt table): Isolated inet family table with PREROUTING and INPUT chains. Performs conntrack-based filtering (e.g., ct state new limit rate 50/second), TCP flag validation (tcp flags syn ct state new limit rate over 100/second drop), and per-source rate-limiting using meter constructs. Operates on packets that XDP passed.

The key architectural insight: XDP handles volumetric floods and obvious malicious patterns (blacklisted IPs, port scans, protocol mismatches), drastically reducing the packet rate reaching nftables. Nftables then applies stateful logic to the remaining traffic, ensuring legitimate users experience low latency while brute-force attempts and low-rate application attacks are throttled.

Because PAKKT creates an isolated inet pakkt table, it does not interfere with Docker's nat table, fail2ban's dynamic filter rules, or iptables-persistent configurations. Administrators retain full control over their existing firewall rules, with PAKKT's protections layered transparently beneath.

Synchronizing IP lists across layers

PAKKT maintains dual-layer IP blacklists and whitelists: entries are stored in both a BPF hash map (consulted by XDP) and an nftables named set (consulted by the inet pakkt table). When an administrator adds an IP to the blacklist via the PAKKT panel or API, the agent updates both data structures atomically. This ensures that even if an attacker varies packet characteristics to evade XDP rules, the IP is still blocked at the nftables layer, and vice versa.

# Example: nftables rule referencing PAKKT's managed set
nft add rule inet pakkt prerouting ip saddr @pakkt_blacklist drop

The BPF map equivalent uses a hash table keyed by IP address (IPv4 as u32, IPv6 as a 128-bit array), with O(1) lookup complexity. The combination provides defense in depth without the performance penalty of checking multiple rulesets in a single layer.



Real-world deployment: PAKKT's single-program, multi-rule XDP architecture

Traditional XDP deployments require compiling a new eBPF program for every rule change, then hot-swapping the program on the interface—a process that introduces brief packet loss and operational complexity. PAKKT.io solves this with a persistent XDP engine: a single eBPF program remains attached to each interface, reading its ruleset from BPF maps updated in real time by the Go agent.

The PAKKT Engine supports up to 256 simultaneous rules, each defining:

  • port_range: Single port or range (e.g., 25565–25570 for Minecraft server clusters).
  • protocol: TCP, UDP, ICMP, or any.
  • rule_type: block (XDP_DROP), rate_limit (per-port PPS cap), or allow_only (drop all traffic not matching this rule).
  • max_pps: Global packets-per-second ceiling for this rule.
  • max_port_pps: Per-destination-port PPS limit.
  • min_packet_size / max_packet_size: Enforce packet size bounds.

Rules are stored in a BPF array map indexed by rule ID. When the agent receives a configuration update from the PAKKT panel (authenticated via mTLS, signed with SHA256), it writes the new ruleset into the map using bpf_map_update_elem. The XDP program on the next packet immediately reflects the change—no recompilation, no interface reload.

This architecture is critical for incident response. During an active DDoS attack, administrators can add a new blacklist entry or tighten a rate-limit via the PAKKT web panel, and the change propagates to all agents within the 30-second heartbeat interval, taking effect in the XDP data path within microseconds.

Integration with third-party panels

Many game server hosts use Pterodactyl (v1.x) to manage customer instances. PAKKT's Pterodactyl integration allows panel administrators to deploy XDP protection per game server node, with rule templates automatically applied based on server type (Minecraft Java, Minecraft Bedrock, FiveM, Rust, etc.). Upcoming integrations for Pelican and WHMCS will extend this to billing workflows, enabling hosts to offer DDoS protection as a value-added service.

The public API (authenticated via API key) exposes endpoints for rule management, agent status, and blacklist updates, enabling custom automation—e.g., automatically blacklisting source IPs detected by an intrusion detection system or integrating with external threat intelligence feeds. The community-shared template marketplace further accelerates deployment by providing peer-reviewed XDP and nftables rulesets for common attack vectors.


Wide-angle photorealistic shot of a secure network operations center, multiple curved monitors displaying PAKKT.io dashboard with world map GeoIP visualization showing attack traffic origins in red, real-time graphs of packet rates, blue ambient lighting, operator's hands on keyboard in foreground, shallow depth of field focusing on central monitor

Observability and metrics

XDP's speed comes with a trade-off: minimal per-packet logging. Logging every dropped packet would negate the performance gains. Instead, PAKKT aggregates metrics in the XDP program (using per-CPU counters) and periodically exports them to the agent. The agent forwards these to the central panel, which stores time-series data in TimescaleDB—an extension of PostgreSQL optimized for high-ingest-rate metrics.

Administrators view real-time dashboards showing:

  • Total packets received, passed, and dropped per interface.
  • Per-rule packet counters (how many packets matched rule #5, how many were rate-limited).
  • GeoIP visualization: world map heatmap of source countries, top source IPs with packet counts.
  • Per-port breakdowns (critical for multi-tenant game server hosts).
  • Audit log of rule changes, blacklist additions, and agent configuration updates.

This telemetry enables proactive capacity planning—if a port consistently hits its max_port_pps limit, legitimate traffic may be affected, signaling the need to adjust thresholds or investigate upstream attack mitigation.



Why XDP filter before kernel remains essential in 2026

As network speeds continue to scale—100 GbE is common in datacenters, 400 GbE deployments are accelerating—the gap between XDP's efficiency and traditional kernel filtering widens. The kernel's networking stack has accrued decades of features and abstractions, each adding latency and complexity. XDP represents a deliberate return to minimalism: a focused, verified program executing at the earliest possible point, with direct hardware access and zero unnecessary overhead.

For operators of publicly exposed infrastructure, XDP filter before kernel processing is not optional—it is the baseline defense against volumetric DDoS attacks. Complemented by stateful firewalling (nftables) for application-layer threats, this dual-layer architecture provides both the speed to survive multi-gigabit floods and the intelligence to mitigate low-and-slow attacks.

Platforms like PAKKT.io democratize this technology, delivering centrally managed XDP and nftables protection without requiring eBPF expertise. At €3 per agent per month, with a 7-day free trial on the first agent, the operational burden of maintaining custom eBPF programs is eliminated. Administrators gain enterprise-grade visibility—GeoIP analytics, per-port metrics, audit trails—alongside the raw packet-processing power of XDP. Whether protecting a single game server or a fleet of API endpoints across multiple datacenters, the architectural principle remains the same: filter malicious traffic before the kernel allocates resources, and scale defenses linearly with attack volume.



Conclusion

Understanding why XDP filter before kernel operations is architecturally superior clarifies the performance chasm between pre-kernel and traditional filtering. By operating on raw frames before sk_buff allocation, XDP eliminates memory exhaustion vectors, preserves CPU cache efficiency, and scales to line-rate packet processing on commodity hardware. When paired with stateful nftables rules for connection tracking and application-layer threats, this dual-layer model provides comprehensive, conflict-free protection. PAKKT.io operationalizes this architecture with a persistent XDP engine supporting 256 simultaneous rules, real-time BPF map updates, and centralized observability—transforming kernel-level defense from an expert-only discipline into a practical, accessible service for any Linux-based infrastructure.



FAQ

Can XDP programs access the full packet payload, or only headers?

XDP programs can access the entire packet payload up to the end of the DMA buffer (data_end). However, for performance, most XDP filters parse only headers (Ethernet, IP, TCP/UDP) to make drop/pass decisions. Deep packet inspection of payload content is possible but increases per-packet processing time. For payload-based filtering, consider passing the packet to the kernel (XDP_PASS) and using nftables string matching or application-layer inspection.

Does XDP filtering work with network interfaces that do not support native XDP (driver mode)?

Yes. If a NIC driver lacks native XDP support, the kernel falls back to Generic XDP (also called SKB mode), which runs the XDP program after sk_buff allocation. This negates the primary performance advantage—memory is already allocated—but the program logic still executes. For maximum efficiency, use NICs with native XDP driver support (most Intel, Mellanox, and Broadcom 10/25/100 GbE adapters support it). Check ip link show output for xdp capability or consult the kernel's XDP driver support matrix at kernel.org documentation.

How does PAKKT's XDP engine handle IPv6 traffic and dual-stack environments?

The PAKKT Engine parses both IPv4 (EtherType 0x0800) and IPv6 (EtherType 0x86DD) frames. Blacklist and whitelist BPF maps support IPv6 addresses stored as 128-bit arrays. Rules specifying port ranges and protocols apply identically to IPv6 traffic. In dual-stack deployments, the same XDP program and nftables inet table evaluate both address families without separate rulesets, ensuring consistent protection regardless of client IP version.

Protect your servers

Deploy PAKKT in 30 seconds

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