一文读懂eBPF/XDP
目录
XDP概述
XDP数据结构
XDP与eBPF的关系
XDP操作模式
XDP操作结果码
XDP和iproute2加载器
XDP和BCC
XDP概述
XDP是Linux网络路径上内核集成的数据包处理器,具有安全、可编程、高性能的特点。当网卡驱动程序收到数据包时,该处理器执行BPF程序。XDP可以在数据包进入协议栈之前就进行处理,因此具有很高的性能,可用于DDoS防御、防火墙、负载均衡等领域。
XDP数据结构
XDP程序使用的数据结构是xdp_buff
,而不是sk_buff
,xdp_buff
可以视为sk_buff
的轻量级版本。
两者的区别在于:sk_buff
包含数据包的元数据,xdp_buff
创建更早,不依赖与其他内核层,因此XDP可以更快的获取和处理数据包。
xdp_buff
数据结构定义如下:
// /linux/include/net/xdp.h
struct xdp_rxq_info {struct net_device *dev;u32 queue_index;u32 reg_state;struct xdp_mem_info mem;
} ____cacheline_aligned; /* perf critical, avoid false-sharing */struct xdp_buff {void *data;void *data_end;void *data_meta;void *data_hard_start;unsigned long handle;struct xdp_rxq_info *rxq;
};
sk_buff
数据结构定义如下:
// /include/linux/skbuff.h
struct sk_buff {union {struct {/* These two members must be first. */struct sk_buff *next;struct sk_buff *prev;union {struct net_device *dev;/* Some protocols might use this space to store information,* while device pointer would be NULL.* UDP receive path is one user.*/unsigned long dev_scratch;};};struct rb_node rbnode; /* used in netem, ip4 defrag, and tcp stack */struct list_head list;};union {struct sock *sk;int ip_defrag_offset;};union {ktime_t tstamp;u64 skb_mstamp_ns; /* earliest departure time */};/** This is the control buffer. It is free to use for every* layer. Please put your private variables there. If you* want to keep them across layers you have to do a skb_clone()* first. This is owned by whoever has the skb queued ATM.*/char cb[48] __aligned(8);union {struct {unsigned long _skb_refdst;void (*destructor)(struct sk_buff *skb);};struct list_head tcp_tsorted_anchor;};#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)unsigned long _nfct;
#endifunsigned int len,data_len;__u16 mac_len,hdr_len;/* Following fields are _not_ copied in __copy_skb_header()* Note that queue_mapping is here mostly to fill a hole.*/__u16 queue_mapping;/* if you move cloned around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define CLONED_MASK (1 << 7)
#else
#define CLONED_MASK 1
#endif
#define CLONED_OFFSET() offsetof(struct sk_buff, __cloned_offset)__u8 __cloned_offset[0];__u8 cloned:1,nohdr:1,fclone:2,peeked:1,head_frag:1,xmit_more:1,pfmemalloc:1;
#ifdef CONFIG_SKB_EXTENSIONS__u8 active_extensions;
#endif/* fields enclosed in headers_start/headers_end are copied* using a single memcpy() in __copy_skb_header()*//* private: */__u32 headers_start[0];/* public: *//* if you move pkt_type around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define PKT_TYPE_MAX (7 << 5)
#else
#define PKT_TYPE_MAX 7
#endif
#define PKT_TYPE_OFFSET() offsetof(struct sk_buff, __pkt_type_offset)__u8 __pkt_type_offset[0];__u8 pkt_type:3;__u8 ignore_df:1;__u8 nf_trace:1;__u8 ip_summed:2;__u8 ooo_okay:1;__u8 l4_hash:1;__u8 sw_hash:1;__u8 wifi_acked_valid:1;__u8 wifi_acked:1;__u8 no_fcs:1;/* Indicates the inner headers are valid in the skbuff. */__u8 encapsulation:1;__u8 encap_hdr_csum:1;__u8 csum_valid:1;#ifdef __BIG_ENDIAN_BITFIELD
#define PKT_VLAN_PRESENT_BIT 7
#else
#define PKT_VLAN_PRESENT_BIT 0
#endif
#define PKT_VLAN_PRESENT_OFFSET() offsetof(struct sk_buff, __pkt_vlan_present_offset)__u8 __pkt_vlan_present_offset[0];__u8 vlan_present:1;__u8 csum_complete_sw:1;__u8 csum_level:2;__u8 csum_not_inet:1;__u8 dst_pending_confirm:1;
#ifdef CONFIG_IPV6_NDISC_NODETYPE__u8 ndisc_nodetype:2;
#endif__u8 ipvs_property:1;__u8 inner_protocol_type:1;__u8 remcsum_offload:1;
#ifdef CONFIG_NET_SWITCHDEV__u8 offload_fwd_mark:1;__u8 offload_l3_fwd_mark:1;
#endif
#ifdef CONFIG_NET_CLS_ACT__u8 tc_skip_classify:1;__u8 tc_at_ingress:1;__u8 tc_redirected:1;__u8 tc_from_ingress:1;
#endif
#ifdef CONFIG_TLS_DEVICE__u8 decrypted:1;
#endif#ifdef CONFIG_NET_SCHED__u16 tc_index; /* traffic control index */
#endifunion {__wsum csum;struct {__u16 csum_start;__u16 csum_offset;};};__u32 priority;int skb_iif;__u32 hash;__be16 vlan_proto;__u16 vlan_tci;
#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS)union {unsigned int napi_id;unsigned int sender_cpu;};
#endif
#ifdef CONFIG_NETWORK_SECMARK__u32 secmark;
#endifunion {__u32 mark;__u32 reserved_tailroom;};union {__be16 inner_protocol;__u8 inner_ipproto;};__u16 inner_transport_header;__u16 inner_network_header;__u16 inner_mac_header;__be16 protocol;__u16 transport_header;__u16 network_header;__u16 mac_header;/* private: */__u32 headers_end[0];/* public: *//* These elements must be at the end, see alloc_skb() for details. */sk_buff_data_t tail;sk_buff_data_t end;unsigned char *head,*data;unsigned int truesize;refcount_t users;#ifdef CONFIG_SKB_EXTENSIONS/* only useable after checking ->active_extensions != 0 */struct skb_ext *extensions;
#endif
};
XDP与eBPF的关系
XDP程序是通过bpf()系统调用控制的,bpf()系统调用使用程序类型BPF_PROG_TYPE_XDP
进行加载。
XDP操作模式
XDP支持3种工作模式,默认使用native模式:
Native XDP
:在native模式下,XDP BPF程序运行在网络驱动的早期接收路径上(RX队列),因此,使用该模式时需要网卡驱动程序支持。Offloaded XDP
:在Offloaded模式下,XDP BFP程序直接在NIC(Network Interface Controller)中处理数据包,而不使用主机CPU,相比native模式,性能更高Generic XDP
:Generic模式主要提供给开发人员测试使用,对于网卡或驱动无法支持native或offloaded模式的情况,内核提供了通用的generic模式,运行在协议栈中,不需要对驱动做任何修改。生产环境中建议使用native或offloaded模式
XDP操作结果码
- XDP_DROP:丢弃数据包,发生在驱动程序的最早RX阶段
- XDP_PASS:将数据包传递到协议栈处理,操作可能为以下两种形式:
1、正常接收数据包,分配愿数据sk_buff结构并且将接收数据包入栈,然后将数据包引导到另一个CPU进行处理。他允许原始接口到用户空间进行处理。 这可能发生在数据包修改前或修改后。
2、通过GRO(Generic receive offload)方式接收大的数据包,并且合并相同连接的数据包。经过处理后,GRO最终将数据包传入“正常接收”流 - XDP_TX:转发数据包,将接收到的数据包发送回数据包到达的同一网卡。这可能在数据包修改前或修改后发生
- XDP_REDIRECT:数据包重定向,XDP_TX,XDP_REDIRECT是将数据包送到另一块网卡或传入到BPF的cpumap中
- XDP_ABORTED:表示eBPF程序发生错误,并导致数据包被丢弃。自己开发的程序不应该使用该返回码
XDP和iproute2加载器
iproute2工具中提供的ip命令可以充当XDP加载器的角色,将XDP程序编译成ELF文件并加载他。
- 编写XDP程序xdp_filter.c,程序功能为丢弃所有TCP连接包,程序将
xdp_md
结构指针作为输入,相当于驱动程序xdp_buff
的BPF结构。程序的入口函数为filter,编译后ELF文件的区域名为mysection。
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>#define SEC(NAME) __attribute__((section(NAME), used))SEC("mysection")
int filter(struct xdp_md *ctx) {int ipsize = 0;void *data = (void *)(long)ctx->data;void *data_end = (void *)(long)ctx->data_end;struct ethhdr *eth = data;struct iphdr *ip;ipsize = sizeof(*eth);ip = data + ipsize;ipsize += sizeof(struct iphdr);if (data + ipsize > data_end) {return XDP_DROP;}if (ip->protocol == IPPROTO_TCP) {return XDP_DROP;}return XDP_PASS;
}
- 将XDP程序编译为ELF文件
clang -O2 -target bpf -c xdp_filter.c -o xdp_filter.o
- 使用ip命令加载XDP程序,将mysection部分作为程序的入口点
sudo ip link set dev ens33 xdp obj xdp_filter.o sec mysection
没有报错即完成加载,可以通过以下命令查看结果:
$ sudo ip a show ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 xdpgeneric/id:56 qdisc fq_codel state UP group default qlen 1000link/ether 00:0c:29:2f:a8:41 brd ff:ff:ff:ff:ff:ffinet 192.168.136.140/24 brd 192.168.136.255 scope global dynamic noprefixroute ens33valid_lft 1629sec preferred_lft 1629secinet6 fe80::d411:ff0d:f428:ce2a/64 scope link noprefixroute valid_lft forever preferred_lft forever
其中,xdpgeneric/id:56
说明使用的驱动程序为xdpgeneric,XDP程序id为56
- 验证连接阻断效果
- 使用
nc -l 8888
监听8888 TCP端口,使用nc xxxxx 8888
连接发送数据,目标主机未收到任何数据,说明TCP连接阻断成功 - 使用
nc -kul 9999
监听UDP 9999端口,使用nc -u xxxxx 9999
连接发送数据,目标主机正常收到数据,说明UDP连接不受影响
- 卸载XDP程序
$ sudo ip link set dev ens33 xdp off
卸载后,连接8888端口,发送数据,通信正常。
XDP和BCC
编写C代码xdp_bcc.c,当TCP连接目的端口为9999时DROP:
#define KBUILD_MODNAME "program"
#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>int filter(struct xdp_md *ctx) {int ipsize = 0;void *data = (void *)(long)ctx->data;void *data_end = (void *)(long)ctx->data_end;struct ethhdr *eth = data;struct iphdr *ip;ipsize = sizeof(*eth);ip = data + ipsize;ipsize += sizeof(struct iphdr);if (data + ipsize > data_end) {return XDP_DROP;}if (ip->protocol == IPPROTO_TCP) {struct tcphdr *tcp = (void *)ip + sizeof(*ip);ipsize += sizeof(struct tcphdr);if (data + ipsize > data_end) {return XDP_DROP;}if (tcp->dest == ntohs(9999)) {bpf_trace_printk("drop tcp dest port 9999\n");return XDP_DROP;}}return XDP_PASS;
}
与使用ip命令加载XDP程序类似,这里编写python加载程序实现对XDP程序的编译和内核注入。
#!/usr/bin/pythonfrom bcc import BPF
import timedevice = "ens33"
b = BPF(src_file="xdp_bcc.c")
fn = b.load_func("filter", BPF.XDP)
b.attach_xdp(device, fn, 0)try:b.trace_print()
except KeyboardInterrupt:passb.remove_xdp(device, 0)
验证效果,使用nc测试,无法与目标主机9999端口实现通信
$ sudo python xdp_bcc.py <idle>-0 [003] ..s. 22870.984559: 0: drop tcp dest port 9999
<idle>-0 [003] ..s. 22871.987644: 0: drop tcp dest port 9999
<idle>-0 [003] ..s. 22872.988840: 0: drop tcp dest port 9999
<idle>-0 [003] ..s. 22873.997261: 0: drop tcp dest port 9999
<idle>-0 [003] ..s. 22875.000567: 0: drop tcp dest port 9999
<idle>-0 [003] ..s. 22876.002998: 0: drop tcp dest port 9999
<idle>-0 [003] ..s. 22878.005414: 0: drop tcp dest port 9999
<idle>-0 [003] ..s. 22882.018119: 0: drop tcp dest port 9999
一文读懂eBPF/XDP相关推荐
- 从实验室走向大众,一文读懂Nanopore测序技术的发展及应用
关键词/Nanopore测序技术 文/基因慧 随着基因测序技术不断突破,二代测序的发展也将基因检测成本大幅降低.理想的测序方法,是对原始DNA模板进行直接.准确的测序,消除PCR扩增带来的偏差, ...
- 一文读懂Faster RCNN
来源:信息网络工程研究中心本文约7500字,建议阅读10+分钟 本文从四个切入点为你介绍Faster R-CNN网络. 经过R-CNN和Fast RCNN的积淀,Ross B. Girshick在20 ...
- 福利 | 一文读懂系列文章精选集发布啦!
大数据时代已经悄然到来,越来越多的人希望学习一定的数据思维和技能来武装自己,虽然各种介绍大数据技术的文章每天都扑面而来,但纷繁又零散的知识常常让我们不知该从何入手:同时,为了感谢和回馈读者朋友对数据派 ...
- 一文读懂EfficientDet
一文读懂EfficientDet. 今年年初Google Brain团队在 CVPR 2020 上发布了 EfficientDet目标检测模型, EfficientDet是一系列可扩展的高效的目标检测 ...
- 一文读懂序列建模(deeplearning.ai)之序列模型与注意力机制
https://www.toutiao.com/a6663809864260649485/ 作者:Pulkit Sharma,2019年1月21日 翻译:陈之炎 校对:丁楠雅 本文约11000字,建议 ...
- AI洞观 | 一文读懂英特尔的AI之路
AI洞观 | 一文读懂英特尔的AI之路 https://mp.weixin.qq.com/s/E9NqeywzQ4H2XCFFOFcKXw 11月13日-14日,英特尔人工智能大会(AIDC)在北京召 ...
- 一文读懂机器学习中的模型偏差
一文读懂机器学习中的模型偏差 http://blog.sina.com.cn/s/blog_cfa68e330102yz2c.html 在人工智能(AI)和机器学习(ML)领域,将预测模型参与决策过程 ...
- 一文读懂AI简史:当年各国烧钱许下的愿,有些至今仍未实现
一文读懂AI简史:当年各国烧钱许下的愿,有些至今仍未实现 导读:近日,马云.马化腾.李彦宏等互联网大佬纷纷亮相2018世界人工智能大会,并登台演讲.关于人工智能的现状与未来,他们提出了各自的观点,也引 ...
- 一文读懂你该了解的5G知识:现在别买5G手机
来源: 腾讯科技 2019年是中国全力布局5G的一年:三大运营商纷纷搭建基站,手机厂商发布5G手机,部分城市已经开启了5G测试--在电信日这天,腾讯科技联合知乎推出重磅策划,聚焦和5G相关的小知识,精 ...
最新文章
- mysql如何让自增id从某个位置开始设置方法
- 专属个人的聊天机器人的实现——图灵机器人
- C# 关于调用微信接口的代码
- python 多项式拟合
- 顺序表应用3:元素位置互换之移位算法
- .NET Core 2.x中使用Named Options处理多个强类型配置实例
- MySQL Oracle默认排序_PostgreSQL、MySQL、Oracle,查询的默认排序是怎样的?
- data=*(vu16*)addr;的理解?
- linux appium 安装教程,Ubuntu 系统安装 Appium 及样例运行教程
- 《WF本质论》第一章OpenSesame我的实现
- 颜色六位码和八位码表示
- 2019年最新淘宝联盟淘宝客升高佣规则
- 墙裂推荐6个优质公众号
- 【JavaSE】入门概述(1~41)
- 数据结构PTA习题:基础实验4-2.7 修理牧场 (25分)
- 图像的灰度化灰度值的读取Matlab
- 陈皓谈对待技术的态度
- extjs6 异步树
- 监听pda扫描_android系统PDA扫描枪,扫描完成后自带回车,为什么回车监听第一次不起作用,手动提交一次后才能正常提交...
- 从微软、FB、华为的网络安全备忘录说开去