在 WebAssembly 中使用 C/C++ 和 libbpf 編寫 eBPF 程序

作者:于桐,鄭昱笙

eBPF(extended Berkeley Packet Filter)是一種高性能的內(nèi)核虛擬機,可以運行在內(nèi)核空間中,用來收集系統(tǒng)和網(wǎng)絡(luò)信息。隨著計算機技術(shù)的不斷發(fā)展,eBPF 的功能日益強大,進而被用來構(gòu)建各種效率高效的在線診斷和跟蹤系統(tǒng),以及安全的網(wǎng)絡(luò)和服務(wù)網(wǎng)格。

WebAssembly(Wasm)最初是以瀏覽器安全沙盒為目的開發(fā)的,發(fā)展到目前為止,WebAssembly 已經(jīng)成為一個用于云原生軟件組件的高性能、跨平臺和多語言軟件沙箱環(huán)境,Wasm 輕量級容器也非常適合作為下一代無服務(wù)器平臺運行時,或在邊緣計算等資源受限的場景高效執(zhí)行。

現(xiàn)在,借助 Wasm-bpf 編譯工具鏈和運行時,我們可以使用 Wasm 將 eBPF 程序編寫為跨平臺的模塊,同時使用 C/C++ 或 Rust 來編寫 Wasm 程序。通過在 WebAssembly 中使用 eBPF 程序,我們不僅能讓 Wasm 應(yīng)用享受到 eBPF 的高性能和對系統(tǒng)接口的訪問能力,還可以讓 eBPF 程序使用到 Wasm 的沙箱、靈活性、跨平臺性、和動態(tài)加載,并且使用 Wasm 的 OCI 鏡像來方便、快捷地分發(fā)和管理 eBPF 程序。結(jié)合這兩種技術(shù),我們將會給 eBPF 和 Wasm 生態(tài)來一個全新的開發(fā)體驗!

使用 Wasm-bpf 工具鏈在 Wasm 中編寫、動態(tài)加載、分發(fā)運行 eBPF 程序

Wasm-bpf 是一個全新的開源項目:https://github.com/eunomia-bpf/wasm-bpf。它定義了一套 eBPF 相關(guān)系統(tǒng)接口的抽象,并提供了一套對應(yīng)的開發(fā)工具鏈、庫以及通用的 Wasm + eBPF 運行時實例。它可以提供和 libbpf-bootstrap 相似的開發(fā)體驗,自動生成對應(yīng)的 skeleton 頭文件,以及用于在 Wasm 和 eBPF 之間無序列化通信的數(shù)據(jù)結(jié)構(gòu)定義。你可以非常容易地使用任何語言,在任何平臺上建立你自己的 Wasm-eBPF 運行時,使用相同的工具鏈來構(gòu)建應(yīng)用。更詳細的介紹,請參考我們的上一篇博客:Wasm-bpf: 架起 Webassembly 和 eBPF 內(nèi)核可編程的橋梁。

基于 Wasm,我們可以使用多種語言構(gòu)建 eBPF 應(yīng)用,并以統(tǒng)一、輕量級的方式管理和發(fā)布。以我們構(gòu)建的示例應(yīng)用 bootstrap.wasm 為例,大小僅為 ~90K,很容易通過網(wǎng)絡(luò)分發(fā),并可以在不到 100ms 的時間內(nèi)在另一臺機器上動態(tài)部署、加載和運行,并且保留輕量級容器的隔離特性。運行時不需要內(nèi)核頭文件、LLVM、clang 等依賴,也不需要做任何消耗資源的重量級的編譯工作。

本文將以 C/C++ 語言為例,討論 C/C++ 編寫 eBPF 程序并編譯為 Wasm 模塊。使用 Rust 語言編寫 eBPF 程序并編譯為 Wasm 模塊的具體示例,將在下一篇文章中描述。

我們在倉庫中提供了幾個示例程序,分別對應(yīng)于可觀測、網(wǎng)絡(luò)、安全等多種場景。

使用 C/C++ 編寫 eBPF 程序并編譯為 Wasm

