ebpf-rust

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

eBPF with Rust (Aya)

基于Rust的eBPF开发(Aya框架)

Purpose

用途

Guide agents through building production eBPF programs in Rust using the Aya framework: writing kernel-side BPF code with
aya-bpf
, structured logging with
aya-log
, sharing maps between BPF and userspace, and integrating with async tokio.
指导开发者使用Aya框架基于Rust构建生产级eBPF程序:包括使用
aya-bpf
编写内核侧BPF代码、使用
aya-log
实现结构化日志、在BPF与用户空间之间共享映射,以及与异步框架tokio集成。

Triggers

触发场景

  • "How do I write an eBPF program in Rust?"
  • "How do I use the Aya framework?"
  • "How do I share a BPF map between kernel and userspace in Rust?"
  • "How do I log from a BPF program in Rust?"
  • "My Aya program fails to load — how do I debug it?"
  • "How do I integrate an eBPF program with tokio?"
  • "如何用Rust编写eBPF程序?"
  • "如何使用Aya框架?"
  • "如何在Rust中在内核与用户空间之间共享BPF映射?"
  • "如何在Rust的BPF程序中记录日志?"
  • "我的Aya程序加载失败——该如何调试?"
  • "如何将eBPF程序与tokio集成?"

Workflow

工作流程

1. Project setup

1. 项目搭建

bash
undefined
bash
undefined

Install aya-tool (generates bindings from vmlinux BTF)

安装aya-tool(从vmlinux BTF生成绑定)

cargo install aya-tool
cargo install aya-tool

Create new Aya project from template

从模板创建新的Aya项目

cargo install cargo-generate cargo generate https://github.com/aya-rs/aya-template
cargo install cargo-generate cargo generate https://github.com/aya-rs/aya-template

Workspace layout (generated)

生成的工作区结构

my-ebpf/

my-ebpf/

├── my-ebpf-ebpf/ <- kernel-side crate (target: bpf)

├── my-ebpf-ebpf/ <- 内核侧crate(目标平台:bpf)

├── my-ebpf/ <- userspace crate (runs on host)

├── my-ebpf/ <- 用户侧crate(在主机运行)

└── xtask/ <- build helper (cargo xtask build/run)

└── xtask/ <- 构建辅助工具(通过cargo xtask build/run执行)


```bash

```bash

Build both sides

构建两侧代码

cargo xtask build-ebpf # builds BPF object cargo xtask run # builds + runs with sudo
undefined
cargo xtask build-ebpf # 构建BPF目标文件 cargo xtask run # 构建并以sudo权限运行
undefined

2. Kernel-side BPF program

2. 内核侧BPF程序

rust
// my-ebpf-ebpf/src/main.rs
#![no_std]
#![no_main]

use aya_bpf::{
    macros::{map, tracepoint},
    maps::HashMap,
    programs::TracePointContext,
    helpers::bpf_get_current_pid_tgid,
};
use aya_log_ebpf::info;

#[map]
static CALL_COUNT: HashMap<u32, u64> = HashMap::with_max_entries(1024, 0);

#[tracepoint]
pub fn trace_read(ctx: TracePointContext) -> u32 {
    let pid = (bpf_get_current_pid_tgid() >> 32) as u32;

    // Lookup or insert
    match unsafe { CALL_COUNT.get(&pid) } {
        Some(count) => {
            let _ = CALL_COUNT.insert(&pid, &(count + 1), 0);
        }
        None => {
            let _ = CALL_COUNT.insert(&pid, &1u64, 0);
        }
    }

    info!(&ctx, "read() called by pid {}", pid);
    0
}

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    unsafe { core::hint::unreachable_unchecked() }
}
rust
// my-ebpf-ebpf/src/main.rs
#![no_std]
#![no_main]

use aya_bpf::{
    macros::{map, tracepoint},
    maps::HashMap,
    programs::TracePointContext,
    helpers::bpf_get_current_pid_tgid,
};
use aya_log_ebpf::info;

#[map]
static CALL_COUNT: HashMap<u32, u64> = HashMap::with_max_entries(1024, 0);

#[tracepoint]
pub fn trace_read(ctx: TracePointContext) -> u32 {
    let pid = (bpf_get_current_pid_tgid() >> 32) as u32;

    // 查找或插入
    match unsafe { CALL_COUNT.get(&pid) } {
        Some(count) => {
            let _ = CALL_COUNT.insert(&pid, &(count + 1), 0);
        }
        None => {
            let _ = CALL_COUNT.insert(&pid, &1u64, 0);
        }
    }

    info!(&ctx, "PID {} 调用了read()", pid);
    0
}

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    unsafe { core::hint::unreachable_unchecked() }
}

3. Userspace loader with tokio

3. 结合tokio的用户空间加载器

