簡體   English   中英

ebpf:如何在 eBPF 匯編程序中使用 BPF_FUNC_trace_printk

[英]ebpf: how to use BPF_FUNC_trace_printk in eBPF assembly program

我有一個小型socket filter類型的 eBPF 程序,我正在嘗試打印從__sk_buff上下文讀取的協議值:

struct bpf_insn prog[] = {
   BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
   BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, offsetof(struct __sk_buff, protocol)),
   BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4),
   BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
   BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -4),
   BPF_MOV64_IMM(BPF_REG_2, 4),
   BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_trace_printk),
   BPF_MOV64_IMM(BPF_REG_0, 0),
   BPF_EXIT_INSN(),
};

...

我創建了一個原始socket並將其綁定到lo接口,然后是setsockopt(fd, SOL_SOCKET, SO_ATTACH_BPF, ...) 它可以毫無問題地編譯和加載,但是每當我ping 127.0.0.1時,我從未在trace_pipe中看到任何痕跡。

因此,為了確保它BPF_FUNC_trace_printk確實可以工作,我對其進行了更改,以便它在堆棧上打印 static 字符串,並在每個到達環回的數據包上打印。

我究竟做錯了什么?

閱讀友好的手冊:)

我不相信您正確調用了bpf_trace_printk()助手(順便說一句, BPF_FUNC_trace_prink只是一個 integer)。 它的簽名在 kernel UAPI header bpf.h 或bpf-helpers手冊頁中注釋如下:

long bpf_trace_printk(const char *fmt, u32 fmt_size, ...);

這意味着第一個參數必須是一個常量、以 null 結尾的格式字符串,而不是像你這樣的 integer。

clang 有什么作用?

我了解您將 eBPF 程序附加到 sockets 並且無法從 C 編譯整個程序。 但是,為什么不將該特定部分編譯為通用網絡 eBPF 程序以查看字節碼應該是什么樣子呢? 讓我們編寫 C 代碼:

#include <linux/bpf.h>

static long (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) BPF_FUNC_trace_printk;

int printk_proto(struct __sk_buff *skb) {
    char fmt[] = "%d\n";

    bpf_trace_printk(fmt, sizeof(fmt), skb->protocol);

    return 0;
}

編譯為 object 文件。 作為記錄,這不會加載,除非我們在加載時同時提供有效的許可證字符串(因為bpf_trace_prink()需要兼容 GPL 的程序)和兼容的程序類型。 但在我們的例子中沒關系,我們只想看看生成的指令。

$ clang -O2 -g -emit-llvm -c prink_protocol.c  -o - | \
        llc -march=bpf -mcpu=probe -filetype=obj -o prink_protocol.o 

轉儲字節碼:

$ llvm-objdump -d prink_protocol.o 

prink_protocol.o:       file format elf64-bpf


Disassembly of section .text:

0000000000000000 <printk_proto>:
       0:       b4 02 00 00 25 64 0a 00 w2 = 680997
       1:       63 2a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r2
       2:       61 13 10 00 00 00 00 00 r3 = *(u32 *)(r1 + 16)
       3:       bf a1 00 00 00 00 00 00 r1 = r10
       4:       07 01 00 00 fc ff ff ff r1 += -4
       5:       b4 02 00 00 04 00 00 00 w2 = 4
       6:       85 00 00 00 06 00 00 00 call 6
       7:       b4 00 00 00 00 00 00 00 w0 = 0
       8:       95 00 00 00 00 00 00 00 exit

我們可以看到,在前兩條指令中,程序將格式字符串(小端序)寫入堆棧: 680997 is 0x000a6425 , \0\nd% r2仍然包含格式字符串的長度。 協議值存儲在r3中,它是調用bpf_trace_prink()的第三個參數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM