mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1470 字
4 分钟
XDP:高性能数据包处理
2026-02-12

当你的服务每秒需要处理数百万甚至数千万个数据包时,内核协议栈的开销就成了瓶颈——每个数据包都要穿越 Netfilter 钩子、路由表查找、连接跟踪,即使最终只是简单地转发。XDP(eXpress Data Path)通过在网卡驱动层插入 eBPF 程序,在数据包进入内核协议栈之前就做出决策,将处理延迟从微秒级降到纳秒级。

XDP 是 eBPF 技术最成功的应用之一——Facebook(Meta)的 Katran 负载均衡器、Cloudflare 的 DDoS 防护、Cilium 的 NodePort 加速,都建立在 XDP 之上。

一、XDP 的架构#

1.1 XDP 在网络栈中的位置#

flowchart TB NIC["网卡硬件"] -->|"DMA"| DRV["网卡驱动"] DRV -->|"XDP 程序<br/> 最早介入点"| XDP["XDP 处理"] XDP -->|"XDP_PASS"| NETSTACK["内核网络协议栈<br/>Netfilter / 路由 / 连接跟踪"] XDP -->|"XDP_DROP"| DROP["丢弃"] XDP -->|"XDP_TX"| TX["原网卡发送"] XDP -->|"XDP_REDIRECT"| REDIR["重定向到其他接口/CPU"] NETSTACK --> APP["用户态应用<br/>Socket"] style XDP fill:#c8e6c9,stroke:#2e7d32 style DROP fill:#ffcdd2,stroke:#c62828 style NETSTACK fill:#fff9c4,stroke:#f9a825

XDP 的核心优势在于它的介入时机——在网卡驱动收到数据包后、内核协议栈处理之前。这意味着:

  • 不经过 sk_buff 分配(节省内存和 CPU)
  • 不经过 Netfilter 钩子(节省规则匹配开销)
  • 不经过路由表查找(节省查表开销)
  • 不经过连接跟踪(节省状态查找开销)

1.2 XDP 与传统网络处理的性能对比#

处理方式处理延迟吞吐量灵活性
传统内核协议栈~10-50μs~1-5 MPPS
XDP(native 模式)~0.1-1μs~10-40 MPPS
DPDK~0.05-0.5μs~40-80 MPPS
AF_XDP~0.2-2μs~10-30 MPPS

1.3 XDP 的上下文结构#

XDP 程序接收 struct xdp_md 作为上下文:

// XDP 上下文结构
struct xdp_md {
__u32 data; // 数据包起始地址
__u32 data_end; // 数据包结束地址
__u32 data_meta; // 元数据起始地址
__u32 ingress_ifindex; // 入接口索引
__u32 rx_queue_index; // 接收队列索引
__u32 egress_ifindex; // 出接口索引(5.19+)
};

二、XDP 的四种返回动作#

2.1 动作详解#

返回值名称语义典型用途
0XDP_PASS交给内核协议栈继续处理正常流量
1XDP_DROP在驱动层直接丢弃DDoS 防护、黑洞路由
2XDP_TX从接收网卡原路发送回去负载均衡(DSR 模式)
3XDP_REDIRECT重定向到其他接口或 CPU负载均衡、流量镜像
4XDP_ABORTED错误丢弃(仅用于调试)程序错误处理

2.2 动作选择决策树#

flowchart TD PKT["收到数据包"] --> Q1{"是攻击流量?"} Q1 -->|"是"| DROP["XDP_DROP<br/>驱动层丢弃"] Q1 -->|"否"| Q2{"需要转发?"} Q2 -->|"否"| Q3{"需要重定向?"} Q2 -->|"是,原路返回"| TX["XDP_TX<br/>原网卡发送"] Q3 -->|"否"| PASS["XDP_PASS<br/>交给协议栈"] Q3 -->|"是"| REDIR["XDP_REDIRECT<br/>重定向"] style DROP fill:#ffcdd2,stroke:#c62828 style PASS fill:#c8e6c9,stroke:#2e7d32 style TX fill:#bbdefb,stroke:#1565c0 style REDIR fill:#fff9c4,stroke:#f9a825

三、XDP 的三种运行模式#

3.1 模式对比#

