libbpf和Rust開(kāi)發(fā)ebpf程序?qū)崙?zhàn)示例
libbpf-bootstrap中Rust example
libbpf-bootstrap中包含Rust example,存放在examples/rust/目錄。
以examples/rust/tracecon為例,看一下libbpf和Rust開(kāi)發(fā)ebpf的開(kāi)發(fā)和運(yùn)行流程:
- tracecon程序監(jiān)聽(tīng)<tcp建立連接>的系統(tǒng)調(diào)用,并記錄其ip或hostname;
- 內(nèi)核態(tài)的ebpf程序tracecon.bpf.c,使用c語(yǔ)言編寫(xiě);
在build.rs中,使用libbpf-cargo這個(gè)依賴(lài)庫(kù),構(gòu)建tracecon.bpf.c并生成tracecon.skel.rs;
- Cargo build會(huì)運(yùn)行builds.rs中的代碼;
- 在main.rs中,調(diào)用生成的tracecon.skel.rs中的函數(shù),加載并運(yùn)行ebpf程序;
內(nèi)核態(tài)ebpf程序tracecon.bpf.c
ebpf程序監(jiān)聽(tīng)了kprobe的函數(shù)tcp_v4_connect,從struct sock結(jié)構(gòu)中讀出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結(jié)構(gòu)定義:
// tracecon.bpf.c struct event { u8 tag; u8 ip[4]; u8 hostname[HOSTNAME_LEN]; };
build.rs
build.rs中,使用libbpf_cargo::SkeletonBuilder,構(gòu)建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這個(gè)crate的依賴(lài):
// Cargo.toml ... [build-dependencies] libbpf-cargo = "0.13"
用戶態(tài)程序main.rs
- 首先,使用tracecon.skel.rs中的TraceconSkelBuilder構(gòu)造skel對(duì)象;
- 然后,使用skel對(duì)象加載并連接kprobe程序;
- 最后,使用perf讀取ebpf中的map數(shù)據(jù);
fn main() -> Result<()> { ... // 使用skel.rs中的TraceconSkelBuilder對(duì)象構(gòu)造skel let mut skel_builder = TraceconSkelBuilder::default(); let mut open_skel = skel_builder.open()?; let mut skel = open_skel.load()?; // 加載并運(yùn)行 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()函數(shù)中:
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)), _ => {} } }
運(yùn)行
# cargo run
運(yùn)行后輸出:
host event: connectivity-check.ubuntu.com.
ip event: 202.96.209.133
ip event: 202.96.209.133
...
以上就是libbpf和Rust開(kāi)發(fā)ebpf程序?qū)崙?zhàn)示例的詳細(xì)內(nèi)容,更多關(guān)于libbpf Rust開(kāi)發(fā)ebpf的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Rust語(yǔ)言實(shí)現(xiàn)圖像編碼轉(zhuǎn)換
image-rs庫(kù)是?Rust?社區(qū)中廣泛使用的一個(gè)開(kāi)源庫(kù),它提供了豐富的圖像編解碼功能,本文主要介紹了Rust語(yǔ)言實(shí)現(xiàn)圖像編碼轉(zhuǎn)換,具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05詳解Rust中三種循環(huán)(loop,while,for)的使用
我們常常需要重復(fù)執(zhí)行同一段代碼,針對(duì)這種場(chǎng)景,Rust?提供了多種循環(huán)(loop)工具。一個(gè)循環(huán)會(huì)執(zhí)行循環(huán)體中的代碼直到結(jié)尾,并緊接著回到開(kāi)頭繼續(xù)執(zhí)行。而?Rust?提供了?3?種循環(huán):loop、while?和?for,下面逐一講解2022-09-09Rust 語(yǔ)言中的 into() 方法及代碼實(shí)例
在 Rust 中,into() 方法通常用于將一個(gè)類(lèi)型的值轉(zhuǎn)換為另一個(gè)類(lèi)型,這通常涉及到資源的所有權(quán)轉(zhuǎn)移,本文給大家介紹Rust 語(yǔ)言中的 into() 方法及代碼實(shí)例,感謝的朋友跟隨小編一起看看吧2024-03-03Rust語(yǔ)言之使用Polar權(quán)限管理方法詳解
權(quán)限管理 (Permission Management) 是一個(gè)涵蓋了系統(tǒng)或網(wǎng)絡(luò)中用戶權(quán)限控制和管理的系統(tǒng),本文將詳細(xì)給大家介紹Rust語(yǔ)言中如何使用Polar權(quán)限管理,需要的朋友可以參考下2023-11-11Rust語(yǔ)言之Prometheus系統(tǒng)監(jiān)控工具包的使用詳解
Prometheus?是一個(gè)開(kāi)源的系統(tǒng)監(jiān)控和警報(bào)工具包,最初是由SoundCloud構(gòu)建的,隨著時(shí)間的發(fā)展,Prometheus已經(jīng)具有適用于各種使用場(chǎng)景的版本,為了開(kāi)發(fā)者方便開(kāi)發(fā),更是有各種語(yǔ)言版本的Prometheus的開(kāi)發(fā)工具包,本文主要介紹Rust版本的Prometheus開(kāi)發(fā)工具包2023-10-10Rust編寫(xiě)自動(dòng)化測(cè)試實(shí)例權(quán)威指南
這篇文章主要為大家介紹了Rust編寫(xiě)自動(dòng)化測(cè)試實(shí)例權(quán)威指南詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12