1 Linux 实现注意事项¶
本文档提供了 eBPF 指令集在 Linux 内核中实现的更多具体细节。
1.1 字节交换指令¶
BPF_FROM_LE
和 BPF_FROM_BE
分别作为 BPF_TO_LE
和 BPF_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’ 字段中存储的某个整数
操作码结构 |
操作码 |
源 |
伪代码 |
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_LD
和 BPF_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))