简介
XDP(eXpress Data Path)是Linux内核中的一种高性能可编程数据路径,专为网络接口级的数据包处理而设计。通过将eBPF程序直接附加到网络设备驱动程序上,XDP能够在数据包到达内核网络栈之前拦截并处理它们。这使得XDP能够进行极低延迟和高效的数据包处理,非常适合如DDoS防护、负载均衡和流量过滤等任务。
由于XDP程序可以直接附加到网络接口,所以每当网络接口接收到新的数据包时,XDP程序就会收到回调,并能迅速对数据包执行操作。可以使用以下三种运行模式将XDP程序连接到接口:
- 通用XDP(Generic XDP):XDP程序作为普通网络路径的一部分加载到内核中,这种方式无法充分发挥性能优势,但它是测试XDP程序或在不提供XDP特定支持的通用硬件上运行这些程序的简便方法。
- 原生XDP(Native XDP):XDP程序由网卡驱动程序作为其初始接收路径的一部分加载,需要网卡驱动程序的支持。
- 卸载式XDP(Offloaded XDP):XDP程序直接加载到网卡上,并在不使用CPU的情况下执行,这需要网络接口设备的支持。
当XDP程序连接到网络接口时,可以通过返回特定的动作来对数据包进行处理,主要包含以下5种标准动作:
| 动作名称 | 核心含义 | 是否经过内核协议栈 | 典型应用场景 |
|---|---|---|---|
| XDP_DROP | 丢弃 | 否 | DDoS防御、防火墙黑名单拦截 |
| XDP_PASS | 放行 | 是 | 正常业务流量、移交内核处理 |
| XDP_TX | 原路发回 | 否 | 负载均衡响应、TCP代理(同一网卡) |
| XDP_REDIRECT | 重定向 | 否 | 转发到其他网卡、发给用户态(AF_XDP) |
| XDP_ABORTED | 异常中止 | 否 | 程序错误处理、异常监控 |

下图展示了Offloaded XDP运行模式下,eBPF程序从从用户空间编译加载,到内核验证处理,最终挂载至网卡驱动的全生命周期流程。
- 编译加载与系统调用:在用户空间,用户编写的C代码经LLVM编译为ELF格式的eBPF字节码,随后由用户态工具(如iproute2或BCC)通过bpf()系统调用,将包含指令、元数据及目标网卡索引(ifindex)的程序描述符提交至内核,请求加载并关联至指定网络设备。
- 内核验证与即时编译:进入内核空间后,核心子系统首先执行严格的静态分析验证,确保字节码逻辑安全、无死循环且内存访问合规,以保障内核稳定性;验证通过后,即时编译器将通用的eBPF字节码翻译为当前CPU架构的原生机器指令,从而极大提升运行时的执行效率。
- 驱动挂载与硬件卸载:内核通过网卡驱动提供的ndo_bpf()接口,将编译好的程序挂载至驱动的XDP钩子上;在此阶段,程序既可在驱动层通过软件解释执行,也可被进一步Offload至支持可编程数据面的智能网卡硬件中,实现零主机CPU占用的线速包处理。

环境搭建
XDP的依赖主要分为内核基础、编译工具链和用户态管理库三个部分。
1 | sudo apt-get install -y \ |
Firewall Demo
编写一个XDP来实现在驱动层拦截恶意IP数据包,在数据包进入内核协议栈之前,利用内核态的哈希表快速查找并丢弃黑名单中的流量。
- 内核程序
- XDP入口点接收数据包,解析IP头
- 黑名单BPF_MAP_TYPE_HASH,key=IP,value=1
- RingBuffer事件流,发送DROP/PASS事件给用户态
- 触发XDP_DROP或XDP_PASS决策
- 用户态程序
- 加载eBPF程序到XDP
- 通过ebpf.Map API管理黑名单
- RingBuffer异步读取事件,格式化打印流量信息
- 通过命令行参数-iface来指定网络接口
- 通过命令行参数-mode来指定XDP运行模式
1 |
|
1 | //go:build linux |
1 | # 变量 |
这里用的是阿里云服务器,所以Native XDP不适用(可以通过ethtool -i eth0来判断网卡是否支持Native XDP,如果driver显示为virtio_net大概率不行),所以选择运行模式为Generic XDP,挂载网卡效果如下,可以看到来自黑名单的访问都被DROP掉了。

Backdoor Demo
编写一个XDP后门程序,在不开额外端口的情况下,通过特定格式的UDP数据包激活并执行命令,实现隐蔽的远程控制功能。
- 内核程序
- XDP入口点接收数据包,进行多层协议解析和过滤(检查以太网头、IP头、UDP头的完整性,验证IPv4协议和UDP协议以及校验UDP数据包长度和边界)
- BPF_MAP_TYPE_ARRAY存储命令,key=0(固定索引),value=命令结构体(包含cmd字段和processed标志)
- 多重信标验证(Payload长度,Magic字符串等)
- 触发XDP_DROP决策,丢弃匹配的数据包以增加隐蔽性
- 用户态程序
- 加载eBPF程序到XDP并附加到指定网络接口
- 通过bpf_map_lookup_elem读取内核态存储的命令,并执行提取的命令
- 支持任意端口激活,无需开放特定端口(数据包在XDP层即被处理)
- 通过命令行参数-iface来指定网络接口
- 通过命令行参数-mode来指定XDP运行模式
1 |
|
1 | //go:build linux |
1 | CLANG ?= clang |
可以看到任意端口都可以被激活,无需开放特定端口,成功执行命令。并且由于发送的Payload通过了多重信标验证,会触发XDP_DROP,导致tcpdump无法捕获到对应的请求。
1 | # 执行命令 |