模式执行位置性能灵活性要求
Native(原生)网卡驱动层最高网卡驱动支持
SKB(通用)内核协议栈入口无特殊要求
Offload(卸载)网卡硬件极高智能网卡支持
flowchart TB subgraph Native模式["Native 模式(驱动层)"] N_NIC["网卡"] --> N_DRV["驱动"] N_DRV --> N_XDP["XDP 程序<br/> 驱动层执行"] N_XDP --> N_STACK["协议栈"] end subgraph SKB模式["SKB 模式(协议栈入口)"] S_NIC["网卡"] --> S_DRV["驱动"] S_DRV --> S_SKBUFF["sk_buff 分配"] S_SKBUFF --> S_XDP["XDP 程序<br/>协议栈入口执行"] S_XDP --> S_STACK["协议栈"] end subgraph Offload模式["Offload 模式(网卡硬件)"] O_NIC["智能网卡<br/>硬件 XDP 引擎"] --> O_XDP["XDP 程序<br/> 硬件执行"] O_XDP --> O_DRV["驱动"] O_DRV --> O_STACK["协议栈"] end style N_XDP fill:#c8e6c9,stroke:#2e7d32 style S_XDP fill:#fff9c4,stroke:#f9a825 style O_XDP fill:#bbdefb,stroke:#1565c0

3.2 检查网卡 XDP 支持#

# 查看网卡是否支持 XDP native 模式
ethtool -i eth0
# 检查 XDP 特性
bpftool feature probe | grep xdp
# 附加 XDP 程序时指定模式
sudo ip link set dev eth0 xdp obj xdp_prog.o sec xdp mode native
sudo ip link set dev eth0 xdp obj xdp_prog.o sec xdp mode skb
sudo ip link set dev eth0 xdp offload obj xdp_prog.o sec xdp

四、XDP 程序开发#

4.1 基础 XDP 程序:DDoS 防护#

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define XDP_PASS 0
#define XDP_DROP 1
// 黑名单 Map
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 100000);
__type(key, __u32); // 源 IP
__type(value, __u32); // 动作
} blacklist SEC(".maps");
// 速率限制 Map
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 65536);
__type(key, __u32); // 源 IP
__type(value, __u64); // 上次允许的时间
} rate_limit SEC(".maps");
SEC("xdp")
int xdp_ddos_filter(struct xdp_md *ctx)
{
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
// 解析以太网头
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 *iph = (void *)(eth + 1);
if ((void *)(iph + 1) > data_end)
return XDP_PASS;
__u32 src_ip = iph->saddr;
// 检查黑名单
__u32 *action = bpf_map_lookup_elem(&blacklist, &src_ip);
if (action && *action == XDP_DROP)
return XDP_DROP;
// 速率限制:每秒最多 1000 个包
__u64 *last_time = bpf_map_lookup_elem(&rate_limit, &src_ip);
__u64 now = bpf_ktime_get_ns();
if (last_time) {
if (now - *last_time < 1000000) // 1ms 内
return XDP_DROP;
}
bpf_map_update_elem(&rate_limit, &src_ip, &now, BPF_ANY);
return XDP_PASS;
}
char LICENSE[] SEC("license") = "GPL";

4.2 XDP 负载均衡器#

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
// 后端服务器列表
struct backend {
__u32 ip;
__u8 mac[6];
__u16 port;
};
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 16);
__type(key, __u32);
__type(value, struct backend);
} backends SEC(".maps");
// 连接状态表(一致性哈希)
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 65536);
__type(key, __u32); // 客户端 IP
__type(value, __u32); // 后端索引
} conn_table SEC(".maps");
SEC("xdp")
int xdp_lb(struct xdp_md *ctx)
{
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end)
return XDP_PASS;
if (eth->h_proto != bpf_htons(ETH_P_IP))
return XDP_PASS;
struct iphdr *iph = (void *)(eth + 1);
if ((void *)(iph + 1) > data_end)
return XDP_PASS;
// 查找连接状态
__u32 client_ip = iph->saddr;
__u32 *backend_idx = bpf_map_lookup_elem(&conn_table, &client_ip);
__u32 idx;
if (backend_idx) {
idx = *backend_idx;
} else {
// 简单哈希选择后端
idx = client_ip % 4; // 假设 4 个后端
bpf_map_update_elem(&conn_table, &client_ip, &idx, BPF_ANY);
}
// 查找后端信息
struct backend *be = bpf_map_lookup_elem(&backends, &idx);
if (!be)
return XDP_PASS;
// 修改目标 IP 和 MAC
iph->daddr = be->ip;
__builtin_memcpy(eth->h_dest, be->mac, 6);
// 重新计算 IP 校验和
iph->check = 0;
iph->check = bpf_csum_diff(0, 0, (void *)iph, sizeof(*iph), 0);
// 重定向到后端所在接口
return bpf_redirect_map(&backends, idx, 0);
}
char LICENSE[] SEC("license") = "GPL";

4.3 XDP 数据包修改#

XDP 程序可以修改数据包内容——增删头部、修改字段:

