XDP原理介绍
受到Bastion论文的启发,我们尝试基于XDP来实现同主机上两个容器之间的端到端转发。XDP全称为eXpress Data Path, 是一种内核网卡上的Rx-hook,支持在网卡驱动上执行用户编写的eBPF程序,优点包括:在接收到数据包之后能够立即对其进行处理;内核还没有为数据包分配skb缓存,开销低。
补充:以下内容参考Veth XDP: XDP for containers
网卡驱动需要做一些修改(加入hook点)才能支持XDP(Native XDP),它的缺点是需要选择支持XDP的网卡驱动,并不灵活。Generic XDP是在内核4.12开始引入的新特性,它是在网络栈中加入hook点(此时内核网络栈已经为数据包分配了skb结构体缓存),需要将skb结构体转换为
xdp_md
结构体,Generic XDP的优点是方便进行功能测试,缺点在于速度并没有native xdp(non-generic)快,原因是:
- 需要在网卡驱动中分配skb结构体缓存;
- 需要进行数据包的拷贝以满足XDP程序的处理要求;
从内核4.14开始,Generic XDP开始支持虚拟设备,veth获得Generic XDP的支持,内核4.19开始加入了对Native XDP的支持,在veth驱动层面上实现了XDP。下图是两种XDP hook的位置,通用XDP在分配skb缓存之后的
receive_skb()
函数之后执行,原生XDP在poll机制内执行(NAPI poll机制不断调用驱动实现的poll方法,后者处理RX队列内的包,并将数据包送到原生XDP程序),在原生XDP之后才是clean_rx()
函数来分配skb缓存。既然像上图两种方式处理数据包并没有太大的不同,为什么还需要实现原生XDP呢?因为可以充分利用
XDP_REDIRECT
来提升性能(Physical NIC drivers can redirect packets to another interface without allocating sk_buff)。实现了一种特殊的结构体xdp_frame
,xdp_frame
结构体转化成xdp_buff
结构体开销非常低,并不需要进行数据包的拷贝。下面的slide是
xdp_frame
结构体和sk_buff
结构体的对比:
- sk_buff(了解skb可参考:博客1, 博客2):
- 用一个单独的对象存放元数据以及指向数据包内容的指针(head, data, tail, end, len),需要内存的分配和释放;
- 在许多情况下,在将
sk_buff
转化成xdp_buff
结构体时由于head空间不够需要进行数据包的拷贝;- xdp_frame:
- 重复利用数据包head空间用来存放元数据;
- 在将
xdp_frame
结构体转化成xdp_buff
结构体时不需要数据包的拷贝(除了AF_XDP零拷贝的情况)。下面展示了XDP在容器情境下的一个使用案例:物理网卡接收到数据包,挂载在上面的XDP程序通过
XDP_REDIRECT
将数据包转发到主机侧veth网卡上并进入到容器侧的veth网卡,容器内的网卡也挂载xdp程序对数据包进行一些处理并返回XDP_TX
将数据包从原来的网卡上返回,主机侧的veth网卡上的XDP程序再次将其重定向到另一个容器的主机侧网卡,并通过veth网卡进入到另一个容器内部。这里需要注意的是,即使这个容器不对数据包进行任何操作也需要挂载一个返回XDP_PASS的XDP程序,否则,由于没有安装XDP程序,容器内的veth网卡上的接收队列并非处于就绪状态,数据包会被丢弃(参考Veth XDP: XDP for containers上对应的paper)。以上就实现了一个基于容器的服务链。在veth网卡上支持原生XDP的实现:
原始的veth代码使用软中断来处理接收队列中的数据包(对应
process_backlog()
函数),因为这个函数对于没有NAPI和RPS机制的驱动来说是很常用的,所以无法加入处理XDP数据包的函数;实现了专有的NAPI机制和rx队列:
- 只有当挂载了XDP程序才会进入到这种处理模式;
- tx侧将数据包放入到对端网卡的rx队列;
- 对端网卡上的NAPI处理机制将从队列中获取数据包并执行XDP程序;
- NAPI能够避免因为
XDP_REDIRECT
链的错误配置导致的环路和栈溢出。
实验设计
实验设计图如上所示,考虑到实验目的是验证同主机容器的连通性问题,并没有实现相应的安全策略检查功能。在这个实验中,在同一台主机上运行了2个tomcat容器,实验目的是通过xdp实现数据包在端到端的直接转发。
实验过程
代码实现
程序的实现逻辑很简单,将xdp程序挂载到容器1的主机侧veth网卡,当容器1发送一个数据包,数据包到达主机侧veth网卡lxc00aa的rx队列,xdp程序执行,将链路层头部的目的MAC地址修改为容器2的目的MAC地址(对应的目的MAC地址和目的IP作为键值对存储在BPF map中,预先存好),然后通过bpf_redirect_map
函数将数据包转发到容器2的主机侧veth网卡lxc00bb的网卡出口(对于xdp程序,bpf_redirect_map
函数只能将数据包重定向到网卡的出口,详情参考bpf-helpers(7) — Linux manual page)这样数据包就可以进入到容器2。这样我们就实现了容器1到容器2的数据包单向传递路径,同理,在容器2的主机侧veth网卡上lxc00bb网卡上挂载同样的xdp程序,当收到容器2发出的数据包之后,修改目的mac地址为容器1的mac地址,并通过bpf_redirect_map
函数将数据包转发到容器1的主机侧veth网卡lxc00aa的网卡出口,数据包就能达到容器1。
另外,在原理的补充部分提到,为了在veth网卡上支持xdp,实现了专有的NAPI机制和rx队列,只有在对端veth网卡(eth0网卡)上安装了xdp程序才能使得数据包经过重定向后到达对端容器的eth0网卡不会被丢弃,因此还需要分别在容器1和容器2的内部eth0网卡上挂载一个xdp程序,对于到达的数据包不作任何处理,仅返回xdp_pass
将数据包传递给内核协议栈。以上便实现了容器1和容器2的端到端直接通信。
关键代码如下所示:
SEC("xdp_redirect_map")
int xdp_redirect_map_func(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct hdr_cursor nh;
struct ethhdr *eth;
int eth_type;
int action = XDP_PASS;
unsigned char *dst;
/* These keep track of the next header type and iterator pointer */
nh.pos = data;
/* Parse Ethernet and IP/IPv6 headers */
eth_type = parse_ethhdr(&nh, data_end, ð);
if (eth_type == -1)
goto out;
/* Do we know where to redirect this packet? */
dst = bpf_map_lookup_elem(&redirect_params, eth->h_source);
if (!dst)
goto out;
/* Set a proper destination address */
memcpy(eth->h_dest, dst, ETH_ALEN);
action = bpf_redirect_map(&tx_port, 0, 0);
out:
// xdp_stats_record_action用来进行数据包的统计,将数据包的处理结果统计信息存放到bpf map,
// 返回action值
return xdp_stats_record_action(ctx, action);
}
在进行实验的测试时,发现从容器1能够正常的ping通容器2。
curl命令无响应
接下来进入到容器1并执行curl命令,尝试访问容器2的tomcat服务,正常来说,应该返回一个网页并将html内容打印出来,但是结果是curl命令无响应:
- 容器1的IP地址为172.17.0.2,容器2的IP地址为172.17.0.3;
- curl命令为
curl 172.17.0.3:8080
在容器2的eth0网卡上使用tcpdump抓包,命令为:tcpdump -i eth0 -ev -w tomcat.cap
,然后使用wireshark查看相应的结果:
可以看到,容器2成功收到了SYN数据包,但是似乎将这个数据包丢弃掉了,并没有返回SYN+ACK的数据包,由于超时,容器1一直在重传SYN数据包。TCP连接无法建立,因此出现了curl命令无响应的结果。
卸载掉挂载在容器1和容器2主机侧veth网卡上的xdp程序,curl命令又能够成功获取到相应的网页。
直到分析了一下tcpdump的输出结果定位到了是tcp数据包的校验和的问题。可以看到,发出的数据包seq序列号是不同的,但是校验和的值都是0x5856,显然这个校验和是不正确的。
root@ubuntu:/home/luo/xdp-tutorial/packet03-redirecting# ip netns exec tomcat02 tcpdump -i eth0 -ev
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
04:06:53.439331 02:42:ac:11:00:02 (oui Unknown) > 02:42:ac:11:00:03 (oui Unknown), ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 50870, offset 0, flags [DF], proto TCP (6), length 60)
172.17.0.2.44498 > ubuntu.http: Flags [S], cksum 0x5856 (incorrect -> 0x9914), seq 1956521919, win 64240, options [mss 1460,sackOK,TS val 2076087700 ecr 0,nop,wscale 7], length 0
04:06:54.441073 02:42:ac:11:00:02 (oui Unknown) > 02:42:ac:11:00:03 (oui Unknown), ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 50871, offset 0, flags [DF], proto TCP (6), length 60)
172.17.0.2.44498 > ubuntu.http: Flags [S], cksum 0x5856 (incorrect -> 0x952a), seq 1956521919, win 64240, options [mss 1460,sackOK,TS val 2076088702 ecr 0,nop,wscale 7], length 0
04:06:56.457771 02:42:ac:11:00:02 (oui Unknown) > 02:42:ac:11:00:03 (oui Unknown), ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 50872, offset 0, flags [DF], proto TCP (6), length 60)
172.17.0.2.44498 > ubuntu.http: Flags [S], cksum 0x5856 (incorrect -> 0x8d4a), seq 1956521919, win 64240, options [mss 1460,sackOK,TS val 2076090718 ecr 0,nop,wscale 7], length 0
04:06:58.665668 02:42:ac:11:00:02 (oui Unknown) > 02:42:ac:11:00:03 (oui Unknown), ethertype ARP (0x0806), length 42: Ethernet (len 6), IPv4 (len 4), Request who-has ubuntu tell 172.17.0.2, length 28
04:06:58.665708 02:42:ac:11:00:03 (oui Unknown) > 02:42:ac:11:00:02 (oui Unknown), ethertype ARP (0x0806), length 42: Ethernet (len 6), IPv4 (len 4), Reply ubuntu is-at 02:42:ac:11:00:03 (oui Unknown), length 28
04:07:00.713382 02:42:ac:11:00:02 (oui Unknown) > 02:42:ac:11:00:03 (oui Unknown), ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 50873, offset 0, flags [DF], proto TCP (6), length 60)
172.17.0.2.44498 > ubuntu.http: Flags [S], cksum 0x5856 (incorrect -> 0x7caa), seq 1956521919, win 64240, options [mss 1460,sackOK,TS val 2076094974 ecr 0,nop,wscale 7], length 0
04:07:08.905609 02:42:ac:11:00:02 (oui Unknown) > 02:42:ac:11:00:03 (oui Unknown), ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 50874, offset 0, flags [DF], proto TCP (6), length 60)
172.17.0.2.44498 > ubuntu.http: Flags [S], cksum 0x5856 (incorrect -> 0x5caa), seq 1956521919, win 64240, options [mss 1460,sackOK,TS val 2076103166 ecr 0,nop,wscale 7], length 0
04:07:25.033508 02:42:ac:11:00:02 (oui Unknown) > 02:42:ac:11:00:03 (oui Unknown), ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 50875, offset 0, flags [DF], proto TCP (6), length 60)
172.17.0.2.44498 > ubuntu.http: Flags [S], cksum 0x5856 (incorrect -> 0x1daa), seq 1956521919, win 64240, options [mss 1460,sackOK,TS val 2076119294 ecr 0,nop,wscale 7], length 0
但是仔细分析之后,有一个疑问是,没有在主机侧网卡上挂载xdp程序时,用tcpdump在容器2里面抓包的结果同样是显示不正确的校验和,那么为什么这个数据包就不会被容器2的eth0网卡丢弃呢?
xdp社区维护者在github issue上回答了这个问题:
意思是:当没有在veth网卡上挂载xdp程序时,数据包的sk_buff
结构体中包含ip_summed
标志位,其值是CHECKSUM_UNNECESSARY
,这样当数据包到达容器2的网络栈,处理时发现该标志位的值是CHECKSUM_UNNESSARY
, 就认为校验和是正确的,所以数据包没有被丢弃。但是当容器1和容器2上挂载了xdp程序,xdp程序运行需要将网卡接收队列中的skb数据包转化并存放到xdp_buff
结构体中,这个过程丢失了CHECKSUM_UNNECESSARY
标志,因此当容器2接收到该数据包就会检查校验和的值是否正确,由于校验和不正确数据包被丢弃。
确认了问题的根源在于“容器发出的TCP数据包的校验和不正确”之后,接下来就是要让容器2收到的tcp数据包具有正确的校验和。
解决方法一:关闭tx checksum offload
以"docker checksum"作为关键词上网检索了一番,发现了几个相关度比较高的网页,并且都是和NIC网卡的network offloading功能相关,这里列出来:
- https://github.com/GNS3/gns3-server/issues/444
- https://docs.gz.ro/tuning-network-cards-on-linux.html
- https://sokratisg.net/2012/04/01/udp-tcp-checksum-errors-from-tcpdump-nic-hardware-offloading/
- https://github.com/kubernetes/kubernetes/issues/18898
这里incorrect checksum是一个称为TCP checksum offloading的功能,发出的TCP数据包的校验和字段在通过操作系统时没有预先计算,而是通过NIC网卡处理的时候进行校验计算。大多数现代操作系统都支持网络卸载功能(networking offloading),即部分网络处理由网卡完成而不是由CPU处理,这样可以释放系统资源以便能够处理更多的连接。
结合上面的分析,要让容器发出的数据包具有正确的校验和,只需要关闭checksum offloading功能,这样操作系统网络栈就能计算正确的校验和并填充到相应的字段。
接下来通过ethtool命令关闭容器1和容器2的eth0网卡的tx checksum offload功能,执行的命令如下:
root@ubuntu:/home/luo/xdp-tutorial/packet03-redirecting# ip netns exec tomcat01 ethtool -K eth0 tx off
Actual changes:
tx-checksumming: off
tx-checksum-ip-generic: off
tx-checksum-sctp: off
tcp-segmentation-offload: off
tx-tcp-segmentation: off [requested on]
tx-tcp-ecn-segmentation: off [requested on]
tx-tcp-mangleid-segmentation: off [requested on]
tx-tcp6-segmentation: off [requested on]
root@ubuntu:/home/luo/xdp-tutorial/packet03-redirecting# ip netns exec tomcat02 ethtool -K eth0 tx off
Actual changes:
tx-checksumming: off
tx-checksum-ip-generic: off
tx-checksum-sctp: off
tcp-segmentation-offload: off
tx-tcp-segmentation: off [requested on]
tx-tcp-ecn-segmentation: off [requested on]
tx-tcp-mangleid-segmentation: off [requested on]
tx-tcp6-segmentation: off [requested on]
重新在容器1内执行curl命令访问容器2的tomcat服务,并且在容器2内的eth0网卡上使用tcpdump监听数据包,结果显示,收到的数据包的校验和都是正确的,并且成功建立TCP连接并对HTTP请求做出了正确的响应:
root@ubuntu:/home/luo/xdp-tutorial# ip netns exec tomcat02 tcpdump -i eth0 -ev
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C23:10:29.101568 02:42:ac:11:00:02 (oui Unknown) > 02:42:ac:11:00:03 (oui Unknown), ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 757, offset 0, flags [DF], proto TCP (6), length 60)
172.17.0.2.56794 > ubuntu.http-alt: Flags [S], cksum 0x3486 (correct), seq 4269127330, win 64240, options [mss 1460,sackOK,TS val 2162724085 ecr 0,nop,wscale 7], length 0
23:10:29.101594 02:42:ac:11:00:03 (oui Unknown) > 02:42:ac:11:00:02 (oui Unknown), ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
ubuntu.http-alt > 172.17.0.2.56794: Flags [S.], cksum 0xff9a (correct), seq 1119689019, ack 4269127331, win 65160, options [mss 1460,sackOK,TS val 1013356771 ecr 2162724085,nop,wscale 7], length 0
23:10:29.101627 02:42:ac:11:00:02 (oui Unknown) > 02:42:ac:11:00:03 (oui Unknown), ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 758, offset 0, flags [DF], proto TCP (6), length 52)
172.17.0.2.56794 > ubuntu.http-alt: Flags [.], cksum 0x2afa (correct), ack 1, win 502, options [nop,nop,TS val 2162724085 ecr 1013356771], length 0
23:10:29.103886 02:42:ac:11:00:02 (oui Unknown) > 02:42:ac:11:00:03 (oui Unknown), ethertype IPv4 (0x0800), length 145: (tos 0x0, ttl 64, id 759, offset 0, flags [DF], proto TCP (6), length 131)
172.17.0.2.56794 > ubuntu.http-alt: Flags [P.], cksum 0x6b44 (correct), seq 1:80, ack 1, win 502, options [nop,nop,TS val 2162724087 ecr 1013356771], length 79: HTTP, length: 79
GET / HTTP/1.1
Host: 172.17.0.3:8080
User-Agent: curl/7.58.0
Accept: */*
23:10:29.103922 02:42:ac:11:00:03 (oui Unknown) > 02:42:ac:11:00:02 (oui Unknown), ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 36544, offset 0, flags [DF], proto TCP (6), length 52)
ubuntu.http-alt > 172.17.0.2.56794: Flags [.], cksum 0x2aa0 (correct), ack 80, win 509, options [nop,nop,TS val 1013356773 ecr 2162724087], length 0
23:10:29.107830 02:42:ac:11:00:03 (oui Unknown) > 02:42:ac:11:00:02 (oui Unknown), ethertype IPv4 (0x0800), length 884: (tos 0x0, ttl 64, id 36545, offset 0, flags [DF], proto TCP (6), length 870)
ubuntu.http-alt > 172.17.0.2.56794: Flags [P.], cksum 0x4d92 (correct), seq 1:819, ack 80, win 509, options [nop,nop,TS val 1013356777 ecr 2162724087], length 818: HTTP, length: 818
HTTP/1.1 404
Content-Type: text/html;charset=utf-8
Content-Language: en
Content-Length: 682
Date: Sun, 25 Oct 2020 06:10:29 GMT
...
使用iperf3测试了一下使用xdp在同主机容器间实现端到端直接转发的TCP速率,结果如下:
解决方法二:由xdp程序来重新计算tcp校验和
第二种解决方法是在xdp程序里面手动计算tcp校验和,由于在xdp例子中计算TCP、UDP或者IP校验和大多是采用增量式更新,增量式更新是基于一个正确的校验和,当修改了几个有限的字段后,通过简单的公式计算完成校验和的重新计算。而在我们的情形中,数据包的校验和字段本身就是不正确的,所以需要手动将所有16bit字段求和然后再去反码。
xdp程序累加方式计算校验和的关键代码如下所示:
__attribute__((__always_inline__))
static inline void ipv4_l4_csum(void *data_start, __u32 data_size,
__u64 *csum, struct iphdr *iph) {
__u32 tmp = 0;
*csum = bpf_csum_diff(0, 0, &iph->saddr, sizeof(__be32), *csum);
*csum = bpf_csum_diff(0, 0, &iph->daddr, sizeof(__be32), *csum);
// __builtin_bswap32 equals to htonl()
tmp = __builtin_bswap32((__u32)(iph->protocol));
*csum = bpf_csum_diff(0, 0, &tmp, sizeof(__u32), *csum);
tmp = __builtin_bswap32((__u32)(data_size));
*csum = bpf_csum_diff(0, 0, &tmp, sizeof(__u32), *csum);
*csum = bpf_csum_diff(0, 0, data_start, data_size, *csum);
*csum = csum_fold_helper(*csum);
}
...
int tcplen = bpf_ntohs(iph->tot_len) - iph->ihl * 4;
bpfprint("tcplen value: %d\n", tcplen);
tcph->check = 0;
cs = 0;
// ipv4_l4_csum(tcph, 20, &cs, iph);
ipv4_l4_csum((void *)tcph, (__u32)tcplen, &cs, iph);
tcph->check = cs;
...
但是上面的xdp代码加载到内核校验器会报错:
// err info
293: (bf) r1 = r8
294: (dc) r1 = be32 r1
295: (63) *(u32 *)(r10 -40) = r1
296: (b7) r1 = 0
297: (b7) r2 = 0
298: (bf) r3 = r7
299: (b7) r4 = 4
300: (bf) r5 = r0
301: (85) call bpf_csum_diff#28
last_idx 301 first_idx 293
regs=4 stack=0 before 300: (bf) r5 = r0
regs=4 stack=0 before 299: (b7) r4 = 4
regs=4 stack=0 before 298: (bf) r3 = r7
regs=4 stack=0 before 297: (b7) r2 = 0
last_idx 301 first_idx 293
regs=10 stack=0 before 300: (bf) r5 = r0
regs=10 stack=0 before 299: (b7) r4 = 4
302: (b7) r1 = 0
303: (b7) r2 = 0
304: (79) r3 = *(u64 *)(r10 -48)
305: (bf) r4 = r8
306: (bf) r5 = r0
307: (85) call bpf_csum_diff#28
last_idx 307 first_idx 293
regs=4 stack=0 before 306: (bf) r5 = r0
regs=4 stack=0 before 305: (bf) r4 = r8
regs=4 stack=0 before 304: (79) r3 = *(u64 *)(r10 -48)
regs=4 stack=0 before 303: (b7) r2 = 0
R4 min value is negative, either use unsigned or 'var &= const'
processed 312 insns (limit 1000000) max_states_per_insn 0 total_states 19 peak_states 19 mark_read 8
libbpf: -- END LOG --
libbpf: failed to load program 'xdp_redirect_new'
libbpf: failed to load object 'xdp_prog_kern.o'
ERR: loading BPF-OBJ file(xdp_prog_kern.o) (-22): Invalid argument
ERR: loading file: xdp_prog_kern.o
在网上搜索,发现这个问题目前并没有得到解决:
- R? min value is negative, either use unsigned or ‘var &= const’
- verifier failure for a xdp code computing udp checksum
接下来只能改写上述代码的实现方式,发现尽管xdp不支持循环,但是可以利用clang编译器的特性,定义一个宏变量N,然后使用for循环,即for(int i=0; i < N; i++)
,这样clang在编译的时候会自动将这个for循环展开,下面是改动后的代码实现,还是比较巧妙的:
#define MAX_TCP_LENGTH 1480
__attribute__((__always_inline__))
static inline void ipv4_l4_csum(void *data_start, __u32 data_size,
__u64 *csum, struct iphdr *iph,
void *data_end) {
__u32 tmp = 0;
*csum = bpf_csum_diff(0, 0, &iph->saddr, sizeof(__be32), *csum);
*csum = bpf_csum_diff(0, 0, &iph->daddr, sizeof(__be32), *csum);
// __builtin_bswap32 equals to htonl()
tmp = __builtin_bswap32((__u32)(iph->protocol));
*csum = bpf_csum_diff(0, 0, &tmp, sizeof(__u32), *csum);
tmp = __builtin_bswap32((__u32)(data_size));
*csum = bpf_csum_diff(0, 0, &tmp, sizeof(__u32), *csum);
// *csum = bpf_csum_diff(0, 0, data_start, data_size, *csum);
// Compute checksum from scratch by a bounded loop
__u16 *buf = data_start;
for (int i = 0; i < MAX_TCP_LENGTH; i += 2) {
if ((void *)(buf + 1) > data_end) {
break;
}
*csum += *buf;
buf++;
}
if ((void *)buf + 1 <= data_end) {
*csum += *(__u8 *)buf;
}
*csum = csum_fold_helper(*csum);
}
基于上述代码,在开启checksum offloading时,xdp程序也能够在转发这个数据包之前计算出正确的校验和并填写到相应的字段,从而建立正常的TCP连接。
在容器1和容器2上使用iperf3工具测试tcp数据包传输速度,结果如下所示:
新的问题
结合上面的iperf3结果,发现无论是关闭tx checksum offloading或者开启offloading情况下基于xdp手动计算校验和,传输速率都不是太理想,这个问题尚待解决。
总结
尽管社区对BPF程序的支持在逐步完善,但是在工程实践的过程中难免会遇到各种问题,在网上搜索和提问能将问题解决的情景十分有限。在和社区贡献者交流的过程中,他们能从内核源码的角度去分析和定位问题,这个能力是目前的我所不具备的,其实不管是BPF还是上升到上层的应用框架,在实际使用过程中遇到的某些问题只能通过阅读源码去找到问题的症结,因此这方面阅读源码的能力是我亟待加强的!!针对BPF,除了学习基本的使用,还需要逐步地去阅读内核中支持BPF的逻辑代码,这样才能游刃有余。
一些学习途径:与大佬交流并提问、会议展示PPT与视频、Github项目、外文博客、Linux内核代码。