直接从BPF发送数据包
摘要
BPF程序现在可以使用netpoll基础设施直接从内核空间发送网络数据包,无需用户空间代理,从而提高如Tetragon等安全监控工具的弹性。
<p><a href="https://lobste.rs/s/3ttebv/sending_packets_directly_from_bpf">评论</a></p>
查看缓存全文
缓存时间: 2026/07/25 10:02
# 直接从 BPF 发送数据包
来源:https://lwn.net/Articles/1081696/
> **想试试 LWN 吗?**订阅 LWN,您就能紧跟 Linux 和自由软件社区的动态,并享受仅限订阅者的站点功能。我们很高兴为您提供**免费试用订阅 (https://lwn.net/Promo/nst-trial1/claim)**,无需信用卡,您可亲自体验。请加入我们吧!
基于 BPF 的安全监控工具 Tetragon (https://tetragon.io/) 使用 BPF 监控运行中内核的各个方面,并执行用户指定的策略。但它需要将数据发送到用户空间进程,再由该进程转发给网络中其他位置的集中监控服务。这带来了一个脆弱点:如果攻击者能杀死 Tetragon 的用户空间代理,它就无法正确报告情况。Song Liu、Mahé Tardy 和 Liam Wiseheart 在 2026 年的 Linux 存储、文件系统、内存管理与 BPF 峰会 (https://events.linuxfoundation.org/lsfmmbpf/) 上介绍了他们消除用户空间代理需求的工作。
Wiseheart 澄清说他在 Meta 工作,而非 Tetragon,但他同样有兴趣解决让 BPF 程序完全脱离任何用户空间组件运行的问题。目前 Meta 的做法是在系统启动时固定程序,这至少能防止用户空间组件被杀死后程序被移除,但并未完全避免问题。
Tardy 解释说,Tetragon(想必类似 Wiseheart 的类似程序)与用户空间的通信方式是通过环形缓冲区。用户空间组件主要负责从该环形缓冲区读取消息,发送到远程服务器,接收回复,再将回复放回环形缓冲区。如果 BPF 程序能直接与远程服务器通信,效率会高得多。BPF 程序已经可以拦截传入的网络数据包;缺少的部分是直接从 BPF 发送数据的能力。
在 2025 年,Tardy 和团队曾提出一个使用 `splice()` (https://man7.org/linux/man-pages/man2/splice.2.html) 的解决方案 (https://lwn.net/Articles/1022034/),但该方案不受欢迎。当时 Andrii Nakryiko 认为同步选项可能不适合 BPF。那场会议的内核开发者建议使用 netconsole (https://docs.kernel.org/networking/netconsole.html) 代码,该代码允许内核将原本发往串口的日志消息改为发送到远程位置。
“我们试过了,”Tardy 说,而且似乎可行。Netpoll (https://elixir.bootlin.com/linux/v7.1.2/source/include/linux/netpoll.h#L3),即 netconsole 背后的内核基础设施,允许内核代码在任何上下文中发送数据包,并绕过正常的网络栈,这很方便。于是他们编写了一个 kfunc,并提交了一个补丁集 (https://lwn.net/ml/all/[email protected]/),随后又发布了更新版本 (https://lwn.net/ml/all/[email protected]/)。使用时,用户空间加载程序会将网络地址信息传递给 BPF 程序,程序调用 `bpf_netpoll_create()` 创建 netpoll 上下文,然后用 `bpf_netpoll_send_udp()` 发送包含任意数据的 UDP 数据包。
接着 Tardy 做了一个小演示。他启动了一个虚拟机,运行了一个演示代理来安装一个 BPF 程序,该程序定期向宿主机发送 ping 消息,并在另一个终端上显示这些消息。然后他杀死了用户空间代理,并显示 ping 消息仍在持续到达。他补充说,新的数据包发送函数可以与内核现有的加密 API 结合,发送加密数据包。目前他的演示使用简单的对称密钥,但也可以使用更复杂的方案。
尽管 netpoll 方案可行,但“我们收到了一些反馈,说 UDP 是邪恶的”,Liu 说道。首先,netpoll 发送的数据包绕过正常的网络栈,这意味着如果 BPF 程序发送过多流量,可能会从其他进程那里抢占带宽,而没有真正有效的方法来限制争用。一位听众建议,不必使用 netpoll 从任何内核上下文发送的能力——BPF 接口可以派生一个内核线程,用正常方式发送数据包,这样网络代码可以对流量应用所有正常的网络设置。
Liu 说,在演讲前大约十小时,他们实验了一个发送 TCP 流量而非 UDP 的版本。网络人员对此更放心,但这样 kfunc 就无法在原子上下文中使用了,这与基于内核线程的解决方案类似。这引发了关于 UDP 与 TCP 优劣的广泛讨论,不过在场多数人似乎倾向于 UDP。Alexei Starovoitov 认为没有理由偏好 TCP,尤其是考虑到 netpoll 已经存在并在内核中使用。John Fastabend 认为对于 Tetragon 的用例,UDP 就足够了。Wiseheart 指出,损坏的安全模块挂钩或类似问题更难干扰基于 netpoll 的日志记录,因为 netpoll 绕过了正常的网络栈。
Starovoitov 分享了一个体现这种健壮性的例子:他曾遇到网卡 (NIC) 部分损坏,无法接收数据包,这搞乱了整个网络栈。但 netpoll 代码仍能通过该设备发送数据。另一个人反对说,基于 netpoll 的方案唯一真正的问题是宿主机上的带宽管理,但这确实是个严重问题。Starovoitov 说,netpoll 只使用网卡上的单个队列,他认为这不足以引起问题。Liu 请 Starovoitov 帮忙向网络维护者解释这一观点,Starovoitov 答应了。
Daniel Borkmann 询问有多少网卡驱动支持 netpoll,还是说它是一个通用的工具,能在每个驱动上工作。Starovoitov 认为大约 90% 的驱动不支持 netpoll——要么是故意不支持,要么就是坏了。他说,只有 Meta 经常使用的那些驱动确实正确处理了 netpoll。
似乎没有人不同意这一点,这使得 netpoll 是否真的是最佳方法变得不确定。不幸的是,此时会议时间已到。自会议以来,Tardy 和团队继续研究这个问题,于 7 月 6 日发布了一个新的补丁集 (https://lwn.net/ml/all/[email protected]/),允许 BPF 程序创建和使用 UDP 内核套接字,而不是使用 netpoll。
本文的索引条目
会议 (https://lwn.net/Archives/ConferenceIndex/)
存储、文件系统、内存管理与 BPF 峰会 / 2026 (https://lwn.net/Archives/ConferenceIndex/#Storage_Filesystem_Memory-Management_and_BPF_Summit-2026)
相似文章
GCC 16及以后版本中的BPF支持
何塞·马奇西(José Marchesi)和GCC-BPF团队提供了GCC 16中BPF支持的更新,突出了在与LLVM功能对等方面取得的进展,以及内核BPF自测通过率的提升。
Linux补丁引入"KNOD",用于将内核网络卸载直接交给AMD GPU
Linux内核补丁引入了KNOD,这是一种用于将内核网络数据包卸载直接交给AMD GPU的机制,能够在无需用户空间依赖(如ROCm)的情况下加速数据包处理。该代码管理GPU队列,对每个数据包程序进行JIT编译,并完全从内核调度工作。
@vivekgalatage: 数据包在Linux内核中的路径 https://net.in.tum.de/fileadmin/TUM/NET/NET-2024-04-1/NET-2024-04-1_16.pd…
这份来自TUM的技术报告详细描述了数据包在Linux内核中的路径,涵盖了网络内部机制。
利用Linux进程进行高性能网络仿真(2022)
本文介绍了Phantom,一个借用Linux进程在高效离散事件网络仿真器中运行未经修改的应用程序的工具,展示了相较于Shadow和NS-3等现有工具的显著速度提升。
SOCKMAP - 未来的TCP拼接
Cloudflare 探索利用Linux内核的SOCKMAP基础设施进行TCP套接字拼接,从而减少用户空间开销,并实现更高效的反向代理数据转发。