SEC("xdp")
int xdp_modify(struct xdp_md *ctx)
{
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end)
return XDP_PASS;
// 增加 VLAN 头部
// bpf_xdp_adjust_head 调整数据包起始位置
int offset = -4; // 向前扩展 4 字节
if (bpf_xdp_adjust_head(ctx, offset))
return XDP_PASS;
// 重新获取指针(adjust_head 后指针可能变化)
data = (void *)(long)ctx->data;
data_end = (void *)(long)ctx->data_end;
// 填充 VLAN 头部
// ...
return XDP_PASS;
}

五、XDP 重定向#

5.1 重定向方式#

方式Map 类型说明
bpf_redirect重定向到指定接口索引
bpf_redirect_mapDEVMAP重定向到 Map 中的设备
bpf_redirect_mapCPUMAP重定向到指定 CPU 处理
bpf_redirect_mapXSKMAP重定向到 AF_XDP Socket

5.2 DEVMAP 重定向#

// DEVMAP:将数据包重定向到其他网卡
struct {
__uint(type, BPF_MAP_TYPE_DEVMAP);
__uint(max_entries, 16);
__type(key, __u32); // 接口索引
__type(value, __u32); // 目标接口索引
} dev_map SEC(".maps");
SEC("xdp")
int xdp_redirect(struct xdp_md *ctx)
{
__u32 key = ctx->ingress_ifindex;
return bpf_redirect_map(&dev_map, key, 0);
}

5.3 CPUMAP 重定向#

// CPUMAP:将数据包重定向到指定 CPU 处理
struct {
__uint(type, BPF_MAP_TYPE_CPUMAP);
__uint(max_entries, 128);
__type(key, __u32); // CPU 编号
__type(value, __u32); // 队列大小
} cpu_map SEC(".maps");
SEC("xdp")
int xdp_cpu_redirect(struct xdp_md *ctx)
{
// 将数据包重定向到 CPU 0 处理
__u32 cpu = 0;
return bpf_redirect_map(&cpu_map, cpu, 0);
}

六、XDP 与 DPDK 的对比#

维度XDPDPDK
架构内核内(驱动层)用户态(绕过内核)
开发语言C(eBPF 限制)C/C++(无限制)
性能10-40 MPPS40-80 MPPS
灵活性受验证器限制完全自由
部署无需独占网卡需要独占网卡
生态Linux 内核原生独立用户态库
调试bpftool/bpftrace传统调试器
网络管理与内核协议栈共存完全接管网络
适用场景DDoS 防护、LB、防火墙NFV、SDN、高性能网关
Note

XDP 和 DPDK 不是互斥的。AF_XDP 提供了 XDP 与用户态程序的桥梁——XDP 程序可以将数据包重定向到 AF_XDP Socket,用户态程序通过该 Socket 接收数据包,实现类似 DPDK 的高性能用户态处理,同时保留与内核协议栈的兼容性。

七、XDP 的生产案例#

7.1 Meta Katran#

Katran 是 Meta 开源的 L4 负载均衡器,基于 XDP 实现:

  • 每秒处理 4000 万+ 数据包
  • 使用 IPIP 封装实现 DSR(Direct Server Return)
  • Consistent Hashing 实现会话保持
  • 已在 Facebook/Instagram 基础设施中大规模部署

7.2 Cloudflare DDoS 防护#

Cloudflare 使用 XDP 实现 DDoS 防护:

  • XDP_DROP 在驱动层丢弃攻击流量
  • 速率限制基于源 IP + 端口
  • 与 iptables 规则联动
  • 全球 300+ 数据中心部署

7.3 Cilium NodePort 加速#

Cilium 使用 XDP 加速 NodePort 和 LoadBalancer Service:

  • XDP 在驱动层处理 NodePort 流量
  • 直接转发到后端 Pod,绕过内核协议栈
  • 性能比 kube-proxy iptables 模式提升 5-10 倍

八、动手实践#

8.1 最简 XDP 程序#

# 使用 bpftrace 写一个 XDP 计数器
sudo bpftrace -e '
xdp:eth0 {
@xdp_packets = count();
}'
# 或者使用 C 编写
cat > xdp_count.bpf.c << 'EOF'
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, __u64);
} pkt_count SEC(".maps");
SEC("xdp")
int xdp_counter(struct xdp_md *ctx)
{
__u32 key = 0;
__u64 *count = bpf_map_lookup_elem(&pkt_count, &key);
if (count)
*count += 1;
return 0; // XDP_PASS
}
char LICENSE[] SEC("license") = "GPL";
EOF

8.2 附加 XDP 程序到网卡#

