1 Linux 实现说明

本文档提供了有关 eBPF 指令集 Linux 内核实现的更多具体细节。

1.1 字节交换指令

BPF_FROM_LEBPF_FROM_BE 分别是 BPF_TO_LEBPF_TO_BE 的别名。

1.2 跳转指令

BPF_CALL | BPF_X | BPF_JMP (0x8d),其中辅助函数整数将从指定的寄存器中读取,验证器目前不支持。任何包含此指令的程序都将无法加载,直到添加此支持。

1.3 映射

Linux 仅支持在具有单个元素的数组映射上进行“map_val(map)”操作。

Linux 使用 fd_array 来存储与 BPF 程序关联的映射。因此,map_by_idx(imm) 使用数组中该索引处的 fd。

1.4 变量

以下 64 位立即数指令指定应加载与“imm”字段中存储的某个整数对应的变量地址

操作码构造

操作码

src

伪代码

imm 类型

dst 类型

BPF_IMM | BPF_DW | BPF_LD

0x18

0x3

dst = var_addr(imm)

变量 ID

数据指针

在 Linux 上,此整数是 BTF ID。

1.5 旧式 BPF 数据包访问指令

正如ISA 标准文档中所述,Linux 具有特殊的 eBPF 指令,用于访问从经典 BPF 延续下来的数据包数据,以保留在 eBPF 解释器中运行的旧式套接字过滤器的性能。

这些指令有两种形式:BPF_ABS | <size> | BPF_LDBPF_IND | <size> | BPF_LD

这些指令用于访问数据包数据,并且仅当程序上下文是指向网络数据包的指针时才能使用。BPF_ABS 在由立即数数据指定的绝对偏移量处访问数据包数据,而 BPF_IND 在包含寄存器值以及立即数数据的偏移量处访问数据包数据。

这些指令有七个隐式操作数

  • 寄存器 R6 是一个隐式输入,必须包含指向 struct sk_buff 的指针。

  • 寄存器 R0 是一个隐式输出,其中包含从数据包中获取的数据。

  • 寄存器 R1-R5 是被指令破坏的暂存寄存器。

这些指令也具有隐式程序退出条件。如果 eBPF 程序尝试访问超出数据包边界的数据,则程序执行将中止。

BPF_ABS | BPF_W | BPF_LD (0x20) 表示

R0 = ntohl(*(u32 *) ((struct sk_buff *) R6->data + imm))

其中 ntohl() 将 32 位值从网络字节顺序转换为主机字节顺序。

BPF_IND | BPF_W | BPF_LD (0x40) 表示

R0 = ntohl(*(u32 *) ((struct sk_buff *) R6->data + src + imm))