[Aya-0] Aya-Ebpf Basic
0. 基本概念
Ebpf
是介于Linux Kernel(内核层)
和User Space(用户层)
的程序,它可以实现在用户层编写代码来对内核层进行一定的观测、介入。具体的技术细节,这里不进行过多的阐释。总体架构可以参考以下两个网站,也是 aya
推荐的:
1. 程序结构
Ebpf
的程序一般由两部分组成,一个用户层的Loader
,一个内核层执行的Program
。在功能上,我觉得和Injector
非常相似,主要的目的就是将自己的代码注入到另一个进程中进行执行。对于Ebpf
来说,相当于系统提供了Api
让我们注入Ebpf-Program
到内核进程中执行。
Ebpf
程序是事件驱动的开发模型,而他的Loader
又是由用户层提供的,所以Ebpf
程序是没有main
函数作为入口的。在流程上,就需要在用户空间指定一个时机来触发事件,从而执行相应的Ebpf
程序,类似一种回调的机制。
以下面一个缩略的 Kprobe Loader
为例,可以清晰的看出 Loader
的结构
#[tokio::main]
async fn main() -> anyhow::Result<()> {
...
// 获取 Ebpf 程序的"句柄"
let program: &mut KProbe = ebpf.program_mut("k_openat").unwrap().try_into()?;
// 把 Ebpf 程序注入到内核
program.load()?;
// 在 "do_sys_openat2" 这个 函数地址 + 0 offset 注册事件,当内核执行到这个位置的时候 `ebpf_program` 将会被调用。
program.attach("do_sys_openat2", 0)?;
...
Ok(())
}
这里的 program
虽然是程序的意思,不过实际上是函数级别的,在 Ebpf
中,一个函数就代表了一个 Ebpf
程序(Program
),比如这里的 k_openat
就是一个 Ebpf
程序。使用#[kprobe]
标记的就是kprobe
类型的 Ebpf
程序,以此类推,并且不同类型的程序处理问题的能力各有不同。
#[kprobe]
pub fn k_openat(ctx: ProbeContext) -> u32 {
match try_k_openat(ctx) {
Ok(ret) => ret,
Err(ret) => ret,
}
}
fn try_k_openat(ctx: ProbeContext) -> Result<u32, u32> {
info!(&ctx, "k_openat called");
Ok(0)
}
2. 项目结构
这里则简单地说一下 aya
给出的 Template 结构,我觉得挺不错的。
首先总共划分为 3 个子模块,分别是:
- 和项目同名的
probe_test
的Loader
模块,这里主要实现用户空间的处理逻辑,对Tokio
的支持也就在这。 probe_test-ebpf
模块,则是真正的ebpf
程序模块,注入到内核的代码在这里实现。probe_test-common
模块可以写一些Loader
和Program
之间共享的一些数据结构、功能函数。
./probe_test
├── Cargo.lock
├── Cargo.toml
├── README.md
├── probe_test
│ ├── Cargo.toml
│ ├── build.rs
│ └── src
│ └── main.rs
├── probe_test-common
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── probe_test-ebpf
│ ├── Cargo.toml
│ ├── build.rs
│ └── src
│ ├── lib.rs
│ └── main.rs
├── rustfmt.toml
└── target
├── CACHEDIR.TAG
└── aarch64-unknown-linux-musl
同时也能看到每一个子模块内,都有build.rs
这个构建脚本来控制编译流程。同时在probe_test/src/main.rs
内使用了 aya::contact_raw_bytes
,将 Program
程序编译生成的 .o
文件内联在 Loader
程序之内。所以不同于其他 Ebpf
框架,Aya 编译的程序只需要将可执行文件复制到目标机器上就可以直接运行了,方便挺多。
3. Aya 的代码怎么看
aya
的文档和源码可以在 https://docs.rs/aya/0.13.1/aya/
找到,不过这里是Aya
用户空间程序的文档。对于 pub fn k_openat(ctx: ProbeContext)
这里的ProbeContext
,此类的Ebpf
侧代码则在 crate::aya-ebpf
中实现。他们其实是不同的 crates
,看源码的时候需要稍微注意一下。
4. Tips
aya 的日志是自己的实现的一套机制,依赖于环境变量RUST_LOG=info|debug|...
的值,在一开始,我以为是自己的代码挂钩失败了,没有回显。后面在 Aya 的 Aya Book
仓库的实例代码中看到 RUST_LOG=info cargo run
我才发现这么个问题,比较坑。