BPF是一种内核技术,可以实现以下功能:将eBPF程序附加到指定的内核代码路径中,当执行该代码路径时,会执行对应的eBPF程序。由于eBPF的起源,它特别适合于编写网络程序,将该网络程序附加到网络socket中,进行流量过滤、流量分类以及执行网络分类器的动作。eBPF程序甚至可以修改一个已建链的网络socket的配置。
XDP项目在网络栈的底层运行eBPF程序,高效地处理接收到的报文。BPF对网络的处理可以分为tc/BPF和XDP/BPF,它们的主要区别在于钩子的位置和上下文。XDP的钩子要早于tc,因此性能更高。tc钩子使用sk_buff结构体作为参数,而XDP使用xdp_md结构体作为参数。sk_buff中的数据要远多于xdp_md,这会对性能产生一定影响,且报文需要上送到tc钩子才会触发处理程序。由于XDP钩子位于网络栈之前,因此XDP使用的xdp_buff无法访问sk_buff元数据。data指向page中的数据包的其实位置,data_end指向数据包的结尾。由于XDP允许headroom(见下文),data_hard_start指向page中headroom的起始位置,即,当对报文进行封装时,data会通过bpf_xdp_adjust_head()向data_hard_start移动。
相同的BPF辅助函数也可以用于解封装,此时data会远离data_hard_start。data_meta一开始指向与data相同的位置,但bpf_xdp_adjust_meta()能够将其朝着data_hard_start移动,进而给用户元数据提供空间,这部分空间对内核网络栈是不可见的,但可以被tcBPF程序读取(tc需要将它从XDP转移到skb)。
反之,可以通过相同的BPF程序将data_meta远离data_hard_start来移除或减少用户元数据大小。data_meta还可以单纯地用于在尾调用间传递状态,与tcBPF程序访问的skb->cb[]控制块类似。对于structxdp_buff中的报文指针,有如下关系:data_hard_start<= data_meta <= data <data_end。rxq字段指向在ring启动期间填充的额外的与每个接受队列相关的元数据。BPF程序可以检索queue_index,以及网络设备上的其他数据(如ifindex等)。
tc能够更好地管理报文。tc的BPF输入上下文是一个sk_buff,不同于XDP使用的xdp_buff,二者各有利弊。当内核的网络栈在XDP层之后接收到一个报文时,会分配一个buffer,解析并保存报文的元数据,这些元数据即sk_buff。该结构体会暴露给BPF的输入上下文,这样tcingress层的tcBPF程序就能够使用网络栈从报文解析到的元数据。使用sk_buff,tc可以更直接地使用这些元数据,因此附加到tcBPF钩子的BPF程序可以读取或写入skb的mark,pkt_type,protocol, priority,queue_mApping, napi_id, cb[] array, hash, tc_classid或tc_index,vlanmetadata等,而XDP能够传输用户的元数据以及其他信息。tcBPF使用的struct__sk_buff定义在linux/bpf.h头文件中。xdp_buff的弊端在于,其无法使用sk_buff中的数据,XDP只能使用原始的报文数据,并传输用户元数据。
XDP能够更快地修改报文。sk_buff包含很多协议相关的信息(如GSO阶段的信息),因此其很难通过简单地修改报文数据达到切换协议的目的,原因是网络栈对报文的处理主要基于报文的元数据,而非每次访问数据包内容的开销。因此,BPF辅助函数需要正确处理内部sk_buff的转换。而xdp_buff则不会有这种问题,因为XDP的处理时间早于内核分配sk_buff的时间,因此可以简单地实现对任何报文的修改(但管理起来要更加困难)。
tc/eBPF和XDP/eBPF可以互补。如果用户需要修改报文,同时对数据进行比较复杂的管理,那么,可以通过运行两种类型的程序来弥补每种程序类型的局限性。XDP程序位于ingress,可以修改完整的报文,并将用户元数据从XDPBPF传递给tcBPF,然后tc可以使用XDP的元数据和sk_buff字段管理报文。
tc/eBPF可以作用于ingress和egress,但XDP只能作用于ingress。与XDP相比,tcBPF程序可以在ingress和egress的网络数据路径上触发,而XDP只能作用于ingress。tc/BPF不需要改变硬件驱动,而XDP通常会使用native驱动模式来获得更高的性能。但tcBPF程序的处理仍作用于早期的内核网络数据路径上(GRO处理之后,协议处理和传统的iptables防火墙的处理之前,如iptablesPREROUTING或NFTablesingress钩子等)。而在egress上,tcBPF程序在将报文传递给驱动之前进行处理,即在传统的iptables防火墙(如iptablesPOSTROUTING)之后,但在内核的GSO引擎之前进行处理。一个特殊情况是,如果使用了offloaded的tcBPF程序(通常通过SmartNIC提供),此时Offloadedtc/eBPF接近于OffloadedXDP的性能