eBPF(ExtendedBerkeleyPacketFilter)内核是一个驻留在内核中的高效虚拟机。最初的目的是一个高效的网络过滤框架,前身是BPF,所以我们先来了解一下BPFBPF框架。上图展示了BPF的位置和框架。需要注意的是,内核和用户使用缓冲区来传输数据,以避免频繁的上下文切换。BPF虚拟机非常紧凑,由累加器、索引寄存器、存储和隐式程序计数器组成。例子下面我们看一下过滤所有ip包的例子。可以使用tcpdump-dip查看:(000)ldh[12]//链路层第12字节的数据加载到寄存器中,ethertype字段为(001)jeq#0x800jt2jf3//比较寄存器的ethertype字段是否为IP类型,true跳转到2,false跳转到3(002)ret#65535//returnstrue(003)ret#0//returns0BPF只用到四个虚拟机指令即可提供非常有用的IP数据包过滤。tcpdump-dtcp(000)ldh[12]//将链??路层的第12字节数据(2字节)加载到寄存器中,ethertype字段(001)jeq#0x86ddjt2jf7//判断是否是IPv6type,true跳转到2,false跳转到7(002)ldb[20]//将链??路层的第20字节数据(1字节)加载到寄存器中,IPv6下一个头域(003)jeq#0x6jt10jf4//判断是否是TCP,true跳转到10,false跳转到4(004)jeq#0x2cjt5jf11//可能是IPv6分片标志,true跳转到5,false跳转到11(005)ldb[54]//写不下去了...(006)jeq#0x6jt10jf11//判断是否是TCP,true跳到10,false跳到11(007)jeq#0x800jt8jf11//判断是否是IP类型,true跳8,false跳11#0x6jt10jf11//判断是否是TCP,true跳10,false跳11(010)ret#65535//returnstrue(011)ret#0//returns0ormore是freebsd的BPF,在Linux中不应该叫这个,应该叫LSF,自己看吧。eBPFeBPF初遇Linux内核3.18版本开始包含eBPF,相比BPF做了一些重要的改进。首先是效率,得益于JIB编译的eBPF代码;二是应用范围,从网络报文扩展到一般事件处理;finally不使用socket,使用map进行高效的数据存储。基于以上改进,内核开发者在不到两年半的时间里做出了包括网络监控、限速、系统监控等方面的改进。目前eBPF可以分解为三个进程:以字节码的形式创建eBPF的程序。编写将LLVM编译为驻留在ELF文件中的eBPF字节码的C代码。将程序加载到内核中并创建必要的eBPF映射。eBPF有socketfilter、kprobeprocessor、流控调度、流控操作、tracepoint处理、eXpressDataPath(XDP)、性能监控、cgroup限制、轻量级隧道等程序类型。将加载的程序附加到系统。根据不同的程序类型附加到不同的内核系统。当程序运行时,启动状态并开始过滤、分析或捕获信息。在2016年10月的NetDev1.2会议上,Netronome的JakubKicinski和NicViljoen做了题为“eBPF/XDPHardwareOffloadtoSmartNIC”的演讲。NicViljoen介绍,NetronomeSmartNIC上的每个FPC达到每秒300万个数据包,每个SmartNIC有72到120个FPC,最大可支持4.3Tbps的eBPF吞吐量!(理论上)eBPFentry接下来我们以内核版本4.14为例进行查看。bpf系统调用kernel/bpf/syscall.cbpf系统调用头文件include/uapi/linux/bpf.h入口函数intbpf(intcmd,unionbpf_attr*attr,unsignedintsize);来自内核/bpf/系统调用。c中的宏定义展开。eBPF命令Linux系统的BPF系统调用有10条命令,其中6条列在手册页中:BPF_PROG_LOAD验证并加载eBPF程序,并返回一个新的文件描述符。BPF_MAP_CREATE创建一个map并返回指向该map的文件描述符BPF_MAP_LOOKUP_ELEM通过key从指定的map中查找元素,并返回值BPF_MAP_UPDATE_ELEM创建或更新指定map中的元素(key/value对)BPF_MAP_DELETE_ELEM从指定的map通过key查找元素并删除BPF_MAP_GET_NEXT_KEY通过key从指定map中查找元素并返回nextkey值上面的命令可以分为两类,加载eBPF程序和eBPF-maps操作。eBPF-maps的操作具有很大的自主性。用于创建eBPF-maps,从中查找、更新和删除元素,遍历eBPF-maps(BPF_MAP_GET_NEXT_KEY)然后列出剩下的4条命令,代码中可以看到:BPF_OBJ_PIN是4.4版本新加入的,属于到持久的eBPF。有了这个,eBPF-maps和eBPF程序就可以放到/sys/fs/bpfBPF_OBJ_GET同上,在此之前,没有工具可以创建eBPF程序,然后结束,因为过滤器会被销毁,文件系统可以在程序之后退出创建它们之后,4.10版本中添加的eBPF-maps和eBPF程序BPF_PROG_ATTACH仍然保留,eBPF程序附加到cgroup,如上应用于containerBPF_PROG_DETACH。eBPF-map类型的Linux系统的BPF系统调用有10个命令,其中6个列在手册页中:BPF_PROG_LOAD验证并加载eBPF程序,并返回一个新的文件描述符。BPF_MAP_CREATE创建一个map并返回指向该map的文件描述符BPF_MAP_LOOKUP_ELEM通过key从指定的map中查找元素,并返回值BPF_MAP_UPDATE_ELEM创建或更新指定map中的元素(key/value对)BPF_MAP_DELETE_ELEM从指定的map通过key查找元素并删除BPF_MAP_GET_NEXT_KEY通过key从指定map中查找元素并返回nextkey值上面的命令可以分为两类,加载eBPF程序和eBPF-maps操作。eBPF-maps的操作具有很大的自主性。用于创建eBPF-maps,从中查找、更新和删除元素,遍历eBPF-maps(BPF_MAP_GET_NEXT_KEY)然后列出剩下的4条命令,代码中可以看到:BPF_OBJ_PIN是4.4版本新加入的,属于到持久的eBPF。有了这个,eBPF-maps和eBPF程序就可以放到/sys/fs/bpfBPF_OBJ_GET同上,在此之前,没有工具可以创建eBPF程序,然后结束,因为过滤器会被销毁,文件系统可以在程序之后退出创建它们之后,4.10版本中添加的eBPF-maps和eBPF程序BPF_PROG_ATTACH仍然保留,eBPF程序附加到cgroup,如上应用于containerBPF_PROG_DETACH。eBPF-map类型BPF_MAP_TYPE_UNSPECBPF_MAP_TYPE_HASHeBPF-maps哈希表是主要使用的前两种方法之一。BPF_MAP_TYPE_ARRAY和上面类似,只是索引有点像数组。BPF_MAP_TYPE_PROG_ARRAY保存了加载的eBPF程序的文件描述符的值,以及常用的数字来识别不同的eBPF程序类型,也可以通过给定的key值从eBPF-maps中查找eBPF程序,用perf跳转到BPF_MAP_TYPE_PERF_EVENT_ARRAY的程序工具、CPU性能计数器、跟踪点、kprobes和uprobes。Youcanviewtracex6_kern.c,tracex6_user.c,tracex6_kern.c,tracex6_user.cunderthepathsamples/bpf/.BPF_MAP_TYPE_PERCPU_HASHisthesameasBPF_MAP_TYPE_HASH,exceptthatitiscreatedforeachCPU.存储stack-tracesBPF_MAP_TYPE_CGROUP_ARRAY检查skb的croup归属BPF_MAP_TYPE_LRU_HASHBPF_MAP_TYPE_LRU_PERCPU_HASHBPF_MAP_TYPE_LPM_TRIE最专业的用法,LPM(LongestPrefixMatch)的一种trieBPF_MAP_TYPE_ARRAY_OF_MAPS可能是针对每个port的BPF_MAP_TYPE_HASH_OF_MAPS可能是针对每个port的BPF_MAP_TYPE_DEVMAP可能是定向报文到dev的BPF_MAP_TYPE_SOCKMAPItmaybeconnectedtothesocket
