AF_XDP Explained: Zero-Copy Packet Processing via eBPF Ring Buffers

This article explains AF_XDP, the user-space socket interface for XDP that enables zero-copy packet processing via shared memory (UMEM) and four single-producer/single-consumer ring queues (RX, TX, FILL, COMPLETION), detailing the packet receive/send flow and the BPF_MAP_TYPE_XSKMAP for redirecting packets to user space.

Code Archaeology
Code Archaeology
Code Archaeology
AF_XDP Explained: Zero-Copy Packet Processing via eBPF Ring Buffers

1. AF-XDP User Interface

AF_XDP provides a user-space socket interface for XDP (eXpress Data Path), allowing DPDK-like high-performance packet processing in user space. The socket is called XSK (XDP Socket). The libxdp library offers a helper function to create an XSK:

int xsk_socket__create_shared(
    struct xsk_socket **xsk_ptr,
    const char *ifname,
    __u32 queue_id,
    struct xsk_umem *umem,
    struct xsk_ring_cons *rx,
    struct xsk_ring_prod *tx,
    struct xsk_ring_prod *fill,
    struct xsk_ring_cons *comp,
    const struct xsk_socket_config *config);

Each XSK binds to one NIC queue . For multi-queue NICs, you need one XSK per queue to achieve a lockless channel from NIC queue to socket, because NAPI softirq schedules only one CPU per queue.

UMEM (user memory) is a memory region shared between kernel and user space, enabling zero-copy reads and writes of network frames. Multiple XSKs can share a single UMEM.

Four ring buffers control frame descriptors in UMEM; all are single-producer/single-consumer :

RX – kernel produces descriptors of received frames; user consumes.

TX – user produces descriptors of frames to transmit; kernel consumes.

FILL – user produces free descriptors for kernel to fill with received data; kernel consumes.

COMPLETION – kernel produces descriptors of successfully transmitted frames; user consumes.

Descriptors are simply offsets into the UMEM.

AF_XDP ring queue overview from Björn Töpel's ECL 2019 presentation
AF_XDP ring queue overview from Björn Töpel's ECL 2019 presentation

2. Packet Flow

2.1 Receiving Packets

User program posts free buffer descriptors to the FILL ring. If FILL is empty, incoming packets are dropped.

Kernel receives a frame, consumes a descriptor from FILL, writes packet data into the corresponding UMEM slot.

Kernel posts the descriptor to the RX ring.

User polls RX ring, consumes descriptors, and processes the packets. For each consumed descriptor the user can:

Retain the buffer (e.g., for later transmission).

Return the buffer to FILL (optionally after copying data for async processing).

Modify the buffer in place and send a response by posting the descriptor to the TX ring.

Initial state of rings and UMEM
Initial state of rings and UMEM
Kernel consumes FILL, writes to UMEM
Kernel consumes FILL, writes to UMEM
Kernel posts descriptor to RX ring
Kernel posts descriptor to RX ring

2.2 Transmitting Packets

User writes packet data into UMEM, then posts the descriptor to the TX ring.

Kernel detects TX entries, transmits the frames, and posts the used descriptors to the COMPLETION ring.

User consumes COMPLETION descriptors; the buffers are now back in user control for reuse.

User posts descriptor to TX ring
User posts descriptor to TX ring
Kernel posts descriptor to COMPLETION ring
Kernel posts descriptor to COMPLETION ring

3. BPF_MAP_TYPE_XSKMAP

The BPF program for AF_XDP still uses XDP_REDIRECT , but instead of redirecting to another NIC, it redirects to an XSK. The map type is BPF_MAP_TYPE_XSKMAP. The map definition:

struct {
    __uint(type, BPF_MAP_TYPE_XSKMAP);
    __type(key, __u32);
    __type(value, __u32);
    __uint(max_entries, 64);
} xsks_map SEC(".maps");

The key is the queue index, the value is the XSK file descriptor. libxdp handles the map setup details.

References

xdp-tutorial repository: https://github.com/xdp-project/xdp-tutorial

eBPF.io AF_XDP concept page: https://docs.ebpf.io/linux/concepts/af_xdp/

Cloudflare blog: debugging AF_XDP: https://blog.cloudflare.com/a-debugging-story-corrupt-packets-in-af_xdp-kernel-bug-or-user-error/

Linux address_families man page: https://man7.org/linux/man-pages/man7/address_families.7.html

ECL 2019 presentation by Björn Töpel and Magnus Karlsson: https://osseu19.sched.com/event/TLFt/low-latency-deterministic-networking-with-standard-linux-using-xdp-sockets-magnus-karlsson-bjorn-topel-intel-corporation

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

eBPFzero-copyXDPAF_XDPring bufferpacket processingBPF_MAP_TYPE_XSKMAPLinux kernel networkingUMEMXSK
Code Archaeology
Written by

Code Archaeology

Learn from the past to understand the present

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.