rust
// my-ebpf/src/main.rs
use aya::{Bpf, programs::TracePoint, maps::HashMap};
use aya_log::BpfLogger;
use tokio::signal;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Load compiled BPF object (embedded at build time)
    let mut bpf = Bpf::load(include_bytes_aligned!(
        "../../target/bpf/my-ebpf-ebpf.bpf.o"
    ))?;

    // Initialize structured logging from BPF programs
    BpfLogger::init(&mut bpf)?;

    // Attach tracepoint
    let program: &mut TracePoint = bpf.program_mut("trace_read").unwrap().try_into()?;
    program.load()?;
    program.attach("syscalls", "sys_enter_read")?;

    // Read from shared map
    let map: HashMap<_, u32, u64> = HashMap::try_from(bpf.map("CALL_COUNT").unwrap())?;

    // Wait for Ctrl+C
    signal::ctrl_c().await?;

    // Print final counts
    for (pid, count) in map.iter().filter_map(|r| r.ok()) {
        println!("PID {}: {} reads", pid, count);
    }
    Ok(())
}
rust
// my-ebpf/src/main.rs
use aya::{Bpf, programs::TracePoint, maps::HashMap};
use aya_log::BpfLogger;
use tokio::signal;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // 加载编译后的BPF对象(构建时嵌入)
    let mut bpf = Bpf::load(include_bytes_aligned!(
        "../../target/bpf/my-ebpf-ebpf.bpf.o"
    ))?;

    // 初始化BPF程序的结构化日志
    BpfLogger::init(&mut bpf)?;

    // 挂载追踪点
    let program: &mut TracePoint = bpf.program_mut("trace_read").unwrap().try_into()?;
    program.load()?;
    program.attach("syscalls", "sys_enter_read")?;

    // 读取共享映射
    let map: HashMap<_, u32, u64> = HashMap::try_from(bpf.map("CALL_COUNT").unwrap())?;

    // 等待Ctrl+C信号
    signal::ctrl_c().await?;

    // 打印最终计数
    for (pid, count) in map.iter().filter_map(|r| r.ok()) {
        println!("PID {}: {} 次读取", pid, count);
    }
    Ok(())
}

4. Map types in Aya

4. Aya中的映射类型

rust
use aya_bpf::maps::{HashMap, Array, RingBuf, PerfEventArray, LruHashMap};

// Hash map
#[map]
static MY_MAP: HashMap<u32, u64> = HashMap::with_max_entries(1024, 0);

// Ring buffer (preferred for events)
#[map]
static EVENTS: RingBuf = RingBuf::with_byte_size(256 * 1024, 0);

// LRU hash (connection tracking)
#[map]
static CONNS: LruHashMap<u32, ConnInfo> = LruHashMap::with_max_entries(10000, 0);

// Sending events via RingBuf (kernel side)
if let Ok(mut entry) = unsafe { EVENTS.reserve::<MyEvent>(0) } {
    entry.write(MyEvent { pid, ts });
    entry.submit(0);
}
rust
// Reading RingBuf events (userspace)
use aya::maps::RingBuf;
use tokio::io::unix::AsyncFd;

let ring = RingBuf::try_from(bpf.take_map("EVENTS").unwrap())?;
let mut ring = AsyncFd::new(ring)?;

loop {
    let mut guard = ring.readable_mut().await?;
    let rb = guard.get_inner_mut();
    while let Some(item) = rb.next() {
        let event: &MyEvent = unsafe { &*(item.as_ptr() as *const MyEvent) };
        println!("Event from PID {}", event.pid);
    }
    guard.clear_ready();
}
rust
use aya_bpf::maps::{HashMap, Array, RingBuf, PerfEventArray, LruHashMap};

// 哈希映射
#[map]
static MY_MAP: HashMap<u32, u64> = HashMap::with_max_entries(1024, 0);

// 环形缓冲区(事件传递首选)
#[map]
static EVENTS: RingBuf = RingBuf::with_byte_size(256 * 1024, 0);

// LRU哈希(连接追踪场景)
#[map]
static CONNS: LruHashMap<u32, ConnInfo> = LruHashMap::with_max_entries(10000, 0);

// 内核侧通过RingBuf发送事件
if let Ok(mut entry) = unsafe { EVENTS.reserve::<MyEvent>(0) } {
    entry.write(MyEvent { pid, ts });
    entry.submit(0);
}
rust
// 用户侧读取RingBuf事件
use aya::maps::RingBuf;
use tokio::io::unix::AsyncFd;

let ring = RingBuf::try_from(bpf.take_map("EVENTS").unwrap())?;
let mut ring = AsyncFd::new(ring)?;

