关于 ebpf-rust
这项技能指导 Agent 使用 Aya 以 Rust 构建和调试生产级 eBPF 程序,涵盖内核端代码、用户态加载、共享映射、日志及 Tokio 集成。
主要功能
- 01Aya eBPF 项目设置
- 02内核端与用户态 Rust 代码
- 03BPF 映射共享
- 04使用 aya-log 进行结构化日志
- 05Tokio 集成与事件读取
- 06加载错误和验证器调试
适用场景
- 01使用 Aya 启动 eBPF 项目
- 02在内核与用户态之间传递事件
- 03为 BPF 程序添加结构化日志
- 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 found | BTF 与内核不匹配 | 重新生成 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