诞生背景
Linux内核的TCP/IP协议栈在诞生之初更注重的是通用性,随着技术的发展,10G、25G、100G甚至更高规格的网卡出现,原生的TCP/IP协议栈的开销会更容易被注意到。
在2010年时,Intel领导提出了DPDK高性能网络应用开发解决方案,全称Data Plane Development Kit。
使用DPDK开发的应用的网络数据包在传递的时候,数据将绕过Linux TCP/IP内核协议栈。
也正式由于DPDK绕开Linux TCP/IP协议栈的原因,Linux开发者们认为,并不是最好的解决方案。2016年,随着eBPF技术的成熟,Linux内核迎来了自己的高速网络通道XDP。
XDP具有在特定使用条件下媲美DPDK性能的同时,能更加与Linux内核整合。但是需要注意的是,目前Linux内核只有ingress链路的XDP能力,对于egress链路的数据,暂时没有XDP支持。
XDP的架构图如下图
XDP运行在TCP/IP协议栈与网络设备/驱动之间,对于接收到的网络请求,可以更早地执行DROP丢弃或FORWARD转发,并且具备修改网络数据包的能力。
XDP加载模式
XDP有三种加载模式,分别是Generic模式、Native模式和Offload模式,这三种模式运行的注册点位不一样,性能也会有一定区别
性能排序从强到弱:Offload > Native > Generic
1. Generic模式
XDP_FLAGS_SKB_MODE
若网卡和驱动都没原生支持XDP运行的话,Linux内核提供了通用的XDP加载方式,这种方式不需要对驱动程序进行任何修改,hook运行在网络堆栈较后面阶段,因此性能相比另外两种方法较差。
从代码来看,这个模式的hook点位在`
1 | static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,struct packet_type **ppt_prev) |
在这个方法中通过do_xdp_generic
方法来进行处理。
2. Native模式
XDP_FLAGS_DRV_MODE
这种模式需要网卡的驱动程序支持,运行的hook点位挂载在网卡驱动的poll()方法,当驱动rx队列有数据时,就可以立刻进行处理。
这篇博客中记录了 https://blog.csdn.net/already_skb/article/details/123073814
3. Offload模式
XDP_FLAGS_HW_MODE
这种模式需要网卡硬件支持,运行在这种模式时,相关的XDP程序会被加载到网卡上运行,XDP部分的逻辑理论不再需要CPU的占用,但是支持这种模式的网卡不太多,已知支持的是Netronome NFP系列服务器网卡。
1 | 在CSDN上找到了有一个大佬分析Native和Generic的hook点位的记录,可以看看大佬的文章学习一下找hook点位的过程 |
XDP 函数返回值
1 | XDP_ABORTED = 0, |
- XDP_ABORTED:程序运行异常的时候退出,数据包相当于XDP_DROP,在XDP_DROP的基础上,可以在内核tracepipe中看到异常事件
- XDP_DROP:丢弃这个数据包
- XDP_PASS:数据包交付到内核TCP/IP协议栈继续传输
- XDP_TX:将数据包从接收的网卡发出去
- XDP_REDIRECT:类似XDP_TX,但是可以用别的网卡发送
应用XDP技术的项目
负载均衡器:https://github.com/davidcoles/vc5
负载均衡器:https://github.com/MageekChiu/xdp4slb
NetBird团队使用XDP在多个DNS解析器之间共享默认53端口:https://netbird.io/knowledge-hub/using-xdp-ebpf-to-share-default-dns-port-between-resolvers
基于XDP实现类似TCPDUMP功能:https://github.com/xdp-project/xdp-tools/tree/master/xdp-dump
这一篇简单介绍了一下XDP技术,屏幕前的你应该对XDP技术有了一定的了解。在之后的推送中,我会分别利用python+bcc方式和libbpf方式来演示XDP程序的开发,展示对ICMP包进行丢包、修改等操作。