loop {
    let mut guard = ring.readable_mut().await?;
    let rb = guard.get_inner_mut();
    while let Some(item) = rb.next() {
        let event: &MyEvent = unsafe { &*(item.as_ptr() as *const MyEvent) };
        println!("来自PID {}的事件", event.pid);
    }
    guard.clear_ready();
}

5. Supported program types

5. 支持的程序类型

Aya macroProgram typeAttach target
#[tracepoint]
Tracepoint
"syscalls", "sys_enter_read"
#[kprobe]
kprobefunction name
#[kretprobe]
kretprobefunction name
#[uprobe]
uprobeuserspace binary + offset
#[xdp]
XDPnetwork interface
#[tc]
TC (traffic control)netdevice + direction
#[socket_filter]
Socket filterraw socket fd
#[perf_event]
Perf eventperf_event fd
#[lsm]
LSM hooksecurity hook name
#[sk_msg]
Sockmapsocket map
Aya宏程序类型挂载目标
#[tracepoint]
追踪点
"syscalls", "sys_enter_read"
#[kprobe]
内核探针函数名称
#[kretprobe]
内核返回探针函数名称
#[uprobe]
用户态探针用户空间二进制文件 + 偏移量
#[xdp]
XDP网络接口
#[tc]
流量控制(TC)网络设备 + 方向
#[socket_filter]
套接字过滤器原始套接字文件描述符
#[perf_event]
性能事件perf_event文件描述符
#[lsm]
LSM钩子安全钩子名称
#[sk_msg]
套接字映射套接字映射表

6. Generating kernel type bindings

6. 生成内核类型绑定

bash
undefined
bash
undefined

Generate bindings from running kernel's BTF

从运行中的内核BTF生成绑定

aya-tool generate task_struct > src/vmlinux.rs
aya-tool generate task_struct > src/vmlinux.rs

Or use btf_type_tag and CO-RE

或使用btf_type_tag和CO-RE

aya-bpf supports CO-RE via bpf_core_read! macro

aya-bpf通过bpf_core_read!宏支持CO-RE


```rust
// CO-RE field access in Aya
use aya_bpf::helpers::bpf_core_read;

let dport: u16 = unsafe {
    bpf_core_read!(sk, __sk_common.skc_dport)?
};

```rust
// Aya中的CO-RE字段访问
use aya_bpf::helpers::bpf_core_read;

let dport: u16 = unsafe {
    bpf_core_read!(sk, __sk_common.skc_dport)?
};

7. Debugging load failures

7. 调试加载失败问题

bash
undefined
bash
undefined

Check verifier errors (Aya surfaces them as Rust errors)

查看验证器错误(Aya会将其转换为Rust错误)

Run with RUST_LOG=debug for verbose output

开启RUST_LOG=debug获取详细输出

RUST_LOG=debug cargo xtask run 2>&1 | grep -A 20 "verifier"
RUST_LOG=debug cargo xtask run 2>&1 | grep -A 20 "verifier"

Check BTF info

查看BTF信息

bpftool btf dump file /sys/kernel/btf/vmlinux | grep task_struct
bpftool btf dump file /sys/kernel/btf/vmlinux | grep task_struct

Inspect loaded programs after load

加载后查看已加载的程序

bpftool prog list bpftool prog dump xlated name trace_read

| Error | Cause | Fix |
|-------|-------|-----|
| `invalid mem access` | Unbounded pointer dereference | Add null check before reading |
| `Type not found` | BTF mismatch with kernel | Regenerate vmlinux bindings |
| `Permission denied` | No CAP_BPF or CAP_SYS_ADMIN | Run with `sudo` or set capability |
| `map already exists` | Map pinned, name collision | Unpin or rename map |
bpftool prog list bpftool prog dump xlated name trace_read

| 错误 | 原因 | 解决方法 |
|-------|-------|-----|
| `invalid mem access` | 无界指针解引用 | 在读取前添加空指针检查 |
| `Type not found` | BTF与内核不匹配 | 重新生成vmlinux绑定 |
| `Permission denied` | 缺少CAP_BPF或CAP_SYS_ADMIN权限 | 使用sudo运行或设置权限 |
| `map already exists` | 映射已固定、名称冲突 | 取消固定或重命名映射 |

Related skills

相关技能

  • Use
    skills/observability/ebpf
    for C-based eBPF with libbpf
  • Use
    skills/rust/rust-async-internals
    for tokio async patterns used in userspace
  • Use
    skills/rust/rust-unsafe
    for unsafe code patterns in BPF helpers
  • 若使用基于libbpf的C语言eBPF开发,请使用
    skills/observability/ebpf
  • 若需了解用户空间中使用的tokio异步模式,请使用
    skills/rust/rust-async-internals
  • 若需了解BPF辅助函数中的不安全代码模式,请使用
    skills/rust/rust-unsafe