一、XDP和TC的技术支撑
在前面分析了XDP和TC技术,从它们的细节里可以看出,它们都在调用eBPF的钩子函数。那么eBPF是什么呢?在2021年曾经写过一篇《eBPF介绍》的初级文章,对eBPF做了一个入门级的普及。但是未曾在技术层面上进行展开。这次借着XDP分析的机会,将其一并进行一下分析。先看一下XDP、TC及eBPF它们的关系图:
从图上可以看出,XDP和TC通过Maps调用内核中的eBPF来实现相关的功能。不过需要说明的是,eBPF的功能可不是支持XDP和TC。
二、eBPF
eBPF可以使相关的程序运行在内核空间,它拥有自己的指令集(等于抽象了一层),是一个抽象的虚拟机(VM)。所谓虚拟机,学习过包括Java在内的开发者应该非常明白它的基本原理了。
一般来说,做为开发者,在应用层开发相关功能已经能够满足大多的需求了。而且,OS将其管理空间划分为用户和内核态空间,也有着设计上的各种考虑。无论是从安全、易用性和可移植性等等方面的情况看,让绝大多数用户在用户态空间上进行开发。既隔离了OS系统本身的复杂和被误处理,又抽象出一层专门对外接口,衍生出专门的上层框架和库。即使从不同的角度来看这种设计形式,相对来说都是非常完善的。
但是世界上总有一些情况,需要在用户空间内想直接处理一些硬件的或者内核的信号或数据。有人说,可以开接口啊?让上层调用这些个接口。OK,这个想法说明具有了初步的面向接口开发的意识。但是,接口开发意味着有几种情况:
1、内核的稳定性,导致接口不可能随心所欲的设计和应用。说白了,就是爱情不是你想买,想买就能买。
2、开发周期的迭代导致的反应不及时。这里需要一个接口,还得等一个小版本甚至大版本。少则论月多则论年。
3、安全性,新的接口是否能够保证不影响其它功能并保持功能最小化。
4、不可动态编程性,接口是静态的,而需求是反复横跳,不断变化的。
而eBPF出现则缓解了这种矛盾。eBPF是虚拟的,意味着它可以在大多数的内核版本间移植(只要支持eBPF),它在内核沙箱中运行,即使有问题,也不会影响内核的安全和稳定。
eBPF的程序有三部分组成:
1、eBPF内核程序:其在内核中运行并响应各种事件
2、eBPF用户层程序:eBPF程序加载到内核并与之响应的程序
3、eBPF Maps(hash maps, arrays, ring / perf buffer):用来将eBPF内核程序和用户空间程序间的数据等共享。
上图很清晰的可以看到相关的eBPF整体的工作流程。因此,eBPF不但可以应用各种网络的安全、监控、过滤以及相关的开发外,还可以对内核的具体的某些情况进行控制和相关处理。
三、分析和说明
eBPF的程序在上面有过一个基本的说明,现在分析一下其基本的流程:
1、首先通过LLVM/CLANG将eBPF源码进行编译,生成bytecode代码
2、通过bpf()调用和BPF_PROG_LOAD调用,内核将bytecode进行验证
3、JIT将字节码编译并附加其到内核对象
4、运行并处理相关应用
目前内核对eBPF程序支持的类型有以下几类:
enum bpf_prog_type {BPF_PROG_TYPE_SOCKET_FILTER,// a network packet filterBPF_PROG_TYPE_KPROBE,//determine whether a kprobe should fire or notBPF_PROG_TYPE_SCHED_CLS,// a network traffic-control classifierBPF_PROG_TYPE_SCHED_ACT,//a network traffic-control actionBPF_PROG_TYPE_TRACEPOINT,// determine whether a tracepoint should fire or notBPF_PROG_TYPE_XDP,// a network packet filter run from the device-driver receive pathBPF_PROG_TYPE_PERF_EVENT,// determine whether a perf event handler should fire or notBPF_PROG_TYPE_CGROUP_SKB,// a network packet filter for control groupsBPF_PROG_TYPE_CGROUP_SOCK,// a network packet filter for control groups that is allowed to modify socket optionsBPF_PROG_TYPE_LWT_IN,BPF_PROG_TYPE_LWT_OUT,BPF_PROG_TYPE_LWT_XMIT,// a network packet filter for lightweight tunnelsBPF_PROG_TYPE_SOCK_OPS,// a program for setting socket parametersBPF_PROG_TYPE_SK_SKB,// a network packet filter for forwarding packets between socketsBPF_PROG_CGROUP_DEVICE,// determine if a device operation should be permitted or notBPF_PROG_TYPE_SK_MSG,BPF_PROG_TYPE_RAW_TRACEPOINT,BPF_PROG_TYPE_CGROUP_SOCK_ADDR,BPF_PROG_TYPE_LWT_SEG6LOCAL,BPF_PROG_TYPE_LIRC_MODE2,BPF_PROG_TYPE_SK_REUSEPORT,BPF_PROG_TYPE_FLOW_DISSECTOR,BPF_PROG_TYPE_CGROUP_SYSCTL,BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,BPF_PROG_TYPE_CGROUP_SOCKOPT,BPF_PROG_TYPE_TRACING,BPF_PROG_TYPE_STRUCT_OPS,BPF_PROG_TYPE_EXT,BPF_PROG_TYPE_LSM,BPF_PROG_TYPE_SK_LOOKUP,BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
};
而eBPF使用的主要数据结构有以下几类:
enum bpf_map_type {BPF_MAP_TYPE_UNSPEC, /* Reserve 0 as invalid map type */BPF_MAP_TYPE_HASH,//a hash tableBPF_MAP_TYPE_ARRAY,//an array map, optimized for fast lookup speeds, often used for countersBPF_MAP_TYPE_PROG_ARRAY,//an array of file descriptors corresponding to eBPF programs; used to implement jump tables//and sub-programs to handle specific packet protocolsBPF_MAP_TYPE_PERF_EVENT_ARRAY,//stores pointers to struct perf_event, used to read and store perf event countersBPF_MAP_TYPE_PERCPU_HASH,//a per-CPU hash tableBPF_MAP_TYPE_PERCPU_ARRAY,//a per-CPU array, used to implement histograms of latencyBPF_MAP_TYPE_STACK_TRACE,// stores stack tracesBPF_MAP_TYPE_CGROUP_ARRAY,//stores pointers to control groupsBPF_MAP_TYPE_LRU_HASH,//a hash table that only retains the most recently used itemsBPF_MAP_TYPE_LRU_PERCPU_HASH,//a per-CPU hash table that only retains the most recently used itemsBPF_MAP_TYPE_LPM_TRIE,//a longest-prefix match trie, good for matching IP addresses to a rangeBPF_MAP_TYPE_ARRAY_OF_MAPS,// a map-in-map data structureBPF_MAP_TYPE_HASH_OF_MAPS,// a map-in-map data structureBPF_MAP_TYPE_DEVMAP,//for storing and looking up network device referencesBPF_MAP_TYPE_SOCKMAP,//stores and looks up sockets and allows socket redirection with BPF helper functionsBPF_MAP_TYPE_CPUMAP,BPF_MAP_TYPE_XSKMAP,BPF_MAP_TYPE_SOCKHASH,BPF_MAP_TYPE_CGROUP_STORAGE,BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,BPF_MAP_TYPE_QUEUE,BPF_MAP_TYPE_STACK,/* See /usr/include/linux/bpf.h for the full list. */
};
更详细的请参考官方文档及下列源码:
include/uapi/linux/bpf.h:BPF头文件,包含相关的辅助函数列表、辅助函数使用的标记及结构体和常量的描述
net/core/filter.c:网络有关的辅助函数及使用的程序类型相关描述
kernel/trace/bpf_trace.c:程序跟踪有关辅助函数
kernel/bpf/verifier.c:辅助函数用于校验eBPF map有效性的函数
kernel/bpf/:其他辅助函数(如cgroups,sockmaps等)
samples/bpf/:例程
侯老师说过,源码之前,了无秘密。可以去查看相关的代码并运行,看一下,就明白了。
四、应用场景
eBPF之所以流行起来,最主要原因之一仍然是云,也就是虚拟化的快速应用。在前面的文章中也提到了BPF不是一个多么新鲜的事情,但真正的被广泛认知就是从云的不断的发展而引起的。云和虚拟化的应用中,动态管理和动态操作是最基本的要求。而此时对网络的管理和控制,特别是网络安全,就提到了最迫切的程度上来。而回顾前面的文章中XDP和TC的应用正好可以解决其中的一部分问题。举一个例子,通过eBPF的可编程性,可以替换iptables的相关策略,从而达到自主控制Socket的传输,实现负载均衡。
当然,eBPF不只是针对网络,只要能够达到的地方,都可以进行相关的事件、数据等的处理。所以从上面的分析来看,它可以应用于以下场景:
1、可观测网络或相关
2、安全监控和审计
3、自定义编程扩展功能
再介绍几个常见的实例:
1、K8S对网络系统的监控
2、各大云厂商基于eBPF的监控
3、mongodb 数据通信的解析
4、Cilium
5、其它很多
五、总结
eBPF的发展史,就是一部需求推进的发展史。所以说,技术不落地,发展的速度就会受限。反过来,强劲的落地需求,推动着技术不断的完善和快速发展。学习也是如此,如果只是为了学习新技术而学习,无法在实际工作中应用,那么技术学习了往往也会被忘记。而如果在实际的场景中应用到,那么对技术的理解会越来越深越来越通晓。这样,在后面的工作中会应用的如鱼得水。甚至有可能反过来,深入技术内部进行完善和修改。