libbpf和Rust开发ebpf程序实战示例

 更新时间:2023年12月27日 08:47:46   作者: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的资料请关注脚本之家其它相关文章!

相关文章

  • Rust并发之异步编程应用教程

    Rust并发之异步编程应用教程

    这篇文章主要介绍了Rust并发之异步编程应用教程,Rust的异步编程是一种强大的并发编程范式,它允许我们编写高效、响应迅速的应用程序,通过掌握异步编程的高级应用,我们可以充分利用系统资源,提高应用程序的性能,需要的朋友可以参考下
    2026-05-05
  • 探索Rust切片与Go有何区别

    探索Rust切片与Go有何区别

    这篇文章主要为大家介绍了Rust切片与Go的区别探索,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Rust使用Trait对象实现多态的详细步骤

    Rust使用Trait对象实现多态的详细步骤

    本文详细介绍了在Rust中使用Trait对象实现运行时多态的方法,通过一个图形渲染系统的案例展示了如何统一管理不同类型的图形对象,文章涵盖了Trait对象的核心概念、语法、性能考量以及在实际项目中的应用场景,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • Rust 语言中的dyn 关键字及用途解析

    Rust 语言中的dyn 关键字及用途解析

    在Rust中,"dyn"关键字用于表示动态分发(dynamic dispatch),它通常与trait对象一起使用,以实现运行时多态, 在Rust中,多态是通过trait和impl来实现的,这篇文章主要介绍了Rust 语言中的 dyn 关键字,需要的朋友可以参考下
    2024-03-03
  • Rust常用特型之Drop特型

    Rust常用特型之Drop特型

    本文主要介绍了Rust常用特型之Drop特型,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-03-03
  • Rust常用特型之ToOwned特型示例详解

    Rust常用特型之ToOwned特型示例详解

    在Rust中,假定某类型实现了Clone特型,如果给你一个对它引用,那我们得到它指向内容的备份的最常见方式是调用其clone()函数,这篇文章主要介绍了Rust常用特型之ToOwned特型,需要的朋友可以参考下
    2024-04-04
  • 使用Rust制作康威生命游戏的实现代码

    使用Rust制作康威生命游戏的实现代码

    这篇文章主要介绍了使用Rust制作康威生命游戏,初始rust项目,使用wasm的项目模板,结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • Rust安装与环境配置超详细教程(零基础)

    Rust安装与环境配置超详细教程(零基础)

    Rust是一门系统编程语言,专注于安全,尤其是并发安全,支持函数式和命令式以及泛型等编程范式的多范式语言,这篇文章主要介绍了Rust安装与环境配置超详细教程的相关资料,需要的朋友可以参考下
    2026-02-02
  • Tauri 打开本地文件踩坑分析解决

    Tauri 打开本地文件踩坑分析解决

    这篇文章主要为大家介绍了Tauri 打开本地文件踩坑分析解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Rust Atomics and Locks内存序Memory Ordering详解

    Rust Atomics and Locks内存序Memory Ordering详解

    这篇文章主要为大家介绍了Rust Atomics and Locks内存序Memory Ordering详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02

最新评论