BPF 许可

背景

  • 经典 BPF 采用 BSD 许可

“BPF” 最初以 BSD 数据包过滤器的形式引入,参见 http://www.tcpdump.org/papers/bpf-usenix93.pdf。 对应的指令集及其实现来自 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 程序。