XDP Packet Processing
XDP Fundamentals and Packet Parsing
XDP (eXpress Data Path) - packet processing at the driver level, before entering the kernel stack. The fastest eBPF hook point.
XDP Fundamentals and Packet Parsing
What you will learn in this lab:
- XDP modes: Generic (test), Native (production), Offload (NIC)
- XDP actions:
XDP_PASS- forward the packet to the kernel stackXDP_DROP- drop the packet (fastest)XDP_TX- send the packet back out the same interfaceXDP_REDIRECT- redirect to another interfaceXDP_ABORTED- error, drop the packet + trace
struct xdp_mdcontext (data,data_end,data_meta)- Ethernet header parsing (
struct ethhdr) - IPv4/IPv6 header parsing
- TCP/UDP header parsing
- ICMP parsing
- Byte order:
bpf_ntohs(),bpf_htons()
Packet Parsing Pattern
SEC("xdp")
int xdp_parser(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return XDP_PASS;
if (eth->h_proto == bpf_htons(ETH_P_IP)) {
struct iphdr *ip = data + sizeof(*eth);
if ((void *)(ip + 1) > data_end)
return XDP_PASS;
// IPv4 packet processing...
}
return XDP_PASS;
}
Full parse chain: Ethernet → IP → TCP/UDP
Extending the basic pattern above into a complete L3/L4 parser:
SEC("xdp")
int xdp_full_parse(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
// L2: Ethernet
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return XDP_PASS;
if (eth->h_proto != bpf_htons(ETH_P_IP))
return XDP_PASS; // not IPv4, skip
// L3: IPv4
struct iphdr *ip = data + sizeof(*eth);
if ((void *)(ip + 1) > data_end)
return XDP_PASS;
// L4: TCP or UDP
void *l4 = (void *)ip + (ip->ihl * 4); // IP header can be variable length
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = l4;
if ((void *)(tcp + 1) > data_end)
return XDP_PASS;
// tcp->dest has the destination port (network byte order)
if (tcp->dest == bpf_htons(80))
return XDP_DROP; // drop HTTP traffic
} else if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp = l4;
if ((void *)(udp + 1) > data_end)
return XDP_PASS;
// udp->dest has the destination port
if (udp->dest == bpf_htons(53))
return XDP_DROP; // drop DNS traffic
}
return XDP_PASS;
}
Notice the pattern: every pointer dereference requires a bounds check against data_end. The verifier enforces this — skip any check and your program will not load.
xdp-tutorial: packet01-parsing/ - do it yourself!
How to work through xdp-tutorial:
- Read
README.orgfirst (understand the assignments) - Read
xdp_prog_kern.c, inspectparsing_helpers.h - Do the assignments yourself
- If you get stuck, check the
packet-solutions/folder
Prerequisite reading: XDP Paper (~15 pages)
Inspect in xdp-tutorial: setup_dependencies.org - dependencies (clang, llvm, libelf, kernel headers, bpftool, libxdp, libbpf).