在一份实验指导书的示例输出里,一台计算机向它的邻居连续发送了两次同样的小数据包。第一次回复用了 2.76 毫秒,第二次只用了 0.052 毫秒。同样的线缆,同样的两台机器,同样的数据包;第二次却快了五十多倍。
这个差别来自第一个数据包在途中必须提出的一个问题:“你打算拿我怎么办?”回答它的不是网络内部的设备,而是运行在网络之外的软件。这正是所谓软件定义网络的核心。
做决定的和听命令的
在传统网络中,每台交换机都自己思考。它学习哪个地址位于哪个端口之后,并自行决定把每个数据包发往哪里。大脑分散在每一台设备上。
软件定义网络把这两项工作拆开了。交换机现在只负责查表:“如果来了这样的包,就那样处理。”这张表由中心的控制器填写,而控制器不过是运行在一台普通服务器上的程序。两者之间的共同语言叫作 OpenFlow。
表中的每一行都有三个部分:看什么(数据包从哪个端口进来、要去哪个地址),做什么(从某个端口发出、丢弃,或者去问控制器),以及优先级。如果有多行同时匹配,优先级高的那一行胜出。
表是空的,网络就是哑的
要看到这一点,并不需要真实的硬件。一个叫 Mininet 的工具可以在一台计算机里虚拟出交换机、线缆和服务器。
搭一个由三台计算机和一台交换机组成的网络,不启动任何控制器,从一台计算机 ping 另一台,就得不到回复。交换机并没有坏,只是它的表是空的,也没有人可以去问。
接着手动往表里写两行:从一号端口进来的都从二号端口出去,从二号端口进来的都从一号端口出去。同样的 ping 现在有了回应。交换机什么也没有学到,它只是照吩咐办事。
规则也有寿命。在实验所用的工具中,手写的表项如果连续六十秒没有匹配到任何数据包,默认就会被删除,ping 随之再次沉默。OpenFlow 在这里定义了两个计时器:空闲超时,只在规则未被使用时才计时;以及硬超时,无论规则被使用了多少次,时间一到就会将其删除。
为什么第一个数据包慢?
现在控制器已经运行起来了。当一台计算机第一次 ping 它的邻居时,交换机里没有与这股流量匹配的表项。表中只有一条优先级最低的兜底规则:“如果什么都匹配不上,就去问控制器。”
交换机把数据包的头部发给控制器。控制器决定这个包该去哪里,往交换机里写入一条新规则,并让它把等待中的数据包从正确的端口发出去。第二个数据包到达时,规则已经在表里了。这个包直接穿过交换机,根本不会到达控制器。
控制器并不会看到每一个数据包。它只听一条流的第一个问题,其余的交给交换机处理。
那 2.76 毫秒的大部分,就是这一来一回的代价。事实上,在第一个 ping 之前还有另一个问题要问:计算机不知道邻居的硬件地址,所以会先发出一个 ARP 请求,向所有人喊话“这个地址是谁的?”这个请求同样会沿着这条路径被送到控制器。
交换机和控制器如何相互认识?
交换机启动时会连接到控制器,双方进行一段简短的自我介绍。用 Wireshark 抓到的消息,一步一步展示了这段对话。
| 消息 | 方向 | 作用 |
|---|---|---|
| HELLO | 双向 | 双方各自声明所支持的最高版本;这里是 0x04,即 OpenFlow 1.3。 |
| FEATURES | 请求与回复 | 交换机报告自己的身份和容量:256 个缓冲区,254 张表。 |
| SET_CONFIG | 控制器发出 | 设定未匹配数据包上送多少字节:128。 |
| PORT_DESC | 请求与回复 | 请求交换机所有端口的列表。 |
| PACKET_IN | 交换机发出 | “这个包我该怎么处理?” |
| FLOW_MOD | 控制器发出 | 在表中添加、修改或删除一条规则。 |
有一个有趣的细节:交换机从不确认 FLOW_MOD 消息。沉默就意味着成功;只有出错时才会返回错误消息。不同版本之间也有差别:在 OpenFlow 1.0 中,端口列表包含在介绍阶段的第一个回复里,而在 1.3 中需要单独请求。
控制器如何绘制地图?
要做决定,控制器需要一张网络地图:哪台交换机连着哪台,经由哪个端口。没有人会把这张地图交给它,它用一个简单的方法自己画出来。
控制器让每台交换机从自己的每个端口发出一个小帧。帧上带着一个标签:“我从这台交换机的这个端口离开。”线缆另一端的交换机不认识这个帧,找不到对应的规则,于是把它连同自己的信息一起交给控制器:“它从我的某某端口进来。”
控制器把这两条信息放在一起,就发现了一根线缆。对每个端口重复这个过程,整张地图就显现出来了。这些帧使用的语言叫 LLDP。它们在以太网类型字段中携带 0x88CC 这个值,目标地址是 01:80:C2:00:00:0E。标准交换机从不转发发往这个地址的帧,所以标签只会走过一根线缆的长度,就被另一侧的交换机交给控制器。
TLV 标签上写着什么?
LLDP 帧由一小段一小段的信息组成,每一段依次写明类型、长度和值。必需的几项是交换机的身份、端口的身份、信息的有效时长以及结束标记。ONOS 控制器还增加了一个“兄弟”,叫 BDDP:思路相同,但发往能到达所有人的广播地址,这样即使中间隔着一台不支持 OpenFlow 的老式交换机,也能发现这条链路。
这种发现并不是做一次就完事,而是定期重复,在 ONOS 中默认每三秒一次。间隔越短,断掉的线缆就能越早被发现,但网络上的控制流量也会越多。间隔越长,负担越轻,代价是地图可能在更长时间里保持过时。
网络是如何崩溃的?
实验中最有启发性的两个实验,都是故意把网络弄坏。
第一个实验把几台廉价交换机连成一个环,而且它们都没有运行防止环路的协议。搭建时没有任何报错,麻烦从第一个 ping 开始。
ping 之前的 ARP 请求是发给所有人的广播。每台交换机都会把它从除入口之外的所有端口发出去。在环形网络里,这个帧会绕圈、回到起点、再次被广播,每绕一圈就成倍增加。这被称为广播风暴:链路被塞满,ping 得不到回应。
IP 数据包每经过一台路由器就减少一个单位的寿命,最终会消亡。以太网帧没有这样的计数器;一旦进入环路,它就会永远循环下去。
在真实网络中,生成树协议可以防止这种情况。它由 Radia Perlman 于 1985 年提出,会关闭部分冗余链路,把环变成树;备用线缆仍然留在原处,只是在某根线缆断开之前不会被使用。
第二个实验模仿数据中心的布局。最上面是一台核心交换机,下面是汇聚交换机,再下面是机架顶部交换机,最底层是服务器。每一层分出 n 个分支;n 等于二时,网络有七台交换机和八台服务器;等于四时,有二十一台交换机和六十四台服务器。
一切正常运行时,每台服务器都能到达其他所有服务器。然后停掉核心交换机,网络一分为二:同一台汇聚交换机下的四台服务器仍然可以相互通信,因为它们之间的路径从不经过核心。但没有任何数据包能从一半跨到另一半。八台服务器之间可能的五十六个通信方向中,有三十二个被切断。
这正是树形布局的薄弱环节:根部只有一台设备,它一旦失效,网络就会碎成一座座孤岛。正因如此,大型数据中心会配置多台核心设备,并在各层之间设置备用路径。
把一个大脑放在中心是好主意吗?
把决策集中到中心有明显的好处。控制器可以在一个地方看到整个网络。想要新的行为时,只需修改一个程序,而不用逐台登录设备。流量可以在掌握全网情况的前提下进行调度。
同样的集中化也带来了新问题。在核心交换机实验中看到的单点故障,会在软件一侧再次出现:如果控制器无法访问,就没有谁能决定如何处理新的流。这就是为什么在实际部署中,控制器会运行在多台互为备份的服务器上。
规模也有其局限。在一台计算机里搭建的网络能忠实地展示概念,却展示不了真实数据中心的时延、流量和故障。这里的计时衡量的是机制如何运作,而不是真实线缆上会发生什么。
本文依据什么?
本文基于 Zhipeng Wang 在埃夫里–巴黎萨克雷大学(Université d'Évry Paris-Saclay)计算机与网络系统硕士项目中所修软件定义网络课程的第二次实验课。ping 的耗时和 OpenFlow 消息中的数值取自实验指导书中的示例输出。
- McKeown et al., OpenFlow: Enabling Innovation in Campus Networks, ACM SIGCOMM Computer Communication Review, 2008.
- Lantz, Heller, McKeown, A Network in a Laptop: Rapid Prototyping for Software-Defined Networks, HotNets, 2010.
- Open Networking Foundation, OpenFlow Switch Specification, Version 1.3.0, 2012.
- IEEE 802.1AB, Station and Media Access Control Connectivity Discovery.
- Perlman, An Algorithm for Distributed Computation of a Spanning Tree in an Extended LAN, SIGCOMM, 1985.