mcpField
E
ebpf-rust

ebpf-rust

github.com/mohitmishra786开发269450 次安装18 周前更新
代码仓库

关于 ebpf-rust

这项技能指导 Agent 使用 Aya 以 Rust 构建和调试生产级 eBPF 程序,涵盖内核端代码、用户态加载、共享映射、日志及 Tokio 集成。

主要功能

  1. 01Aya eBPF 项目设置
  2. 02内核端与用户态 Rust 代码
  3. 03BPF 映射共享
  4. 04使用 aya-log 进行结构化日志
  5. 05Tokio 集成与事件读取
  6. 06加载错误和验证器调试

适用场景

  1. 01使用 Aya 启动 eBPF 项目
  2. 02在内核与用户态之间传递事件
  3. 03为 BPF 程序添加结构化日志
  4. 04诊断 Aya 加载及验证器错误

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill ebpf-rust

安装后在 Claude Code 中输入/ebpf-rust确认已加载。

详细介绍

使用 Rust(Aya)编写 eBPF

用途

指导 Agent 使用 Aya 框架构建生产级 Rust eBPF 程序:使用 aya-bpf 编写内核侧 BPF 代码,使用 aya-log 进行结构化日志记录,在 BPF 与用户空间之间共享映射,并与异步 tokio 集成。

触发条件

  • “如何用 Rust 编写 eBPF 程序?”
  • “如何使用 Aya 框架?”
  • “如何在 Rust 中共享内核与用户空间之间的 BPF 映射?”
  • “如何从 Rust BPF 程序中记录日志?”
  • “我的 Aya 程序加载失败了,该如何调试?”
  • “如何将 eBPF 程序与 tokio 集成?”

工作流

1. 项目设置
# Install aya-tool (generates bindings from vmlinux BTF)
cargo install aya-tool

# Create new Aya project from template
cargo install cargo-generate
cargo generate https://github.com/aya-rs/aya-template

# Workspace layout (generated)
# my-ebpf/
# ├── my-ebpf-ebpf/    <- kernel-side crate (target: bpf)
# ├── my-ebpf/         <- userspace crate (runs on host)
# └── xtask/           <- build helper (cargo xtask build/run)
# Build both sides
cargo xtask build-ebpf          # builds BPF object
cargo xtask run                 # builds + runs with sudo
2. 内核侧 BPF 程序
// 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() }
}
3. 使用 tokio 的用户空间加载器
// 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(())
}
4. Aya 中的映射类型
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);
}
// 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();
}
5. 支持的程序类型
Aya 宏程序类型附加目标
#[tracepoint]跟踪点"syscalls", "sys_enter_read"
#[kprobe]kprobe函数名称
#[kretprobe]kretprobe函数名称
#[uprobe]uprobe用户空间二进制文件 + 偏移量
#[xdp]XDP网络接口
#[tc]TC(流量控制)网络设备 + 方向
#[socket_filter]套接字过滤器原始套接字 fd
#[perf_event]Perf 事件perf_event fd
#[lsm]LSM 钩子安全钩子名称
#[sk_msg]Sockmap套接字映射
6. 生成内核类型绑定
# Generate bindings from running kernel's BTF
aya-tool generate task_struct > src/vmlinux.rs

# Or use btf_type_tag and CO-RE
# aya-bpf supports CO-RE via bpf_core_read! macro
// 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)?
};
7. 调试加载失败
# Check verifier errors (Aya surfaces them as Rust errors)
# Run with RUST_LOG=debug for verbose output
RUST_LOG=debug cargo xtask run 2>&1 | grep -A 20 "verifier"

# Check BTF info
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
错误原因修复方法
invalid mem access指针解引用没有边界限制读取前添加空值检查
Type not foundBTF 与内核不匹配重新生成 vmlinux 绑定
Permission denied没有 CAP_BPF 或 CAP_SYS_ADMIN使用 sudo 运行或设置 capability
map already exists映射已固定,名称冲突取消固定或重命名映射

相关技能

  • 使用 skills/observability/ebpf 编写基于 C 和 libbpf 的 eBPF
  • 使用 skills/rust/rust-async-internals 了解用户空间中使用的 tokio 异步模式
  • 使用 skills/rust/rust-unsafe 了解 BPF 辅助函数中的不安全代码模式

评论

支持 Markdown