2. eBPF 基础:程序类型、验证器、Map 与 Helper 函数、内核执行路径
eBPF 并非单一技术,而是一套由 程序类型、验证器、Map、Helper 函数 以及 内核执行路径 构成的完整生态。理解这五个核心组件,是掌握 eBPF 租户隔离与流量可视化的前提。
2.1 eBPF 程序类型:挂载点决定能力
eBPF 程序类型决定了它可以挂载到内核的哪个位置,以及能够访问哪些上下文数据。不同的租户隔离场景需要选择不同的程序类型。
| 程序类型 | 挂载点 | 典型用途 | 租户隔离场景 |
|---|---|---|---|
| XDP | 网卡驱动层(最早) | 高速包过滤、DDoS 防护 | 租户流量入口限速、非法包丢弃 |
| TC (cls_bpf) | 内核协议栈流量控制层 | 流量整形、包标记、转发 | 租户出向带宽控制、QoS 标记 |
| Kprobe/Uprobe | 内核/用户态函数入口/出口 | 动态追踪、性能分析 | 监控租户进程系统调用、文件操作 |
| Tracepoint | 内核静态插桩点 | 稳定追踪、网络事件监控 | 采集租户网络连接事件、套接字状态 |
| Socket Filter | 套接字层 | 套接字级别包过滤 | 租户容器内流量过滤、安全策略 |
| cgroup BPF | cgroup 层级 | cgroup 级别资源控制 | 按 cgroup 隔离租户 CPU/内存/IO |
选型建议: 流量可视化场景优先选择 XDP(高性能抓包)和 TC(协议栈内处理);资源隔离场景优先选择 cgroup BPF 和 Kprobe。
2.2 eBPF 验证器:安全执行的守门人
验证器是 eBPF 安全性的核心。它确保加载到内核的程序不会导致内核崩溃、死锁或信息泄露。验证过程分为两个阶段:
- 第一阶段:CFG 检查(控制流图)
- 确保程序无循环(或循环有界且可预测)
- 确保程序终止(无无限递归)
- 确保指令数不超过上限(通常 4096 条)
- 第二阶段:静态分析
- 模拟执行所有路径,跟踪寄存器状态
- 检查指针算术是否越界
- 检查内存访问是否合法(如只读数据不可写)
- 检查 Helper 函数参数类型是否匹配
常见验证失败原因: 未检查的指针运算、未初始化的变量、循环次数不可预测、访问超出 Map 边界。在租户隔离场景中,验证器是防止恶意租户程序破坏内核的第一道防线。
2.3 eBPF Map 与 Helper 函数:程序与内核的桥梁
Map 是 eBPF 程序与用户态共享数据的关键结构,Helper 函数 则是 eBPF 程序安全访问内核功能的唯一途径。
2.3.1 常用 Map 类型
| Map 类型 | 数据结构 | 用途 |
|---|---|---|
| BPF_MAP_TYPE_HASH | 哈希表 | 租户 ID 到策略的映射、连接跟踪表 |
| BPF_MAP_TYPE_ARRAY | 数组 | 统计计数器、固定大小配置表 |
| BPF_MAP_TYPE_PERCPU_HASH | 每 CPU 哈希表 | 高性能租户流量统计(无锁) |
| BPF_MAP_TYPE_RINGBUF | 环形缓冲区 | 高效事件通知、日志输出 |
| BPF_MAP_TYPE_STACK | 栈 | 临时数据存储、函数调用上下文 |
2.3.2 关键 Helper 函数
- bpf_map_lookup_elem / bpf_map_update_elem:Map 读写操作
- bpf_get_current_pid_tgid:获取当前进程 PID 和 TGID,用于识别租户
- bpf_get_current_cgroup_id:获取当前 cgroup ID,用于租户分组
- bpf_skb_load_bytes / bpf_skb_store_bytes:安全读写网络包数据
- bpf_perf_event_output:将数据发送到用户态 perf 缓冲区
- bpf_redirect:重定向网络包到其他接口或 CPU
// 示例:在 XDP 程序中查询租户限速策略
struct tenant_policy *policy;
policy = bpf_map_lookup_elem(&tenant_policy_map, &tenant_id);
if (policy) {
// 根据 policy->rate_limit 执行限速逻辑
if (current_rate > policy->rate_limit) {
return XDP_DROP;
}
}
2.4 eBPF 在内核中的执行路径
从用户态加载 eBPF 程序到内核执行,经历了一条完整的管道。理解这条路径有助于排查加载失败、性能瓶颈和隔离失效问题。
2.5 执行路径中的隔离保障机制
在租户隔离场景中,eBPF 执行路径的每个环节都提供了隔离保障:
- 验证器阶段: 确保租户程序不能访问其他租户的内存空间
- JIT 编译阶段: 生成位置无关代码,防止地址泄露
- Map 访问阶段: 通过 Key 隔离(如 tenant_id 作为 Map Key)实现数据隔离
- Helper 调用阶段: 限制租户程序只能调用白名单内的 Helper 函数
- 执行上下文: 通过 cgroup_id 或 pid 区分不同租户的事件
核心结论: eBPF 的隔离能力并非事后添加,而是内建于验证器、Map 模型和 Helper 函数的设计之中。理解这些基础组件,是构建安全、高性能租户隔离系统的起点。