Mininet中虚拟机构成的分析
最近一直在使用Mininet,也逐渐的开始修改Mininet的代码。最初是修改基于Python的命令行,后来写拓扑生成的代码。早这个过程中,我发现mininet所构建出来的虚拟机都是基于当前用户的home文件夹,并且可以调用当前主机上几乎所有的文件、程序。在这个过程中对Mininet的机制越来越感兴趣。找来Nick教授的论文,但是没看几眼就看不动了,学习机制的事也就先放了下来。
昨天,需要在虚拟的host上运行基于scapy的脚本。然而在外面跑得很顺利的代码在Mininet里面连启动都启动不了。错误信息如下:
<br />
mininet> h2 python test_scp.py<br />
Traceback (most recent call last):<br />
File "test_scp.py", line 3, in <module><br />
from scapy.all import *<br />
File "/usr/lib/pymodules/python2.6/scapy/all.py", line 22, in <module><br />
from route import *<br />
File "/usr/lib/pymodules/python2.6/scapy/route.py", line 158, in <module><br />
conf.route=Route()<br />
File "/usr/lib/pymodules/python2.6/scapy/route.py", line 18, in __init__<br />
self.resync()<br />
File "/usr/lib/pymodules/python2.6/scapy/route.py", line 27, in resync<br />
self.routes = read_routes()<br />
File "/usr/lib/pymodules/python2.6/scapy/arch/linux.py", line 148, in read_routes<br />
ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",LOOPBACK_NAME))<br />
IOError: [Errno 99] Cannot assign requested address<br />
单纯从错误名称可以看出来,这是绑定地址/端口的错误。但是这个错误在switch中并不会出现(switch端的测试后于host端)。这让我想到了之前手册上的说明。host和switch/controller的区别主要是namespace。host使用的是独立的namespace,而另外两种虚拟终端用的则是root namespace,也就是当前系统的root用户的命名空间。这两种的区别是什么呢?以至于使得在不同的终端中绑定行为会有不同的结果。查阅资料的时候,我看到了一位复旦童鞋的Mininet笔记。笔记中提到:“mininet使用了linux内核的一个特性:”Network Namespace”…”,同时,作者还不忘给出相关链接。下面是对etwork Namespace的简单介绍:
The network namespace is a private set of network resources assigned
to one or several processes. These have their own set of network
devices, IP addresses, routes, sockets and so on … Other processes
outside of the namespace cannot access these network resources,
neither know they exist.
按照上面说法,不难得出(其实是这段英文下面的具体解释),这样的特性可以做到两件事情:virtualization和isolation。具体来讲,原文还给出了几种应用场景:
- several network namespaces can have eth0 and lo network devices.
- several apache servers listening on *:80 can be launched into different network namespaces.
- a process cannot sniff traffic related to another network namespace.
- a process cannot shutdown an interface belonging to another network namespace.
最基本的原理搞清楚了,如果要更深如的理解Mininet,就需要分析代码+实验,看看Mininet具体使用什么方法建立的这个虚拟网络系统。不过,在这之前,先看一张图片。这张图片是Nick教授团队文章中对于Mininet结构的描述。
借着Namespace的功能来看,Mininet的链接构建部分基本上比较容易理解了。借着这个理解,看看Mininet创建连接的代码究竟是怎么写的。
这事说起来简单,不过Bob童鞋的代码能力实在是牛逼。从net.py的buildFromTopo函数(line272),调用到util.py的createLink函数(line129),再到node.py的linkTo(line336)函数,最后才到了util的makeIntfPair函数(line79)…这么来回来去很多次,终于将topology的描述细化到真正的mn结构体中。下面贴上makeIntfPair函数的代码:
<br />
def makeIntfPair( intf1, intf2 ):<br />
"""Make a veth pair connecting intf1 and intf2.<br />
intf1: string, interface<br />
intf2: string, interface<br />
returns: success boolean"""<br />
# Delete any old interfaces with the same names<br />
quietRun( 'ip link del ' + intf1 )<br />
quietRun( 'ip link del ' + intf2 )<br />
# Create new pair<br />
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2<br />
return quietRun( cmd )<br />
最重要的命令在第10行。这一行里面,生成了两个veth口。并且,这两个设备是完全对称的。也就是说,除了名字不一样,它们的做用是完全相同的,从一个发出,就会从另一个收到。这样还不算完,如果只是生成两个虚拟网口+一个虚拟链接,数据包在实际通信的时候并不会在这两个的端口传输,而是集中在loopback网口中。为此,Bob童鞋终于用上了之前说到的Network Namespace。在下面的代码中,新创建出来的虚拟网口被移动到了拥有host namespace的node中。这样,先在主机中创建好网口和连接,然后再把其中一个网口挪动到node端,一个veth pair就算完工了。
<br />
def moveIntfNoRetry( intf, node, printError=False ):<br />
"""Move interface to node, without retrying.<br />
intf: string, interface<br />
node: Node object<br />
printError: if true, print error"""<br />
cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid )<br />
quietRun( cmd )<br />
links = node.cmd( 'ip link show' )<br />
if not ( ' %s:' % intf ) in links:<br />
if printError:<br />
error( '*** Error: moveIntf: ' + intf +<br />
' not successfully moved to ' + node.name + 'n' )<br />
return False<br />
return True<br />
然后,开始实验~把Mininet搭出来的拓扑拆掉~
执行语句infoall,查看所有host的信息:
<br />
mininet> infoall<br />
Print info for all hosts.<br />
mn: <mininet.net.Mininet object at 0x8ce970c><br />
*** infoall: Print all the host info<br />
h2: IP=192.168.1.10 intfs=h2-eth0 pid=8328<br />
h3: IP=192.168.1.11 intfs=h3-eth0 pid=8329<br />
然后在Mininet外查看进程信息
<br />
fnl@fnl-OptiPlex-790:~$ ps -A | grep 832*<br />
983 ? 00:00:00 dbus-daemon<br />
8321 pts/5 00:00:00 mn<br />
8327 ? 00:00:00 bash<br />
8328 ? 00:00:00 bash<br />
8329 ? 00:00:00 bash<br />
8330 ? 00:00:00 bash<br />
如果kill掉bash进程,然后再执行infoall命令,则会报“OSError: [Errno 32] Broken pipe”错误。很明显,Mininet与bash进程之间的通信被打断了,但是ping还是通的(Node实例还在)。但是,如果把switch和host之间的veth连接断掉呢?执行下面语句,断掉网口的veth连接(因为是对称结构,所以方案只有一种:直接删掉交换机端虚拟网口…)
sudo ip link delete s1-eth1
结果很明显,ping不通了。如果拿infoall命令看各个host的信息,就会发现虽然虚拟网口、pid还在,但是IP已经变成了None:
<br />
mininet> infoall<br />
Print info for all hosts.<br />
mn: <mininet.net.Mininet object at 0x9c2770c><br />
*** infoall: Print all the host info<br />
h2: IP=10.0.0.2 intfs=h2-eth0 pid=20329<br />
h3: IP=None intfs=h3-eth0 pid=20330<br />
到现在,模拟虚拟主机的做法已经比较清楚了。唯一剩下的问题就是:在不同namespace之间通信的mn类驶如何实现呢?这个留到下次写~
至于遇到的问题…很简单,把回环网卡打开,一切恢复初…【早怎么没想到呢?