简介
XDP是利用ebpf提供的全新内核网络性能提升方案,主要优势包括:
- 它不需要任何专门的硬件
- 它不需要内核绕过
- 它不会取代 TCP/IP 堆栈
- 它与 TCP/IP 堆栈以及 BPF 的所有优点协同工作
在没有引入XDP之前,原来是的内核网络数据包传输路径是这样的:
NIC > driver 驱动> tc流控 ==> netfilter ==> IP/TCP协议栈 ==> socket
启用XDP后,网络包传输路径是这样的:
①NIC > ②driver 驱动> ③Generic XDP==> tc流控 ==> netfilter ==> IP/TCP协议栈 ==> socket
① offload模式,NIC支持xdp程序的offload, XDP程序直接hook到可编程网卡硬件设备上,与其他两种模式相比,它的处理性能最强;由于处于数据链路的最前端,过滤效率也是最高的。如果需要使用这种模式,需要在加载程序时明确声明。目前支持这种模式的网卡设备不多,有一家叫netronome网卡。
② native 模式,XDP程序hook到网络设备的驱动上,它是XDP最原始的模式,因为还是先于操作系统进行数据处理,它的执行性能还是很高的,当然你的网络驱动需要支持,目前已知的有i40e, nfp, mlx系列和ixgbe系列。
③ generic模式,这是操作系统内核提供的通用XDP兼容模式,它可以在没有硬件或驱动程序支持的主机上执行XDP程序。在这种模式下,XDP的执行是由操作系统本身来完成的,以模拟native模式执行。好处是,只要内核够高,人人都能玩XDP;缺点是由于是仿真执行,需要分配额外的套接字缓冲区(SKB),导致处理性能下降,跟native模式在10倍左右的差距。
环境安装
- 测试环境:
[root@localhost xdp]# cat /etc/redhat-release
CentOS Linux release 8.4.2105
编译可能需要内核相关头文件,建议安装 内核相关包
yum install -y kernel-devel
yum install -y kernel-headers
-
编译bpf代码需要依赖下面的工具
- clang >= version 3.4.0
- llvm >= version 3.7.1
yum install -y clang yum install -y llvm
-
加载和卸载xdp程序依赖于ip命令
yum install -y iproute
测试代码
#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_hello_world(struct xdp_md *ctx) {
// drop everything
return XDP_DROP;
}
char _license[] SEC("license") = "GPL";
该例子比较简单,把收到的报文都drop掉,加载这个例子需要实现的效果就是ping不通网口,卸载则能正常ping通
XDP 结果码
使用 XDP 进行数据包处理,有 5 个返回码指示网络驱动处理数据包,列举如下:
- 丢弃(XDP_DROP)
- 转发(XDP_TX)
- 重定向(XDP_REDIRECT)
- 传递(XDP_PASS)
- 错误(XDP_ABORTED)
编译demo
[root@localhost xdp]# clang -O2 -target bpf -c xdp-hello-world.c -o xdp-hello-world.o
查看生成的elf格式的可执行文件的相关信息,使用下面的命令能看到上文提到的Section信息:
[root@localhost xdp]# llvm-objdump -S xdp_hello_world.o
xdp_hello_world.o: file format elf64-bpf
Disassembly of section xdp:
0000000000000000 <xdp_hello_world>:
llvm-objdump: warning: 'xdp_hello_world.o': failed to parse debug information for xdp_hello_world.o
0: b7 00 00 00 01 00 00 00 r0 = 1
1: 95 00 00 00 00 00 00 00 exit
[root@localhost xdp]# readelf -a xdp_hello_world.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Linux BPF
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 376 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 9
Section header string table index: 1
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .strtab STRTAB 0000000000000000 00000112
0000000000000062 0000000000000000 0 0 1
[ 2] .text PROGBITS 0000000000000000 00000040
0000000000000000 0000000000000000 AX 0 0 4
[ 3] xdp PROGBITS 0000000000000000 00000040
0000000000000010 0000000000000000 AX 0 0 8
[ 4] license PROGBITS 0000000000000000 00000050
0000000000000004 0000000000000000 WA 0 0 1
[ 5] .eh_frame PROGBITS 0000000000000000 00000058
0000000000000030 0000000000000000 A 0 0 8
[ 6] .rel.eh_frame REL 0000000000000000 00000100
0000000000000010 0000000000000010 8 5 8
[ 7] .llvm_addrsig LOOS+0xfff4c03 0000000000000000 00000110
0000000000000002 0000000000000000 E 8 0 1
[ 8] .symtab SYMTAB 0000000000000000 00000088
0000000000000078 0000000000000018 1 3 8
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
There are no section groups in this file.
There are no program headers in this file.
There is no dynamic section in this file.
Relocation section '.rel.eh_frame' at offset 0x100 contains 1 entry:
Offset Info Type Sym. Value Sym. Name
00000000001c 000200000001 unrecognized: 1 0000000000000000 xdp
The decoding of unwind sections for machine type Linux BPF is not currently supported.
Symbol table '.symtab' contains 5 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS xdp_hello_world.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 3
3: 0000000000000000 4 OBJECT GLOBAL DEFAULT 4 _license
4: 0000000000000000 16 FUNC GLOBAL DEFAULT 3 xdp_hello_world
No version information found in this file.
还可以通过llvm-objdump这个工具来分析下这个可执行文件的反汇编指令信息:
[root@localhost xdp]# llvm-objdump -S xdp_hello_world.o
xdp_hello_world.o: file format elf64-bpf
Disassembly of section xdp:
0000000000000000 <xdp_hello_world>:
0: b7 00 00 00 01 00 00 00 r0 = 1
1: 95 00 00 00 00 00 00 00 exit
测试demo
- 加载
[root@localhost xdp]# ip link set dev ens19 xdp obj xdp_hello_world.o sec xdp
ip link set命令可以支持 ip link help查看帮助
ip link set [ { xdp | xdpgeneric | xdpdrv | xdpoffload } { off |
object FILE [ section NAME ] [ verbose ] |
pinned FILE } ]
sec [section name] 就是上文提到的通过Section来指定程序入口
device name 是本机某个网卡设备的名称,可以通过ip a
查看本机所有的网卡设备。一般可以选取本机对外的IP所在的网卡设备。
xdpgeneric 对应上面的generic模式,xdpdrv对应上面的native模式,xdpoffload对应上面的offload模式
- 卸载
ip link set dev ens19 xdp off
-
测试效果
3.1 加载xdp程序前
[ych@localhost ~]$ ping 5.5.5.2 PING 5.5.5.2 (5.5.5.2) 56(84) bytes of data. 64 bytes from 5.5.5.2: icmp_seq=1 ttl=63 time=1.60 ms 64 bytes from 5.5.5.2: icmp_seq=2 ttl=63 time=6.09 ms 64 bytes from 5.5.5.2: icmp_seq=3 ttl=63 time=2.42 ms 64 bytes from 5.5.5.2: icmp_seq=4 ttl=63 time=2.53 ms 64 bytes from 5.5.5.2: icmp_seq=5 ttl=63 time=1.25 ms 64 bytes from 5.5.5.2: icmp_seq=6 ttl=63 time=4.25 ms 64 bytes from 5.5.5.2: icmp_seq=7 ttl=63 time=6.98 ms 64 bytes from 5.5.5.2: icmp_seq=8 ttl=63 time=2.98 ms ^C --- 5.5.5.2 ping statistics --- 8 packets transmitted, 8 received, 0% packet loss, time 18ms
3.2 加载xdp程序后
[ych@localhost ~]$ ping 5.5.5.2 PING 5.5.5.2 (5.5.5.2) 56(84) bytes of data. 64 bytes from 5.5.5.2: icmp_seq=1 ttl=63 time=1.53 ms 64 bytes from 5.5.5.2: icmp_seq=2 ttl=63 time=2.45 ms 64 bytes from 5.5.5.2: icmp_seq=3 ttl=63 time=3.62 ms 64 bytes from 5.5.5.2: icmp_seq=4 ttl=63 time=1.19 ms ^C --- 5.5.5.2 ping statistics --- 20 packets transmitted, 4 received, 80% packet loss, time 393ms rtt min/avg/max/mdev = 1.191/2.199/3.624/0.942 ms
3.3 卸载后则能正常ping通
XDP 和 DPDK
XDP 有时与 DPDK 并列,当两者都是完美的方法时。XDP 为希望获得性能同时仍然利用内核的可编程性的用户提供了另一种选择。XDP 提供的一些功能包括:
- 无需第三方代码和许可
- 允许选择忙轮询或中断驱动网络
- 无需分配大页面
- 无需专用 CPU,因为用户在 CPU 之间构建工作时有更多选择
- 无需从 3rd 方用户空间应用程序向内核注入数据包
- 无需为访问网络硬件定义新的安全模型