ebpf程序如何更改内核执行流或调用内核函数



我正试图弄清楚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程序类型,它可以用来替换白名单结构中的函数指针。但同样,内核明确允许。

  1. 使内核应用程序从ebpf程序读取内存

这是不可能的,因为eBPF程序的内存是星历表,但您可以定义自己的自定义eBPF程序类型,并通过自定义上下文类型传入一些内存以修改到eBPF程序。

  1. 使ebpf更改函数的返回值

除非您从该函数显式调用eBPF程序,否则不可能。

  1. 允许ebpf程序调用内核函数

虽然可以使用数字,但这通常无法更改任意函数的返回值。

你是对的,某些程序类型被允许调用一些内核函数。但正如你所发现的,这些再次被列入白名单。

内核如何知道这个结构的存在?

宏魔术。验证器构建这些结构的列表。但前提是程序类型存在于程序类型列表中。

/virtual/main.c:25:5:error:无法调用非静态辅助函数

这似乎是BCC的一个限制,所以如果你想玩这些东西,你可能必须手动编译eBPF程序,并用libbpf或cilium/eBPF加载它。

最新更新