• Rust如何开发eBPF应用?(一)


    前言

    eBPF是一项革命性的技术,可以在Linux内核中运行沙盒程序,而无需重新编译内核或加载内核模块。它能够在许多内核 hook 点安全地执行字节码,主要应用在云原生网络、安全、跟踪监控等方面。

    eBPF 基金会 (https://ebpf.io) 是一个为 eBPF 技术而创建的非盈利性组织,隶属于 Linux 基金会,其意在推动 eBPF 更好地发展,使其得到更加广泛的运用。

    下面我将介绍如何在Rust中开发基于eBPF技术的应用示例。(该示例教程主要面向具备Rust开发基础的同学)

     

    (一)环境准备

    一台VM或Linux系统主机

    推荐系统:Ubuntu20.04

    内存:4G以上

    Rust开发工具链:v1.56~

     

    (二)安装llvm13(编译bpf字节码需要)

    apt-get update 
    apt-get -y install wget build-essential software-properties-common lsb-release libelf-dev linux-headers-generic pkg-config
    wget https://apt.llvm.org/llvm.sh
    chmod +x llvm.sh
    ./llvm.sh 13
    rm -f ./llvm.sh
    //检验是否安装成功,输出版本号表示安装成功
    llvm-config-13 --version | grep 13

     

    (三)安装 cargo-bpf脚手架

    cargo install cargo-bpf --no-default-features --features=llvm13

     

    (四)应用示例

    #1、创建用户空间代码目录
    cargo new bpfdemo
    cd bpfdemo
    #2、创建bpf代码目录
    cargo bpf new probes
    ls
    >>Cargo.lock  Cargo.toml  probes  src
    #3、编写一个openmonitor bpf程序 输出系统打开的文件
    cd probes
    cargo bpf add openmonitor
    cd src 
    ls
    >>lib.rs  openmonitor
    cd openmonitor
    nano main.rs

    完整openmonitor/main.rs代码如下:

    #![no_std]
    #![no_main]
    
    use probes::openmonitor::*;
    use redbpf_probes::kprobe::prelude::*;
    
    program!(0xFFFFFFFE, "GPL");
    
    #[map]
    static mut OPEN_PATHS: PerfMap<OpenPath> = PerfMap::with_max_entries(1024);
    
    #[kprobe]
    fn do_sys_open(regs: Registers) {
        let mut path = OpenPath::default();
        unsafe {
            let filename = regs.parm2() as *const u8;
            if bpf_probe_read_user_str(
                path.filename.as_mut_ptr() as *mut _,
                path.filename.len() as u32,
                filename as *const _,
            ) <= 0
            {
                bpf_trace_printk(b"error on bpf_probe_read_user_str\0");
                return;
            }
            OPEN_PATHS.insert(regs.ctx, &path);
        }
    }

    完整openmonitor/mod.rs代码如下

    pub const PATHLEN: usize = 256;
    
    #[repr(C)]
    #[derive(Debug, Clone)]
    pub struct OpenPath {
        pub filename: [u8; PATHLEN],
    }
    
    impl Default for OpenPath {
        fn default() -> OpenPath {
            OpenPath {
                filename: [0; PATHLEN],
            }
        }
    }
    #4.在probes目录下编译bpf程序,生成openmonitor.elf文件
    cargo bpf build --target-dir=../target
    ls ../target/bpf/programs/openmonitor/openmonitor.elf
    
    #5.在用户空间代码中使用bpf程序,获取系统打开文件
    cd ../src
    nano main.rs

    完整bpfdemo/src/main.rs代码如下

    use futures::stream::StreamExt;
    use std::{ffi::CStr, ptr};
    use tracing::Level;
    use tracing_subscriber::FmtSubscriber;
    
    use redbpf::load::Loader;
    
    use probes::openmonitor::OpenPath;
    
    fn probe_code() -> &'static [u8] {
        include_bytes!(concat!(
            env!("CARGO_MANIFEST_DIR"),
            "/target/bpf/programs/openmonitor/openmonitor.elf"
        ))
    }
    
    #[tokio::main(flavor = "current_thread")]
    async fn main() {
        let subscriber = FmtSubscriber::builder()
            .with_max_level(Level::WARN)
            .finish();
        tracing::subscriber::set_global_default(subscriber).unwrap();
    
        let mut loaded = Loader::load(probe_code()).expect("error on Loader::load");
    
        let probe = loaded
            .kprobe_mut("do_sys_open")
            .expect("error on Loaded::kprobe_mut");
        probe
            .attach_kprobe("do_sys_open", 0)
            .expect("error on KProbe::attach_kprobe");
        probe
            .attach_kprobe("do_sys_openat2", 0)
            .expect("error on KProbe::attach_kprobe");
    
        while let Some((map_name, events)) = loaded.events.next().await {
            if map_name == "OPEN_PATHS" {
                for event in events {
                    let open_path = unsafe { ptr::read(event.as_ptr() as *const OpenPath) };
                    unsafe {
                        let cfilename = CStr::from_ptr(open_path.filename.as_ptr() as *const _);
                        println!("{}", cfilename.to_string_lossy());
                    };
                }
            }
        }
    }
    #6.在bpfdemo目录下编译运行
    cargo build
    cargo run
    
    #将会输出系统实时打开的文件
    >>
    /proc/driver/nvidia/params
    /dev/nvidia0
    /proc/driver/nvidia/params
    /dev/nvidia0
    /proc/driver/nvidia/params
    /dev/nvidia0
    /etc/localtime
    /lib/x86_64-linux-gnu/libcuda.so.1
    /lib/x86_64-linux-gnu/libm.so.6
    /etc/netconfig
    /sys/fs/cgroup/unified/system.slice/systemd-udevd.service/cgroup.procs
    /sys/fs/cgroup/unified/system.slice/systemd-udevd.service/cgroup.threads
    /proc/3084/cmdline
    /proc/3729/cmdline
    /proc/3994/cmdline
    /proc/8823/cmdline
    /proc/2231364/cmdline
    /proc/2431788/cmdline
    /proc/2560949/cmdline
    /sys/class/hwmon
    /sys/class/hwmon/hwmon6
    /sys/class/hwmon/hwmon4
    /sys/class/hwmon/hwmon2
    /sys/class/hwmon/hwmon0
    /sys/class/hwmon/hwmon7
    /sys/class/hwmon/hwmon5

    完毕!

  • 相关阅读:
    AWS S3加密
    <C++>多继承以及典型的菱形继承案例
    华为云云耀云服务器L实例评测 | 云服务器搭建自己的gitlab代码仓库手把手教学
    社交媒体&社交商务提升策略,看这一篇就够!
    EChart常见配置项解析
    React 基础使用
    操作系统,计算机网络,数据库刷题笔记2
    CPP 核心编程8-模板
    前瞻|Java 21 新特性 String Templates(字符串模版)
    pytorch固定随机数中种子
  • 原文地址:https://www.cnblogs.com/jaciots/p/16155257.html