1 BPF 指令集架构 (ISA)

eBPF,通常也称为 BPF,是一项起源于 Linux 内核的技术,可以在特权上下文中(例如操作系统内核)运行不受信任的程序。本文档规定了 BPF 指令集架构 (ISA)。

作为历史注释,BPF 最初代表伯克利数据包过滤器,但现在它可以做的数据包过滤之外的事情太多了,这个首字母缩略词不再有意义。 BPF 现在被认为是一个独立术语,不代表任何东西。原始 BPF 有时被称为 cBPF(经典 BPF),以区别于现在广泛部署的 eBPF(扩展 BPF)。

1.1 文档约定

本文档中的关键词“必须 (MUST)”、“不得 (MUST NOT)”、“必需 (REQUIRED)”、“应该 (SHALL)”、“不应该 (SHALL NOT)”、“应当 (SHOULD)”、“不应当 (SHOULD NOT)”、“推荐 (RECOMMENDED)”、“不推荐 (NOT RECOMMENDED)”、“可以 (MAY)”和“可选 (OPTIONAL)”应按照 BCP 14 https://www.rfc-editor.org/info/rfc2119 https://www.rfc-editor.org/info/rfc8174 中的描述进行解释,当且仅当它们全部以大写形式出现时,如此处所示。

为了简洁和一致,本文档使用简写语法来引用类型族,并在描述指令的语义时引用几个说明性的助记函数。这些类型的有效值范围和这些函数的语义在以下小节中定义。

1.1.1 类型

本文档使用符号 SN 来引用整数类型,以分别指定类型的符号 (S) 和位宽 (N)。

符号表示法的含义

S

含义

u

无符号

s

有符号

位宽表示法的含义

N

位宽

8

8 位

16

16 位

32

32 位

64

64 位

128

128 位

例如,u32 是一种类型,其有效值是所有 32 位无符号数,而 s16 是一种类型,其有效值是所有 16 位有符号数。

1.1.2 函数

以下字节交换函数与方向无关。也就是说,对于下面讨论的任一方向的转换,都使用相同的函数。

  • be16:接受一个无符号 16 位数字,并在主机字节顺序和大端 (IEN137) 字节顺序之间进行转换。

  • be32:接受一个无符号 32 位数字,并在主机字节顺序和大端字节顺序之间进行转换。

  • be64:接受一个无符号 64 位数字,并在主机字节顺序和大端字节顺序之间进行转换。

  • bswap16:接受一个大端或小端格式的无符号 16 位数字,并返回等效的数字,但位宽相同,但字节序相反。

  • bswap32:接受一个大端或小端格式的无符号 32 位数字,并返回等效的数字,但位宽相同,但字节序相反。

  • bswap64:接受一个大端或小端格式的无符号 64 位数字,并返回等效的数字,但位宽相同,但字节序相反。

  • le16:接受一个无符号 16 位数字,并在主机字节顺序和小端字节顺序之间进行转换。

  • le32:接受一个无符号 32 位数字,并在主机字节顺序和小端字节顺序之间进行转换。

  • le64:接受一个无符号 64 位数字,并在主机字节顺序和小端字节顺序之间进行转换。

1.1.3 定义

符号扩展

要将 X 位数字 A 符号扩展为 Y 位数字 B,表示

  1. A 中的所有 X 位复制到 B 的较低 X 位。

  2. B 的剩余 Y - X 位的值设置为 A 的最高有效位的值。

示例

在大端平台上将 8 位数字 A 符号扩展为 16 位数字 B

A:          10000110
B: 11111111 10000110

1.1.4 一致性组

实现不需要支持本文档中指定的所有指令(例如,已弃用的指令)。相反,指定了多个一致性组。实现必须支持 base32 一致性组,并且可以支持其他一致性组,其中支持一致性组意味着它必须支持该一致性组中的所有指令。

命名一致性组的使用实现了执行指令的运行时与生成运行时指令的工具(如编译器)之间的互操作性。因此,对一致性组的能力发现可能由用户手动完成,也可能由工具自动完成。

每个一致性组都有一个简短的 ASCII 标签(例如,“base32”),对应于一组强制性指令。也就是说,每个指令都有一个或多个它所属的一致性组。

