一、什么是eBPF,干什么用的?
eBPF(extended Berkeley Packet Filter)是一种在Linux内核中执行安全、可编程和高性能网络数据包过滤和处理的技术。
eBPF最初是在网络数据包过滤和网络性能分析领域引入的,但后来扩展到其他领域,如安全、观测和运行时扩展等。它提供了一种在内核中运行安全且高性能的虚拟机,允许用户编写和加载自定义的程序来处理网络数据包或拦截内核事件。
使用eBPF,可以编写一段特定的eBPF程序,将其加载到内核,并将其绑定到特定的事件或挂钩点。这些eBPF程序可以直接在内核中执行,而无需修改内核源代码或重新编译内核。eBPF程序可以进行复杂的数据包过滤、网络监控、性能跟踪、安全审计等操作,同时保持较低的运行时开销。
eBPF程序由特定的指令集组成,这些指令集经过精心设计,以确保其安全性和可控性。eBPF还提供了一些安全机制,如指令计数器和内存访问限制,以防止恶意或错误的程序影响系统的稳定性和安全性。
eBPF已经成为Linux内核的核心组件,并且在网络安全、性能优化、运行时观测和分析等领域得到广泛应用。它为开发者提供了一种强大而灵活的方式来扩展和定制Linux内核的功能,同时保持较好的性能和安全性。
二、安装开发工具或者跑eBPF示例的准备工作
不论是要安装使用eunomia-bpf 开发工具还是跑libbpf-bbotstrap所提供的示例,都需要在Linux系统上安装clang以及llvm等一系列编译器以及安装包。
2.1开启BTF
如果你的系统选择没有被限定死,我的建议是尽可能安装推荐的系统,因为这省去了内核部分权限开启以及重新编译以及安装包手动安装的工作(这一步真的有不少坑)。
你的系统版本满足以下要求,就能省去内核开启BTF并重新编译内核的工作:
-
Fedora 31+
-
RHEL 8.2+
-
OpenSUSE Tumbleweed (in the next release, as of 2020-06-04)
-
Arch Linux (from kernel 5.7.1.arch1-1)
-
Manjaro (from kernel 5.4 if compiled after 2021-06-18)
-
Ubuntu 20.10
-
Debian 11 (amd64/arm64)
(至于为什么要开启BTF见参考链接1以及参考链接2)。总而言之,咱还是得让内核支持BTF。
Linux系统可以通过查看/sys/kernel/btf/vmlinux文件是否存在确定内核是否支持BTF编程。
我使用的Ubuntu22.04直接内核开启了CONFIG_DEBUG_INFO_BTF=y,存在该文件
但是使用低于20.10的Ubuntu18.04,不存在该文件:
所以针对未默认开启BTF选项的,就需要开启CONFIG_DEBUG_INFO_BTF=y选项,重新编译内核。当然直接编译新的内核会有错,缺省包pahole,所以我们需要先安装pahole(dwarves包的一部分 版本1.16+ GitHub - acmel/dwarves: Pahole and the dwarves)。但是我还是在编译过程中还是遇到了各种各样的问题,可惜没记录下来。
自编译,如何开启CONFIG_DEBUG_INFO_BTF?
下载新的linux内核源代码压缩包,解压,运行命令行:
make menuconfig
在可视化界面输入"/",搜索"CONFIG_DEBUG_INFO_BTF":结果如下:
记录对应的设置选项,exit后,将对应的选项设置为y:
之后的编译过程不在赘述,和正常Linux编译过程一样。这样的编译对make、gcc等工具都有版本要求,编译过程繁琐。我的编译过程总是报各种错误,最后放弃了自行编译内核的工作,选择了使用符合其要求的版本系统直接运行使用 eunomia-bpf 开发工具,省去了繁琐的配置环境的过程。
针对要自行编译内核的选手,推荐看知乎文章:
2.2Clang+llvm
要编译构建BPF项目,需要Clang以及LLVM10+,10+。以下系统版本能直接命令行下载安装满足要求的Clang以及llvm:
-
Fedora 32+
-
Ubuntu 20.04+
-
Arch Linux
-
Ubuntu 20.10 (LLVM 11)
-
Debian 11 (LLVM 11)
-
Alpine 3.13+
不满足版本要求的系统就只能自行下载相应系统对应的clang+llvm的安装文件,执行安装。
如能够直接命令行安装的Ubuntu22.04直接运行命令:
apt install clang libelf1 libelf-dev zlib1g-dev llvm
得到的Clang以及llvm版本如下:
但是,针对不满足的Ubuntu18.04,直接运行命令行安装 得到的版本就不满足10+要求。
所以,针对这些版本的Linux系统,就需要去搜索Linux环境下对应的LLVM + clang安装脚本进行手动安装了。
对于需要手动安装的选手,推荐看这篇文章:一份关于各种安装LLVM的方法的总结_llvm官网-CSDN博客(转)
三、下载安装 eunomia-bpf 开发工具
针对的是满足上述版本要求的系统Ubuntu22.04。
3.1下载 ecli 工具,用于运行 eBPF 程序
wget https://aka.pw/bpf-ecli -O ecli && chmod +x ./ecli
#展示用法 Usage: ecli [--help] [--version] [--json] [--no-cache] url-and-args
./ecli -h
3.2下载编译器工具链,用于将 eBPF 内核代码编译为 config 文件或 WASM 模块
wget https://github.com/eunomia-bpf/eunomia-bpf/releases/latest/download/ecc && chmod +x ./ecc
#展示用法eunomia-bpf compiler
#Usage: ecc [OPTIONS] <SOURCE_PATH> [EXPORT_EVENT_HEADER]
./ecc -h
注:假如在 aarch64 平台上,请从 release 下载 ecc-aarch64 和 ecli-aarch64。Ubuntu22.04为x86_64架构,直接执行上述命令。
四、利用eunomia-bpf跑一个简单示例
简单的 eBPF 程序示例minimal.bpf.c,它会在内核中打印一条消息。我们会使用 eunomia-bpf 的编译器工具链将其编译为 bpf 字节码文件,然后使用 ecli 工具加载并运行该程序。作为示例,我们可以暂时省略用户态程序的部分。
4.1minimal.btf.c文件
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#define BPF_NO_GLOBAL_DATA
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
typedef unsigned int u32;
typedef int pid_t;
const pid_t pid_filter = 0;
char LICENSE[] SEC("license") = "Dual BSD/GPL";
SEC("tp/syscalls/sys_enter_write")
int handle_tp(void *ctx)
{
pid_t pid = bpf_get_current_pid_tgid() >> 32;
if (pid_filter && pid != pid_filter)
return 0;
bpf_printk("BPF triggered sys_enter_write from PID %d.\n", pid);
return 0;
}
这段程序通过定义一个 handle_tp 函数并使用 SEC 宏把它附加到 sys_enter_write tracepoint(即在进入 write 系统调用时执行)。该函数通过使用 bpf_get_current_pid_tgid 和 bpf_printk 函数获取调用 write 系统调用的进程 ID,并在内核日志中打印出来。
-
bpf_trace_printk()
: 一种将信息输出到trace_pipe(/sys/kernel/debug/tracing/trace_pipe)简单机制。 在一些简单用例中这样使用没有问题, but它也有一些限制:最多3 参数; 第一个参数必须是%s(即字符串);同时trace_pipe在内核中全局共享,其他并行使用trace_pipe的程序有可能会将 trace_pipe 的输出扰乱。 一个更好的方式是通过 BPF_PERF_OUTPUT(), 稍后将会讲到。 -
void *ctx
:ctx本来是具体类型的参数, 但是由于我们这里没有使用这个参数,因此就将其写成void *类型。 -
return 0
;:必须这样,返回0 (如果要知道why, 参考 #139 iovisor/bcc#139)。
要编译和运行这段程序,可以使用 ecc 工具和 ecli 命令。
4.2编译运行
使用 ecc 编译程序
./ecc minimal.bpf.c
Compiling bpf object...
Packing ebpf object and config into package.json...
然后使用 ecli 运行编译后的程序:
./ecli run package.json
Runing eBPF program...
运行这段程序后,可以通过新开命令行界面查看 /sys/kernel/debug/tracing/trace_pipe 文件来查看 eBPF 程序的输出:
cat /sys/kernel/debug/tracing/trace_pipe
按 Ctrl+C 停止 ecli 进程之后,可以看到对应的输出也停止。
注意:如果正在使用的 Linux 发行版(例如 Ubuntu )默认情况下没有启用跟踪子系统可能看不到任何输出,使用以下指令打开这个功能:
sudo su
echo 1 > /sys/kernel/debug/tracing/tracing_on
注:这份代码示例并没有涉及用户态程序代码,直接编译使用的内核态程序代码。
此部分原文地址:eBPF 入门开发实践教程一:Hello World,基本框架和开发流程 - eunomia
五、可以通过跑libbbpf-bootstrap下的示例代码初步学习
libbpf-bootstrap项目github地址:GitHub - libbpf/libbpf-bootstrap: Scaffolding for BPF application development with libbpf and BPF CO-RE
#在直接使用满足要求的机器上运行libbpf-bootstrap的示例
git clone https://github.com/libbpf/libbpf-bootstrap.git
# 里面部分的代码需要开启BTF才能跑起来,一些更简单的则不需要
cd /libbpf-bootstrap/examples/c
make 文件名 #make minimal
./可执行文件名 #./minimal
#查看/sys/kernel/debug/tracing/trace_pipe打印信息即可看见程序运行效果
cat /sys/kernel/debug/tracing/trace_pipe
多出可执行文件(绿):
运行成功:
-
六、学习需要看的
- 简要了解一下 ebpf 内核相关文档:eBPF - extended Berkeley Packet Filter — Prototype Kernel 0.0.1 documentation
- 阅读 ebpf 简介:eBPF - Introduction, Tutorials & Community Resources
- ebpf中文入门指南:【BPF入门系列-1】eBPF 技术简介 | Head First eBPF
- 基于 libbpf 和 eunomia-bpf 的教程:GitHub - eunomia-bpf/bpf-developer-tutorial: eBPF Developer Tutorial: Learning eBPF Step by Step with Examples
- https://www.ebpf.top/post/kernel_btf/
- eBPF 开发实践教程:基于 CO-RE,通过小工具快速上手 eBPF 开发 - eunomia
- 《BPF之巅 洞悉Linux系统和应用性能》、《Linux内核观测技术BPF》 科学上网zlibrary上能直接下载pdf。私我发也行,要是我看到的话-_-。
七、参考来源:
[2]:GitHub - libbpf/libbpf: Automated upstream mirror for libbpf stand-alone build.
......