AF_XDP and Kyber Key Exchange in Userspace: Efficient, Flexible, and Modern
Introduction to eBPF and XDP (Quick Primer)
AF_XDP and Kyber Key Exchange in Userspace: Efficient, Flexible, and Modern
Introduction to eBPF and XDP (Quick Primer)
eBPF (extended Berkeley Packet Filter) is a powerful technology that allows sandboxed programs to run inside the Linux kernel without modifying kernel source code. Originally designed for filtering packets, eBPF has evolved into a general-purpose framework used for performance monitoring, security, and networking. If you want to know more about it go through their official **documentation**.
XDP (eXpress Data Path) is one of the most exciting applications of eBPF, allowing packet processing to occur as early as possible — right at the driver level. This means packets can be dropped, redirected, or modified before any higher-level networking stack (IP, TCP, etc.) sees them.
What makes XDP fast:
- Operates at the earliest possible stage of the networking stack.
- Zero-copy and kernel-bypass capabilities.
- Minimal overhead and maximum performance.
This makes XDP especially well-suited for high-performance, low-latency use cases such as DDoS mitigation, load balancing, and custom packet filtering. More on XDP Here.
Why AF_XDP for Flexible Network Stack Bypass
AF_XDP is a socket type that pairs beautifully with XDP to provide user-space applications direct access to network packets. Built atop XDP and eBPF, it allows applications to process packets without going through the traditional Linux networking stack — a big win when you want performance and full control over network behavior.
Advantages of AF_XDP:
- Bypasses the kernel network stack completely.
- Provides zero-copy packet delivery between NIC and user space.
- Works with standard socket interfaces, making integration easier.
In cases where custom protocol handling is needed — like implementing new cryptographic key exchange protocols — AF_XDP provides the best of both worlds: speed and flexibility.
Kyber Key Exchange: An Overview
Kyber is a post-quantum key encapsulation mechanism (KEM) that is part of the NIST-approved suite of algorithms resilient against quantum computer attacks. The basic idea is that two parties can securely exchange a shared key over an insecure channel.
However, Kyber’s computational and data footprint is significantly higher than classical key exchange protocols. This creates two challenges:
- Payload Size: Kyber’s encapsulated keys and ciphertexts are larger.
- Latency Sensitivity: Adding key exchange logic in userspace means the kernel’s networking stack becomes an unnecessary overhead.
These challenges make Kyber a great candidate for AF_XDP-based delivery systems.
But why AF_XDP for Kyber Key Exchange?
Here’s the insight: If the entire key exchange logic happens in userspace using libraries like CIRCL (Cloudflare’s cryptographic library for Kyber), why should the packets traverse the kernel’s complex networking stack at all?
Instead, AF_XDP allows us to:
- Exchange Kyber payloads (public key, ciphertext, shared key) directly between two user-space applications.
- Avoid any kernel-side TCP/IP stack overhead.
- Tune performance more precisely (MTU, buffers, batch processing, etc.).
This minimizes CPU usage, improves determinism, and ensures faster and more reliable secure communication channels.
This makes AF_XDP the perfect delivery mechanism for something like Kyber, which simply needs to reliably send a few specific packets between two user-space applications.
The result? A fast, reliable, kernel-bypassing, post-quantum key exchange that respects neither the kernel’s bureaucracy nor outdated networking norms.
Architecture of the Solution: Bringing Kyber to AF_XDP
After understanding AF_XDP and kyber, let’s walk through the architecture we’ve implemented — a system designed to demonstrate how a Kyber key exchange can be performed entirely in user space at high speed, bypassing the traditional network stack.
This section covers two components:
- A reference/testbed implementation using Go + CIRCL library
- The AF_XDP-powered version, showing how we eliminate kernel overhead
Part 1: Testbed Kyber Key Exchange using CIRCL
To validate the cryptographic part of the system, I started by replicating the shared key generation using Cloudflare’s CIRCL Kyber implementation in Go. This served as a testbed to verify the correctness and usability of the Kyber primitives.
How Kyber Works (Quick Recap)
- Server generates a Kyber public/private keypair.
- Client uses the server’s public key to encapsulate a shared secret and sends the ciphertext.
- Server decapsulates the ciphertext that contains the client’s public key to obtain the same shared secret.
- Now both parties have a common symmetric key — ideal for secure sessions.
In the Go testbed, this was implemented over traditional Go networking (TCP/UDP), but it proved the core functionality and packet sizes required.
However, the networking overhead in this setup was a bottleneck — hence the need for AF_XDP.
Part 2: High-Speed Kyber Exchange using AF_XDP
Now comes the core solution — a fully functional Kyber key exchange system built on AF_XDP.
- Two veth pairs connecting two isolated network namespaces.
- XDP program attached to veth interfaces, enabling redirection.
int xdp_sock_prog(struct xdp_md *ctx) {
...
struct tcphdr *tcp = (void *)(ip + 1);
if ((void *)(tcp + 1) > data_end)
return XDP_ABORTED;
__u16 src_port = bpf_ntohs(tcp->source);
__u16 dst_port = bpf_ntohs(tcp->dest);
bpf_printk("TCP src port: %d, dst port: %d\\n", src_port, dst_port);
if (dst_port == 8080){
bpf_printk("Redirecting TCP packet\n");
return bpf_redirect_map(&xsks_map, index, 0);
}
...
}
- AF_XDP sockets bound to the interfaces in both client and server namespaces.
- XSK map, used for redirecting packets to AF_XDP sockets.
struct {
__uint(type, BPF_MAP_TYPE_XSKMAP);
__uint(max_entries, 64);
__type(key, __u32);
__type(value, __u32);
} xsks_map SEC(".maps");
- Userspace implementation of Kyber key exchange using the CIRCL Kyber library in Golang.
Project Structure Highlights
- Replicated the shared key establishment process using CIRCL in Go as a baseline.
- Implemented the same using AF_XDP sockets for raw, direct packet transfer.
- Cilium’s ebpf2go for compiling and loading XDP programs.
- slavc bindings and helpers to manage AF_XDP sockets.
Flow:
- Server Initiates Key Generation: The server starts by generating its own Kyber key pair using the CIRCL library in Go. It then sends the public key directly to the client over the veth interface via AF_XDP sockets. This packet is redirected efficiently by the XDP program attached to the interface, ensuring minimal overhead.
- Client Processes Server’s Public Key and Generates Shared Secret: Upon receiving, the client generates its own Kyber key pair. It then creates a shared secret by encapsulating a random key using the server's public key (this step involves the client's logic but doesn't directly use its private key for encapsulation—rather, encapsulation relies on
pk_s). The client produces a ciphertextct_ccontaining the encapsulated shared key. - Client Responds with Ciphertext and Its Public Key: The client sends the ciphertext
ct_c(which effectively includes or is bundled with its public keypk_cin an encrypted form) back to the server via AF_XDP. This mirrors the process, allowing the server to participate in the exchange. - Server Decapsulates and Creates Shared Key: The server receives
ct_cand uses its private keysk_sto decapsulate it, retrieving the shared secret. At this point, both sides have derived the same shared key independently (the server through decapsulation, and the client from its initial encapsulation). - Communication Begins: With the mutual shared key established, secure communication can start. The applications can now encrypt and exchange data using this key, all handled in user space with AF_XDP for zero-copy efficiency. This setup is ideal for custom protocols in security-focused environments, like implementing runtime security checks or observability probes in Kubernetes clusters.
You can also test it on your own system. Here is my repository.

Traffic Flow using AF_XDP
Benchmarks and Performance Evaluation
While AF_XDP is often praised for its low latency and high throughput, in cryptographic protocols like Kyber, predictability and consistency can be equally (if not more) important than sheer speed.
In this section, we compare two versions of our Kyber key exchange implementation:In this section, we compare two versions of our Kyber key exchange implementation:
- A traditional user-space version using Go + CIRCL + TCP/UDP
- A fully user-space, AF_XDP-accelerated version that bypasses the kernel networking stack
Benchmark Setup
- Operation measured: Complete Kyber key exchange (encapsulation + decapsulation) per transaction.
- Total iterations: 4000–5000
Metrics captured:
- Execution time (in milliseconds)
- Variance across runs
- Moving average trends
- Failures or errors encountered
Refer to this for python scripts.
| Metric | Traditional Go Stack | AF_XDP-based Stack |
|---------------------|----------------------|---------------------|
| Mean Latency | 2.15 ms | 2.05 ms |
| Variance / Std Dev | High (±0.99 ms) | Low (±0.53 ms) |
| IQR (Boxplot) | 0.351 ms | 0.279 ms |
| Failures | 1050/5000 | 0/5000 |
| Stability | Moderate | Excellent |
| Kernel Bypass | ❌ No | ✅ Yes |

