eBPF-Chap3

eBPF程序结构

本章展示了eBPF程序从源代码到执行的整个过程中经历的各个阶段

  • eBPF程序是一组eBPF字节码指令,可以直接使用eBPF字节码编写,但是一般来说使用高级语言编写会更轻松

eBPF 虚拟机

  • 计算机软件实现
  • 以eBPF字节码(由一组指令组成)指令的形式接受程序,这些指令必须转换为在CPU上运行的本地机器指令。

eBPF寄存器

  • 使用10个通用寄存器(0-9)
  • 寄存器10用作栈指针
  • eBPF程序的上下文参数在开始执行之前加载到寄存器1中,函数的返回值存储在寄存器0
  • eBPF调用函数之前,参数放到寄存器1-5中(传递参数)

eBPF指令

eBPF指令的结构:

1
2
3
4
5
6
7
struct bpf_insn {
__u8 code; /* opcode */
__u8 dst_reg:4; /* dest register */
__u8 src_reg:4; /* source register */
__s16 off; /* signed offset */
__s32 imm; /* signed immediate constant */
};
  • 如果一条指令长度超过了8byte,则可以使用宽指令编码(wide instruction encoding)
  • 操作码类别:
    • 将值写入寄存器(立即数或从内存中读取的值或从其他寄存器中读取的值)
    • 存储寄存器中的值到内存中
    • 进行数值计算
    • 跳转到其他指令(满足一定条件时)

例子

example1:

当网络数据包到达时触发它并写入一行跟踪

XDP 的返回值
在 XDP 程序中,返回值决定了数据包的处理方式,常用的返回值包括:

  • XDP_DROP: 丢弃数据包。
  • XDP_PASS: 将数据包传递给内核网络栈进行正常处理。
  • XDP_TX: 数据包直接回送到接收的网卡(即本地发送)。
  • XDP_REDIRECT: 将数据包重定向到另一个网络接口或用户空间。

程序解释:
SEC("edp") 声明这是一个XDP程序,运行在网络驱动处理的阶段
bpf_printk() 用于向内核日志打印调试信息

编译

可以使用clang来编译
makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
TARGETS = hello hello-func

all: $(TARGETS)

.PHONY: all

$(TARGETS): %: %.bpf.o

%.bpf.o: %.bpf.c

    clang \
        -target bpf \
        -I/usr/include/$(shell uname -m)-linux-gnu \
        -g \
        -O2 -o $@ -c $<
clean:
    - rm *.bpf.o
    - rm -f /sys/fs/bpf/hello
    - rm -f /sys/fs/bpf/hello-func

检查eBPF对象文件

file hello.bpf.o 命令可以用于查看文件的内容

1
2
root@PQCU:~/eBPF/learning-ebpf/chapter3# file hello.bpf.o
hello.bpf.o: ELF 64-bit LSB relocatable, eBPF, version 1 (SYSV), with debug_info, not stripped

表明它是一个ELF文件,包含eBPF代码,适用于64bit架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
root@PQCU:~/eBPF/learning-ebpf/chapter3# llvm-objdump -S hello.bpf.o

hello.bpf.o: file format elf64-bpf

Disassembly of section xdp:

0000000000000000 <hello>:
; int hello(struct xdp_md *ctx) {
0: b7 01 00 00 00 00 00 00 r1 = 0
; bpf_printk("Hello World %d", counter);
1: 73 1a fe ff 00 00 00 00 *(u8 *)(r10 - 2) = r1
2: b7 01 00 00 25 64 00 00 r1 = 25637
3: 6b 1a fc ff 00 00 00 00 *(u16 *)(r10 - 4) = r1
4: b7 01 00 00 72 6c 64 20 r1 = 543452274
5: 63 1a f8 ff 00 00 00 00 *(u32 *)(r10 - 8) = r1
6: 18 01 00 00 48 65 6c 6c 00 00 00 00 6f 20 57 6f r1 = 8022916924116329800 ll
8: 7b 1a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r1
9: 18 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r6 = 0 ll
11: 61 63 00 00 00 00 00 00 r3 = *(u32 *)(r6 + 0)
12: bf a1 00 00 00 00 00 00 r1 = r10
13: 07 01 00 00 f0 ff ff ff r1 += -16
; bpf_printk("Hello World %d", counter);
14: b7 02 00 00 0f 00 00 00 r2 = 15
15: 85 00 00 00 06 00 00 00 call 6
; counter++;
16: 61 61 00 00 00 00 00 00 r1 = *(u32 *)(r6 + 0)
17: 07 01 00 00 01 00 00 00 r1 += 1
18: 63 16 00 00 00 00 00 00 *(u32 *)(r6 + 0) = r1
; return XDP_PASS;
19: b7 00 00 00 02 00 00 00 r0 = 2
20: 95 00 00 00 00 00 00 00 exit
  • llvm-objdump用于显示目标文件或可执行文件信息;可以显示文件头信息、符号表、反汇编代码等。

eBPF-Chap3
https://pqcu77.github.io/2025/04/10/eBPF-chap3/
作者
linqt
发布于
2025年4月10日
许可协议