软件定义网络(SDN)学习笔记(4)--Mininet
Mininet简洁 Mininet是基于Linux C ontainer架构开发的一个进程虚拟化网络仿真工具,可以创建一个含有主机、交换机、控制器和链路的虚拟网络,其交换机支持OpenFlow,具有高度灵活的自定义软件定义网络。Mininet可以用一个命令在一台主机上(虚拟机、云或者本地)以秒级创建一个虚拟网络,并在上面运行真正的内核、交换机和应用程序代码。 Mininet能实现如下功能:
为OpenFlow应用程序提供一个简单、便宜的网络测试平台
启用复杂的拓扑测试,无需连接物理网络
具有拓扑感知和OpenFlow感知的CLI,用于调试或运行网络范围的测试
支持任意自定义拓扑,主机数可达4096,并包括一组基本的参数化拓扑
提供用于网络创建和实验的可扩展Python API
Miniedit可视化,直接在界面上编辑任意拓扑,生成python自定义拓扑脚本,使用Mininet可视化界面方便了用户自定义拓扑创建,为不熟悉python脚本的使用者创造了更简单的环境,界面直观,可操作性强。
Mininet 2.2.0+内置miniedit 。在mininet/examples下提供miniedit.py脚本,执行脚本后显示可视化界面,可自定义拓扑及配置属性。 MiniEdit使用主要分三个步骤:Miniedit启动→自定义创建拓扑,设置设备信息→运行拓扑并生成拓扑脚本。
Mininet 安装与卸载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 sudo apt-get update git clone http://github.com/mininet/mininet.git cd mininet ``cat INSTALL|more cd util/./install.sh -a mn --test pingall mn --version sudo rm -rf /usr/local /bin/mn /usr/local /bin/mnexec /usr/local /lib/python*/*/*mininet* /usr/local /bin/ovs-* /usr/local /sbin/ovs-* sudo apt-get remove mininet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 sudo mv /var/lib/dpkg/info/ /var/lib/dpkg/info_old/ sudo mkdir /var/lib/dpkg/info/ sudo apt-get update sudo apt-get -f install sudo mv /var/lib/dpkg/info/* /var/lib/dpkg/info_old/ sudo rm -rf /var/lib/dpkg/info sudo mv /var/lib/dpkg/info_old/ /var/lib/dpkg/info/ sudo apt-get update sudo apt-get upgrade sudo apt-get autoremove sudo apt-get --purge remove && sudo apt-get autoclean sudo apt-get -f install sudo apt-get update sudo apt-get upgrade && sudo apt-get dist-upgrade sudo dpkg-reconfigure -a sudo dpkg --configure -a
Mininet拓朴构建与命令使用 topo:用于指定网络拓扑,Mininet支持创建的网络拓扑为:minimal、single、linear和tree。
minimal:创建一个交换机和两个主机相连的简单拓扑。默认无—topo参数的情况下就是这样。其内部实现就是调用了single,2对应的函数。
single,n:设置一个交换机和n个主机相连的拓扑。
linear,n:创建n个交换机,每个交换机只连接一个主机,并且所有交换机成线型排列。
tree,depth=n,fanout=m:创建深度为n,每层树枝为m的树型拓扑。因此形成的拓扑的交换机个数为(mn-1)/(m-1),主机个数为mn。
–custom:在上述已有拓扑的基础上,Mininet支持自定义的拓扑,使用一个简单的Python API即可。—custom需和—topo一起使用,如mn —custom file.py -topo mytopo。
网络构建参数使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 sudo mn --topo=single,3 exit sudo mn --topo=linear,3 sudo mn --topo=tree,depth=2,fanout=2 cd /home/openlab/openlab/mininet/customsudo mn --custom topo-2sw-2host.py --topo mytopo --switch=Switch
内部交互命令使用 1 2 3 4 5 6 7 net nodes links pingall xterm h1 h2 exit mn -c
可视化构建网络拓扑
启动
1 2 cd mininet/examples sudo ./miniedit.py
Miniedit拓扑建立:选择左侧的网络组件,在空白区域单击鼠标左键即可添加网络组件,可选择的组件主要有主机、OpenFlow交换机、传统交换机,传统路由器、链路、控制器。
Miniedit拓扑建立:选择左侧的网络组件,在空白区域单击鼠标左键即可添加网络组件,可选择的组件主要有主机、OpenFlow交换机、传统交换机,传统路由器、链路、控制器。
Miniedit全局配置:Miniedit左上角“Edit”中可以剪切删除设备,及对整个网络进行全局配置
Miniedit运行:点击左下角“run”,即可运行设置好的网络拓扑,同时在后台可以看到相应的配置信息。运行后对交换机、主机进行右击长按,可查看交换机的bridge信息及打开Host的终端
Miniedit保存脚本:miniedit设置好拓扑后,可通过选择File-Export Level 2 Script,将其保存为python脚本,默认在mininet/examples目录下,通过chmod给此脚本权限后,直接运行即可重现拓扑
Miniedit脚本执行
1 2 3 4 chmod –R 777 sdnlab.py ./sdnlab.py
Mininet 调用API扩展自定义拓扑 1 2 3 4 5 6 7 8 9 10 11 12 cd mininet/custom sudo mn --custom topo-2sw-2host.py --topo mytopo py net.addHost('h3' ) py net.addLink(s3,net.get('h3' )) py s3.attach('s3-eth3' ) py net.get('h3' ).cmd('ifconfig h3-eth0 10.3' ) dump nodes h1 ping h3 pingall
Mininet可视化构建网络拓扑 1 2 3 4 cd mininet/mininet/examplessudo ./miniedit.py
在控制器上进行鼠标右击长按,选择Properties即可对控制器进行配置,如下所示
在交换机上进行鼠标右击长按,选择Properties即可对交换机进行配置,交换机属性需配置16位的DPID,如下所示:
在主机上进行鼠标右击长按,选择Properties即可对主机进行配置,主机属性需配置IP地址,如下所示。
也可对链路进行属性配置,主要配置带宽、时延、丢包率等,此项可配置亦可不配置,如下所示:
Miniedit全局配置。Miniedit左上角“Edit”中可以剪切删除设备,及对整个网络进行全局配置,如下所示。
Miniedit运行。
Miniedit保存脚本。Miniedit设置好拓扑后,可通过选择“File > Export Level 2 Script”,将其保存为python脚本,默认在mininet/examples目录下。
Miniedit脚本执行。
通过后台查看保存的sdnlab.py脚本文件,并给脚本赋予权限:
1 sudo chmod -R 777 sdnlab.py
Mininet流表应用1——手动添加流表 创建拓扑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 vim ./custom/exper1.py #!/usr/bin/python from mininet.topo import Topo from mininet.net import Mininet from mininet.node import RemoteController from mininet.link import TCLink from mininet.util import dumpNodeConnections class MyTopo( Topo ): "Simple topology example." def __init__( self ): "Create custom topo." Topo.__init__( self ) Host1 = self.addHost( 'h1' ) Host2 = self.addHost( 'h2' ) Host3 = self.addHost( 'h3' ) Switch1 = self.addSwitch( 's1' ) Switch2 = self.addSwitch( 's2' ) self.addLink( Host1, Switch1 ) self.addLink( Host2, Switch1 ) self.addLink( Host3, Switch2 ) self.addLink( Switch1, Switch2 ) topos = { 'mytopo' : ( lambda: MyTopo() ) } sudo mn --custom exper1.py --topo mytopo --controller=remote,ip=127.0.0.1,port=6633
测试流表状态下的通信 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 xterm h1 h2 h3 dpctl dump-flows mininet> dpctl dump-flows *** s1 ------------------------- *** s2 ------------------------- root@sdntest:~/mininet/custom tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on h2-eth0, link-type EN10MB (Ethernet), capture size 262144 bytes PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data. From 10.0.0.1 icmp_seq=1 Destination Host Unreachable From 10.0.0.1 icmp_seq=2 Destination Host Unreachable From 10.0.0.1 icmp_seq=3 Destination Host Unreachable --- 10.0.0.2 ping statistics --- 3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 1999ms pipe 3
添加流表并测试主机间的通信 1 2 3 4 5 6 mininet> dpctl add-flow in_port=1,actions=output:2 dpctl add-flow in_port=2,actions=output:1 mininet> dpctl dump-flows
可以看到主机h1成功ping通h2,且h3没收到任何ping包。原理解析:用dpctl对交换机添加flow,让交换机从s1-eth1这个端口接收到的所有traffic都从s1-eth2这个端口发出去。用dpctl给交换机添加双向流表,因为ping包除了echo request还有echo reply。所以还需要用dpctl对交换机添加flow,让交换机从s1-eth2这个端口接收到的所有traffic都从s1-eth1这个端口发出去。添加这两条flow后,h1能够ping通h2,但是并没有为h1和h3之间添加对应的端口流表,所以h1与h3不通。
添加协议流表使h1/h2通信 1 2 3 4 5 6 7 8 9 10 11 dpctl del-flows dpctl dump-flows dpctl add-flow dl_type=0x0800,nw_dst=10.0.0.2,actions=output:2 dpctl add-flow dl_type=0x0800,nw_dst=10.0.0.1,actions=output:1 dpctl dump-flows ping -c 3 10.0.0.2 ping -c 3 10.0.0.3
原理解析:用dpctl对交换机添加flow,让交换机把所有EtherType为0x0800(IPv4)并且destiation IP为10.0.0.2的traffic从s1-eth2这个端口发出去。用dpctl对交换机添加flow,让交换机把所有EtherType为0x0800(IPv4)并且destiation IP为10.0.0.1的traffic从s1-eth1这个端口发出去。但处在同一网段下的主机,它们之间的交流是L2 forwarding,需要靠ARP来解析MAC地址,之前只匹配了0x0800(IPv4)协议,并没有匹配到0x0806(ARP),这样当交换机收到h1的ARP包后,因为没有控制器,flow table里面也没有相应的flow告诉它如何转发这个ARP包,交换机只能将它丢弃,从而导致h1 ping h2失败,所以需要添加ARP协议的流表来使通信。
1 2 3 4 dpctl add-flow dl_type=0x0806,actions=NORMAL dpctl dump-flows
Mininet流表应用实战2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 netstat -anput|grep 6653 netstat -anput|grep 8181 ./bin/karaf feature:install odl-l2switch-switch-ui odl-openflowplugin-flow-services-ui odl-mdsal-apidocs odl-dluxapps-applications odl-faas-all feature:install odl-restconf feature:install odl-l2switch-switch-ui feature:install odl-openflowplugin-flow-services-ui feature:install odl-mdsal-apidocs feature:install odl-dluxapps-applications feature:install odl-faas-all sudo wireshark sudo mn --custom exper1.py --topo mytopo --controller=remote,ip=119.255.243.31,port=6633 --switch ovs,protocols=OpenFlow13
1 2 3 error: ovs-ofctl:version negotiation failed (we support version 0x01, peer supports version 0x04) 查看ovs中的流表时报错。原因是ovs-ofctl dump-flows命令默认是1.0版本,需要在命令中指定OpenFlow版本 sudo ovs-ofctl dump-flows -O OpenFlow13 s1
OpenFlow协议解析
步骤1 首先发送HELLO消息,建立初始化连接,协商使用的OpenFlow协议版本。由下图可知,ODL与Mininet之间应用的是OpenFlow1.0版本协议。
步骤2 OpenFlow版本协商完成后,控制器发送一条features_request消息获取交换机的特性信息,包括交换机的ID(DPID)、缓冲区数量、端口及端口属性等等。相应的,交换机回复features_reply消息。ofpt_feature_reply数据包详情如下,交换机的DPID是数据通道独一无二的标识符。本实验中交换机缓冲区数量(n_buffers)为256,交换机支持的流表数量(n_tables)为254,交换机所支持的功能,如下所示。
步骤3 stats reply消息用于回应stats request信息,主要是交换机回应给控制器的状态信息
步骤4 当交换机收到数据包后查找流表无匹配项时,将数据包封装在packet_in消息发给控制器,由控制器通过packet_out消息下发决策,使发送和接收数据包的两主机间进行通信。
步骤5 flow mod消息涉及流表项的下发匹配信息,下图显示的是flow mod匹配项的类型信息。
Mininet 多数据中心网络拓扑流量带宽实验 实验内容
通过Mininet模拟搭建基于不同数据中心的网络拓扑。
通过程序生成真实网络流量。
实验原理 数据中心基础 数据中心不仅是一个网络概念,还是一个服务概念,它构成了网络基础资源的一部分,提供了一种高端的数据传输服务和高速接入服务。数据中心提供给用户综合全面的解决方法,为政府上网、企业上网、企业IT管理提供专业服务,使企业和个人能够迅速借助网络开展业务,把精力集中在其核心业务策划和网站建设上,而减少IT方面的后顾之忧。
使用mininet中的iperf工具在网络中生成UDP流量,iperf客户端传送数据流到iperf的服务端,由服务端接收并记录相关信息。网络性能评估中一个巨大的挑战就是如何生成真实的网络流量,可以通过程序来创造人工的网络流量,通过建立测试环境来模拟真实的状况。此应用主要以数据中心网络为目标场景,在mininet仿真环境中尽可能地还原数据中心内部的真实流量情况。 Mininet数据中心应用价值:
树状拓扑结构容错能力强
降低数据中心成本消耗
提供重新排列的全带宽无阻碍路径
提高带宽利用率
分析数据中心网络流量性能
为真实数据中心和仿真测试床提供有用信息
在mininet中进行自定义命令iperfmulti功能拓展主要分为4步:
修改mininet/net.py
修改mininet/cli.py
修改bin/mn
重新安装Mininet核心文件:~/mininet/util/install.sh -n
操作步骤 一、编写网络测试程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 def iperf_single ( self,hosts=None , udpBw='10M' , period=60 , port=5001 ): """Run iperf between two hosts using UDP. hosts: list of hosts; if None, uses opposite hosts returns: results two-element array of server and client speeds""" if not hosts: return else : assert len ( hosts ) == 2 client, server = hosts filename = client.name[1 :] + '.out' output( '*** Iperf: testing bandwidth between ' ) output( "%s and %s\n" % ( client.name, server.name ) ) iperfArgs = 'iperf -u ' bwArgs = '-b ' + udpBw + ' ' print "***start server***" server.cmd( iperfArgs + '-s -i 1' + ' > /home/sdnlab/log/' + filename + '&' ) print "***start client***" client.cmd( iperfArgs + '-t ' + str (period) + ' -c ' + server.IP() + ' ' + bwArgs +' > /home/sdnlab/log/' + 'client' + filename +'&' ) def iperfMulti (self, bw, period=60 ): base_port = 5001 server_list = [] client_list = [h for h in self.hosts] host_list = [] host_list = [h for h in self.hosts] cli_outs = [] ser_outs = [] _len = len (host_list) for i in xrange(0 , _len ): client = host_list[i] server = client while ( server == client ): server = random.choice(host_list) server_list.append(server) self.iperf_single(hosts = [client, server], udpBw=bw, period= period, port=base_port) sleep(.05 ) base_port += 1 sleep(period) print "test has done" def isReadable ( poller ): "Check whether a Poll object has a readable fd." for fdmask in poller.poll( 0 ): mask = fdmask[ 1 ] if mask & POLLIN: return True return False def do_iperfmulti ( self, line ): """Multi iperf UDP test between nodes""" args = line.split() if len (args) == 1 : udpBw = args[ 0 ] self.mn.iperfMulti(udpBw) elif len (args) == 2 : udpBw = args[ 0 ] period = args[ 1 ] err = False self.mn.iperfMulti(udpBw, float (period)) else : error('invalid number of args: iperfmulti udpBw period\n' + 'udpBw examples: 1M 120\n' ) TESTS = { name: True for name in ( 'pingall' , 'pingpair' , 'iperf' , 'iperfudp' , 'iperfmulti' ) } ALTSPELLING = { 'pingall' : 'pingAll' , 'pingpair' : 'pingPair' , 'iperfudp' : 'iperfUdp' ,'iperfMulti' :'iperfMulti' } $ cd openlab/mininet/util $./install.sh -n
二、构建多数据中心网络拓扑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 $ cd /home/openlab/openlab/mininet/custom $ sudo vi fattree.py """Custom topology example Adding the 'topos' dict with a key/value pair to generate our newly defined topology enables one to pass in '--topo=mytopo' from the command line. """ from mininet.topo import Topofrom mininet.net import Mininetfrom mininet.node import RemoteController,CPULimitedHostfrom mininet.link import TCLinkfrom mininet.util import dumpNodeConnectionsclass MyTopo ( Topo ): "Simple topology example." def __init__ ( self ): "Create custom topo." Topo.__init__( self ) L1 = 2 L2 = L1 * 2 L3 = L2 c = [] a = [] e = [] for i in range ( L1 ): sw = self.addSwitch( 'c{}' .format ( i + 1 ) ) c.append( sw ) for i in range ( L2 ): sw = self.addSwitch( 'a{}' .format ( L1 + i + 1 ) ) a.append( sw ) for i in range ( L3 ): sw = self.addSwitch( 'e{}' .format ( L1 + L2 + i + 1 ) ) e.append( sw ) for i in range ( L1 ): sw1 = c[i] for sw2 in a[i/2 ::L1/2 ]: self.addLink( sw2, sw1 ) for i in range ( 0 , L2, 2 ): for sw1 in a[i:i+2 ]: for sw2 in e[i:i+2 ]: self.addLink( sw2, sw1 ) count = 1 for sw1 in e: for i in range (2 ): host = self.addHost( 'h{}' .format ( count ) ) self.addLink( sw1, host ) count += 1 topos = { 'mytopo' : ( lambda : MyTopo() ) }
Mininet创建网络拓扑的代码中,可以通过改变代码中定义的L1变量来设置核心交换机的数量,并通过添加额外的交换机和链路来构成更复杂的数据中心网络拓扑。随着边缘交换机的增加,主机个数也随之增长,利用Mininet的易用性和扩展性,可以创建基于多种数据中心场景下的网络拓扑,达到更好更全面的实验效果。 说明:为方便用户实验,该段代码在/home/ftp/fattree.py文件中已预置。
1 2 sudo mn --custom fattree.py --topo mytopo --controller=remote,ip=119.255.243.31,port=6633 --switch ovs,protocols=OpenFlow13