xdp高速转发小demo

本文最后更新于:2026年4月1日 晚上

我们知道linux内核转发数据包的路径非常冗长,要经过bridge,netfilter,路由等,导致性能低。而XDP的点位非常靠前,把转发做在XDP里面有没有搞头?
下文就做一个写死的demo小程序,看看XDP里面能不能把数据包转发出去,性能能提高多少。

环境搭建

参考搭建参考 [[单台设备如何模拟测试数据转发性能]]

xdp程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// xdp_forward.c
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <bpf/bpf_endian.h>

SEC("xdp")
int xdp_forward_prog(struct xdp_md *ctx)
{
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;

// 解析 Ethernet
struct ethhdr *eth = data;
if ((void*)(eth + 1) > data_end)
return XDP_PASS;

// 只处理 IPv4
if (eth->h_proto != bpf_htons(ETH_P_IP))
return XDP_PASS;

// 解析 IP
struct iphdr *ip = (void *)(eth + 1);
if ((void*)(ip + 1) > data_end)
return XDP_PASS;

// 目标 IP: 10.0.2.1
if (ip->daddr != bpf_htonl(0x0a000201)) {
return XDP_PASS;
}

// 修改 MAC(非常关键)
unsigned char dst_mac[ETH_ALEN] = {0x6a,0x68,0x07,0x59,0x0b,0xb7}; // 改成 ns3 的 MAC
unsigned char src_mac[ETH_ALEN] = {0x02,0x72,0xdb,0x4c,0xaf,0x08}; // 改成 ns2 出口 MAC

__builtin_memcpy(eth->h_dest, dst_mac, ETH_ALEN);
__builtin_memcpy(eth->h_source, src_mac, ETH_ALEN);

// 转发到另一个接口(ifindex)
int ifindex = 9; // ns2 的出口接口索引,需根据实际情况修改

return bpf_redirect(ifindex, 0);
}

char _license[] SEC("license") = "GPL";

编译加载xdp程序

编译

1
clang -O2 -g -target bpf   -I/usr/include/x86_64-linux-gnu   -I/usr/include   -c xdp_forward.c -o xdp_forward.o

加载到veth1-peer

1
ip netns exec ns2 ip link set dev veth1-peer xdp obj xdp_forward.o sec xdp

卸载命令

1
ip netns exec ns2 ip link set dev veth1-peer xdp off

解决veth下的xdp丢包问题

加载xdp程序后,发现网络就不通了,转发完全丢包。经过搜索发现,bpf_redirect针对veth网卡有限制,需要对端也挂XDP。
我们给ns3也编写一个xdp程序

1
2
3
4
5
6
7
8
9
10
11
12
// xdp_pass.c

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <bpf/bpf_endian.h>

SEC("xdp")
int xdp_pass_prog(struct xdp_md *ctx) {
return XDP_PASS;
}

然后编译加载

1
2
clang -O2 -g -target bpf   -I/usr/include/x86_64-linux-gnu   -I/usr/include   -c xdp_pass.c -o xdp_pass.o
ip netns exec ns3 ip link set dev veth2-peer xdp obj xdp_pass.o sec xdp

性能对比

ns1上使用pktgen发包

1
ip netns exec ns1 ./pktgen_sample01_simple.sh -i veth1 -s 64 -n 0 -d 10.0.2.1 -m 2a:cd:06:e5:5c:79

观测ns2的收发包情况:ip netns exec ns2 sar -n DEV 1

1
2
3
4
221120秒     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
221121秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
221121秒 veth1-peer 301884.00 1.00 14740.43 0.09 0.00 0.00 0.00 1.21
221121秒 veth2 1.00 301884.00 0.09 18867.75 0.00 0.00 0.00 1.55

对比不使用xdp的情况,性能只提高了6%。

性能 pps
默认 283040
XDP转发 301884

说明此时netfilter,路由查找的代价还不是很高。我们把代价提高,添加一个连接跟踪规则。

1
2
ip netns exec ns2 bash -c '
iptables -I FORWARD -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT'

然后再对比性能,走默认协议栈的pss性能下降很多,但是xdp的性能不变,此时性能提升40%。

性能 pps
添加netfilter连接跟踪规则 210708
XDP转发 302602

总结

(1)xdp里面确实可以搞数据包转发,但要完成一个真正可用的转发程序还有很多路要走。记录转发路径、老化超时、数据包修改、重新计算校验和等等
(2)默认情况下,系统(ubuntu 24.04, linux 6.8.0)的转发性能还是很优秀的。现代的nftables机制默认没有任何表和链,netfilter的钩子点消耗降到了最低,通过iptables添加规则后,就添加了一些默认表和链,增加了些消耗,走连接跟踪后,消耗进一步加重。
(3)本文是基于veth接口,容器之间转发数据才是这种模型,和真实的物理网卡存在差异,如果驱动支持XDP,那么性能还会飞速提升


人生苦短,远离bug Leon, 2026-04-01

xdp高速转发小demo
https://leon0625.github.io/2026/04/01/673a267a6558/
作者
leon.liu
发布于
2026年4月1日
许可协议