AI 资讯 知识库 后端技术操作系统内功 · 第 9 篇|eBPF:从抓包过滤器到内核可编程层

操作系统内功 · 第 9 篇|eBPF:从抓包过滤器到内核可编程层

2018 年,Cloudflare 的工程师写了一篇标题极其嚣张的文章:《如何每秒丢弃一千万个包》——DDoS 攻击打过来,与其让协议栈处理完再扔,不如在网卡驱动层直接丢弃,单核每秒一千万包,靠的正是 XDP 与 eBPF。一门 1992 年为 tcpdump 设计的过滤语言,在 2014 年被改造成图灵完备的内核可编程层,最终成为云原生网络与可观测性的地基。eBPF 的故事值得所有工程师读一遍:它展示了「在不牺牲安全的前提下开放内核」这件近乎矛盾的事是怎么做成的,又是怎么付出代价的。

一、BPF 的两次诞生

第一代 BPF(Berkeley Packet Filter)诞生于 1992 年 McCanne 与 Jacobson 的论文,用途单一:给 tcpdump 提供高效抓包过滤——一个寄存器虚拟机在内核里跑用户写的过滤表达式,只把命中的包拷出来。它安全、快,但只读、只服务抓包——而且没有退场:今天 tcpdump 的过滤表达式与 seccomp 沙箱(第 8 篇里 Android 拿它挡 io_uring 的那把锁)跑的仍是经典 BPF 字节码,新范式接管的是更广的疆域,而不是替换旧工具。第二代在 2014 年随 Linux 3.18 合入主线,史称 eBPF(extended BPF):全新的指令集、bpf() 系统调用、一个验证器和一个 JIT 编译器。这四件套让 BPF 从「过滤器」升维成「内核可编程层」——用户程序可以安全地向内核注入代码,挂到 kprobes、tracepoints、网络钩子等任意观测点上,内核数据从此对工具敞开。

驱动这一切的核心设计是验证器。加载 eBPF 程序时,内核静态分析它的全部执行路径:模拟执行每一条分支,证明没有空指针解引用、没有越界访问、没有无界循环,全部路径安全后才 JIT 成机器码执行。这份保守换来一个惊人的性质:有界循环直到近年才被放开,递归直接禁止——但对于观测与网络处理,「能写的」恰好覆盖了「要写的」。代价是工程体验:验证器报错以晦涩著称,稍复杂的程序要手工标注指针边界、拆解循环;libbpf 的报错改进与内核近年的全局函数、BPF-to-BPF 调用等特性一直在降低这道门槛。程序的数据面是 maps:哈希表、数组、per-CPU 计数器等内核态与用户态共享的数据结构,eBPF 程序在内核里记账,用户态随时来读——观测工具的整个数据通道就是这么搭起来的;高频事件流则走 perf buffer 与后来的 BPF ring buffer 向用户态推送。程序本体用 C 的一个严格子集编写(clang 以 bpf 目标编译),复杂逻辑还能用 tail call 在程序间接力——这些约束共同保证验证器能证明每一段代码的安全。

二、CO-RE:跨内核可移植性之战

eBPF 早期有个工程痛点:内核结构体的布局随版本变化,工具要针对每个内核版本重新编译。BCC 时代的解法是运行时带 LLVM 头文件编译——工具分发一个几百 MB 的编译环境,装个 bcc-tools 拖着 Clang 跑,运维体验极其沉重。2018 年前后的 CO-RE(Compile Once – Run Everywhere)解决了这个问题:内核把自己类型的元数据导出为 BTF(BPF Type Format),libbpf 在加载时按目标内核的实际布局做重定位——同一份 .bpf 二进制在 5.x、6.x 各版本内核上都能跑。发明者 Andrii Nakryiko 2020 年的技术长文是这个转折点的权威记录,Brendan Gregg 随后宣告 BPF 工具进入「一次编译、处处运行」时代。

工程格局随之改变:bpftrace 用一行 DSL 就能追踪内核事件(tracepoint:syscalls:sys_enter_openat { @[comm] = count(); } 统计谁在开文件),BCC 工具集全面转向 CO-RE 后端,libbpf 成为加载器的事实标准。BCC 家族的工具命名法值得一提:execsnoop(谁在起新进程)、opensnoop(谁在开文件)、biolatency(块设备延迟分布)、tcptop(谁在吃带宽)——名字就是它们回答的问题,「一个工具回答一个问题」是这套工具链的灵魂。这层可移植性基础设施的意义不亚于验证器——没有它,eBPF 只是内核黑客的玩具;有了它,才是工程师的日用工具。

三、XDP:内核最前置的钩子