本文档定义了以下一致性组

  • base32:包括本规范中定义的所有指令,除非另有说明。

  • base64:包括 base32,以及明确指出属于 base64 一致性组的指令。

  • atomic32:包括 32 位原子操作指令(参见原子操作)。

  • atomic64:包括 atomic32,以及 64 位原子操作指令。

  • divmul32:包括 32 位除法、乘法和模数指令。

  • divmul64:包括 divmul32,以及 64 位除法、乘法和模数指令。

  • packet:已弃用的数据包访问指令。

1.2 指令编码

BPF 有两种指令编码

  • 基本指令编码,使用 64 位来编码指令

  • 宽指令编码,在基本指令之后附加第二个 64 位,总共 128 位。

1.2.1 基本指令编码

基本指令编码如下

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    opcode     |     regs      |            offset             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                              imm                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
操作码

要执行的操作,编码如下

+-+-+-+-+-+-+-+-+
|specific |class|
+-+-+-+-+-+-+-+-+
特定的

这些位的格式因指令类而异

指令类(参见指令类

寄存器

源寄存器和目标寄存器号,在小端主机上编码如下

+-+-+-+-+-+-+-+-+
|src_reg|dst_reg|
+-+-+-+-+-+-+-+-+

在大端主机上编码如下

+-+-+-+-+-+-+-+-+
|dst_reg|src_reg|
+-+-+-+-+-+-+-+-+
src_reg

源寄存器号 (0-10),除非另有说明(64 位立即数指令将此字段重用于其他目的)

dst_reg

目标寄存器号 (0-10),除非另有说明(未来的指令可能会将此字段重用于其他目的)

偏移量

与指针运算一起使用的有符号整数偏移量,除非另有说明(某些算术指令将此字段重用于其他目的)

imm

有符号整数立即数值

请注意,多字节字段(“offset”和“imm”)的内容在使用大端字节顺序的主机上使用大端字节顺序存储,而在使用小端字节顺序的主机上使用小端字节顺序存储。

例如

opcode                  offset imm          assembly
       src_reg dst_reg
07     0       1        00 00  44 33 22 11  r1 += 0x11223344 // little
       dst_reg src_reg
07     1       0        00 00  11 22 33 44  r1 += 0x11223344 // big

请注意,大多数指令不使用所有字段。未使用的字段应清除为零。

1.2.2 宽指令编码

某些指令被定义为使用宽指令编码,它使用两个 32 位立即数值。基本指令格式之后的 64 位包含一个伪指令,其中 “opcode”、“dst_reg”、“src_reg” 和 “offset” 全部设置为零。

这在下图中描述

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    opcode     |     regs      |            offset             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                              imm                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           reserved                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           next_imm                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
操作码

要执行的操作,如上所述编码

寄存器

源寄存器号和目标寄存器号(除非另有说明),如上所述编码

偏移量

与指针运算一起使用的有符号整数偏移量,除非另有说明

imm

有符号整数立即数值

保留

未使用,设置为零

next_imm

第二个有符号整数立即数值

1.2.3 指令类

“opcode” 字段的三个最低有效位存储指令类

指令类

描述

参考

LD

0x0

非标准加载操作

加载和存储指令

LDX

0x1

加载到寄存器操作

加载和存储指令

ST

0x2

从立即数存储操作

加载和存储指令

STX

0x3

从寄存器存储操作

加载和存储指令

ALU

0x4

32 位算术运算

算术和跳转指令

JMP

0x5

64 位跳转操作

算术和跳转指令

JMP32

0x6

32 位跳转操作

算术和跳转指令

ALU64

0x7

64 位算术运算

算术和跳转指令

1.3 算术和跳转指令

对于算术和跳转指令 (ALUALU64JMPJMP32),8 位 “opcode” 字段分为三个部分

+-+-+-+-+-+-+-+-+
|  code |s|class|
+-+-+-+-+-+-+-+-+
代码

操作码,其含义因指令类而异

s(源)

源操作数位置,除非另有说明,否则为以下之一

源操作数位置

来源

描述

K

0

使用 32 位 “imm” 值作为源操作数

X

1

使用 “src_reg” 寄存器值作为源操作数

指令类

指令类(参见指令类

1.3.1 算术指令

ALU 使用 32 位宽操作数,而 ALU64 使用 64 位宽操作数进行其他相同的操作。ALU64 指令属于 base64 一致性组,除非另有说明。“code” 字段编码如下操作,其中 “src” 指的是源操作数,“dst” 指的是目标寄存器的值。

算术指令

名称

代码

偏移量

描述

ADD

0x0

0

dst += src

SUB

0x1

0

dst -= src

MUL

0x2

0

dst *= src

DIV

0x3

0

dst = (src != 0) ? (dst / src) : 0

SDIV

0x3

1

dst = (src == 0) ? 0 : ((src == -1 && dst == LLONG_MIN) ? LLONG_MIN : (dst s/ src))

OR

0x4

0

dst |= src

AND

0x5

0

dst &= src

LSH

0x6

0

dst <<= (src & mask)

RSH

0x7

0

dst >>= (src & mask)

NEG

0x8

0

dst = -dst

MOD

0x9

0

dst = (src != 0) ? (dst % src) : dst

SMOD

0x9

1

dst = (src == 0) ? dst : ((src == -1 && dst == LLONG_MIN) ? 0: (dst s% src))

XOR

0xa

0

dst ^= src

MOV

0xb

0

dst = src

MOVSX

0xb

8/16/32

dst = (s8,s16,s32)src

ARSH

0xc

0

符号扩展 dst >>= (src & mask)

END

0xd

0

字节交换操作(参见下面的字节交换指令

算术运算期间允许下溢和溢出,这意味着 64 位或 32 位值将环绕。如果 BPF 程序执行导致除以零,则目标寄存器将设置为零。否则,对于 ALU64,如果执行将导致 LLONG_MIN 除以 -1,则目标寄存器将设置为 LLONG_MIN。对于 ALU,如果执行将导致 INT_MIN 除以 -1,则目标寄存器将设置为 INT_MIN

如果执行将导致模数为零,则对于 ALU64,目标寄存器的值不变,而对于 ALU,目标寄存器的较高 32 位将清零。否则,对于 ALU64,如果执行将导致 LLONG_MIN 模数 -1,则目标寄存器将设置为 0。对于 ALU,如果执行将导致 INT_MIN 模数 -1,则目标寄存器将设置为 0。

{ADD, X, ALU},其中 “code” = ADD,“source” = X 并且 “class” = ALU 表示

dst = (u32) ((u32) dst + (u32) src)

其中 “(u32)” 表示较高 32 位已清零。

{ADD, X, ALU64} 表示

dst = dst + src

{XOR, K, ALU} 表示

dst = (u32) dst ^ (u32) imm

{XOR, K, ALU64} 表示

dst = dst ^ imm

请注意,大多数算术指令的 “offset” 设置为 0。只有三个指令 (SDIVSMODMOVSX) 具有非零 “offset”。

对于 ALU 的除法、乘法和模数运算是 “divmul32” 一致性组的一部分,对于 ALU64 的除法、乘法和模数运算是 “divmul64” 一致性组的一部分。除法和模数运算支持无符号和有符号形式。

对于无符号运算 (DIVMOD),对于 ALU,“imm” 被解释为 32 位无符号值。对于 ALU64,“imm” 首先从 32 位符号扩展到 64 位,然后被解释为 64 位无符号值。

对于有符号运算 (SDIVSMOD),对于 ALU,“imm” 被解释为 32 位有符号值。对于 ALU64,“imm” 首先从 32 位符号扩展到 64 位,然后被解释为 64 位有符号值。

请注意,当被除数或除数为负数时,有符号模数运算有不同的定义,其中实现通常因语言而异,因此 Python、Ruby 等与 C、Go、Java 等不同。此规范要求有符号模数必须使用截断除法(其中 -13 % 3 == -1),如 C、Go 等中实现的那样。

a % n = a - n * trunc(a / n)

MOVSX 指令执行带有符号扩展的移动操作。{MOVSX, X, ALU} 将 8 位和 16 位操作数符号扩展为 32 位操作数,并将剩余的较高 32 位清零。{MOVSX, X, ALU64} 将 8 位、16 位和 32 位操作数符号扩展为 64 位操作数。与其他算术指令不同,MOVSX 仅为寄存器源操作数 (X) 定义。

{MOV, K, ALU64} 表示

dst = (s64)imm

{MOV, X, ALU} 表示

dst = (u32)src

{MOVSX, X, ALU} 且 “offset” 为 8 表示

dst = (u32)(s32)(s8)src

NEG 指令仅在源位清除 (K) 时定义。

移位操作对于 64 位操作使用 0x3F (63) 的掩码,对于 32 位操作使用 0x1F (31) 的掩码。

1.3.2 字节交换指令

字节交换指令使用 ALUALU64 的指令类,以及 END 的 4 位 “code” 字段。

字节交换指令仅对目标寄存器进行操作,不使用单独的源寄存器或立即数值。

对于 ALU,操作码中的 1 位源操作数字段用于选择操作转换自或转换到的字节顺序。对于 ALU64,操作码中的 1 位源操作数字段保留,必须设置为 0。

字节交换指令

来源

描述

ALU

LE

0

在主机字节顺序和小端之间转换

ALU

BE

1

在主机字节顺序和大端之间转换

ALU64

保留

0

无条件地进行字节交换

“imm” 字段编码交换操作的宽度。支持以下宽度:16、32 和 64。宽度 64 操作属于 base64 一致性组,其他交换操作属于 base32 一致性组。

示例

{END, LE, ALU} 且 “imm” = 16/32/64 表示

dst = le16(dst)
dst = le32(dst)
dst = le64(dst)

{END, BE, ALU} 且 “imm” = 16/32/64 表示

dst = be16(dst)
dst = be32(dst)
dst = be64(dst)

{END, TO, ALU64} 且 “imm” = 16/32/64 表示

dst = bswap16(dst)
dst = bswap32(dst)
dst = bswap64(dst)

1.3.3 跳转指令

JMP32 使用 32 位宽操作数并指示 base32 一致性组,而 JMP 使用 64 位宽操作数进行其他相同的操作,并指示 base64 一致性组,除非另有说明。“code” 字段编码如下操作

跳转指令

代码

src_reg

描述

备注

JA

0x0

0x0

PC += 偏移量

仅限 {JA, K, JMP}

JA

0x0

0x0

PC += imm

仅限 {JA, K, JMP32}

JEQ

0x1

任何

如果 dst == src,则 PC += 偏移量

JGT

0x2

任何

如果 dst > src,则 PC += 偏移量

无符号

JGE

0x3

任何

如果 dst >= src,则 PC += 偏移量

无符号

JSET

0x4

任何

如果 dst & src,则 PC += 偏移量

JNE

0x5

任何

如果 dst != src,则 PC += 偏移量

JSGT

0x6

任何

如果 dst > src,则 PC += 偏移量

有符号

JSGE

0x7

任何

如果 dst >= src,则 PC += 偏移量

有符号

CALL

0x8

0x0

按静态 ID 调用辅助函数

仅限 {CALL, K, JMP},请参见辅助函数

CALL

0x8

0x1

调用 PC += imm

仅限 {CALL, K, JMP},请参见程序本地函数

CALL

0x8

0x2

按 BTF ID 调用辅助函数

仅限 {CALL, K, JMP},请参见辅助函数

EXIT

0x9

0x0

返回

仅限 {CALL, K, JMP}

JLT

0xa

任何

如果 dst < src,则 PC += 偏移量

无符号

JLE

0xb

任何

如果 dst <= src,则 PC += 偏移量

无符号

JSLT

0xc

任何

如果 dst < src,则 PC += 偏移量

有符号

JSLE

0xd

任何

如果 dst <= src,则 PC += 偏移量

有符号

其中 “PC” 表示程序计数器,要递增的偏移量以 64 位指令为单位,相对于跳转指令之后的指令。因此,“PC += 1” 会跳过下一条指令的执行(如果它是基本指令),如果下一条指令是 128 位宽指令,则会导致未定义的行为。

示例

{JSGE, X, JMP32} 表示

if (s32)dst s>= (s32)src goto +offset

其中 “s>=” 表示有符号 “>=” 比较。

{JLE, K, JMP} 表示

if dst <= (u64)(s64)imm goto +offset

{JA, K, JMP32} 表示

gotol +imm

其中 “imm” 表示分支偏移量来自 “imm” 字段。

请注意,JA 指令有两种形式。JMP 类允许由 “offset” 字段指定的 16 位跳转偏移量,而 JMP32 类允许由 “imm” 字段指定的 32 位跳转偏移量。大于 16 位的条件跳转可以转换为小于 16 位的条件跳转加上 32 位无条件跳转。

所有 CALLJA 指令都属于 base32 一致性组。

1.3.3.1 辅助函数

辅助函数是一个概念,BPF 程序可以通过它调用底层平台公开的一组函数调用。

从历史上看,每个辅助函数都由 “imm” 字段中编码的静态 ID 标识。辅助函数的进一步文档不在本文档的范围内,标准化留给未来的工作,但使用已广泛部署,更多信息可以在特定于平台的文档中找到(例如,Linux 内核文档)。

支持 BPF 类型格式 (BTF) 的平台支持通过编码在 ‘imm’ 字段中的 BTF ID 来识别辅助函数,其中 BTF ID 标识辅助函数的名称和类型。BTF 的进一步文档不在本文档的范围内,标准化留待未来进行,但其使用已得到广泛部署,更多信息可以在特定于平台的文档中找到 (例如,Linux 内核文档)。

1.3.3.2 程序本地函数

程序本地函数是由与调用者相同的 BPF 程序公开的函数,并通过相对于调用指令后一条指令的偏移量来引用,类似于 JA。偏移量编码在调用指令的 ‘imm’ 字段中。程序本地函数中的 EXIT 将返回到调用者。

1.4 加载和存储指令

对于加载和存储指令 (LDLDXSTSTX),8 位 ‘opcode’ 字段的划分如下

+-+-+-+-+-+-+-+-+
|mode |sz |class|
+-+-+-+-+-+-+-+-+
模式

模式修饰符是以下之一

模式修饰符

模式修饰符

描述

参考

IMM

0

64 位立即数指令

64 位立即数指令

ABS

1

传统 BPF 数据包访问(绝对)

传统 BPF 数据包访问指令

IND

2

传统 BPF 数据包访问(间接)

传统 BPF 数据包访问指令

MEM

3

常规加载和存储操作

常规加载和存储操作

MEMSX

4

符号扩展加载操作

符号扩展加载操作

ATOMIC

6

原子操作

原子操作

sz(大小)

大小修饰符是以下之一

大小修饰符

大小

描述

W

0

字(4 字节)

H

1

半字(2 字节)

B

2

字节

DW

3

双字(8 字节)

使用 DW 的指令属于 base64 一致性组。

指令类(参见指令类

1.4.1 常规加载和存储操作

MEM 模式修饰符用于编码在寄存器和内存之间传输数据的常规加载和存储指令。

{MEM, <size>, STX} 表示

*(size *) (dst + offset) = src

{MEM, <size>, ST} 表示

*(size *) (dst + offset) = imm

{MEM, <size>, LDX} 表示

dst = *(unsigned size *) (src + offset)

其中 ‘<size>’ 是以下之一:BHWDW,并且 ‘无符号大小’ 是以下之一:u8、u16、u32 或 u64。

1.4.2 符号扩展加载操作

MEMSX 模式修饰符用于编码在寄存器和内存之间传输数据的符号扩展加载指令。

{MEMSX, <size>, LDX} 表示

dst = *(signed size *) (src + offset)

其中 ‘<size>’ 是以下之一:BHW,并且 ‘有符号大小’ 是以下之一:s8、s16 或 s32。

1.4.3 原子操作

原子操作是对内存进行操作的操作,不会被其他 BPF 程序或本规范之外的其他方式对同一内存区域的访问中断或破坏。

BPF 支持的所有原子操作都编码为使用 ATOMIC 模式修饰符的存储操作,如下所示

  • 对于 32 位操作,{ATOMIC, W, STX},它是 “atomic32” 一致性组的一部分。

  • 对于 64 位操作,{ATOMIC, DW, STX},它是 “atomic64” 一致性组的一部分。

  • 不支持 8 位和 16 位宽的原子操作。

‘imm’ 字段用于编码实际的原子操作。简单的原子操作使用在 ‘imm’ 字段中定义的用于编码算术操作的值的子集来编码原子操作

简单原子操作

imm

描述

ADD

0x00

原子加

OR

0x40

原子或

AND

0x50

原子与

XOR

0xa0

原子异或

带有 ‘imm’ = ADD 的 {ATOMIC, W, STX} 表示

*(u32 *)(dst + offset) += src

带有 ‘imm’ = ADD 的 {ATOMIC, DW, STX} 表示

*(u64 *)(dst + offset) += src

除了简单的原子操作之外,还有一个修饰符和两个复杂的原子操作

复杂原子操作

imm

描述

FETCH

0x01

修饰符:返回旧值

XCHG

0xe0 | FETCH

原子交换

CMPXCHG

0xf0 | FETCH

原子比较和交换

对于简单的原子操作,FETCH 修饰符是可选的,对于复杂的原子操作始终设置。如果设置了 FETCH 标志,则该操作也会使用修改之前内存中的值覆盖 src

XCHG 操作原子地将 srcdst + offset 寻址的值交换。

CMPXCHG 操作原子地将 dst + offset 寻址的值与 R0 比较。如果它们匹配,则 dst + offset 寻址的值将替换为 src。在任何情况下,操作之前 dst + offset 处的值都会被零扩展并加载回 R0

1.4.4 64 位立即数指令

带有 IMM ‘模式’ 修饰符的指令使用 指令编码中定义的宽指令编码,并使用基本指令的 ‘src_reg’ 字段来保存操作码子类型。

下表定义了一组带有 ‘src_reg’ 字段中操作码子类型的 {IMM, DW, LD} 指令,使用新的术语,如 “map”,在下面进一步定义

64 位立即数指令

src_reg

伪代码

imm 类型

dst 类型

0x0

dst = (next_imm << 32) | imm

整数

整数

0x1

dst = map_by_fd(imm)

map fd

map

0x2

dst = map_val(map_by_fd(imm)) + next_imm

map fd

数据地址

0x3

dst = var_addr(imm)

变量 ID

数据地址

0x4

dst = code_addr(imm)

整数

代码地址

0x5

dst = map_by_idx(imm)

map 索引

map

0x6

dst = map_val(map_by_idx(imm)) + next_imm

map 索引

数据地址

其中

  • map_by_fd(imm) 表示将 32 位文件描述符转换为 map 的地址(请参阅 Maps

  • map_by_idx(imm) 表示将 32 位索引转换为 map 的地址

  • map_val(map) 获取给定 map 中第一个值的地址

  • var_addr(imm) 获取具有给定 id 的平台变量(请参阅 平台变量)的地址

  • code_addr(imm) 获取指定相对偏移量的指令地址,以(64 位)指令数为单位

  • 反汇编程序可以使用 ‘imm type’ 进行显示

  • 验证和 JIT 编译可以使用 ‘dst type’

1.4.4.1 Maps

Map 是某些平台上 BPF 程序可访问的共享内存区域。Map 可以具有各种语义,如单独的文档中所定义,并且可能具有也可能没有单个连续的内存区域,但 ‘map_val(map)’ 目前仅为具有单个连续内存区域的 map 定义。

如果平台支持,每个 map 都可以具有文件描述符 (fd),其中 ‘map_by_fd(imm)’ 表示获取具有指定文件描述符的 map。每个 BPF 程序也可以定义为使用加载时与程序关联的一组 map,并且 ‘map_by_idx(imm)’ 表示获取具有 BPF 程序包含指令的集合中给定索引的 map。

1.4.4.2 平台变量

平台变量是由运行时公开并通过整数 id 标识的内存区域,某些平台上的 BPF 程序可以访问这些内存区域。‘var_addr(imm)’ 操作表示获取由给定 id 标识的内存区域的地址。

1.4.5 传统 BPF 数据包访问指令

BPF 之前引入了特殊的指令来访问数据包数据,这些指令是从经典 BPF 继承而来的。这些指令使用了 LD 的指令类,WHB 的大小修饰符,以及 ABSIND 的模式修饰符。‘dst_reg’ 和 ‘offset’ 字段设置为零,对于 ABS,‘src_reg’ 设置为零。但是,这些指令已被弃用,不应再使用。所有传统数据包访问指令都属于 “packet” 一致性组。