Using aya-tool
Source Code
Full code for the example in this chapter is available here
Very often you will need to use type definitions that your running Linux kernel uses in its source code. For example, you might need a definition of task_struct, because you are about to write a BPF program which receives an information about new scheduled process/task. Aya doesn't provide any definition of this structure. What should be done to get that definition? And we also need that definition in Rust, not in C.
That's what aya-tool is designed for. It's a tool which allows to generate Rust bindings for specific kernel structures.
It can be installed with the following commands:
```console
$ cargo install bindgen-cli
$ cargo install --git https://github.com/aya-rs/aya -- aya-tool
```
Ensure that you have bpftool
and bindgen
installed in your system,
aya-tool
is not going to work without it.
The syntax of the command is:
```console
$ aya-tool
aya-tool
USAGE:
aya-tool <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
SUBCOMMANDS:
generate Generate Rust bindings to Kernel types using bpftool
help Print this message or the help of the given subcommand(s)
```
Let's assume that we want to generate Rust definition of
task_struct. Let's also assume that your project is called
myapp
. Your userspace part is in myapp
subdirectory, your eBPF part is in
myapp-ebpf
. We need to generate the bindings for the eBPF part, which can be
done with:
```console
$ aya-tool generate task_struct > myapp-ebpf/src/vmlinux.rs
```
Generating for multiple types
You can also specify multiple types to generate, for example:
But in the following example, we will focus only ontask_struct
.
Then we can use vmlinux
as a module with mod vmlinux
in our eBPF program,
like here:
```rust linenums="1" title="myapp-ebpf/src/main.rs"
#![no_std]
#![no_main]
#[allow(non_upper_case_globals)]
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
mod vmlinux;
use aya_ebpf::{
cty::{c_int, c_ulong},
macros::{lsm, map},
maps::HashMap,
programs::LsmContext,
};
use vmlinux::task_struct;
#[map]
static PROCESSES: HashMap<i32, i32> = HashMap::with_max_entries(32768, 0);
#[lsm(hook = "task_alloc")]
pub fn task_alloc(ctx: LsmContext) -> i32 {
match unsafe { try_task_alloc(ctx) } {
Ok(ret) => ret,
Err(ret) => ret,
}
}
unsafe fn try_task_alloc(ctx: LsmContext) -> Result<i32, i32> {
let task: *const task_struct = ctx.arg(0);
let _clone_flags: c_ulong = ctx.arg(1);
let retval: c_int = ctx.arg(2);
// Save the PID of a new process in map.
let pid = (*task).pid;
PROCESSES.insert(&pid, &pid, 0).map_err(|e| e as i32)?;
// Handle results of previous LSM programs.
if retval != 0 {
return Ok(retval);
}
Ok(0)
}
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
```
Portability and different kernel versions
Structures generated by aya-tool are portable across different Linux kernel
versions thanks to mechanism called
BPF CO-RE.
The structures are not simply generated from kernel headers. However, the
target kernel (regardless of version) should have CONFIG_DEBUG_INFO_BTF
option enabled.