kernel/bpf 内核 BPF 机制与原理详解
源码路径:rk3588/kernel-6.1/kernel/bpf/
内核版本:Linux 6.1(RK3588 平台)
平台:RK3588(4×Cortex-A76 + 4×Cortex-A55 big.LITTLE)
该目录实现 Linux 内核 eBPF(extended BPF) 子系统的核心代码。eBPF 允许用户空间加载经过严格验证的字节码程序,在内核中安全、高效地执行,用于网络过滤、追踪、安全策略、 cgroup 管控等场景。所有操作通过 bpf() 系统调用及文件描述符(prog/map/link fd)完成。
目录
一、源码目录结构
1.1 编译依赖关系(Kconfig)
1 2 3 4 5 6 7
| BPF (基础 classic BPF 解释器) └── BPF_SYSCALL (menuconfig "BPF subsystem") ├── BPF_JIT ← JIT 编译器 ├── BPF_JIT_ALWAYS_ON ← 永久启用 JIT,移除解释器 ├── BPF_UNPRIV_DEFAULT_OFF ← 默认禁止非特权 BPF ├── BPF_LSM ← LSM 安全钩子挂载 └── BPF_PRELOAD ← 启动时预加载 BPF 程序
|
关键配置项:
| 配置项 |
说明 |
CONFIG_BPF |
基础 BPF 支持(classic socket filter 依赖) |
CONFIG_BPF_SYSCALL |
启用 bpf() 系统调用 |
CONFIG_BPF_JIT |
JIT 编译,将 eBPF 字节码编译为本地机器码 |
CONFIG_HAVE_EBPF_JIT |
架构支持 eBPF JIT(ARM64 支持) |
CONFIG_BPF_JIT_ALWAYS_ON |
仅 JIT,无解释器(防侧信道) |
CONFIG_DEBUG_INFO_BTF |
内核 BTF 调试信息(CO-RE、fentry 依赖) |
CONFIG_CGROUP_BPF |
cgroup 附加 BPF 程序 |
CONFIG_BPF_LSM |
BPF LSM 安全策略 |
CONFIG_BPF_EVENTS |
BPF 挂载 tracepoint/kprobe(在 trace 子系统) |
1.2 编译单元(Makefile)
| 编译单元 |
源文件 |
规模 |
功能 |
| 核心解释器 |
core.c |
~2,781 行 |
classic/eBPF 解释器、bpf_prog_run、程序分配 |
| 系统调用 |
syscall.c |
~5,378 行 |
bpf() syscall、prog/map/link 生命周期 |
| 验证器 |
verifier.c |
~15,726 行 |
静态分析、类型检查、安全验证 |
| BTF |
btf.c |
~8,045 行 |
BPF Type Format 解析、验证、内核 BTF |
| Cgroup |
cgroup.c |
~2,578 行 |
cgroup BPF 附加与执行 |
| Hash Map |
hashtab.c |
~2,547 行 |
HASH / LRU / PERCPU hash 实现 |
| Helper |
helpers.c |
~1,745 行 |
通用 helper 函数实现 |
| Array Map |
arraymap.c |
~1,359 行 |
ARRAY / PERCPU_ARRAY / PROG_ARRAY 等 |
| Trampoline |
trampoline.c |
~1,080 行 |
fentry/fexit/modify_return 跳板 |
| Devmap |
devmap.c |
~1,137 行 |
网络设备重定向 map |
| BPF Iter |
bpf_iter.c |
~778 行 |
BPF 迭代器框架 |
| Ringbuf |
ringbuf.c |
~795 行 |
BPF ring buffer map |
| Inode/FS |
inode.c |
~820 行 |
bpffs 文件系统(对象 pin) |
| Dispatcher |
dispatcher.c |
~174 行 |
多路分支直接调用优化 |
| LSM |
bpf_lsm.c |
~357 行 |
BPF LSM 钩子 |
| Preload |
preload/ |
— |
启动时预加载迭代器 BPF |
1.3 主要源文件分类
核心基础设施
| 文件 |
功能 |
core.c |
eBPF 解释器(___bpf_prog_run)、classic BPF、bpf_prog_alloc、JIT 辅助 |
syscall.c |
__sys_bpf()、bpf_prog_load()、map_create()、link 管理 |
verifier.c |
bpf_check() 静态验证器:CFG 分析、寄存器类型追踪、内存访问检查 |
helpers.c |
通用 helper:map_lookup/update/delete、时间、随机数等 |
btf.c |
BTF 加载/验证/解析、vmlinux BTF、CO-RE relocation |
log.c |
验证器日志输出 |
tnum.c |
追踪数值(tracked number)用于验证器常量传播 |
disasm.c |
eBPF 指令反汇编(调试用) |
memalloc.c |
BPF 专用内存分配 |
inode.c |
bpffs 伪文件系统,支持 prog/map/link pin |
dispatcher.c |
避免 indirect call 的多路分支代码生成 |
relo_core.c |
CO-RE relocation 核心(来自 tools/lib/bpf) |
Map 实现
| 文件 |
Map 类型 |
arraymap.c |
ARRAY, PERCPU_ARRAY, PROG_ARRAY, PERF_EVENT_ARRAY, CGROUP_ARRAY |
hashtab.c |
HASH, PERCPU_HASH, LRU_HASH, LRU_PERCPU_HASH, HASH_OF_MAPS |
lpm_trie.c |
LPM_TRIE(最长前缀匹配) |
map_in_map.c |
ARRAY_OF_MAPS, HASH_OF_MAPS |
queue_stack_maps.c |
QUEUE, STACK |
ringbuf.c |
RINGBUF, USER_RINGBUF |
bloom_filter.c |
BLOOM_FILTER |
stackmap.c |
STACK_TRACE |
reuseport_array.c |
REUSEPORT_SOCKARRAY |
local_storage.c |
CGROUP_STORAGE, PERCPU_CGROUP_STORAGE |
bpf_local_storage.c |
SK_STORAGE |
bpf_task_storage.c |
TASK_STORAGE |
bpf_inode_storage.c |
INODE_STORAGE |
网络相关
| 文件 |
功能 |
devmap.c |
DEVMAP / DEVMAP_HASH — XDP 设备重定向 |
cpumap.c |
CPUMAP — XDP 跨 CPU 转发 |
offload.c |
硬件 offload BPF |
net_namespace.c |
网络命名空间 BPF 隔离 |
挂载与迭代
| 文件 |
功能 |
trampoline.c |
BPF trampoline:fentry/fexit/modify_return |
cgroup.c |
cgroup BPF 程序附加与 bpf_prog_run_array_cg() |
bpf_lsm.c |
LSM 钩子 BPF 程序挂载 |
bpf_struct_ops.c |
struct_ops BPF(如 TCP congestion control) |
bpf_iter.c |
BPF iterator 框架 |
map_iter.c |
迭代所有 BPF map |
prog_iter.c |
迭代所有 BPF prog |
task_iter.c |
迭代 task 结构 |
link_iter.c |
迭代 BPF link |
cgroup_iter.c |
迭代 cgroup |
预加载
| 文件 |
功能 |
preload/bpf_preload_kern.c |
启动时加载 skeleton BPF(map/prog 调试迭代器) |
preload/iterators/ |
预置 BPF 迭代器程序 |
二、整体架构
2.1 分层模型
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
| ┌─────────────────────────────────────────────────────────────┐ │ 用户空间 │ │ libbpf / bpftool / clang -target bpf │ └──────────────────────────┬──────────────────────────────────┘ │ bpf() syscall + fd (prog/map/link) ┌──────────────────────────▼──────────────────────────────────┐ │ syscall.c │ │ __sys_bpf() │ prog_load │ map_create │ link_create │ └──────┬──────────┬──────────┬──────────┬─────────────────────┘ │ │ │ │ ┌──────▼──┐ ┌─────▼────┐ ┌──▼──────┐ ┌─▼──────────────┐ │Verifier │ │ Map Ops │ │ BTF │ │ Trampoline/ │ │verifier │ │ array/ │ │ btf.c │ │ Attach/Link │ │ .c │ │ hashtab │ │ │ │ trampoline.c │ └──────┬──┘ └─────┬────┘ └──┬──────┘ └─┬──────────────┘ │ │ │ │ └──────────┴──────────┴──────────┘ │ ┌────────────▼────────────┐ │ core.c + arch JIT │ │ 解释器 / JIT 机器码 │ │ bpf_prog_run() │ └────────────┬────────────┘ │ ┌────────────▼────────────┐ │ 内核 Hook 点 │ │ XDP/cgroup/trace/LSM/ │ │ kprobe/fentry/... │ └─────────────────────────┘
|
2.2 核心设计原则
- 安全优先:所有用户加载的程序必须经过 verifier 静态验证,保证有界执行、类型安全、无非法内存访问
- JIT 加速:默认通过架构相关 JIT(ARM64:
arch/arm64/net/bpf_jit_comp.c)编译为本地代码
- Map 共享状态:prog 通过 fd 引用 map,实现内核与用户空间、prog 与 prog 之间的数据共享
- Link 抽象:BPF link 统一管理 prog 与挂载点的生命周期
- BTF 类型安全:携带 BTF 的程序支持 CO-RE、typed context access、fentry/fexit
三、核心数据结构
3.1 struct bpf_prog — BPF 程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| struct bpf_prog { u16 pages; u16 jited:1, gpl_compatible:1, jit_requested:1, blinding_requested:1; enum bpf_prog_type type; enum bpf_attach_type expected_attach_type; u32 len; struct bpf_insn *insnsi; struct bpf_prog_aux *aux; unsigned int (*bpf_func)(const void *ctx, const struct bpf_insn *insn); };
|
bpf_prog_aux 包含 BTF、used_maps、attach 信息、stats、line info 等。
3.2 struct bpf_map — BPF 映射
1 2 3 4 5 6 7 8 9 10
| struct bpf_map { const struct bpf_map_ops *ops; enum bpf_map_type map_type; u32 key_size; u32 value_size; u32 max_entries; atomic64_t writecnt; };
|
3.3 struct bpf_map_ops — Map 虚函数表
1 2 3 4 5 6 7 8 9
| struct bpf_map_ops { int (*map_alloc_check)(union bpf_attr *attr); struct bpf_map *(*map_alloc)(union bpf_attr *attr); void (*map_free)(struct bpf_map *map); void *(*map_lookup_elem)(struct bpf_map *map, void *key); int (*map_update_elem)(struct bpf_map *map, void *key, void *value, u64 flags); int (*map_delete_elem)(struct bpf_map *map, void *key); };
|
各 map 类型在 linux/bpf_types.h 中通过 BPF_MAP_TYPE 宏注册到 bpf_map_types[]。
3.4 struct bpf_insn — eBPF 指令
1 2 3 4 5 6 7 8
| struct bpf_insn { __u8 code; __u8 dst_reg:4; __u8 src_reg:4; __s16 off; __s32 imm; };
|
寄存器约定:
R0 — 返回值
R1-R5 — 参数传递(R1 = context 指针)
R6-R9 — callee saved
R10 — 帧指针(只读)
3.5 struct bpf_link — 挂载链接
1 2 3 4 5 6 7 8
| struct bpf_link { atomic64_t refcnt; u32 id; enum bpf_link_type type; struct bpf_link_ops *ops; struct bpf_prog *prog; };
|
Link 替代旧的 BPF_PROG_ATTACH 方式,提供更清晰的生命周期管理。
四、bpf() 系统调用
4.1 入口
1 2 3 4 5
| SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) { return __sys_bpf(cmd, USER_BPFPTR(uattr), size); }
|
4.2 命令列表
| 命令 |
功能 |
BPF_MAP_CREATE |
创建 map,返回 fd |
BPF_MAP_LOOKUP_ELEM |
查找 map 元素 |
BPF_MAP_UPDATE_ELEM |
更新 map 元素 |
BPF_MAP_DELETE_ELEM |
删除 map 元素 |
BPF_MAP_GET_NEXT_KEY |
遍历 map |
BPF_MAP_FREEZE |
冻结 map(只读) |
BPF_MAP_*_BATCH |
批量 lookup/update/delete |
BPF_PROG_LOAD |
加载 BPF 程序,返回 fd |
BPF_PROG_ATTACH |
附加程序(旧接口) |
BPF_PROG_DETACH |
分离程序 |
BPF_PROG_TEST_RUN |
测试运行程序 |
BPF_PROG_BIND_MAP |
绑定 map 到 prog |
BPF_OBJ_PIN |
pin 到 bpffs |
BPF_OBJ_GET |
从 bpffs 获取 |
BPF_OBJ_GET_INFO_BY_FD |
查询对象信息 |
BPF_*_GET_FD_BY_ID |
通过 ID 获取 fd |
BPF_*_GET_NEXT_ID |
遍历 ID |
BPF_BTF_LOAD |
加载 BTF 数据 |
BPF_RAW_TRACEPOINT_OPEN |
打开 raw tracepoint |
BPF_LINK_CREATE |
创建 link |
BPF_LINK_UPDATE |
更新 link |
BPF_LINK_DETACH |
分离 link |
BPF_ITER_CREATE |
创建 BPF iterator |
BPF_ENABLE_STATS |
启用运行时统计 |
BPF_TASK_FD_QUERY |
查询 task 关联的 fd |
4.3 权限控制
1 2 3 4 5 6
|
if (!capable && (cmd == BPF_MAP_CREATE || cmd == BPF_PROG_LOAD)) return -EPERM;
|
五、程序加载与验证器
5.1 加载流程
1 2 3 4 5 6 7 8 9 10 11 12
| bpf_prog_load() ├── 检查 license(GPL 兼容才能调用 GPL-only helper) ├── 检查 prog_type 权限(网络类需 CAP_NET_ADMIN 等) ├── bpf_prog_alloc() — 分配 prog 结构 ├── copy_from_user(insns) — 复制字节码 ├── bpf_prog_select_runtime() — 选择解释器或 JIT ├── bpf_check() — 验证器(verifier.c) │ ├── 第一遍:CFG 分析(DAG、无环、可达性) │ └── 第二遍:寄存器类型状态追踪 ├── bpf_prog_load_btf() — 加载关联 BTF ├── bpf_prog_alloc_id() — 分配全局 ID └── fd_install() — 返回文件描述符
|
5.2 验证器核心逻辑
verifier.c 中 bpf_check() 是 eBPF 安全的核心:
第一遍 — CFG 验证:
- 程序必须是 DAG(无循环)
- 指令数 ≤
BPF_MAXINSNS(4096,特权用户更高)
- 所有跳转目标合法
第二遍 — 状态追踪:
- 每个寄存器维护类型状态(
PTR_TO_CTX、PTR_TO_MAP_VALUE、SCALAR_VALUE 等)
- 追踪算术运算对指针类型的影响
- 检查 helper 调用的参数类型约束
- 检查 map 访问边界(key/value size)
- 检查 null 指针解引用(通过 branch 类型细化)
寄存器类型示例:
1 2 3 4 5 6
| 程序入口: R1 = PTR_TO_CTX BPF_MOV64_REG(R2, R10) → R2 = FRAME_PTR BPF_ALU64_IMM(ADD, R2, -4) → R2 = PTR_TO_STACK (offset -4) BPF_LD_MAP_FD(R1, map_fd) → R1 = CONST_PTR_TO_MAP BPF_CALL(map_lookup_elem) → 检查 arg1=MAP, arg2=STACK key → R0 = PTR_TO_MAP_VALUE_OR_NULL
|
Reference 类型追踪:
PTR_TO_SOCKET_OR_NULL 等内核资源引用
- 必须在程序所有路径上正确释放(如
bpf_sk_release)
5.3 程序类型验证器
每种 BPF_PROG_TYPE 有独立的 bpf_verifier_ops:
1 2 3
| static const struct bpf_verifier_ops * const bpf_verifier_ops[] = { #include <linux/bpf_types.h> };
|
提供:get_func_proto(helper 原型)、is_valid_access(context 访问)、check_attach 等。
六、程序执行引擎
6.1 bpf_prog_run
1 2 3 4 5
| static __always_inline u32 bpf_prog_run(const struct bpf_prog *prog, const void *ctx) { return prog->bpf_func(ctx, prog->insnsi); }
|
- JIT 模式:
prog->bpf_func 指向 JIT 编译后的机器码入口
- 解释器模式:
prog->bpf_func = __bpf_prog_run,内部调用 ___bpf_prog_run()
6.2 eBPF 解释器
core.c 中 ___bpf_prog_run() 使用 computed goto 跳转表实现高效解释执行:
1 2 3 4 5 6 7 8 9 10 11
| static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn) { static const void * const jumptable[256] = { ... }; select_insn: goto *jumptable[insn->code]; ALU(ADD, +) ALU(SUB, -) BPF_JMP | BPF_CALL: BPF_JMP | BPF_EXIT: }
|
6.3 JIT 编译
架构相关 JIT 位于 arch/arm64/net/bpf_jit_comp.c(RK3588):
- 将 eBPF 指令翻译为 ARM64 机器码
- 处理 tail call、helper 调用、map 访问
- 支持 hardening(常量 blinding)
- 生成 line info 用于 stack trace
控制接口:
1 2 3
| /proc/sys/net/core/bpf_jit_enable # 0=解释器 1=JIT 2=JIT+debug /proc/sys/net/core/bpf_jit_harden # hardening 级别 /proc/sys/net/core/bpf_jit_kallsyms # JIT 代码加入 kallsyms
|
6.4 Dispatcher — 避免间接调用
dispatcher.c 为多 prog 挂载点生成 multiway branch 代码,将 indirect call 转为 direct call,在 retpoline 启用时显著提升性能。
七、Map 映射类型
7.1 通用 Map
| Map 类型 |
源文件 |
特点 |
HASH |
hashtab.c |
通用 hash 表,O(1) 查找 |
ARRAY |
arraymap.c |
索引数组,key 为 u32 |
PERCPU_HASH |
hashtab.c |
per-CPU hash,无锁 |
PERCPU_ARRAY |
arraymap.c |
per-CPU 数组 |
LRU_HASH |
hashtab.c |
LRU 淘汰策略 |
LPM_TRIE |
lpm_trie.c |
最长前缀匹配(路由表) |
QUEUE / STACK |
queue_stack_maps.c |
队列/栈语义 |
RINGBUF |
ringbuf.c |
单生产者/单消费者 ring buffer |
BLOOM_FILTER |
bloom_filter.c |
布隆过滤器 |
7.2 特殊用途 Map
| Map 类型 |
用途 |
PROG_ARRAY |
tail call 跳转表 |
PERF_EVENT_ARRAY |
perf event 重定向 |
CGROUP_ARRAY |
cgroup 引用数组 |
ARRAY_OF_MAPS / HASH_OF_MAPS |
map 嵌套 |
STACK_TRACE |
存储栈回溯 |
CGROUP_STORAGE |
per-cgroup 数据 |
SK_STORAGE |
per-socket 数据 |
TASK_STORAGE |
per-task 数据 |
INODE_STORAGE |
per-inode 数据 |
7.3 网络 Map
| Map 类型 |
源文件 |
用途 |
DEVMAP / DEVMAP_HASH |
devmap.c |
XDP 重定向到网络设备 |
CPUMAP |
cpumap.c |
XDP 转发到其他 CPU |
XSKMAP |
(net/xdp) |
AF_XDP socket 重定向 |
SOCKMAP / SOCKHASH |
(net/core) |
socket 重定向/负载均衡 |
REUSEPORT_SOCKARRAY |
reuseport_array.c |
SO_REUSEPORT 选 socket |
八、Helper 辅助函数
8.1 通用 Helper(helpers.c)
| Helper |
功能 |
bpf_map_lookup_elem |
查找 map 元素 |
bpf_map_update_elem |
更新 map 元素 |
bpf_map_delete_elem |
删除 map 元素 |
bpf_map_push/pop/peek_elem |
queue/stack 操作 |
bpf_get_smp_processor_id |
当前 CPU ID |
bpf_get_current_pid_tgid |
当前 pid/tgid |
bpf_get_current_comm |
当前进程名 |
bpf_ktime_get_ns/boot_ns/coarse_ns |
时间获取 |
bpf_get_prandom_u32 |
伪随机数 |
bpf_printk |
内核日志输出 |
8.2 Helper 原型约束
每个 helper 通过 bpf_func_proto 声明参数/返回类型约束,供 verifier 检查:
1 2 3 4 5 6
| const struct bpf_func_proto bpf_map_lookup_elem_proto = { .func = bpf_map_lookup_elem, .ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL, .arg1_type = ARG_CONST_MAP_PTR, .arg2_type = ARG_PTR_TO_MAP_KEY, };
|
8.3 类型特定 Helper
各 prog type 在各自 verifier ops 的 get_func_proto() 中提供额外 helper,例如:
- XDP:
bpf_xdp_adjust_head/meta/room
- Tracing:
bpf_probe_read*、bpf_get_stackid
- Cgroup:
bpf_get_current_cgroup_id
- LSM:
bpf_inode_storage_*
九、BTF 类型系统
9.1 概述
BTF(BPF Type Format)描述 BPF 程序/ map 的数据类型,存储在 ELF .BTF section 或独立加载。
btf.c 功能:
- 解析 BTF type section 和 string section
- 两遍验证(收集 type_id → 检查引用)
- 内核 vmlinux BTF 支持(
CONFIG_DEBUG_INFO_BTF)
- CO-RE relocation 处理
9.2 主要用途
| 用途 |
说明 |
| fentry/fexit |
通过 BTF 获取函数签名,实现 typed 参数访问 |
| CO-RE |
Compile Once — Run Everywhere,跨内核版本移植 |
| map 类型安全 |
map 的 key/value BTF 类型检查 |
| struct_ops |
替换内核 struct ops(如 TCP CC) |
9.3 BTF ID 集合
1 2 3 4
| BTF_SET_START(bpf_lsm_hooks) #include <linux/lsm_hook_defs.h> BTF_SET_END(bpf_lsm_hooks)
|
十、程序挂载与 Trampoline
10.1 挂载方式对比
| 方式 |
接口 |
特点 |
| BPF Link |
BPF_LINK_CREATE |
推荐,统一生命周期 |
| BPF_PROG_ATTACH |
旧接口 |
逐步被 link 替代 |
| Trampoline |
fentry/fexit/modify_return |
基于 ftrace direct call |
10.2 BPF Trampoline
trampoline.c 实现函数级 BPF 挂载:
1 2 3 4 5 6
| 被追踪函数入口 → ftrace direct call → bpf_trampoline(跳板代码) → BPF_PROG_TYPE_TRACING (fentry) → 原始函数(可选) → BPF_PROG_TYPE_TRACING (fexit)
|
支持类型:
BPF_TRACE_FENTRY — 函数入口
BPF_TRACE_FEXIT — 函数返回
BPF_MODIFY_RETURN — 修改返回值
10.3 程序类型与挂载点
| Prog Type |
典型挂载点 |
BPF_PROG_TYPE_SOCKET_FILTER |
经典 socket 过滤 |
BPF_PROG_TYPE_KPROBE |
kprobe 探针 |
BPF_PROG_TYPE_TRACEPOINT |
内核 tracepoint |
BPF_PROG_TYPE_XDP |
网络驱动 XDP hook |
BPF_PROG_TYPE_SCHED_CLS/ACT |
TC (traffic control) |
BPF_PROG_TYPE_CGROUP_* |
cgroup 各 hook 点 |
BPF_PROG_TYPE_LSM |
LSM 安全钩子 |
BPF_PROG_TYPE_TRACING |
fentry/fexit/raw_tp |
BPF_PROG_TYPE_STRUCT_OPS |
内核 struct ops |
BPF_PROG_TYPE_SYSCALL |
允许 bpf() 的 prog |
十一、Cgroup BPF 与 LSM
11.1 Cgroup BPF
cgroup.c 实现将 BPF 程序附加到 cgroup 层级:
1 2 3 4 5 6 7
| cgroup/ ├── BPF_CGROUP_INET_INGRESS/EGRESS ← 网络 ingress/egress ├── BPF_CGROUP_INET4/6_BIND/CONNECT ← socket 操作 ├── BPF_CGROUP_UDP4/6_SENDMSG/RECVMSG ├── BPF_CGROUP_SOCK_OPS ← socket 生命周期 ├── BPF_CGROUP_DEVICE ← 设备访问控制 └── BPF_CGROUP_SYSCTL ← sysctl 访问控制
|
执行路径:
1 2 3 4 5
| bpf_prog_run_array_cg(cgrp, atype, ctx, run_prog, retval, ret_flags) → 遍历 effective[atype] prog 数组 → 依次 bpf_prog_run(prog, ctx) → 累积返回值(如 -EPERM 拒绝)
|
11.2 BPF LSM
bpf_lsm.c 为每个 LSM hook 创建 nop 函数 bpf_lsm_<hookname>(),BPF 程序通过 trampoline 挂载:
- 支持 MAC(强制访问控制)策略
- 支持 Audit 策略
- 通过 BTF 匹配 hook 签名
- 部分 hook 通过 cgroup shim 执行
十二、BPF Iterator 与 Link
12.1 BPF Iterator
bpf_iter.c 提供内核对象迭代框架:
- 用户通过
BPF_ITER_CREATE 创建 iterator link
- 读取
/proc/self/fd/<iter_fd> 触发 BPF prog 对每个对象执行
- 预置 target:map、prog、task、cgroup、link
预加载迭代器(preload/)在启动时自动注册 maps.debug 和 progs.debug link。
12.2 bpffs 文件系统
inode.c 实现 bpffs 伪文件系统:
1 2 3
| mount -t bpf bpf /sys/fs/bpf echo <prog_fd> > /sys/fs/bpf/my_prog cat /sys/fs/bpf/my_prog
|
支持 pin prog、map、link,实现跨进程共享和持久化。
十三、网络相关 Map
13.1 Devmap — 设备重定向
devmap.c 实现 XDP 帧重定向到指定网络设备:
1 2 3
| XDP prog: bpf_redirect_map(devmap, ifindex, 0) → 查找 devmap[ifindex] → 将 skb/xdp_frame 发送到目标设备 TX 队列
|
13.2 CPUMAP — 跨 CPU 转发
cpumap.c 实现 XDP 帧转发到其他 CPU 处理:
1 2 3 4
| XDP prog: bpf_redirect_map(cpumap, cpu_id, 0) → 将帧放入目标 CPU 的 cpumap 队列 → 目标 CPU kthread 取出并运行 BPF prog → 最终通过 devmap 或直接 TX 发送
|
十四、完整加载执行时序
以 kprobe BPF 程序 为例:
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
| [用户空间 - libbpf] bpf_object__open("prog.o") bpf_object__load() → bpf(BPF_BTF_LOAD) // 加载 BTF → bpf(BPF_MAP_CREATE) × N // 创建 maps → bpf(BPF_PROG_LOAD) // 加载 prog(含 verifier) → bpf(BPF_LINK_CREATE) // 创建 kprobe link
[内核 - BPF_PROG_LOAD] bpf_prog_load() → copy insns from userspace → bpf_check() // verifier 验证 → bpf_int_jit_compile() // ARM64 JIT → 返回 prog fd
[内核 - BPF_LINK_CREATE] link_create() → 注册 kprobe/uprobe/tracepoint → 关联 prog 到挂载点
[内核 - 运行时触发] 被 probe 的函数执行 → kprobe handler → bpf_prog_run(prog, ctx) → JIT 机器码 / 解释器 → helper 调用(如 bpf_printk) → 返回 R0
[用户空间 - 读取结果] 读取 trace_pipe / ringbuf map / perf buffer
|
十五、RK3588 平台调试建议
15.1 确认 BPF 支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| zcat /proc/config.gz | grep -E 'BPF|BTF|CGROUP_BPF'
cat /proc/sys/net/core/bpf_jit_enable
mount | grep bpf ls /sys/fs/bpf/
|
15.2 常用调试工具
| 工具 |
用途 |
bpftool prog/map/link show |
查看已加载对象 |
bpftool prog dump xlated/jited |
查看 JIT 代码 |
bpftool btf dump |
查看 BTF 信息 |
bpftool prog tracelog |
查看验证器日志 |
bpf_printk / trace_pipe |
prog 内日志 |
15.3 RK3588 典型应用场景
| 场景 |
Prog Type |
说明 |
| 网络包过滤 |
XDP / SCHED_CLS |
在驱动层或 TC 层过滤 |
| 容器网络策略 |
CGROUP_SKB/SOCK |
cgroup 级网络管控 |
| 内核函数追踪 |
TRACING (fentry) |
基于 BTF 的函数追踪 |
| 系统调用审计 |
TRACEPOINT / LSM |
安全审计 |
| 性能 profiling |
PERF_EVENT |
perf 集成 |
15.4 注意事项
- RK3588 默认可能启用
CONFIG_BPF_UNPRIV_DEFAULT_OFF,需要 root/CAP_BPF 权限
- big.LITTLE 环境下 per-CPU map 在 A76/A55 核心间独立,注意数据一致性
- JIT 代码位于 module_alloc 区域,可通过
bpftool prog dump jited 查看
- 验证失败时查看
dmesg 或 bpftool prog tracelog 获取详细日志
十六、总结
kernel/bpf 是 Linux 内核 eBPF 子系统的核心实现,其设计要点:
- Verifier — 静态分析保证安全,是指令级类型检查器而非简单 sandbox
- Map — 提供 prog 间、内核与用户间的共享状态机制,多种专用 map 适配不同场景
- JIT — ARM64 JIT 将字节码编译为本地代码,接近原生性能
- BTF — 类型系统支撑 CO-RE、fentry、struct_ops 等高级特性
- Link — 统一的挂载生命周期管理
- Trampoline — 基于 ftrace 的高效函数级挂载
在 RK3588 平台上,eBPF 可用于网络加速(XDP/TC)、容器安全(cgroup/LSM)、内核可观测性(fentry/tracepoint)等,是嵌入式 Linux 系统调优和安全加固的重要工具。
附录:源文件完整清单
| 文件 |
行数 |
分类 |
verifier.c |
15,726 |
验证器 |
btf.c |
8,045 |
BTF 类型 |
syscall.c |
5,378 |
系统调用 |
core.c |
2,781 |
解释器/JIT 入口 |
cgroup.c |
2,578 |
Cgroup BPF |
hashtab.c |
2,547 |
Hash map |
helpers.c |
1,745 |
Helper 函数 |
arraymap.c |
1,359 |
Array map |
trampoline.c |
1,080 |
Trampoline |
devmap.c |
1,137 |
Devmap |
task_iter.c |
864 |
Task 迭代器 |
cpumap.c |
820 |
CPUMAP |
inode.c |
820 |
bpffs |
ringbuf.c |
795 |
Ring buffer |
bpf_iter.c |
778 |
Iterator 框架 |
lpm_trie.c |
745 |
LPM trie |
offload.c |
709 |
HW offload |
bpf_local_storage.c |
707 |
Local storage |
bpf_struct_ops.c |
701 |
Struct ops |
dispatcher.c |
~174 |
Dispatcher |
bpf_lsm.c |
~357 |
LSM |
正在加载留言…