Skip to content

Latest commit

 

History

History
100 lines (67 loc) · 6.55 KB

README_zh.md

File metadata and controls

100 lines (67 loc) · 6.55 KB

bpf-compatible

简介

这个仓库包含一套工具链,用以实现在不依赖于本地 BTF 的情况下, eBPF 程序的跨内核版本兼容。

我们基于btfhub来实现。btfhub提供了各种发行版各种内核的预编译好的 BTF 存档(包括不支持本地 BTF 的内核版本)。

通过这个仓库提供的工具链,你可以从btfhub下载你所需要的内核版本的BTF存档,而后将这些BTF存档进行裁剪,使之仅保留你所写的eBPF程序中使用到了的类型,而后将这些裁剪后的BTF存档与编译好的package.json打包成tar存档发布。

而后通过这个仓库中提供的bpf-compatible-rs库,用户程序可以加载一个按照上述格式打包好的tar存档,从中选取适合当前内核的BTF存档,并从tar中获取package.json

设计

Tar的结构

|- package.json <eBPF程序本身>
|- btfhub-archive
|- ---- ubuntu <ID in os-release>
|- ---- ---- 22.04 <VERSION in os-release>
|- ---- ---- ---- x86_64 <machine in uname>
|- ---- ---- ---- ---- 5.15.0-71-generic.btf <kernel-release in uname>

一个由此工具链打包出来的tar文件的可能结构如上。其中包括由ecc所生成的package.json,以及一个btfhub-archive文件夹。btfhub-archive的结构与仓库btfhub-archive一致,但只保留了其中部分内容(即我们需要的内核的BTF文件)。

具体的功能

  • btfhub-archive下载所需的内核版本的BTF
  • 根据编译好的eBPF程序,对我们需要的BTF存档进行裁剪
  • 将裁剪好的BTF与编译好的eBPF程序进行打包
  • 解压以tar存档形式发布的 eBPF 程序,从中选取适合当前内核版本的BTF存档并运行

工具链

这个仓库所提供的工具链主要包括两部分。

  • script/btfgen:一个shell脚本,用于从btfhub-archive下载BTF存档,以及生成裁剪过的BTF存档
  • bpf-compatible-rs: 一个Rust库,用以提供解压tar存档的支持,以及选择适合当前内核的BTF存档的功能。目前bpf-loader-rsecli基于这个库实现了tar存档的加载与运行。

API的设计

bpf-compitable-rs 的API主要包含以下两个函数:

  • fn unpack_tar(tar_data: &[u8]) -> Result<(Vec<u8>, BtfArchive)>:解压一个tar存档,读取其package.json的内容并返回。如果存档中存在btfhub-archive目录,则会一同返回对应的TempDir对象及btfhub-archive目录的PathBuf
  • fn get_current_system_btf_file( archive_path: impl AsRef<Path>) -> Result<PathBuf>:从给定的btfhub-archive目录中选取适合当前内核的BTF存档。

btfgen 所提供的功能如下:

  • btfgen fetch: 从btfhub-archive仓库下载BTF存档至~/.cache/eunomia/btfhub。也可以使用$BTFHUB_CACHE来手动指定下载目录
  • btfgen btfgen [-j JSON_FILE] <BPF>: 基于<BPF>所指定的BPF程序,创建裁剪版的BTF存档,并打包成tar存档。如果指定了-j参数,则会将对应的package.json一起打包进去。

bpf-compatible-sys 是一个用于链接到原有的libbpf程序上的适配bpf-compatible-rs的接口库,其API包括:

  • int ensure_core_btf_with_linked_tar(char** path): 使用程序内弱符号_binary_min_core_btfs_tar_gz_start_binary_min_core_btfs_tar_gz_end所指明的字节范围作为tar文件的binary,从中读取当前内核的BTF存档,并在获取成功的情况下将生成的临时文件的路径的字符串指针存储在*path中。需要注意的是,内存会由bpf-compatible-sys申请。在无法从程序内链接的tar中获取当前内核的BTF的情况下,返回对应的errno。
  • int ensure_core_btf_with_tar_binary(char** path, const char* tar_bin, int tar_len): 与ensure_core_btf_with_linked_tar类似,但是使用参数提供的tar_binary
  • int clean_core_btf_rs(char* path): 清理临时文件并释放path对应的内存。用户总应该在程序结束前调用此函数进行清理。

此外,为了便于C程序使用bpf-compatible-sys,我们同样需要一个头文件btf_core.h。在将btf-compatible应用在原有的libbpf程序时,用户总应优先考虑此头文件中所定义的函数。这个头文件中包括:

  • bpf-compatible-sys提供的C函数的原型
  • int ensure_core_btf(struct bpf_object_open_opts *opts):使用btf_context提供的路径信息填充opts中的btf_custom_path。此函数尽可能保证了与bcc中的ensure_core_btf相同的语义。此函数会调用ensure_core_btf_with_linked_tar来实现。
  • int ensure_core_btf_tar(struct bpf_object_open_opts *opts, const char* tar_bin, int tar_len):与ensure_core_btf类似,但是使用参数传入的tar存档。
  • void clean_core_btf(struct bpf_object_open_opts *opts): 清理临时文件。用户总应在程序结束前调用此函数进行清理。

此外,为了实现ensure_core_btf函数,btf_core.h需要包含libbpf中的libbpf.h

在应用于libbpf程序时,用户可以优先考虑ensure_core_btfclean_core_btf。这两个函数提供了与bccbtf_helper完全相同的语义。

流程图

flow

flow-dev

协议

MIT

近期目标 / Roadmap

  • 实现只裁剪和打包特定内核版本的BTF存档
  • 提供一个 example
  • 在 rust 之上提供一个 c header wrapper
  • 提供一些兼容性相关的 API,包含 bpf buffer 等
  • 发布二进制库和 c 头文件

对旧内核的兼容 - 细节

一方面,我们需要保证兼容性,这意味着我们可能需要折中一些特性。例如,高版本内核可能支持 ring buffer,而低版本内核只能使用 perf event。我们需要设计一个能够在两种环境中都能工作的解决方案,例如使用一种 bpf buffer 作为抽象,例如:

https://github.com/iovisor/bcc/blob/master/libbpf-tools/compat.h

typedef int (*bpf_buffer_sample_fn)(void *ctx, void *data, size_t size);
typedef void (*bpf_buffer_lost_fn)(void *ctx, int cpu, __u64 cnt);

struct bpf_buffer *bpf_buffer__new(struct bpf_map *events, struct bpf_map *heap);
int bpf_buffer__open(struct bpf_buffer *buffer, bpf_buffer_sample_fn sample_cb,
		     bpf_buffer_lost_fn lost_cb, void *ctx);
int bpf_buffer__poll(struct bpf_buffer *, int timeout_ms);
void bpf_buffer__free(struct bpf_buffer *);