# 编译
clang -O2 -target bpf -c xdp_count.bpf.c -o xdp_count.bpf.o
# 附加到网卡(native 模式)
sudo ip link set dev eth0 xdp obj xdp_count.bpf.o sec xdp
# 查看附加的 XDP 程序
ip link show dev eth0
# 查看 XDP 程序统计
sudo bpftool prog show
# 卸载 XDP 程序
sudo ip link set dev eth0 xdp off

8.3 XDP 性能测试#

# 使用 pktgen 生成测试流量
sudo modprobe pktgen
echo "add_device eth0" > /proc/net/pktgen/kpktgend_0
# 使用 tcpreplay 回放 pcap
sudo tcpreplay -i eth0 -K -l 1000000 capture.pcap
# 查看 XDP 处理统计
sudo bpftool prog show id <prog_id>
# 输出包含:run_time_ns, run_cnt, etc.
flowchart TB PKT["网卡收包"] --> XDP["XDP 程序"] --> ACTION{"返回动作"} ACTION -->|"XDP_PASS"| TC["交给 TC<br/>正常协议栈"] ACTION -->|"XDP_DROP"| DROP["丢弃<br/>DDoS 防护"] ACTION -->|"XDP_TX"| TX["原网卡回发<br/>负载均衡"] ACTION -->|"XDP_REDIRECT"| REDIR["重定向<br/>到其他接口/CPU"] style XDP fill:#bbdefb,stroke:#1565c0 style DROP fill:#ffcdd2,stroke:#c62828
Warning

XDP 程序运行在驱动层,早于内核协议栈处理。这意味着 XDP 无法访问 socket、路由表等内核数据结构。如果需要这些信息,应使用 TC eBPF 程序代替。此外,并非所有网卡驱动都支持 XDP——确认你的驱动在 内核文档 的支持列表中。

九、本章小结#

上一章理解了CO-RE 可移植性机制。 本章详解了 XDP 的完整技术栈:

主题核心要点关键词
架构XDP 在网卡驱动层执行,绕过内核协议栈,实现纳秒级处理延迟架构
四种动作PASS(交给协议栈)、DROP(丢弃)、TX(原路发送)、REDIRECT(重定向)四种动作
三种模式Native(驱动层,最快)、SKB(通用兼容)、Offload(硬件卸载)三种模式
重定向DEVMAP(接口重定向)、CPUMAP(CPU 重定向)、XSKMAP(AF_XDP)重定向
与 DPDK 对比XDP 更易部署,DPDK 性能更高,AF_XDP 桥接两者与 DPDK 对比
生产案例Katran(LB)、Cloudflare(DDoS)、Cilium(NodePort)生产案例

参考#

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

XDP:高性能数据包处理
https://blog.souloss.com/posts/ebpf/xdp/
作者
Souloss
发布于
2026-02-12
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

相关文章 智能推荐
1
XDP 与 eBPF 高性能网络
高性能网络 深入 XDP(eXpress Data Path)与 eBPF——eBPF 验证器与 JIT 编译、XDP 五种动作语义、BPF Map 类型体系、cpumap/devmap 重定向、AF_XDP 套接字、XDP 与 DPDK 的全面对比——掌握内核态高性能网络的完整技术栈。
2
eBPF Map:数据结构详解
eBPF eBPF Map 是内核态与用户态之间数据交互的核心桥梁——它支持哈希表、数组、环形缓冲区等 20+ 种数据结构。本章详解每种 Map 类型的特性、使用场景、性能特征,并通过实战代码展示 Map 的创建、查询、更新与遍历操作。
3
综合实战:构建高性能网络应用
高性能网络 综合实战——技术选型决策树、构建 DPDK L4 负载均衡器(全代码走读)、集成 XDP DDoS 防护、RDMA 远程存储访问、性能基准方法论(pktgen/TRex/MoonGen)、调优检查清单——将前 14 章知识融会贯通,构建生产级高性能网络应用。
4
TC:流量控制与 eBPF
eBPF TC(Traffic Control)是 Linux 内核的流量控制子系统,通过 cls_bpf 分类器可以在 TC 层挂载 eBPF 程序,实现灵活的数据包分类、修改和重定向。本章详解 TC eBPF 的架构、ingress/egress 双向处理、direct action 模式、sk_buff 操作,以及 TC 与 XDP 的选择策略。
5
eBPF Hook 点:kprobe/tracepoint/uprobe
eBPF eBPF 程序的价值在于它能挂载到内核的各种检查点——Hook 点。本章详解三大 Hook 机制——kprobe(动态内核函数追踪)、tracepoint(静态追踪点)、uprobe(用户态函数追踪),以及 USDT 静态用户态追踪点,并通过实战代码展示每种 Hook 的使用方式与适用场景。