网络路径上,eBPF 找到了一个绝妙的挂载点:XDP(eXpress Data Path)把程序挂在网卡驱动收包之后、协议栈处理之前——比 iptables、conntrack 都早。它还有三档挂载模式:通用模式挂在协议栈入口(任何网卡可用,收益打折)、原生模式挂在驱动层(最快的常规选择)、卸载模式直接把程序下到网卡芯片执行(需要硬件支持,理论最快)。在这里丢包的成本几乎是物理下限,Cloudflare 的基准是单核每秒一千万包。实战记录一路升级:2017 年 netdev 会议 Bertin 的「XDP in Practice」用一个 XDP 规则集扛住每秒一百万包的 DNS 攻击;2018 年 Cloudflare 的 L4Drop 把 XDP 做成 DDoS 缓解平台,真实攻击中观测到每秒八百万以上的持续丢弃。对比一下传统路径——攻击包要穿过驱动、协议栈、socket 才被应用丢弃,CPU 在半路就被打满——XDP 的价值就是把「防御决策」搬到了数据的第一现场。程序处理完每个包后按返回码决定去向:XDP_PASS 放行进协议栈、XDP_DROP 就地丢弃、XDP_TX 从原网卡弹回、XDP_REDIRECT 转发他处——DDoS 应答、负载均衡、容器网络快路径全建立在这几个返回码上,Meta 开源的 Katran 负载均衡器正是这个模式的代表作。

四、生态:网络与观测的两极

eBPF 的生态沿两个方向爆发。网络侧的代表作是 Cilium:用 eBPF 程序替换 kube-proxy 的 iptables 规则——service 查找从 iptables 的 O(n) 线性遍历变成 O(1) 哈希命中,官方基准里连接建立类负载延迟显著优于 iptables 路径,NodePort 场景还能保留客户端源 IP。Cilium 2023 年 10 月从 CNCF 毕业,是 eBPF 网络路线成熟度的官方背书。观测侧的家族更庞大:内核 tracepoint、kprobe、USDT 用户态探针全覆盖,Pixie 提供「免改码的 K8s 全链路观测」(2020 年被 New Relic 收购后于 2021 年捐赠 CNCF),还有 off-CPU 分析、TCP 状态追踪等一整族 BCC/bpftrace 工具——下一篇要讲的性能方法论,一半的武器库都架在 eBPF 上。还有一类常被低估的应用是运行时安全:Falco 这类威胁检测引擎用 eBPF 回答「这个容器里谁执行了什么系统调用」,可观测与安全的边界在内核观测点上天然重合。

五、安全争议:最强大的工具也是最大的攻击面

eBPF 的权力膨胀引发了系统的反弹。2019 年内核社区就明确放弃了对非特权用户开放 BPF 的目标(LWN 专文记录了这个决定的来龙去脉);此后验证器漏洞持续出现——CVE-2023-2163 是验证器路径剪枝的错误,Google 用自研 fuzzer 发现并公开复盘;主要发行版相继默认收紧:RHEL 默认阻止非特权 eBPF(验证器漏洞在 RHEL 上被降级处理),SUSE 自 2021 年底、Ubuntu 20.04 LTS 系也先后默认禁用。行业层面,Linux Foundation 2024 年委托 ControlPlane 完成 eBPF 安全威胁模型与形式化审计,结论包括「主线内核与多数发行版出厂即禁用非特权 eBPF,建议保持」;USENIX Security 2023 的跨容器攻击研究还实测到云厂商的极端态度——Google Cloud Run 干脆不把 eBPF 编进内核。

给工程师的立场要写清楚:eBPF 的安全模型保护的是「内核不被恶意程序攻破」,不承诺「加载 eBPF 的进程可信」——root 权限加载的程序依然能看到一切,这正是它与生俱来的观测能力。生产环境的正确姿势:非特权 eBPF 保持禁用,特权加载收敛到监控与网络组件,把「谁能加载 eBPF」当成与「谁能登 root」同级的权限治理问题。

六、工程落地清单

  1. 发行版安全基线先行。确认 kernel.unprivileged_bpf_disabled 为 1/2,把 eBPF 加载能力收敛到受控组件。
  2. 新工具链直上 CO-RE。bpftrace/libbpf 优先,BCC 只留给原型验证,别再把 LLVM 拖上生产机。
  3. 网络优化评估 XDP。DDoS 防护、负载分发这类「越早决策越便宜」的场景,XDP 是现行最优解。
  4. K8s 网络选型把 Cilium 类方案纳入候选。服务网格与 kube-proxy 替换的收益要用自家负载压测,iptables 规则规模大的集群收益最明显。
  5. 在容器平台确认 eBPF 可用性。部分托管环境(如 Cloud Run 类沙箱)根本没编译 eBPF——观测工具部署前先探测。
  6. 把 eBPF 纳入内核升级回归。验证器与特性随内核版本快速演进,升级前跑一遍自有 eBPF 程序的兼容集。
  7. 上 CO-RE 前确认 BTF。ls /sys/kernel/btf/vmlinux 存在说明内核带 BTF,CO-RE 工具链才有重定位依据。

结语

eBPF 是 Linux 历史上一次罕见的范式扩张:不 fork 内核、不打补丁、不重启机器,就把内核变成可编程平台。它证明了验证器 + JIT + BTF 这套「带锁链跳舞」的路线可以走得通,也用安全争议提醒所有人:内核可编程性的每一步开放,都要用等量的权限治理来平衡。从 tcpdump 的一行过滤表达式,到每核千万包的丢包机器,这条路走了三十年——下一篇我们用它搭起的工具箱,回答整个系列的终极问题:系统变慢了,怎么找答案。

参考资料

读者留言

COMMENTS 暂无
仅本站原创文章开放留言 · 请勿留下手机号、邮箱等个人信息

还没有留言,来说第一句?