kernel/bpf 内核 BPF 机制与原理详解

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
// include/linux/filter.h
struct bpf_prog {
u16 pages; // 程序占用页数
u16 jited:1, // 是否已 JIT
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; // eBPF 指令数组
struct bpf_prog_aux *aux; // 扩展信息
unsigned int (*bpf_func)(const void *ctx,
const struct bpf_insn *insn);
// JIT 后 bpf_func 指向编译后的机器码
};

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
// include/linux/bpf.h
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
// include/uapi/linux/linux/bpf.h
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 — 帧指针(只读)
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; // release/dealloc/update_map
struct bpf_prog *prog;
// ...
};

Link 替代旧的 BPF_PROG_ATTACH 方式,提供更清晰的生命周期管理。


四、bpf() 系统调用

4.1 入口

1
2
3
4
5
// syscall.c
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
// sysctl: /proc/sys/kernel/unprivileged_bpf_disabled
// 0 = 允许非特权 1 = 永久禁止 2 = 默认禁止(CONFIG_BPF_UNPRIV_DEFAULT_OFF)

// BPF_MAP_CREATE 和 BPF_PROG_LOAD 需要 CAP_BPF 或 CAP_SYS_ADMIN
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.cbpf_check() 是 eBPF 安全的核心:

第一遍 — CFG 验证:

  • 程序必须是 DAG(无循环)
  • 指令数 ≤ BPF_MAXINSNS(4096,特权用户更高)
  • 所有跳转目标合法

第二遍 — 状态追踪:

  • 每个寄存器维护类型状态(PTR_TO_CTXPTR_TO_MAP_VALUESCALAR_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> // BPF_PROG_TYPE 宏展开
};

提供:get_func_proto(helper 原型)、is_valid_access(context 访问)、check_attach 等。


六、程序执行引擎

6.1 bpf_prog_run

1
2
3
4
5
// include/linux/filter.h
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, -)
// ... 所有 eBPF 指令
BPF_JMP | BPF_CALL: // helper 调用
BPF_JMP | BPF_EXIT: // 程序退出,返回 R0
}

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,例如:

  • XDPbpf_xdp_adjust_head/meta/room
  • Tracingbpf_probe_read*bpf_get_stackid
  • Cgroupbpf_get_current_cgroup_id
  • LSMbpf_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
// bpf_lsm.c 示例
BTF_SET_START(bpf_lsm_hooks)
#include <linux/lsm_hook_defs.h> // 自动生成所有 LSM hook 的 BTF ID
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
// cgroup.c
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 执行

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.debugprogs.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 # pin prog
cat /sys/fs/bpf/my_prog # 获取 fd

支持 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'

# 关键项(ARM64 RK3588 典型配置)
# CONFIG_BPF_SYSCALL=y
# CONFIG_BPF_JIT=y
# CONFIG_HAVE_EBPF_JIT=y
# CONFIG_DEBUG_INFO_BTF=y
# CONFIG_CGROUP_BPF=y

# 检查 JIT 状态
cat /proc/sys/net/core/bpf_jit_enable # 应为 1

# 检查 bpffs
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 查看
  • 验证失败时查看 dmesgbpftool prog tracelog 获取详细日志

十六、总结

kernel/bpf 是 Linux 内核 eBPF 子系统的核心实现,其设计要点:

  1. Verifier — 静态分析保证安全,是指令级类型检查器而非简单 sandbox
  2. Map — 提供 prog 间、内核与用户间的共享状态机制,多种专用 map 适配不同场景
  3. JIT — ARM64 JIT 将字节码编译为本地代码,接近原生性能
  4. BTF — 类型系统支撑 CO-RE、fentry、struct_ops 等高级特性
  5. Link — 统一的挂载生命周期管理
  6. 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

文章互动

阅读 --

留言

0 条留言

正在加载留言…