Performance using AF_XDP

Performance without using AF_XDP
According to the graphs and results, the speed and latency are marginally better, but the major improvements are in sustainability and reliability.
Issues, Limitations, and Future Improvements
Building a Kyber key exchange mechanism entirely in userspace using AF_XDP was an exciting and insightful journey, but not without its fair share of challenges. Let’s look at some of the core issues faced during implementation and discuss areas for future improvements.
1. The TCP State Machine: Reinventing the Wheel?
The first major challenge arose from the nature of AF_XDP itself — it operates entirely in userspace and bypasses the kernel’s network stack. While this is excellent for performance, it means you lose access to the kernel’s mature and battle-tested TCP stack, which handles connection management, retransmissions, congestion control, and more.
To achieve a functioning communication channel, we had to re-implement a minimal TCP state machine in userspace. This includes handling the 3-way handshake, connection teardown, and state transitions (e.g., SYN_SENT, ESTABLISHED, FIN_WAIT). Though this doesn’t cause significant overhead in a controlled lab/testbed environment, it adds complexity and fragility in real-world scenarios, especially when reliability, packet loss, or reordering comes into play.
Future Direction: Consider supporting UDP-based transport such as QUIC protocol.
2. Fragmentation and MTU Limitations
A second major issue was the lack of kernel-managed IP fragmentation and reassembly. Because everything happens in userspace, large payloads — especially Kyber ciphertexts and shared secrets — can exceed the typical Ethernet MTU (1500 bytes). This results in truncated or dropped packets unless:
- We manually handle fragmentation and reassembly in our userland code.
- Or we increase the MTU of the veth interfaces on both the server and client side (e.g., up to 2500 bytes for jumbo frames).
Currently, we have opted for the latter: increasing the MTU to support larger key exchange payloads. However, this introduces a tradeoff:
- Not all networks support jumbo frames.
- It becomes hard to scale or migrate this system outside a controlled environment.
🛠 Future Ideas:
- Explore a custom fragmentation/reassembly protocol tailored to Kyber’s payload size.
- Consider using multiple XDP sockets or channels to chunk large data dynamically.
3. Observability and Debugging
One unexpected difficulty was the lack of introspection tools. Unlike kernel-based networking, userspace packet processing lacks conventional logging or tracing mechanisms. Tools like tcpdump don’t help with AF_XDP unless packets also hit the traditional stack.
Future Improvements
1. Efficient Fragmentation Handling in User Space
A promising direction is to implement custom reassembly logic based on sequence numbers or application-layer framing. This can help support larger payloads without depending on MTU tuning. Alternatively, implementing a lightweight layer over UDP with packet sequencing might be feasible.
2. Kernel-Bound Optimizations
Although the current solution works entirely in user space, some hybrid models may allow us to offload certain tasks back to the kernel (e.g., TCP state tracking), using shared memory or eBPF tail calls to optimize performance without duplicating kernel logic.
3. Packaging as a Pluggable Module
Ultimately, we envision this entire setup — Kyber key exchange using AF_XDP — as a modular, reusable component that can be plugged into existing systems.
We aim to:
- Package this as a Go module.
- Provide a standardized API for initiating Kyber key exchange over AF_XDP.
- Integrate with service meshes, proxies, or even IoT platforms that need secure and fast post-quantum key exchange without kernel traversal.
Conclusion
This project is an exciting proof-of-concept demonstrating how modern cryptography (Kyber) and modern networking (AF_XDP, eBPF) can work hand in hand to build secure, performant, and programmable communication systems — all from userspace.
It shows how Linux’s programmable networking stack is not just fast, but flexible enough to accommodate emerging technologies like post-quantum cryptography.
Stay tuned for the open-source release and next iteration as a reusable module!
메타데이터
- post_id
- d787d1c6dcef
- slug
- af-xdp-and-kyber-key-exchange-in-userspace-efficient-flexible-and-modern-d787d1c6dcef
- url
- https://medium.com/@Nikhil690/af-xdp-and-kyber-key-exchange-in-userspace-efficient-flexible-and-modern-d787d1c6dcef
- canonical_url
- https://medium.com/@Nikhil690/af-xdp-and-kyber-key-exchange-in-userspace-efficient-flexible-and-modern-d787d1c6dcef
- author_url
- https://medium.com/@Nikhil690
- status
- ok
- fetched_at
- 2026-07-17 10:26:55