XDP runs an eBPF program inside the network driver, before Linux allocates a socket buffer. That is the whole idea. What matters is what you can make it do once it is there — and whether it still holds up with PPPoE termination, carrier-grade NAT, hierarchical QoS and ACLs all switched on at once.
Ours does 50 Mpps per box with every one of those enabled. That is 33.6 Gbps at 64-byte frames, the hardest test there is.
Full feature set
Engine ceiling per box
At 64-byte frames
The hardest test there is
Full-CGNAT path
Measured per-packet cost
No appliance
Servers you already buy
XDP — eXpress Data Path — is a hook in the Linux kernel that runs an eBPF program in the network driver’s receive path, before the kernel allocates an sk_buff for the packet. Allocating and initialising that structure is most of what the kernel spends on a packet it is going to drop or forward unchanged, so the saving is not marginal.
The program returns one of four verdicts, and the driver acts on it immediately:
XDP_DROP — free the buffer and return. Nothing is allocated, nothing is queued. This is why XDP is the natural place for anti-spoof filtering and DDoS mitigation.XDP_PASS — hand the packet on to the normal network stack.XDP_TX — send it back out the interface it arrived on, after whatever the program rewrote.XDP_REDIRECT — send it to another interface, a CPU map, or an AF_XDP socket.There are three places the program can run. Native mode is inside the driver, which is where the performance is. Generic mode runs after sk_buff allocation — useful for testing, not for a data plane. Offloaded mode runs on the NIC itself, on the few cards that support it.
Everything an XDP program does passes the eBPF verifier before it is allowed to load: bounded loops, no unchecked pointer arithmetic, no unbounded execution. A forwarding program that would fault does not load in the first place. That property is the reason a data plane can be replaced on a running box.
DPDK is the obvious alternative and it is a serious piece of engineering. The reason we did not build on it comes down to five structural consequences of taking the port out of the kernel.
A poll-mode driver spins a core whether or not there are packets. Processor cost is fixed at the worst case. In-kernel, cost tracks offered load — an idle gateway at 04:00 is an idle gateway.
No hugepage pool to size and lock away before the data plane will run. Packet memory is ordinary kernel memory, which means the box is still a box you can run other things on.
The interface stays a normal interface. ip, tcpdump, ethtool and your monitoring keep working on it, because nothing bound it away from the kernel.
A userspace data plane needs its own FIB and its own view of the network, kept in step with the kernel’s. Two tables that must agree is a class of bug that simply does not exist here.
The forwarding program can be swapped on a running data plane. That is what makes shipping a security fix in days instead of at the next line-card refresh a normal operation rather than a maintenance window.
The long version, with the kernel-bypass camp’s own published measurements: XDP vs DPDK — the benchmark everyone quotes has no subscribers in it.
Not six products chained together. One XDP program, one pass over the packet.
Subscriber sessions with RADIUS AAA, CoA and QinQ, with every CPU core carrying its share rather than one core owning the control path. vBNG / BRAS
Port-block allocation per subscriber, NAT44 with conntrack sized to real sessions, and logging that maps a public IP:port back to a customer. XDP CGNAT
The plan rate delivered per subscriber, with AQM and L4S underneath so a full line still feels fast. Low latency
Filtering at the subscriber port, in the driver, before the kernel has spent anything on the packet. Edge security
Dual-stack, DS-Lite, NAT64 and a stateless MAP-T / MAP-E border relay in the same fast path. NAT64 brief
Border routing on the same box that terminates subscribers — no separate edge router to licence. Border router brief
Most BNG datasheets quote a headline Gbps and leave out the frame size, which makes the figure unfalsifiable and useless to anyone specifying hardware. The engine ceiling is a packet rate. Bandwidth follows from it and from frame size.
| Frame size | Wire bits | At 50 Mpps |
|---|---|---|
| 64 B | 672 | 33.6 Gbps |
| 256 B | 2,208 | 110.4 Gbps |
| IMIX ~340 B | 2,880 | 144.0 Gbps |
| 1500 B | 12,160 | 608 Gbps — port-capped |
One measured constant and visible arithmetic:
wire_bits = (frame_bytes + 20) × 8, then
Gbps = pps × wire_bits ÷ 1e9. Check it yourself.
Port capacity is not throughput. A box with 400G of ports fitted — two dual-port E810s — does not forward 400 Gbps of 64-byte frames. That would be 595 Mpps, which no x86 software data plane does. Anyone quoting the port number as a forwarding number is quoting the wrong number.
Running in the driver means the driver is yours to fix. When a card misbehaves at line rate, the fix goes into the driver rather than into a support ticket that ends with a firmware release note six months out. Kernel 6.18.2 to 6.18.3 took five days on our fleet. Security fixes ship as software, because there is no line card to refresh.
That is the part of an XDP data plane nobody puts on a datasheet, and it is the part that decides whether the thing survives contact with real hardware.
Tell us subscriber count and busy-hour throughput and we will size it against your numbers, not ours. A proof of concept runs on your own x86 against your own traffic.
Contact us if you need a solution.