我正试图弄清楚ebpf程序如何改变内核空间中函数(在我的情况下不是系统调用(的结果。我发现了很多关于ebpf如何将内核变成可编程内核的文章和博客文章,但似乎每个例子都只是只读跟踪和收集统计数据。
我可以想出几种方法:1(让内核应用程序从ebpf程序读取内存,2(让ebpf更改函数的返回值,3(允许ebpf程序调用内核函数。
第一种方法似乎不是一个好主意。第二个就足够了,但据我所知,这并不容易。这个问题表明系统调用是只读的。这个bcc文档说这是可能的,但是函数需要在内核中被列入白名单。这让我认为白名单是固定的,只能通过重新编译内核来更改,这是正确的吗?第三个似乎是最灵活的一个,这篇博客文章鼓励我去研究它。这就是我想要的。
我从一个全新的5.15内核开始,它应该具有以下功能正如博客文章所说,我做了一些任何人都不应该做的事情(安全性不是问题,因为我只是在玩这个(,并通过将其添加到net/core/filter.c
来打开ebpf的每个函数(我不确定这是正确的位置(:
static bool accept_the_world(int off, int size,
enum bpf_access_type type,
const struct bpf_prog *prog,
struct bpf_insn_access_aux *info)
{
return true;
}
bool export_the_world(u32 kfunc_id)
{
return true;
}
const struct bpf_verifier_ops all_verifier_ops = {
.check_kfunc_call = export_the_world,
.is_valid_access = accept_the_world,
};
内核如何知道这个结构的存在?我不知道。声明的其他bpf_verifier_ops
都没有在其他地方使用,所以看起来没有register_bpf_ops
接下来,我能够安装bcc(由于许多安装指南损坏,经过长时间的斗争(。我不得不签出bcc的0.24版本。我在某个地方读到编译内核时需要pahole,所以我把我的更新到了v1.19。
我的python文件非常简单,我刚刚从bcc复制了vfs示例并简化了它:
bpf_text_kfunc = """
extern void hello_test_kfunc(void) __attribute__((section(".ksyms")));
KFUNC_PROBE(vfs_open)
{
stats_increment(S_OPEN);
hello_test_kfunc();
return 0;
}
"""
b = BPF(text=bpf_text_kfunc)
其中hello_test_kfunc只是一个执行printk的函数,作为模块插入内核(它存在于kallsyms
中(。当我尝试运行它时,我得到:
/virtual/main.c:25:5: error: cannot call non-static helper function
hello_test_kfunc();
^
这就是我被困的地方。似乎是JIT不允许这样做,但到底是谁造成了这个问题?BCC、libbpf还是其他什么?我需要手动编写bpf代码来调用内核函数吗?
有人有我链接的lwn博客文章所说的实际工作代码的例子吗?
eBPF从根本上是为了以非常特定的有限方式扩展内核功能。本质上是一个非常先进的插件系统。eBPF的主要设计原则之一是不允许程序破坏内核。因此,不可能改变为任意内核函数的结果。
内核可以随时调用eBPF程序,然后使用返回值或助手调用的副作用来实现某些功能。这里的关键是内核总是知道它在做这件事。
一种例外是BPF_PROG_TYPE_STRUCT_OPS
程序类型,它可以用来替换白名单结构中的函数指针。但同样,内核明确允许。
- 使内核应用程序从ebpf程序读取内存
这是不可能的,因为eBPF程序的内存是星历表,但您可以定义自己的自定义eBPF程序类型,并通过自定义上下文类型传入一些内存以修改到eBPF程序。
- 使ebpf更改函数的返回值
除非您从该函数显式调用eBPF程序,否则不可能。
- 允许ebpf程序调用内核函数
虽然可以使用数字,但这通常无法更改任意函数的返回值。
你是对的,某些程序类型被允许调用一些内核函数。但正如你所发现的,这些再次被列入白名单。
内核如何知道这个结构的存在?
宏魔术。验证器构建这些结构的列表。但前提是程序类型存在于程序类型列表中。
/virtual/main.c:25:5:error:无法调用非静态辅助函数
这似乎是BCC的一个限制,所以如果你想玩这些东西,你可能必须手动编译eBPF程序,并用libbpf或cilium/eBPF加载它。