libbpf 是一個 C/C++ 的 eBPF 用戶態(tài)加載和控制庫,隨著內(nèi)核一起分發(fā),幾乎已經(jīng)成為 eBPF 用戶態(tài)事實上的 API 標(biāo)準(zhǔn),libbpf 也支持 CO-RE(Compile Once – Run Everywhere) 的解決方案,即預(yù)編譯的 bpf 代碼可以在不同內(nèi)核版本上正常工作,而無需為每個特定內(nèi)核重新編譯。我們希望盡可能的保持與 libbpf 的用戶態(tài) API 以及行為一致,盡可能減少應(yīng)用遷移到 Wasm (如果需要的話)的成本。

libbpf-bootstrap 為生成基于 libbpf 的 bpf 程序提供了模板,開發(fā)者可以很方便的使用該模板生成自定義的 bpf 程序。一般說來,在非 Wasm 沙箱的用戶態(tài)空間,使用 libbpf-bootstrap 腳手架,可以快速、輕松地使用 C/C++構(gòu)建 BPF 應(yīng)用程序。

編譯、構(gòu)建和運行 eBPF 程序(無論是采用什么語言),通常包含以下幾個步驟:

  • 編寫內(nèi)核態(tài) eBPF 程序的代碼,一般使用 C/C++ 或 Rust 語言
  • 使用 clang 編譯器或者相關(guān)工具鏈編譯 eBPF 程序(要實現(xiàn)跨內(nèi)核版本移植的話,需要包含 BTF 信息)。
  • 在用戶態(tài)的開發(fā)程序中,編寫對應(yīng)的加載、控制、掛載、數(shù)據(jù)處理邏輯;
  • 在實際運行的階段,從用戶態(tài)將 eBPF 程序加載進入內(nèi)核,并實際執(zhí)行。

bootstrap

bootstrap是一個簡單(但實用)的BPF應(yīng)用程序的例子。它跟蹤進程的啟動(準(zhǔn)確地說,是 exec() 系列的系統(tǒng)調(diào)用)和退出,并發(fā)送關(guān)于文件名、PID 和 父 PID 的數(shù)據(jù),以及退出狀態(tài)和進程的持續(xù)時間。用-d <min-duration-ms> 你可以指定要記錄的進程的最小持續(xù)時間。

bootstrap 是在 libbpf-bootstrap 中,根據(jù) BCC 軟件包中的libbpf-tools的類似思想創(chuàng)建的,但它被設(shè)計成更獨立的,并且有更簡單的 Makefile 以簡化用戶的特殊需求。它演示了典型的BPF特性,包含使用多個 BPF 程序段進行合作,使用 BPF map 來維護狀態(tài),使用 BPF ring buffer 來發(fā)送數(shù)據(jù)到用戶空間,以及使用全局變量來參數(shù)化應(yīng)用程序行為。

以下是我們使用 Wasm 編譯運行 bootstrap 的一個輸出示例:

$ sudo sudo ./wasm-bpf bootstrap.wasm -h
BPF bootstrap demo application.

It traces process start and exits and shows associated
information (filename, process duration, PID and PPID, etc).

USAGE: ./bootstrap [-d <min-duration-ms>] -v
$ sudo ./wasm-bpf bootstrap.wasm
TIME     EVENT COMM             PID     PPID    FILENAME/EXIT CODE
18:57:58 EXEC  sed              74911   74910   /usr/bin/sed
18:57:58 EXIT  sed              74911   74910   [0] (2ms)
18:57:58 EXIT  cat              74912   74910   [0] (0ms)
18:57:58 EXEC  cat              74913   74910   /usr/bin/cat
18:57:59 EXIT  cat              74913   74910   [0] (0ms)
18:57:59 EXEC  cat              74914   74910   /usr/bin/cat
18:57:59 EXIT  cat              74914   74910   [0] (0ms)
18:57:59 EXEC  cat              74915   74910   /usr/bin/cat
18:57:59 EXIT  cat              74915   74910   [0] (1ms)
18:57:59 EXEC  sleep            74916   74910   /usr/bin/sleep

我們可以提供與 libbpf-bootstrap 開發(fā)相似的開發(fā)體驗。只需運行 make 即可構(gòu)建 wasm 二進制文件:

git clone https://github.com/eunomia-bpf/wasm-bpf --recursive
cd examples/bootstrap
make

編寫內(nèi)核態(tài)的 eBPF 程序

要構(gòu)建一個完整的 eBPF 程序,首先要編寫內(nèi)核態(tài)的 bpf 代碼。通常使用 C 語言編寫,并使用 clang 完成編譯:

