BPF 许可

背景

  • 经典 BPF 采用 BSD 许可

“BPF”最初在 http://www.tcpdump.org/papers/bpf-usenix93.pdf 中作为 BSD Packet Filter 引入。相应的指令集及其实现源自 BSD 并采用 BSD 许可。该原始指令集现在被称为“经典 BPF”。

然而,指令集是机器语言交互的规范,类似于编程语言。它不是代码。因此,在特定上下文中,应用 BSD 许可可能会产生误导,因为指令集可能不受版权保护。

  • eBPF(扩展 BPF)指令集仍采用 BSD 许可

2014 年,经典 BPF 指令集得到了显著扩展。我们通常将此指令集称为 eBPF,以与 cBPF 区分开来。eBPF 指令集仍采用 BSD 许可。

eBPF 的实现

使用 eBPF 指令集需要在内核空间和用户空间中都实现代码。

在 Linux 内核中

eBPF 解释器和各种即时编译器的参考实现是 Linux 的一部分,并采用 GPLv2 许可。eBPF 辅助函数的实现也采用 GPLv2 许可。解释器、JIT、辅助函数和验证器统称为 eBPF 运行时。

在用户空间中

还有采用 Apache2 (https://github.com/iovisor/ubpf)、MIT (https://github.com/qmonnet/rbpf) 和 BSD (https://github.com/DPDK/dpdk/blob/main/lib/librte_bpf) 许可的 eBPF 运行时(解释器、JIT、辅助函数)实现。

在硬件中

硬件可以选择原生执行 eBPF 指令,并通过使用专有许可的固件来提供硬件中的 eBPF 运行时。

在其他操作系统中

eBPF 指令集和运行时的其他内核或用户空间实现可以采用专有许可。

在 Linux 内核中使用 BPF 程序

Linux 内核(尽管采用 GPLv2 许可)允许根据以下规则链接专有内核模块:Linux 内核许可规则

当加载内核模块时,Linux 内核会检查它打算使用的函数。如果任何函数被标记为“仅限 GPL”,则相应的模块或程序必须具有与 GPL 兼容的许可。

将 BPF 程序加载到 Linux 内核中类似于加载内核模块。BPF 是在运行时加载的,而不是静态链接到 Linux 内核。BPF 程序加载遵循与内核模块相同的许可检查规则。如果 BPF 程序不使用“仅限 GPL”的 BPF 辅助函数,则它们可以是专有的。

此外,截至 2021 年 8 月,某些 BPF 程序类型——Linux 安全模块 (LSM) 和 TCP 拥塞控制 (struct_ops)——即使不直接使用“仅限 GPL”的辅助函数,也要求与 GPL 兼容。Linux 内核的 LSM 和 TCP 拥塞控制模块的注册步骤是通过 EXPORT_SYMBOL_GPL 内核函数完成的。从这个意义上说,LSM 和 struct_ops BPF 程序是隐式调用“仅限 GPL”函数的。同样的限制也适用于通过不稳定接口(也称为“kfunc”)直接调用内核函数的 BPF 程序。

将 BPF 程序与用户空间应用程序打包

通常,同一包中采用专有许可的应用程序和为 Linux 内核编写的 GPL 许可的 BPF 程序可以共存,因为它们是独立的可执行进程。这适用于 cBPF 和 eBPF 程序。