libbpf和Rust开发ebpf程序实战示例
作者:a朋
这篇文章主要为大家介绍了libbpf和Rust开发ebpf程序实战示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
libbpf-bootstrap中Rust example
libbpf-bootstrap中包含Rust example,存放在examples/rust/目录。
以examples/rust/tracecon为例,看一下libbpf和Rust开发ebpf的开发和运行流程:
- tracecon程序监听<tcp建立连接>的系统调用,并记录其ip或hostname;
- 内核态的ebpf程序tracecon.bpf.c,使用c语言编写;
在build.rs中,使用libbpf-cargo这个依赖库,构建tracecon.bpf.c并生成tracecon.skel.rs;
- Cargo build会运行builds.rs中的代码;
- 在main.rs中,调用生成的tracecon.skel.rs中的函数,加载并运行ebpf程序;
内核态ebpf程序tracecon.bpf.c
ebpf程序监听了kprobe的函数tcp_v4_connect,从struct sock结构中读出ip:
// tracecon.bpf.c #include "vmlinux.h" #include <bpf/bpf_helpers.h> #include <bpf/bpf_core_read.h> #include <bpf/bpf_tracing.h> … SEC("kprobe/tcp_v4_connect") int BPF_KPROBE(tcp_v4_connect_enter, struct sock *sk, struct sockaddr *uaddr, int addr_len) { u32 tid = get_tid(); if (!tid) return 0; bpf_map_update_elem(&sockets, &tid, &sk, 0); return 0; }; SEC("kretprobe/tcp_v4_connect") int BPF_KRETPROBE(tcp_v4_connect_exit, int ret) { u32 tid = get_tid(); struct sock **sockpp; struct lookup *lookup; struct event event = {}; u32 ip; if (!tid) return 0; if (ret != 0) goto cleanup; sockpp = bpf_map_lookup_elem(&sockets, &tid); if (!sockpp) return 0; ip = BPF_CORE_READ(*sockpp, __sk_common.skc_daddr); // 读出ip lookup = bpf_map_lookup_elem(&hostnames, &ip); // 查找hostname if (!lookup) { event.tag = IP; memcpy(&event.ip, &ip, sizeof(event.ip)); } else { event.tag = HOSTNAME; memcpy(&event.hostname, &lookup->c, sizeof(lookup->c)); bpf_map_delete_elem(&hostnames, &ip); } /* ctx is implied in the signature macro */ bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); cleanup: bpf_map_delete_elem(&sockets, &tid); return 0; } ...
记录连接事件的event结构定义:
// tracecon.bpf.c struct event { u8 tag; u8 ip[4]; u8 hostname[HOSTNAME_LEN]; };
build.rs
build.rs中,使用libbpf_cargo::SkeletonBuilder,构建traceconn.bpf.c并生成tracecon.skel.rs文件:
// build.rs use libbpf_cargo::SkeletonBuilder; const SRC: &str = "./src/bpf/tracecon.bpf.c"; fn main() { create_dir_all("./src/bpf/.output").unwrap(); let skel = Path::new("./src/bpf/.output/tracecon.skel.rs"); SkeletonBuilder::new() .source(SRC) .build_and_generate(&skel) .expect("bpf compilation failed"); println!("cargo:rerun-if-changed={}", SRC); }
为此,Cargo.toml中引入了libbpf_cargo这个crate的依赖:
// Cargo.toml ... [build-dependencies] libbpf-cargo = "0.13"
用户态程序main.rs
- 首先,使用tracecon.skel.rs中的TraceconSkelBuilder构造skel对象;
- 然后,使用skel对象加载并连接kprobe程序;
- 最后,使用perf读取ebpf中的map数据;
fn main() -> Result<()> { ... // 使用skel.rs中的TraceconSkelBuilder对象构造skel let mut skel_builder = TraceconSkelBuilder::default(); let mut open_skel = skel_builder.open()?; let mut skel = open_skel.load()?; // 加载并运行 let _kprobe = skel .progs_mut() .tcp_v4_connect_enter() .attach_kprobe(false, "tcp_v4_connect")?; let _kretprobe = skel .progs_mut() .tcp_v4_connect_exit() .attach_kprobe(true, "tcp_v4_connect")?; // 使用perf读events let perf = PerfBufferBuilder::new(skel.maps_mut().events()) .sample_cb(handle_event) .build()?; while running.load(Ordering::SeqCst) { perf.poll(Duration::from_millis(100))?; } ... }
具体事件信息的显示,在handle_event()函数中:
fn handle_event(_cpu: i32, data: &[u8]) { let mut event = Event::default(); plain::copy_from_bytes(&mut event, data).expect("Event data buffer was too short"); match event.tag { 0 => println!("ip event: {}", Ipv4Addr::from(event.ip)), 1 => println!("host event: {}", String::from_utf8_lossy(&event.hostname)), _ => {} } }
运行
# cargo run
运行后输出:
host event: connectivity-check.ubuntu.com.
ip event: 202.96.209.133
ip event: 202.96.209.133
...
以上就是libbpf和Rust开发ebpf程序实战示例的详细内容,更多关于libbpf Rust开发ebpf的资料请关注脚本之家其它相关文章!