char LICENSE[] SEC("license") = "Dual BSD/GPL";

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 8192);
    __type(key, pid_t);
    __type(value, u64);
} exec_start SEC(".maps");

struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 256 * 1024);
} rb SEC(".maps");

const volatile unsigned long long min_duration_ns = 0;
const volatile int *name_ptr;

SEC("tp/sched/sched_process_exec")
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
{
    struct task_struct *task;
    unsigned fname_off;
    struct event *e;
    pid_t pid;
    u64 ts;
....

受篇幅所限,這里沒有貼出完整的代碼。內(nèi)核態(tài)代碼的編寫方式和其他基于 libbpf 的程序完全相同,一般來說會包含一些全局變量,通過 SEC 聲明掛載點的 eBPF 函數(shù),以及用于保存狀態(tài),或者在用戶態(tài)和內(nèi)核態(tài)之間相互通信的 map 對象(我們還在進行另外一項工作:bcc to libbpf converter,等它完成后就可以以這種方式編譯 BCC 風(fēng)格的 eBPF 內(nèi)核態(tài)程序)。在編寫完 eBPF 程序之后,運行 make 會在 Makefile 調(diào)用 clang 和 llvm-strip 構(gòu)建BPF程序,以剝離調(diào)試信息:

clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -I../../third_party/vmlinux/x86/ -idirafter /usr/local/include -idirafter /usr/include -c bootstrap.bpf.c -o bootstrap.bpf.o
llvm-strip -g bootstrap.bpf.o # strip useless DWARF info

之后,我們會提供一個為了 Wasm 專門實現(xiàn)的 bpftool,用于從 BPF 程序生成C頭文件:

../../third_party/bpftool/src/bpftool gen skeleton -j bootstrap.bpf.o > bootstrap.skel.h

由于 eBPF 本身的所有 C 內(nèi)存布局是和當(dāng)前所在機器的指令集一樣的,但是 wasm 是有一套確定的內(nèi)存布局(比如當(dāng)前所在機器是 64 位的,Wasm 虛擬機里面是 32 位的,C struct layout 、指針寬度、大小端等等都可能不一樣),為了確保 eBPF 程序能正確和 Wasm 之間進行相互通信,我們需要定制一個專門的 bpftool 等工具,實現(xiàn)正確生成可以在 Wasm 中工作的用戶態(tài)開發(fā)框架。

skel 包含一個 BPF 程序的skeleton,用于操作 BPF 對象,并控制 BPF 程序的生命周期,例如:

    struct bootstrap_bpf {
        struct bpf_object_skeleton *skeleton;
        struct bpf_object *obj;
        struct {
            struct bpf_map *exec_start;
            struct bpf_map *rb;
            struct bpf_map *rodata;
        } maps;
        struct {
            struct bpf_program *handle_exec;
            struct bpf_program *handle_exit;
        } progs;
        struct bootstrap_bpf__rodata {
            unsigned long long min_duration_ns;
        } *rodata;
        struct bootstrap_bpf__bss {
            uint64_t /* pointer */ name_ptr;
        } *bss;
    };

我們會將所有指針都將根據(jù) eBPF 程序目標(biāo)所在的指令集的指針大小轉(zhuǎn)換為整數(shù),例如,name_ptr。此外,填充字節(jié)將明確添加到結(jié)構(gòu)體中以確保結(jié)構(gòu)體布局與目標(biāo)端相同,例如使用 char __pad0[4];。我們還會使用 static_assert 來確保結(jié)構(gòu)體的內(nèi)存長度和原先 BTF 信息中的類型長度相同。

構(gòu)建用戶態(tài)的 Wasm 代碼,并獲取內(nèi)核態(tài)數(shù)據(jù)

我們默認使用 wasi-sdk 從 C/C++ 代碼構(gòu)建 wasm 二進制文件。您也可以使用 emcc 工具鏈來構(gòu)建 wasm 二進制文件,命令應(yīng)該是相似的。您可以運行以下命令來安裝 wasi-sdk:

wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-17/wasi-sdk-17.0-linux.tar.gz
tar -zxf wasi-sdk-17.0-linux.tar.gz
sudo mkdir -p /opt/wasi-sdk/ && sudo mv wasi-sdk-17.0/* /opt/wasi-sdk/

然后運行 make 會在 Makefile 中使用 wasi-clang 編譯 C 代碼,生成 Wasm 字節(jié)碼:

/opt/wasi-sdk/bin/clang -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined -o bootstrap.wasm bootstrap.c

由于宿主機(或 eBPF 端)的 C 結(jié)構(gòu)布局可能與目標(biāo)(Wasm 端)的結(jié)構(gòu)布局不同,因此您可以使用 ecc 和我們的 wasm-bpftool 生成用戶空間代碼的 C 頭文件:

ecc bootstrap.h --header-only
../../third_party/bpftool/src/bpftool btf dump file bootstrap.bpf.o format c -j > bootstrap.wasm.h

例如,原先內(nèi)核態(tài)的頭文件中結(jié)構(gòu)體定義如下:

struct event {
    int pid;
    int ppid;
    unsigned exit_code;
    unsigned long long duration_ns;
    char comm[TASK_COMM_LEN];
    char filename[MAX_FILENAME_LEN];
    char exit_event;
};

我們的工具會將其轉(zhuǎn)換為:

struct event {
    int pid;
    int ppid;
    unsigned int exit_code;
    char __pad0[4];
    unsigned long long duration_ns;
    char comm[16];
    char filename[127];
    char exit_event;
} __attribute__((packed));
static_assert(sizeof(struct event) == 168, "Size of event is not 168");

注意:此過程和工具并不總是必需的,對于簡單的應(yīng)用,你可以手動完成。對于內(nèi)核態(tài)和 Wasm 應(yīng)用都使用 C/C++ 語言的情況下,你可以手動編寫所有事件結(jié)構(gòu)體定義,使用 __attribute__((packed)) 避免填充字節(jié),并在主機和 wasm 端之間轉(zhuǎn)換所有指針為正確的整數(shù)。所有類型必須在 wasm 中定義與主機端相同的大小和布局。

對于復(fù)雜的程序,手動確認內(nèi)存布局的正確是分困難,因此我們創(chuàng)建了 wasm 特定的 bpftool,用于從 BTF 信息中生成包含所有類型定義和正確結(jié)構(gòu)體布局的 C 頭文件,以便用戶空間代碼使用。可以通過類似的方案,一次性將 eBPF 程序中所有的結(jié)構(gòu)體定義轉(zhuǎn)換為 Wasm 端的內(nèi)存布局,并確保大小端一致,即可正確訪問。

對于 Wasm 中不是由 C 語言進行開發(fā)的情況下,借助 Wasm 的組件模型,我們還可以將這些 BTF 信息結(jié)構(gòu)體定義作為 wit 類型聲明輸出,然后在用戶空間代碼中使用 wit-bindgen 工具一次性生成多種語言(如 C/C++/Rust/Go)的類型定義。這部分會在關(guān)于如何使用 Rust 在 Wasm 中編寫 eBPF 程序的部分詳細描述,我們也會將這些步驟和工具鏈繼續(xù)完善,以改進 Wasm-bpf 程序的編程體驗。

我們?yōu)?wasm 程序提供了一個僅包含頭文件的 libbpf API 庫,您可以在 libbpf-wasm.h(wasm-include/libbpf-wasm.h)中找到它,它包含了一部分 libbpf 常用的用戶態(tài) API 和類型定義。Wasm 程序可以使用 libbpf API 操作 BPF 對象,例如:

/* Load and verify BPF application */
skel = bootstrap_bpf__open();
/* Parameterize BPF code with minimum duration parameter */
skel->rodata->min_duration_ns = env.min_duration_ms * 1000000ULL;
/* Load & verify BPF programs */
err = bootstrap_bpf__load(skel);
/* Attach tracepoints */
err = bootstrap_bpf__attach(skel);

rodata 部分用于存儲 BPF 程序中的常量,這些值將在 bpftool gen skeleton 的時候由代碼生成映射到 object 中正確的偏移量,然后在 open 之后通過內(nèi)存映射修改對應(yīng)的值,因此不需要在 Wasm 中編譯 libelf 庫,運行時仍可動態(tài)加載和操作 BPF 對象。

Wasm 端的 C 代碼與本地 libbpf 代碼略有不同,但它可以從 eBPF 端提供大部分功能,例如,從環(huán)形緩沖區(qū)或 perf 緩沖區(qū)輪詢,從 Wasm 端和 eBPF 端訪問映射,加載、附加和分離 BPF 程序等。它可以支持大量的 eBPF 程序類型和映射,涵蓋從跟蹤、網(wǎng)絡(luò)、安全等方面的大多數(shù) eBPF 程序的使用場景。

由于 Wasm 端缺少一些功能,例如 signal handler 還不支持(2023年2月),原始的C代碼有可能無法直接編譯為 wasm,您需要稍微修改代碼以使其工作。我們將盡最大努力使 wasm 端的 libbpf API 與通常在用戶空間運行的 libbpf API盡可能相似,以便用戶空間代碼可以在未來直接編譯為 wasm。我們還將盡快提供更多語言綁定(Go等)的 wasm 側(cè) eBPF 程序開發(fā)庫。

可以在用戶態(tài)程序中使用 polling API 獲取內(nèi)核態(tài)上傳的數(shù)據(jù)。它將是 ring buffer 和 perf buffer 的一個封裝,用戶空間代碼可以使用相同的 API 從環(huán)形緩沖區(qū)或性能緩沖區(qū)中輪詢事件,具體取決于BPF程序中指定的類型。例如,環(huán)形緩沖區(qū)輪詢定義為BPF_MAP_TYPE_RINGBUF

struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 256 * 1024);
} rb SEC(".maps");

你可以在用戶態(tài)使用以下代碼從 ring buffer 中輪詢事件:

rb = bpf_buffer__open(skel->maps.rb, handle_event, NULL);
/* Process events */
printf("%-8s %-5s %-16s %-7s %-7s %s\n", "TIME", "EVENT", "COMM", "PID",
       "PPID", "FILENAME/EXIT CODE");
while (!exiting) {
    // poll buffer
    err = bpf_buffer__poll(rb, 100 /* timeout, ms */);

ring buffer polling 不需要序列化開銷。bpf_buffer__poll API 將調(diào)用 handle_event 回調(diào)函數(shù)來處理環(huán)形緩沖區(qū)中的事件數(shù)據(jù):


static int
handle_event(void *ctx, void *data, size_t data_sz)
{
    const struct event *e = data;
    ...
    if (e->exit_event) {
        printf("%-8s %-5s %-16s %-7d %-7d [%u]", ts, "EXIT", e->comm, e->pid,
               e->ppid, e->exit_code);
        if (e->duration_ns)
            printf(" (%llums)", e->duration_ns / 1000000);
        printf("\n");
    }
    ...
    return 0;
}

運行時基于 libbpf CO-RE(Compile Once, Run Everywhere)API,用于將 bpf 對象加載到內(nèi)核中,因此 wasm-bpf 程序不受它編譯的內(nèi)核版本的影響,可以在任何支持 BPF CO-RE 的內(nèi)核版本上運行。

從用戶態(tài)程序中訪問和更新 eBPF 程序的 map 數(shù)據(jù)

runqlat 是一個更復(fù)雜的示例,這個程序通過直方圖展示調(diào)度器運行隊列延遲,給我們展現(xiàn)了任務(wù)等了多久才能運行。

$ sudo ./wasm-bpf runqlat.wasm -h
Summarize run queue (scheduler) latency as a histogram.

USAGE: runqlat [--help] [interval] [count]

EXAMPLES:
    runqlat         # summarize run queue latency as a histogram
    runqlat 1 10    # print 1 second summaries, 10 times
$ sudo ./wasm-bpf runqlat.wasm 1

Tracing run queue latency... Hit Ctrl-C to end.

     usecs               : count    distribution
         0 -> 1          : 72       |*****************************           |
         2 -> 3          : 93       |*************************************   |
         4 -> 7          : 98       |****************************************|
         8 -> 15         : 96       |*************************************** |
        16 -> 31         : 38       |***************                         |
        32 -> 63         : 4        |*                                       |
        64 -> 127        : 5        |**                                      |
       128 -> 255        : 6        |**                                      |
       256 -> 511        : 0        |                                        |
       512 -> 1023       : 0        |                                        |
      1024 -> 2047       : 0        |                                        |
      2048 -> 4095       : 1        |                                        |

runqlat 中使用 map API 來從用戶態(tài)訪問內(nèi)核里的 map 并直接讀取數(shù)據(jù),例如:

    while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
        err = bpf_map_lookup_elem(fd, &next_key, &hist);
        ...
        lookup_key = next_key;
    }
    lookup_key = -2;
    while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
        err = bpf_map_delete_elem(fd, &next_key);
        ...
        lookup_key = next_key;
    }

運行時 wasm 代碼將會使用共享內(nèi)存來訪問內(nèi)核 map,內(nèi)核態(tài)可以直接把數(shù)據(jù)拷貝到用戶態(tài) Wasm 虛擬機的堆棧中,而不需要面對用戶態(tài)主機側(cè)程序和 Wasm 運行時之間的額外拷貝開銷。同樣,對于 Wasm 虛擬機和內(nèi)核態(tài)之間共享的類型定義,需要經(jīng)過仔細檢查以確保它們在 Wasm 和內(nèi)核態(tài)中的類型是一致的。

可以使用 bpf_map_update_elem 在用戶態(tài)程序內(nèi)更新內(nèi)核的 eBPF map,比如:

        cg_map_fd = bpf_map__fd(obj->maps.cgroup_map);
        cgfd = open(env.cgroupspath, O_RDONLY);
        if (cgfd < 0) {
            ...
        }
        if (bpf_map_update_elem(cg_map_fd, &idx, &cgfd, BPF_ANY)) {
            ...
        }

因此內(nèi)核的 eBPF 程序可以從 Wasm 側(cè)的程序獲取配置,或者在運行的時候接收消息。

更多的例子:socket filter 和 lsm

在倉庫中,我們還提供了更多的示例,例如使用 socket filter 監(jiān)控和過濾數(shù)據(jù)包:

SEC("socket")
int socket_handler(struct __sk_buff *skb)
{
    struct so_event *e;
    __u8 verlen;
    __u16 proto;
    __u32 nhoff = ETH_HLEN;

    bpf_skb_load_bytes(skb, 12, &proto, 2);
    ...

    bpf_skb_load_bytes(skb, nhoff + 0, &verlen, 1);
    bpf_skb_load_bytes(skb, nhoff + ((verlen & 0xF) << 2), &(e->ports), 4);
    e->pkt_type = skb->pkt_type;
    e->ifindex = skb->ifindex;
    bpf_ringbuf_submit(e, 0);

    return skb->len;
}

Linux Security Modules(LSM)是一個基于鉤子的框架,用于在Linux內(nèi)核中實現(xiàn)安全策略和強制訪問控制。直到現(xiàn)在,能夠?qū)崿F(xiàn)實施安全策略目標(biāo)的方式只有兩種選擇,配置現(xiàn)有的LSM模塊(如AppArmor、SELinux),或編寫自定義內(nèi)核模塊。

Linux Kernel 5.7 引入了第三種方式:LSM eBPF。LSM BPF 允許開發(fā)人員編寫自定義策略,而無需配置或加載內(nèi)核模塊。LSM BPF 程序在加載時被驗證,然后在調(diào)用路徑中,到達LSM鉤子時被執(zhí)行。例如,我們可以在 Wasm 輕量級容器中,使用 lsm 限制文件系統(tǒng)操作:

// all lsm the hook point refer https://www.kernel.org/doc/html/v5.2/security/LSM.html
SEC("lsm/path_rmdir")
int path_rmdir(const struct path *dir, struct dentry *dentry) {
  char comm[16];
  bpf_get_current_comm(comm, sizeof(comm));
  unsigned char dir_name[] = "can_not_rm";
  unsigned char d_iname[32];
  bpf_probe_read_kernel(&d_iname[0], sizeof(d_iname),
                        &(dir->dentry->d_iname[0]));

  bpf_printk("comm %s try to rmdir %s", comm, d_iname);
  for (int i = 0;i<sizeof(dir_name);i++){
    if (d_iname[i]!=dir_name[i]){
        return 0;
    }
  }
  return -1;
}

總結(jié)

本以 C/C++ 語言為例,討論了如何使用 C/C++ 編寫 eBPF 程序并編譯為 Wasm 模塊。更完整的代碼,請參考我們的 Github 倉庫:https://github.com/eunomia-bpf/wasm-bpf.

在下一篇文章中,我們會討論使用 Rust 編寫 eBPF 程序并編譯為 Wasm 模塊,并使用 OCI 鏡像發(fā)布、部署、管理 eBPF 程序,獲得類似 Docker 的體驗。

接下來,我們也會繼續(xù)完善在 Wasm 中使用多種語言開發(fā)和運行 eBPF 程序的體驗,提供更完善的示例和用戶態(tài)開發(fā)庫/工具鏈,以及更具體的應(yīng)用場景。

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容