Linux内核之XDP指南

XDP的系统架构

 XDP 与 Linux 网络栈的集成

XDP的工作位置

XDP程序的运行位置

内核中的转发路径

kube-proxy包转发路径

XDP的工作模式

XDP 总共支持三种工作模式(operation mode)

  • xdpdrv: xdpdrv 表示 native XDP(原生 XDP), 意味着 BPF 程序直接在驱动的接收路 径上运行,理论上这是软件层最早可以处理包的位置(the earliest possible point)。这是常规/传统的 XDP 模式,需要驱动实现对 XDP 的支持,目前 Linux 内核中主流的 10G/40G 网卡都已经支持。
  • xdpgeneric: xdpgeneric 表示 generic XDP(通用 XDP),用于给那些还没有原生支持 XDP 的驱动进行试验性测试。generic XDP hook 位于内核协议栈的主接收路径(main receive path)上,接受的是 skb 格式的包,但由于 这些 hook 位于 ingress 路 径的很后面(a much later point),因此与 native XDP 相比性能有明显下降。因 此,xdpgeneric 大部分情况下只能用于试验目的,很少用于生产环境。
  • xdpoffload: 一些智能网卡(例如支持 Netronome’s nfp 驱动的网卡)实现了 xdpoffload 模式 ,允许将整个 BPF/XDP 程序 offload 到硬件,因此程序在网卡收到包时就直接在网卡进行处理。这提供了比native XDP 更高的性能,虽然在这种模式中某些 BPF map 类型和BPF 辅助函数是不能用的。BPF 校验器检测到这种情况时会直接报错,告诉用户哪些东西是不支持的。除了这些不支持的 BPF 特性之外,其他方面与 native XDP 都是一样的。

XDP程序的返回码

XDP程序执行结束后会返回一个结果,告诉调用者接下来如何处理这个包:

  • XDP_DROP,丢弃这个包,主要用于报文过滤的安全场景;
  • XDP_PASS,将这个包“交给/还给”内核,继续走正常的内核处理流程;
  • XDP_TX,从收到包的网卡上再将这个包发出去(即hairpin模式),主要用于负载均衡场景;
  • XDP_REDIRECT,何XDP_TX类似,但是是通过另外一个网卡将包发出去。除此之外还可以实现将报文重定向到其他的CPU处理,类似于XDP_PASS继续走内核处理流程,但是由其他的CPU处理,当前CPU继续处理后续的报文接收;
  • XDP_ABORTED,表示程序产生异常,行为类似XDP_DROP,但是会通过一个tracepoint打印日志义工追踪;

Linux内核之XDP测试

软件安装

1
2
3
4
5
6
$ apt-get update
$ apt-get install make gcc g++ cmake clang llvm gcc-multilib -y
$ apt-get install bison libtool libelf-dev libreadline-dev -y
$ apt-get install vim git jq curl pkg-config openssl -y
$ apt-get install libpcap-dev libzip-dev libssl-dev -y
$ apt-get install libnl-3-dev libnl-route-3-dev libnl-cli-3-dev -y

第一个XDP程序

1. 代码xdp-drop.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <linux/bpf.h>

/*
* Comments from Linux Kernel:
* Helper macro to place programs, maps, license in
* different sections in elf_bpf file. Section names
* are interpreted by elf_bpf loader.
* End of comments

* You can either use the helper header file below
* so that you don't need to define it yourself:
* #include <bpf/bpf_helpers.h>
*/
#define SEC(NAME) __attribute__((section(NAME), used))

SEC("xdp")
int xdp_drop_the_world(struct xdp_md *ctx) {
// drop everything
return XDP_DROP;
}

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

2. 编译

1
$ clang -O2 -target bpf -c xdp-drop.c -o xdp-drop.o

3. 反汇编

1
$ llvm-objdump -S xdp-drop.o

4. 加载XDP程序

1
$ ip link set dev ens18 xdp obj xdp-drop.o sec xdp verbose

5. 查看XDP加载状态

1
$ ip link show dev ens18

6. 卸载XDP程序

1
$ ip link set dev ens18 xdp off

5. 查看效果

  • ens18加载XDP后,从外部ping网口ens18的IP地址,不通
  • ens18卸载XDP后,从外部ping网口ens18的IP地址,能通

调试技巧

1. 使用xdpdump在网口抓包

1
$ xdpdump -i ens18 -w - | tcpdump -r - -nn

2. 开启trace_event日志

1
2
3
4
$ cd /sys/kernel/tracing
$ cat available_events
$ echo "xdp:*" >set_event
$ cat trace_pipe

XDP相关项目

参考