#ebpf #xdp #linux #api-bindings #perf-event

rxdp

与 XDP 程序和 eBPF 映射交互的绑定

4 份发布 (2 个重大更改)

0.3.1 2021 年 3 月 3 日
0.3.0 2021 年 2 月 22 日
0.2.0 2021 年 2 月 9 日
0.1.0 2021 年 1 月 17 日

#797 in Unix API

MIT 许可证

66KB
1.5K SLoC

rxdp

Rust 用于与 XDP 程序和 eBPF 映射交互的绑定。

此库为用户空间与 XDP 程序和 eBPF 映射交互提供了常用基本操作的绑定。它构建在 libbpf-sys 之上。撰写本文时,它只支持所有可能的 eBPF 映射类型的一个子集(请参阅测试目录以了解支持哪些映射)。

先决条件

示例

从 ELF 文件创建对象

use rxdp;

let obj_path = "/path/to/elf/file";
let obj = match rxdp::XDPObject::new(obj_path) {
    Ok(obj) => {
        println!("Successfully created object from {}", obj_path);
        obj
    },
    Err(err) => panic!("{:?}", err),
};

设置已固定映射。

已固定映射将从文件系统加载,前提是映射名称与文件系统中的名称匹配。HashSet 中的任何新映射都将设置固定路径,以便在程序加载后自动固定。

let mut pinned_maps = HashSet::new();
pinned_maps.insert("my_map_name".to_string());
obj.pinned_maps(pinned_maps).unwrap();

将对象(程序和映射)加载到内核中。

这将消耗上面创建的 XDPObject 并返回一个 XDPLoadedObject

let obj = obj.load().unwrap();

获取 XDP 程序的引用并将其附加到接口

let dev = "eth0";
let flags = rxdp::AttachFlags::SKB_MODE;

let prog = obj.get_program("prog_name").unwrap();
match prog.attach_to_interface(dev, flags) {
    Ok(_) => println!("Successfully attached to {}", dev),
    Err(e) => panic!("{:?}", e),
}

访问底层 eBPF Map

let m: rxdp::Map<u32, u64> = match rxdp::Map::new(&obj, "map_name") {
    Ok(m) => m,
    Err(e) => panic!("{:?}", e),
};

注意:键/值大小必须与 eBPF 代码中定义的键/值大小匹配,否则创建映射将失败。

执行映射操作

use rxdp::MapLike;

let key = 0u32;
let value = 1000u64;
m.update(&key, &value, rxdp::MapFlags::BpfAny).unwrap();
let got = m.lookup(&key).unwrap();
assert_eq!(value, got.into_single());

// iterate through all items
for kv in m.items().unwrap() {
    println!("key: {}, value: {}", kv.key, kv.value.into_single());
}

对于 per-cpu 映射,使用 PerCpuMap

let m: rxdp::PerCpuMap<u32, u64> = rxdp::PerCpuMap::new(&obj, "map_name").unwrap();

注意:键大小必须与 eBPF 代码中定义的键大小匹配,否则创建映射将失败。

Per CPU 映射操作

Per CPU 映射在查找期间返回 MapValue::Multi(Vec<T>) 变体,每个可能的 CPU 一个

use rxdp::MapLike;

let key = 0u32;
let value = 1000u64;
m.update(&key, &value, rxdp::MapFlags::BpfAny).unwrap();
let got = m.lookup(&key).unwrap();
assert_eq!(got.into_vec(), vec![value; rxdp::num_cpus()]);

// iterate through all items
for kv in m.items().unwrap() {
    println!("key: {}", kv.key);
    for v in kv.value.into_vec() {
        println!("value: {}", v);
    }
}

Perf 事件映射

可以通过 PerfMap 获取从 eBPF 发送的 Perf 事件。

let mut perfmap = rxdp::PerfMap::<u32>::new(&obj, "map_name").unwrap();
let r: Receiver<rxdp::PerfEvent<u32>> = perfmap.start_polling(10000);

// Wait for events on the receiver side of the channel
loop {
    r.recv().map_or_else(
        |e| println!("error: {:?}", e),
        |event| println!("event: {:?}", event),
    );
}

批处理支持(取决于内核)

如果内核支持,您可以对更新/查找执行批处理操作

if rxdp::is_batching_supported() {
    let mut next_key = None;
    let r = m.lookup_batch(10u32, next_key).unwrap();
    // do something with r.items...
    next_key = r.next_key;
}

测试

运行测试需要 root 权限,因此最好在 Docker 容器中运行它们

make docker-test

基准测试

运行基准测试需要root权限,因此最好在Docker容器中运行它们。

make docker-bench

许可

该crate采用MIT许可发布,并具有以下第三方依赖项:

网站 许可 链接
libbpf-sys github.com/alexforster/libbpf-sys BSD-2-条款 静态
libbpf github.com/libbpf/libbpf LGPL-2.1- BSD-2-条款 静态
libelf sourceware.org/elfutils LGPL-2.1--更新的版本 LGPL-3.0--更新的版本 动态
zlib zlib.net Zlib 动态

依赖项

~8MB
~184K SLoC