6

在 ebpf/libbpf 程序使用尾调用(tail calls)

 1 year ago
source link: https://mozillazg.com/2022/10/ebpf-libbpf-use-tail-calls.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文将介绍如何在 ebpf/libbpf 程序 eBPF 的尾调用(tail calls)特性。

尾调用(tail calls)

eBPF 的尾调用(tail calls)特性允许一个 eBPF 程序可以调用另一个 eBPF 程序, 并且调用完成后不会返回原来的程序。

image

图片来源:https://docs.cilium.io/en/v1.12/bpf/#tail-calls

尾调用涉及两个步骤:

  1. 定义一个类型为 BPF_MAP_TYPE_PROG_ARRAY 的 map , map 的 value 是在尾调用中被调用的 eBPF 程序的文件描述符。 我们可以在用户态程序中更新这个 map 的 key/value。
  2. 在 eBPF 程序中,我们可以通过 bpf_tail_call() 这个辅助函数 从第1步的 map 中获取 eBPF 程序然后执行该程序进行尾调用。

使用示例

如前面所说,要使用尾调用特性我们需要定义一个 map 以及在 eBPF 程序中使用辅助函数执行尾调用。下面将以示例的代码的方式讲述每个步骤的关键代码。

定义 BPF_MAP_TYPE_PROG_ARRAY 类型的 map

可以通过下面的方法定义一个 BPF_MAP_TYPE_PROG_ARRAY 类型的 map:

struct {
        __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
        __uint(key_size, sizeof(u32));
        __uint(value_size, sizeof(u32));
        __uint(max_entries, 1024);
} tail_jmp_map SEC(".maps");

如果想要在定义这个 map 的时候初始化一些值的话,可以用下面的方法:

struct {
        __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
        __uint(key_size, sizeof(u32));
        __uint(value_size, sizeof(u32));
        __uint(max_entries, 1024);
        __array(values, int (void *));   // 这个 values 必须有
} tail_jmp_map SEC(".maps") = {
        .values = {                      // 初始化一些值
                [268] = (void *)&enter_fchmodat,
        },
};

用户态更新 map

在用户态程序中可以通过 bpf_map_update_elem 函数更新这个 map:

tail_jump_map_fd = bpf_object__find_map_fd_by_name(bpf_obj, "tail_jmp_map");
bpf_map_update_elem(tail_jump_map_fd, &key, &bpf_program_fd, BPF_ANY);

尾调用

eBPF 程序中可以通过 bpf_tail_call 辅助函数执行尾调用:

SEC("raw_tracepoint/sys_enter")
int raw_tracepoint__sys_enter(struct bpf_raw_tracepoint_args *ctx) {
        u32 syscall_id = ctx->args[1];

        // 执行尾调用
        bpf_tail_call(ctx, &tail_jmp_map, syscall_id);

        // 如果在 map 中找不到对应的 ebpf 程序的话,会继续走到后面的代码
        char fmt[] = "no bpf program for syscall %d\n";
        bpf_trace_printk(fmt, sizeof(fmt), syscall_id);
        return 0;
}

完整的示例程序,详见: https://github.com/mozillazg/hello-libbpfgo/tree/master/22-tail-calls


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK