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

作者:于桐撑螺,鄭昱笙

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

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

現(xiàn)在,借助 Wasm-bpf 編譯工具鏈和運行時佛纫,我們可以使用 Wasm 將 eBPF 程序編寫為跨平臺的模塊妓局,同時使用 C/C++ 或 Rust 來編寫 Wasm 程序。通過在 WebAssembly 中使用 eBPF 程序雳旅,我們不僅能讓 Wasm 應用享受到 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)接口的抽象,并提供了一套對應的開發(fā)工具鏈、庫以及通用的 Wasm + eBPF 運行時實例谆吴。它可以提供和 libbpf-bootstrap 相似的開發(fā)體驗倒源,自動生成對應的 skeleton 頭文件,以及用于在 Wasm 和 eBPF 之間無序列化通信的數(shù)據(jù)結(jié)構(gòu)定義句狼。你可以非常容易地使用任何語言笋熬,在任何平臺上建立你自己的 Wasm-eBPF 運行時,使用相同的工具鏈來構(gòu)建應用腻菇。更詳細的介紹胳螟,請參考我們的上一篇博客:Wasm-bpf: 架起 Webassembly 和 eBPF 內(nèi)核可編程的橋梁

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

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

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

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

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

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

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

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

bootstrap

bootstrap是一個簡單(但實用)的BPF應用程序的例子贫橙。它跟蹤進程的啟動(準確地說贪婉,是 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)建的,但它被設計成更獨立的幅垮,并且有更簡單的 Makefile 以簡化用戶的特殊需求腰池。它演示了典型的BPF特性,包含使用多個 BPF 程序段進行合作,使用 BPF map 來維護狀態(tài),使用 BPF ring buffer 來發(fā)送數(shù)據(jù)到用戶空間坦袍,以及使用全局變量來參數(shù)化應用程序行為捅厂。

以下是我們使用 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 風格的 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)存布局是和當前所在機器的指令集一樣的,但是 wasm 是有一套確定的內(nèi)存布局(比如當前所在機器是 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 程序目標所在的指令集的指針大小轉(zhuǎn)換為整數(shù),例如晰奖,name_ptr谈撒。此外,填充字節(jié)將明確添加到結(jié)構(gòu)體中以確保結(jié)構(gòu)體布局與目標端相同匾南,例如使用 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 二進制文件,命令應該是相似的豹爹。您可以運行以下命令來安裝 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)布局可能與目標(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");

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

對于復雜的程序,手動確認內(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)存映射修改對應的值受啥,因此不需要在 Wasm 中編譯 libelf 庫做个,運行時仍可動態(tài)加載和操作 BPF 對象。

Wasm 端的 C 代碼與本地 libbpf 代碼略有不同滚局,但它可以從 eBPF 端提供大部分功能居暖,例如,從環(huán)形緩沖區(qū)或 perf 緩沖區(qū)輪詢藤肢,從 Wasm 端和 eBPF 端訪問映射太闺,加載、附加和分離 BPF 程序等谤草。它可以支持大量的 eBPF 程序類型和映射跟束,涵蓋從跟蹤莺奸、網(wǎng)絡丑孩、安全等方面的大多數(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 是一個更復雜的示例载矿,這個程序通過直方圖展示調(diào)度器運行隊列延遲,給我們展現(xiàn)了任務等了多久才能運行烹卒。

$ 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)實施安全策略目標的方式只有兩種選擇辖众,配置現(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ā)庫/工具鏈,以及更具體的應用場景桐汤。

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怔毛,一起剝皮案震驚了整個濱河市员萍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拣度,老刑警劉巖碎绎,帶你破解...
    沈念sama閱讀 212,686評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抗果,居然都是意外死亡筋帖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評論 3 385
  • 文/潘曉璐 我一進店門冤馏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來日麸,“玉大人,你說我怎么就攤上這事逮光〈” “怎么了墩划?”我有些...
    開封第一講書人閱讀 158,160評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嗡综。 經(jīng)常有香客問我乙帮,道長,這世上最難降的妖魔是什么蛤高? 我笑而不...
    開封第一講書人閱讀 56,736評論 1 284
  • 正文 為了忘掉前任蚣旱,我火速辦了婚禮碑幅,結(jié)果婚禮上戴陡,老公的妹妹穿的比我還像新娘。我一直安慰自己沟涨,他們只是感情好恤批,可當我...
    茶點故事閱讀 65,847評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著裹赴,像睡著了一般喜庞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上棋返,一...
    開封第一講書人閱讀 50,043評論 1 291
  • 那天延都,我揣著相機與錄音,去河邊找鬼睛竣。 笑死晰房,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的射沟。 我是一名探鬼主播殊者,決...
    沈念sama閱讀 39,129評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼验夯!你這毒婦竟也來了猖吴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,872評論 0 268
  • 序言:老撾萬榮一對情侶失蹤挥转,失蹤者是張志新(化名)和其女友劉穎海蔽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绑谣,經(jīng)...
    沈念sama閱讀 44,318評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡党窜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,645評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了域仇。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刑然。...
    茶點故事閱讀 38,777評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖暇务,靈堂內(nèi)的尸體忽然破棺而出泼掠,到底是詐尸還是另有隱情怔软,我是刑警寧澤,帶...
    沈念sama閱讀 34,470評論 4 333
  • 正文 年R本政府宣布择镇,位于F島的核電站挡逼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏腻豌。R本人自食惡果不足惜家坎,卻給世界環(huán)境...
    茶點故事閱讀 40,126評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吝梅。 院中可真熱鬧虱疏,春花似錦、人聲如沸苏携。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,861評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽右冻。三九已至装蓬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纱扭,已是汗流浹背牍帚。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乳蛾,地道東北人暗赶。 一個月前我還...
    沈念sama閱讀 46,589評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像屡久,于是被迫代替她去往敵國和親忆首。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,687評論 2 351

推薦閱讀更多精彩內(nèi)容