BSD 路由器项目
作者:Olivier Cochard-Labbé
我的 FreeNAS 经历
先从我自己的 FreeNAS 经历说起。2005 年 10 月,我想为家用寻找一套小型 NAS 软件方案,却找不到符合需求的——我的需求是从 16MB 的小型 USB 闪存盘引导,并用 4 块 PATA 硬盘组建软件 RAID 5。于是我决定自己来做。我当时是 Linux/Windows 的高级用户,掌握每位网络工程师都必须具备的有限 shell 脚本知识。我尝试构建一套基于嵌入式 Linux 的方案,类似于我的 GeeXboX 家庭媒体中心,但我的技能甚至迈不过 busybox 编译的第一步。几天后,在为家里的 m0n0wall 防火墙做维护时,我产生了把这套现成软件改造成 NAS 的念头。正是在研究 m0n0wall、用 Samba 和 ftp 替换防火墙功能的过程中,我结识了 FreeBSD。我边做边学了一些 PHP 编程基础,一周后就发布了第一个版本。我给它取了个简单的名字——FreeNAS,并把项目发布到网上,以防其他人和我有相同的小型 NAS 需求。把这个个人项目发布到网上改变了我的人生,把我引入了开源软件背后那个不可思议的社区。
当时的处境是:
我不喜欢编写 WebGUI,因为我既没时间、也没意愿去学 JavaScript 来打造酷炫的 AJAX 用户界面;
我并不是存储领域的行家。除了 FreeNAS 我从没碰过别的 NAS,而这个本应仅限家用的项目却被越来越多企业采用。读着用户提出的功能需求清单,我才了解到专业 NAS 所要求的标准特性(比如快照),这让我颇感不安。举个例子,我是在 2007 年 BSDCan 上首次展示 FreeNAS 时才了解到 ZFS 的,时至今日我仍不太确信把这么复杂的文件系统用于家用是否合理;
我有全职工作(网络工程师)。FreeNAS 只是爱好,但处理 m0n0wall 补丁出乎意料的成功占用了我大量业余时间,这让我不得不重新审视自己在项目中的投入。同时,我也无法为存储技术指明清晰的方向,更无法理解诸如 CIFS 与 Unix 文件权限映射之类的某些用户问题。
我的目标是让 FreeNAS 始终是一款 NAS,而不是变成提供打印或 BitTorrent 客户端服务的通用服务器,但我理解用户想要这些功能的心情。解决方案是加入插件机制,在保持以 NAS 作为核心功能的同时,让用户在愿意时把它转成通用服务器。到这一步,我考虑过对 FreeNAS 做彻底重写,因为 m0n0wall 基础——它使用 mfsroot——并不便于添加插件。这一时期我发现了 nanoBSD,但有限的业余时间让我无法亲自动手(孩子简直是靠业余时间维护的开源项目最大的敌人!)。内部团队讨论聚焦于下一代 FreeNAS 的新基础,当主力开发者(这一时期已非我本人)公布采用 Linux Debian 作为新版本基础的设想时,在网上引发了意料之外的热议。iXsystems 注意到这一反响后主动联系我,并提出协助开发。我决定把整个项目无偿交给他们,因为我一直尽量让金钱远离我的爱好。
2009 年 12 月从 FreeNAS 中解脱出来后,我的想法是启动一个新项目,不过这次要选我更熟悉的领域。它将是一款基于 FreeBSD 的软件路由器,因为 FreeBSD 是唯一让我有信心的操作系统。
我的目标包括:
不面向家庭用户,因为已有 m0n0wall(如今被 t1n1wall 或 SmallWall 取代)或 pfSense(或 OPNsense)这样的 WebGUI 防火墙在做这件事;
使用 nanobsd 作为基础,因为我讨厌重复造轮子。nanobsd 是构建基于 FreeBSD 的设备的绝佳工具;
不设 WebGUI。我实在想象不出如何用 GUI 来表达路由器配置。而我此前使用 Nortel Networks 的“图形化”Site Manager 软件配置 AN/ARN/ASN 路由器的不愉快经历也无益于此。我觉得直接管理一个文本文件更简单。这一“特性”也顺带把家庭用户挡在了项目之外;
配置管理。起初我考虑加入 NETCONF 支持,但其惊人的复杂度(涉及 20 多个 RFC)和缓慢的部署节奏让我重新斟酌,转而选择 Ansible 这类标准且广为人知的工具;
我相信软件路由器(2009 年之前),但总觉得通用 x86 服务器软件转发的性能有点不对劲。它们的 CPU 和网卡都很强劲,但转发速率却非常低;几年后 netmap 的出现证实了这其实只是软件问题。
使用 BSDRP 的好处
BSDRP 是一张面向路由器用途定制的 nanobsd 磁盘镜像。它是标准的 FreeBSD,行为与网络设备相同,并被网络管理员所接受。
系统升级就像老式 Cisco IOS。你只需安装新的固件文件并重启,这得益于 FreeBSD 的 POLA(最小惊讶原则),让你在升级系统时无需对既有配置文件做重大改动;
文件系统的只读模式让你可以放心地给设备断电再上电而不会出问题;
体积小巧。一块 512MB 的闪存盘对 BSDRP 就足够了。压缩后的升级文件约 40MB。
默认的 nanobsd 镜像构建脚本允许你把现有软件包加入最终的 nanobsd 镜像。BSDRP 使用了一份高度定制的 nanobsd 配置文件,允许在 nanobsd 镜像生成期间构建 Ports(带有特定的编译选项)。该特性还支持从 amd64 主机交叉编译 i386 镜像。
BSDRP 镜像针对 i386 和 amd64 架构发布,nanobsd 脚本经过改进也可以生成 sparc64 镜像。不过自从我最后一台 sparc64 服务器报废之后,我就停止发布 sparc64 镜像了。
当前包含的软件包有:
Quagga 和 Bird 作为单播路由守护进程;前者面向老 Cisco 用户,后者是下一代路由软件;
mrouted、pimd 和 pimdd 作为多播路由守护进程;
原生 carp、ucarp 和 freevrrpd 用于高可用。ucarp 的存在让你可以在某些接口上使用 carp,而在另一些接口上使用 freevrrpd;
原生 netgraph netflow 和 pmacct 用于 IP 计费。启用 netgraph 会对转发性能产生负面影响,因此才有了 pmacct;
mpd5 用于支持所有高级 PPP 协议;
OpenVPN、ipsec-tool 用于 IKEv1,strongswan 用于 IKEv2;
Python,因为这门语言越来越被网络管理团队采用。这也让 BSDRP 可以由 Ansible 管理;
Exabgp,基于 Python 的工具——BGP 路由的瑞士军刀;
iperf 和 netmap 的 pkt-gen,用于基准测试;
ISC-DHCP server 和 dhcprelay;
tayga,用户态的无状态 NAT64 守护进程;
以及 monit 作为进程监控。
为此软件编写了一些专用工具和脚本:
config——允许保存/回滚/发送/接收系统配置;system——允许升级系统并检查其完整性;tuning——尝试采集系统数据(CPU 数量、网卡型号等),并推荐能获得最佳转发性能的调优值;equilibrium——帮助对 VPN 配置(IPSec、OpenVPN 等)进行基准测试;quagga-bgp-netgen——使用 quagga 的非常简单的 BGP 路由生成器;以及一些针对路由器的 tcsh 命令补全。
测试与文档化用例
BSDRP 镜像生成之后,我得搭建一个网络实验室,但要搭建网络就需要多台路由器。我为不同的 hypervisor(bhyve、virtualbox 和 qemu)编写了一些 shell 脚本,可以用一条命令轻松生成多台路由器的全互联网络。我主要的实验用 bhyve 完成,因为虚拟机速度惊人。但 bhyve 提供的 virtIO 网卡不支持 ALTQ,于是我不得不改用 VirtualBox,它能模拟兼容 ALTQ 的虚拟网卡。
我的设想是在 BSDRP 网站上发布大量网络实验示例——包括完整的配置——但目前仅有少量示例可用(BGP、OSPF、多播、VRRP、PPP、GRE、GIF、IPsec)。
转发性能基准测试
BSDRP 应当默认调优到能提供最佳的 FreeBSD 转发性能。但如何为路由器用途调优 FreeBSD 呢?网上有许多“调优指南”,但大多数只给出魔法数值,却不解释为何某些值更好。我的想法是聚焦于某个具体变量,并针对该变量测试一组不同的取值。为此,我必须先学会如何正确地做基准测试,因为在这一过程中我意识到,即便对经验丰富的网络人而言,做基准测试也是一项复杂的工作。
基准测试一台路由器的主要参数有哪些?好在有一些 RFC 说明了如何测量路由性能,比如 RFC 1242、2544、3222 和 5180。但到了测量 IPSec/GRE 隧道等更高级特性时,就没有官方指南了。
我把基准测试方法适配到自己的“终端用户”用法。我既不是硬件厂商,也不是路由软件卖家,所以我并不在意用多种帧长做测试,也无意像防火墙厂商那样只呈现“最佳”画像——他们在以 Mb/s 展示其出众的防火墙性能时……只采用巨型帧。我只关心“最坏”场景,也就是只用最小帧长做基准测试。这意味着我的许多基准测试名称其实可以改作“拒绝服务期间的转发包数”。
但 RFC 并未给出做基准测试的全部细节。比如 RFC2544 要求多次试验。可是多少次?又该如何处理多次试验的结果?
答案来自阅读 FreeBSD 邮件列表:列表上发表的许多基准测试结果都借助 ministat 工具。ministat 计算试验结果的基本统计属性:最小值、最大值、中位数、平均值和标准差。这给出了“多少次试验?”这一问题的第一个答案:至少三次,但越多越好。在我的基准测试中,由于被测设备(DUT)在每次试验之间都要重启,我受限于通用服务器冗长的 BIOS POST 启动时间。一次 30 秒的小测试,可能要为我的 HP 或 IBM 服务器损失大约 4 分钟的启动时间(而我的测试往往需要 200 次以上重启)。所以对于 BIOS POST 慢的服务器,我把试验次数限制为 5 次;对于 BIOS POST 快的设备(如 PC Engine APU 或 Netgate 的 RCC-VE 设备),则增加到 10 次。
这意味着,如果你在网上看到任何只呈现一个测量值(既无标准差、也无试验次数)的所谓“基准测试”,你大可忽略它。一旦收集齐输入——待测参数、方法、试验次数——我就编写了一个 shell 脚本来自动化基准测试。呈现最终结果并不容易,因为我需要在最终图表上表现误差线概念,更重要的是要拟一个好标题。举个例子,我那张经典的“服务器汇总性能”图表显示三个柱形:一个是 fast-forwarding,第二个是启用 IPFW,最后一个是启用 PF。
一些读者误读这些结果,认为 IPFW 的每秒包数(PPS)值更高,所以它比 PF 更好。但这并不正确。基准测试防火墙属于一个截然不同(也更复杂)的世界,而且希望防火墙不应被简化为 PPS 性能。我不得不在图表上用很长的标题来避免这种解读。对于关注以“FreeBSD 11-routing”之名展示的惊人数值的人,那是 Alexander V. Chernikov 在公共 FreeBSD svn 上的 projects/routing。
把结果发布到网上总是充满挑战,因为它会受到质疑,你必须确保其中没有错误。我最不愿意做的就是浪费开发者的时间去处理一个并不存在的问题。好在我早期的错误很快就被社区指出。举例来说,起初我只生成一条 IP 流量,这无法用上网卡的全部多队列特性。第二个错误是没有禁用绘图软件的自动缩放,结果图表人为放大了结果之间的差异。
呈现数据的最后一个问题是单位。路由器的主要性能指标是每秒包数,但普通用户期望的是以 Mb/s(更糟的是 MB/s)表示的最大带宽。由于我的基准测试只使用最小包长(64B),可以采用简单 IMIX(Internet Mix)分布来估算以 Mb/s 为单位的等价值。即便简单 IMIX 的参考分布(58% 的 64B 包、33% 的 570B 包和 8% 的 1518B 包)已与当前更趋于双峰的分布(2012 年时小于 100B 的占 40%、大于 1500B 的占 30%)不符,采用简单 IMIX 仍能让我们得到在时间上保持稳定的数值。
既然方法已经确定,我接下来需要添加更多基准测试特性,比如 IPSec/OpenVPN,或一种测试转发性能与路由条数关系的方法。
EINE 子项目是什么?
2014 年,我的雇主 Orange 让我搭建一个概念验证,以便通过互联网部署和管理任意类型的 x86 网络设备,且尽量减少管理开销。我为此复用了 BSDRP,并创建了一个名为 EINE 的子项目,意为“Easy Internet vpN Extender”(简易互联网 VPN 扩展)。这一独特的 nanobsd 镜像安装后可被配置为不同的角色。
Manager(管理者)。它是存放所有设备配置参数和所有角色 Ansible playbook 的主机(以纯文本文件形式)。
VPN 网关。用于终结客户端路由器的 VPN 隧道。
终端客户端,可以是 VPN-Wifi 路由器、串口终端服务器、强制门户设备等。这是该 nanobsd 镜像配置成即插即用的默认配置。即插即用由一块以 DHCP 客户端模式配置、面向互联网的网卡完成,并配合一个使用通用证书连接到 VPN 网关的 openVPN 客户端实现。
当使用通用证书时,设备处于“未注册”状态,其发出的所有流量都被拒绝。设备需要从 Manager 接收它的配置文件(包括证书),才能转为“已注册”状态。
所有的管理任务都从 Manager 完成,Manager 使用简单的辅助脚本(用 Python 编写)和 Ansible。
典型任务包括:
显示连接到所有 VPN 网关的未注册设备列表;
为一台未注册设备分配角色(这只是一次简单的 Ansible 组映射);
升级所有已连接的设备;
删除一台被申报失窃的已注册设备。
值得一提的是,我为本项目选择了 FreeBSD -head,主要出于两个原因:
通过测试 -head 来帮助 FreeBSD;
逼迫自己学习如何搭建持续集成服务器并编写测试(因为 -head 太好用了,我此前一直没腾出时间做这件事)。
计划中的特性
下一个重大变化是测试一种 nanobsd 之外的方案。我目前在用一份高度定制的 nanobsd 脚本,它允许在 nanobsd 镜像生成期间构建、移植或编译 /usr/src/tools 中的部分内容。但 FreeBSD 的 Ports 系统变化很大,跟进其演进要花大量时间。最近 poudriere 增加了以 nanobsd 格式生成镜像和固件的能力。采用这一特性,就能把 Ports 系统的所有变化隐藏在强大的 poudriere 背后。
第二个重大变化是对 FreeBSD 的 projects/routing 及其稳定性做更多测试。我最终会从 release 分支切换到这一分支。最后同样重要的是,我急切地想测试新的 netmap-forwarding 软件(在 BSDCon Brasil 2015 上发布)! •
OLIVIER COCHARD-LABBÉ 拥有 16 年网络工程经验。2005 年,他在为 m0n0wall 打补丁增加 NAS 特性时偶然结识了 FreeBSD。此后他主要通过聚焦网络和维护简单的 Port 为 FreeBSD 做贡献。试图说服同事相信“在通用 x86 服务器上运行开源软件才是未来”,是他工作中最爱做的事。橄榄球、跑步和自由潜水是他的运动爱好。他与妻子和两个年幼的女儿住在法国南特附近(他正设法让孩子们远离电子游戏和电视)。
最后更新于