SDN实验(一):Mininet的安装问题与Fat-Tree的构建
最初因为大创项目,接触了 OpenStack 相关的知识。但重点不在网络,所以对于其中可以随便定义、删除的网络只是有点好奇,没有多想。恰逢软件定义网络课程实验之要求,上手尝试 SDN 的相关实验,试着折腾这个不怕玩坏的网络。
然而尝试之初不免遇到问题,在此简短记述以备查阅,并可作同道者参考。如有问题,欢迎斧正。
Mininet
按照官网上的描述,Mininet 是一个强大的 SDN 实验环境,可以在单机上完成各种网络拓扑的测试。然而如果把他的代码仓库 clone 下来,会发现代码量真的不是很大——不像是一个大型框架的样子。
大致阅读一下源码,或者将其在debug
级别下启动,就会发现 Mininet 是通过包装 SDN 工具命令来实现构建拓扑的,所以其核心管理功能并不包含软件定义网络的数据平面;而重要的控制节点,也使用外部的ovs-testcontroller
或者pox
、ryu
等等。本质上,Mininet 用于构建网络的操作,也都是可以手动完成的。不过 Mininet 用一个进程来模拟一个 host,让每个 host 可以调用宿主机上几乎所有的程序,确实带来了非常大的方便。
安装
官方文档已经给出了各种安装方案,截至目前,各种方案的版本和环境如下:
- 虚拟机镜像:官方已迁移至 Ubuntu 20.04 LTS,在 GitHub Releases 上可以下载。
- 包管理器:Ubuntu 18.04/20.04 LTS 下,均更新到2.2版本。
- 源码安装:最新的稳定版本为2.3.0版本。
需要注意的是,即使是最新的 Ubuntu 发行版,软件源中也没有最新版的 Mininet 。考虑到 Mininet 是大学开发的教学实验用软件,我认为在新系统下,追最新版本是没问题的。当前 Mininet 官方已经完成了向 Python3 的迁移,也鼓励用户迁移到 Python3,不过 POX 控制器仍然只能在 Python2 下运行。
我本机的环境是 Kubuntu 20.04 LTS, Mininet 2.3.0,附带安装了 POX, Ryu 和 Wireshark。很推荐源码安装时的-w
选项,安装后 Wireshark 就能分清 OpenFlow 的不同 tag,从而能单独抓取某个虚拟交换机的数据。
问题排查和解决
其实这个问题出现得挺让我哭笑不得的。
安装之初,我按照文档说明,进行最简单的测试:
只见每个节点之间 ping 的结果都正常,Mininet 的统计结果却非说节点都不通,还有报错:
翻遍了文档、GitHub Issue、StackOverflow,我也没找到什么解释。不服得很,遂翻找源码。源码组织得比较整齐,于是很快找到了解析 ping 输出的部分:就在mininet/mininet/net.py
当中,是Mininet
类的一个方法。
一看之下,直笑过去了。本以为是调了哪个系统级的 lib 之类,拿着返回值在解析,谁知是直接把ping
命令的输出文字拿来正则匹配了,真有点儿倒拔垂杨柳式的蛮力。
问题其实已经明了:我的 Linux 是双系统,打算长期使用的,因此配置了中文环境、中文输入法种种,ping
命令的输出也被本地化成了中文。这样一来,用来匹配英文输出的正则永远失败,Mininet 就认为ping
都不通。
我考虑的解决方法是设置临时环境变量。根据以前对 Linux 的了解,本地化工作应该受LANG
这个环境变量控制,于是改变运行 Mininet 的方式:
以期 Linux 按照en_US
来本地化输出,避免输出中文。结果是成功的,不过总这样有点麻烦,可以设置别名:
第一条让sudo
命令也查找别名(默认是不查找的),第二条就是设置语言环境。这两个别名要全局生效,可以放在/etc/profile
或者/etc/bash.bashrc
当中。
装系统只为用来实验、语言环境是英文的同学,完全不会有这个问题——这就是各种资料上找不到的原因。
Fat-Tree 拓扑的搭建
关于 Fat-Tree,我参考的主要是这篇文章: Fat-tree:A Scalable, Commodity Data Center Network Architecture 解读
在构建 Fat-Tree 时,我是用自底向上的顺序连接节点,将每个 Pod 都存放在一个 Object 里。具体代码就不贴了,想来思路上大同小异,不会有太多的差别。
在实现了一个FatTreeTopo
类之后,我用一个小函数来测试:
然而在解决了上面的一切问题后,还是所有的ping
都不通,且这次是真的不通了,h1 ping -c1 h4
这样的命令显示完全是访问不到的。于是先尝试着建一个 k = 2 的 Fat-Tree,每个 Pod 只有两台交换机,方便调试。奇怪的是,在 k = 2 的情况下,一切都工作正常。
在没有头绪的情况下,我只好重新建起 k = 4 的拓扑,再打开 Wireshark 抓包。这里让h1
连续尝试ping
另一个 Pod 的主机h6
,让 Wireshark 抓取连接了h1
的边缘交换机上通过的数据包,过滤规则是icmp
。当我在h1
上发起ping
时,发现根本抓不到想要的 ICMPv4 数据包,只有疯狂刷屏的 ICMPv6 包,h1
发出的 ARP 包完全淹没在其中,h6
连 ARP 应答都没有作出。
又是一阵好找,找着找着摸到一点线索:k = 4 和 k = 2 情况最大的不同,应该是拓扑中出现了环路。这时,用来探测网络连接、更新路由等信息的数据包可能形成广播风暴,让网络处于极度拥塞的状态下。最终搜出了官方 Wiki 上的一条解答:
其中提到,在这种情况下,应该为网络开启生成树协议(STP),这样可以明确转发路径,进而避免数据包在一个环上来回转发。对应到实现上,需要修改的是addSwitch
部分,为其增加一些参数:
这里指定使用OVSBridge
类型的交换机,stp=1
开启 STP 协议。另外,开启 STP 后需要等待交换机连接,所以脚本中
是必不可少的,在连接上之前,网络都是不通的。或者也可以在 CLI 下调用这个方法:(启动时创建的Mininet
实例一般都叫做net
)
再启动拓扑进行测试,一切就都符合预期了。
一些其他用途
Mininet 用来当网络测试工具也不错。在启动时,加上 NAT 选项,就可以让 host 接入外部网络:
这个网络环境非常干净、简单(没防火墙、没拥塞,甚至没多少流量),还比用多台虚拟机组网轻量许多。通过 Xterm 或者 SSH 连接到 host 之后,网络实验中的 ARP 攻击、DNS 攻击等等都可以在这里尝试,并且完全没有后遗症。sudo mn -c
之后,一切灰飞烟灭不留痕迹,纵然南朝四百八十寺,再无楼台烟雨中。
一点小问题:host 的 DNS 解析跟随宿主机设置,所以如果系统中有
systemd-resolved
这个服务,需要暂时将/etc/resolv.conf
这个文件中的nameserver
项修改成一个可用的 DNS 服务器地址。 原先的nameserver
一般是127.0.0.53
,这是systemd-resolved
维护的一个本地 DNS,经过它指向真正的 DNS 服务器,但是 Mininet 并不能使用这个东西。 注意玩完之后,还是把这一项该回来为好,虽说哪怕不改重启后也会恢复。
另外,在addLink
时,Mininet 提供了限制带宽的选项。如果想要观察拥塞之类的现象,完全可以在添加连接时就把带宽限得很低,本机上的网络环境总是比复杂的外网要好下手许多。