# FreeBSD 接口 API（IfAPI）

* 原文链接：[FreeBSD Interface API (IfAPI)](https://freebsdfoundation.org/our-work/journal/browser-based-edition/networking-10th-anniversary/freebsd-interface-api-ifapi/)
* 作者：Justin Hibbits

如某些人所知，瞻博（Juniper）使用自己定制的网络栈，该网络栈基于 FreeBSD 开发的长期分支，因此在表面上它看起来与当前的 FreeBSD 网络栈相似，但实际上存在诸多差异。当前 FreeBSD 网络栈中的某些状态在 Junos 中并不存在，反之亦然。

## 为什么以及如何实现

Junos 非常庞大。同步 FreeBSD 是一项艰巨的任务。为简化这个过程，Juniper 将 FreeBSD 组件拆分成了独立的仓库，将瞻博的增强功能保留在独立的仓库中。这就带来了一个难题——我们如何在 FreeBSD 中保留驱动程序，而将网络栈放在其他地方？作为分拆 FreeBSD 项目的一部分，提出了原始的 DrvAPI。通过这个 API，驱动程序可以存在于 FreeBSD 仓库中，同时把 Junos 网络栈保持为独立部分。

但什么是网络栈？我们在哪里划定网络栈与其他部分的界限？最初的方法是“sys/ 目录下所有以 `net` 开头的目录”，这种方法有效。然而，最近添加了组件 `netlink` ，从概念上来说，它并不属于网络栈的一部分，所以这一方法被抛弃了。现在，网络栈有 `net`、`net80211`、`netgraph`、`netinet`、`netinet6` 和 `netpfil`。保持网络栈的细节只关乎网络栈本身，也让核心内核的细节得以遮蔽。某些内核的其他部分也需要做出调整，以适应 IfAPI，包括 NFS 无根（boot）和 mbuf 处理。

## 设计

当前 IfAPI 的设计主要是访问器和迭代器。这种方法被认为是将驱动程序转化并遮蔽 `struct ifnet` 的最便捷方式，尽管它并非最理想的方式。转化过程大多是自动化的，Juniper 提供了一个 shell 脚本 `tools/ifnet/convert_ifapi.sh` 来解决大部分转化。显然，这可能会漏掉一些转化，例如当 `ifp` 是另一个结构体的成员，或者其名称是其他形式（如 `foo_ifp`）时，但它能处理大多数情况。

至于迭代器，最初的实现基于 Gleb Smirnoff 的 `if_foreach_lladdr()`，在迭代给定类型时使用回调函数。这被应用于 `if_addr` 和 `if_t`，在迭代接口时只会遍历当前的 VNET。最近，增加了一种新的迭代器 API，能使用更传统的循环进行迭代；要求是你必须调用 `if_iter_finish()`/`ifa_iter_finish()` 来清理迭代所设置的任何状态，包括释放实现可能分配的任何内存（FreeBSD 网络栈未做此处理，但其他栈可以做到）。

## 好处

将设备驱动程序与网络栈细节解耦，不仅对瞻博的源代码管理有益，还带来了一些额外的好处。通过稳定的 ABI，单个设备驱动程序能与多个不同的网络栈一起使用。例如，在数据中心的所有计算机上，在启动时，某款镜像能选择不同的网络栈，依据执行配置选择一款高性能的有限网络栈用于某些设备，而为其他设备使用完整的网络栈，所有设备仍使用相同的网络驱动程序。

另一个较小的好处是，驱动程序更改和网络栈更改可以同时发生，而不会互相干涉。在 IfAPI 出现之前，对 `struct ifnet` 的任何更改都需要重新构建所有设备驱动程序。现在，由于 `struct ifnet` 是完全私有的，任何对该结构的更改只需要重新构建那些直接引用它的文件，从而缩短了调试周期。

## 接下来会做什么？

IfAPI 只是第一步，仍然有很多工作要做，以便真正地抽象化网络栈。Gleb Smirnoff 提出了使用 KOBJ 接口来允许一个更加可插拔的网络栈，并完全解耦网络栈与其他部分。这甚至可以允许在运行时替换网络栈（`kldunload/kldload`）。进一步来说，我们甚至可以允许多个网络栈，并将不同的设备分配到不同的网络栈中。如此一来，甚至可能实现动态地在网络栈之间移动接口。

## 结论

IfAPI 只是解耦网络驱动程序与网络栈内部实现之间关系的第一阶段。随着进一步的工作，多个网络栈可以同时使用——甚至是可重新加载的网络栈。

***

**Justin Hibbits** 于 2011 年因其对 PowerPC 的执着被愚蠢地授予了 FreeBSD 提交权限。从那时起，他专注于 PowerPC 和其他嵌入式架构的工作。目前他在瞻博 Networks 工作，负责所有与 FreeBSD 内核相关的事务，并继续他对底层开发和异构架构的热情。
