Corrector: TinyCorrect v0.1 - [spaces tables urls]
Author: envestcc chen1233216@hotmail.com
Date: 2022/11/04
Revisor: Falcon falcon@tinylab.org
Project: RISC-V Linux 内核剖析
Environment: 泰晓 Linux 实验盘
Sponsor: PLCT Lab, ISCAS
本文会对 eBPF 技术进行初步的介绍,包括基本原理,相关基础设施以及主要的应用场景,帮助读者对 eBPF 技术有一个总体的认识,了解涉及的众多概念,方便后续需要深入了解时知道要查找什么内容。
BPF (Berkeley Packet Filter) 是类 Unix 系统上数据链路层的一种原始接口,提供原始链路层封包的收发。Linux 内核采用 BPF 作为网络数据包过滤技术。 包括像 tcpdump 的底层也是采用 BPF 作为底层包过滤技术。
eBPF 是一项革命性技术,它能在内核中运行沙箱程序(sandbox programs),而无需修改内核源码或者加载内核模块。
eBPF (extended Berkeley Packet Filter) 是在 BPF 基础上经过重新设计,逐步演进为一个通用执行引擎,允许用户在操作系统的内核中加载和运行自定义程序,可用于扩展甚至修改内核行为。
eBPF 最早出现在 3.18 内核版本中,此后原来的 BPF 就被称为经典 BPF,缩写为 cBPF(classic BPF)。
Linux 内核的主要作用是管理硬件或虚拟硬件,并提供一致的 API(系统调用),以实现应用程序运行和共享资源。Linux 划分了一组子系统来分别管理。每个子系统一般在一定程度上都提供了可配置的功能,来满足用户的不同需求。如果满足不了需求,则需要对内核进行更改,可以通过直接修改子系统或者编写一个内核模块两种方式,但都存在痛点:
而使用 eBPF,可以使用一种新方法来重新对内核行为进行编程,而无需更改内核源码或者加载内核模块。
eBPF 程序是事件驱动的,当内核或应用程序执行到某个钩子时运行。预定义的钩子包括系统调用,函数进入/退出,内核跟踪点,网络事件等。如果预定义的钩子不满足需求,可以创建 kprobe 或 uprobe 来作为自定义钩子触发 ePBF 程序。
一般是通过编写高级编程语言(C)或者一些 eBPF 项目(bcc,bpftrace)提供的命令工具,将高级语言转换成 eBPF 字节码的形式。
通过 eBPF 库或者直接使用 bpf 系统调用将 eBPF 程序加载到 Linux 内核时,要经历 vertification(验证)和 JIT Compilation(即时编译)两个步骤才能被附加到请求的钩子上。
验证步骤确保 eBPF 程序可以安全地运行,它主要验证以下条件:
即时编译步骤将 eBPF 字节码转换为机器特定指令集,来优化程序的执行速度。
eBPF 程序可以使用 eBPF maps 来存储和查询数据,map 可以通过 eBPF 程序访问,也能通过系统调用从用户态程序中访问。
map 支持多种数据类型,并且每种数据类型都有共享和线程独有两种。支持的数据类型例如:
为了程序兼容性考虑,eBPF 程序不能随意调用内核函数。相对的,内核提供了一组稳定的 API 供 eBPF 程序调用。例如:
eBPF 程序中除了可以定义和调用函数,还可以使用 Tail Call(尾部调用)来调用和执行另一个 eBPF 程序,并替换执行上下文,与 exec 系统调用对进程的处理类似。
Linux 内核中实现了 eBPF Runtime(运行时)来执行 eBPF 程序。用户程序可以通过 bpf 系统调用来设置 eBPF 程序执行的内容以及时机。内核会使用 eBPF verifier 来检查 eBPF 程序的安全性,通过后 JIT compiler 就将程序翻译成可执行的机器码。
直接编写 eBPF 指令码跟编写汇编代码一样繁琐,因此 LLVM 和 GCC 编译器都提供了生成 eBPF 指令码的功能,现在用户可以通过编写 C 程序来使用 eBPF 功能。
LLVM 编译器包含了 eBPF 后端,它会生成包含程序码,重定位等信息的 eBPF ELF 文件,这些文件可以被 eBPF loader 分析并加载到内核中。
GCC 编译器从 10 开始支持 eBPF 后端,在此之前 LLVM 是唯一的选择。目前对 eBPF 特性的支持上已经和 LLVM 差不多了。
bcc 是一个编程框架,用户通过它可以实现编写 python 代码来进行 eBPF 编程。该框架主要适用于需要对应用程序和系统进行分析和追踪的场景。通常 eBPF 程序用于收集统计数据或者生成对应事件,然后由用户态程序统一处理并以人类可读的方式进行展示。python 程序将生成 eBPF 字节码并加载到内核中。
bpftrace 是 eBPF 编程使用的高级追踪语言,它使用 LLVM 作为后端,将脚本编译成 eBPF 字节码,并使用 bcc 与 Linux eBPF 子系统以及像 kprobes,uprobes,tracepoints 这些现有的 Linux 追踪功能进行交互。
目前支持 eBPF Libraries 的语言主要由 C/C++,Rust 和 Golang。下面列举了这三种语言主流的库。
libbpf 是一个基于 C/C++ 的库,属于 Linux 内核上游的一部分。它包含一个 eBPF loader,它会处理 LLVM 生成的 eBPF ELF 文件,以便加载到内核中。与 BCC 相比,其功能和复杂性有很大的提升,还支持如全局变量,BPF 框架等特性。而 libbpf-rs 和 libbpfgo 都是对 libbpf 的一层封装,分别适用于 Rust 和 Go 语言。
除上述提到的项目外,还有一些值得关注的新兴内容:
目前 eBPF 技术主要应用于 Networking(网络)、Security(安全)、Observability & Tracing(观测监控和追踪)这几个领域。下面列出了各个领域方向比较知名的一些项目。
领域 | 项目 |
---|---|
Networking | Cilium,Katran,pwru,Merbridge |
Security | Falco,Tracee,KubeArmor |
Observability & Tracing | Pixie,Pyroscope,Parca,Hubble,Tetragon,kubectl trace,Inspektor Gadget,Apache SkyWalking |
上述这些项目大部分都跟云原生,容器关系密切。比如 Katran 提供了高性能 4 层负载均衡器;Cilium 为 ServiceMesh 打造了具备 API 感知和安全高效的容器网络方案;kubectl trace 提供了在 kubernetes 集群中使用 BPF 程序调试的方便操作。看来大火的云原生技术也促进了 eBPF 技术的发展。
eBPF 技术在解决很多技术难题的同时,也被很多非法的组织和机构恶意利用。可以通过下面两个分享案例了解 eBPF rootkit 的相关原理:
Linux Kernel 6.1 版本中 BPF 相关修改主要有以下两处:
更多详情可以参考 The first half of the 6.1 merge window
本文主要给大家介绍了 eBPF 技术是什么以及它解决的问题。并通过介绍 eBPF 技术原理中的相关概念,帮助读者对 eBPF 有一个总体的认识。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。