1.实验目的
熟悉Mininet自定义拓扑脚本的编写与损耗率的设定;
熟悉编写POX脚本,测量路径损耗速率。
2.实验原理
在SDN环境中,控制器可以通过对交换机下发流表操作来控制交换机的转发行为,此外,还可以利用控制器测量路径的损耗率。在本实验中,基于Mininet脚本,设置特定的交换机间的路径损耗速率,然后编写POX脚本,实现对路径的损耗率的测量。
3.实验任务
本次实验的拓扑图如下。
在该环境下,h0向h1发送数据包,由于在mininet脚本中设置了连接损耗率,在传输过程中会丢失一些包,本次实验的目的是展示如何通过控制器计算路径损耗速率(h0-s0-s1-h1)。这里假设控制器预先知道网络拓扑,所以我没有显示发现网络的代码以及其他相关代码。控制器将向s0和s1发送flow_stats_request,当控制器接收到来自s0的response时,将特定流的数据包数保存在input_pkts中,当控制器接收到来自s1的response时,将接收到特定流的数据包数保存在output_pkts中,差值就是丢失的数据包数量。
4.实验步骤
4.1 搭建环境
本实验需要安装POX、支持OpenFlow1.3协议的Mininet。
4.2 实验操作
步骤1:编写Mininet脚本
1在装有Mininet的虚拟机里新建文件mymininet.py。
#touch mymininet.py
2 编辑文件mymininet.py,内容如下:
#!/usr/bin/python from mininet.net import Mininet from mininet.node import Node from mininet.link import TCLink from mininet.log import setLogLevel, info from threading import Timer from mininet.util import quietRun from time import sleep def myNet(cname='controller', cargs='-v ptcp:'): "Create network from scratch using Open vSwitch." info( "*** Creating nodes\n" ) controller = Node( 'c0', inNamespace=False ) switch = Node( 's0', inNamespace=False ) switch1 = Node( 's1', inNamespace=False ) h0 = Node( 'h0' ) h1 = Node( 'h1' ) info( "*** Creating links\n" ) linkopts0=dict(bw=100, delay='1ms', loss=0) linkopts1=dict(bw=100, delay='1ms', loss=10) link0=TCLink( h0, switch, **linkopts0) link1 = TCLink( switch, switch1, **linkopts1) link2 = TCLink( h1, switch1, **linkopts0) #print link0.intf1, link0.intf2 link0.intf2.setMAC("0:0:0:0:0:1") link1.intf1.setMAC("0:0:0:0:0:2") link1.intf2.setMAC("0:1:0:0:0:1") link2.intf2.setMAC("0:1:0:0:0:2") info( "*** Configuring hosts\n" ) h0.setIP( '192.168.123.1/24' ) h1.setIP( '192.168.123.2/24' ) info( "*** Starting network using Open vSwitch\n" ) switch.cmd( 'ovs-vsctl del-br dp0' ) switch.cmd( 'ovs-vsctl add-br dp0' ) switch1.cmd( 'ovs-vsctl del-br dp1' ) switch1.cmd( 'ovs-vsctl add-br dp1' ) controller.cmd( cname + ' ' + cargs + '&' ) for intf in switch.intfs.values(): print intf print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf ) for intf in switch1.intfs.values(): print intf print switch1.cmd( 'ovs-vsctl add-port dp1 %s' % intf ) # Note: controller and switch are in root namespace, and we # can connect via loopback interface switch.cmd( 'ovs-vsctl set-controller dp0 tcp:10.0.0.13:6633' ) switch1.cmd( 'ovs-vsctl set-controller dp1 tcp:10.0.0.13:6633' ) info( '*** Waiting for switch to connect to controller' ) while 'is_connected' not in quietRun( 'ovs-vsctl show' ): sleep( 1 ) info( '.' ) info( '\n' ) #info( "*** Running test\n" ) h0.cmdPrint( 'ping -Q 0x64 -c 20 ' + h1.IP() ) sleep( 1 ) info( "*** Stopping network\n" ) controller.cmd( 'kill %' + cname ) switch.cmd( 'ovs-vsctl del-br dp0' ) switch.deleteIntfs() switch1.cmd( 'ovs-vsctl del-br dp1' ) switch1.deleteIntfs() info( '\n' ) if __name__ == '__main__': setLogLevel( 'info' ) info( '*** Scratch network demo (kernel datapath)\n' ) Mininet.init() myNet()
步骤2:编写POX脚本
1 在装有POX的虚拟机里新建文件flow_stats.py。
#cd pox #touch flow_stats.py
2 编辑文件flow_stats.py,内容如下:
#!/usr/bin/python # Copyright 2012 William Yu # wyu@ateneo.edu # # This file is part of POX. # # POX is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # POX is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with POX. If not, see <http://www.gnu.org/licenses/>. # """ This is a demonstration file created to show how to obtain flow and port statistics from OpenFlow 1.0-enabled switches. The flow statistics handler contains a summary of web-only traffic. """ # standard includes from pox.core import core from pox.lib.util import dpidToStr import pox.openflow.libopenflow_01 as of from pox.lib.addresses import IPAddr, EthAddr # include as part of the betta branch from pox.openflow.of_json import * from pox.lib.recoco import Timer import time log = core.getLogger() src_dpid = 0 dst_dpid = 0 input_pkts = 0 output_pkts = 0 def getTheTime(): #fuction to create a timestamp flock = time.localtime() then = "[%s-%s-%s" %(str(flock.tm_year),str(flock.tm_mon),str(flock.tm_mday)) if int(flock.tm_hour)<10: hrs = "0%s" % (str(flock.tm_hour)) else: hrs = str(flock.tm_hour) if int(flock.tm_min)<10: mins = "0%s" % (str(flock.tm_min)) else: mins = str(flock.tm_min) if int(flock.tm_sec)<10: secs = "0%s" % (str(flock.tm_sec)) else: secs = str(flock.tm_sec) then +="]%s.%s.%s" % (hrs,mins,secs) return then # handler for timer function that sends the requests to all the # switches connected to the controller. def _timer_func (): for connection in core.openflow._connections.values(): connection.send(of.ofp_stats_request(body=of.ofp_flow_stats_request())) connection.send(of.ofp_stats_request(body=of.ofp_port_stats_request())) log.debug("Sent %i flow/port stats request(s)", len(core.openflow._connections)) # handler to display flow statistics received in JSON format # structure of event.stats is defined by ofp_flow_stats() def _handle_flowstats_received (event): #stats = flow_stats_to_list(event.stats) #log.debug("FlowStatsReceived from %s: %s", dpidToStr(event.connection.dpid), stats) global src_dpid, dst_dpid, input_pkts, output_pkts #print "src_dpid=", dpidToStr(src_dpid), "dst_dpid=", dpidToStr(dst_dpid) for f in event.stats: if f.match.dl_type==0x0800 and f.match.nw_dst==IPAddr("192.168.123.2") and f.match.nw_tos==0x64 and event.connection.dpid==src_dpid: #print "input: ", f.byte_count, f.packet_count input_pkts = f.packet_count if f.match.dl_type==0x0800 and f.match.nw_dst==IPAddr("192.168.123.2") and f.match.nw_tos==0x64 and event.connection.dpid==dst_dpid: #print "output: ", f.byte_count, f.packet_count output_pkts = f.packet_count if input_pkts !=0: print getTheTime(), "Path Loss Rate =", (input_pkts-output_pkts)*1.0/input_pkts*100, "%" # handler to display port statistics received in JSON format def _handle_portstats_received (event): #print "\n<<<STATS-REPLY: Return PORT stats for Switch", event.connection.dpid,"at ",getTheTime() #for f in event.stats: #if int(f.port_no)<65534: #print " PortNo:", f.port_no, " Fwd's Pkts:", f.tx_packets, " Fwd's Bytes:", f.tx_bytes, " Rc'd Pkts:", f.rx_packets, " Rc's Bytes:", f.rx_bytes #print " PortNo:", f.port_no, " TxDrop:", f.tx_dropped, " RxDrop:", f.rx_dropped, " TxErr:", f.tx_errors, " RxErr:", f.rx_errors, " CRC:", f.rx_crc_err, " Coll:", f.collisions stats = flow_stats_to_list(event.stats) log.debug("PortStatsReceived from %s: %s", dpidToStr(event.connection.dpid), stats) def _handle_ConnectionUp (event): global src_dpid, dst_dpid print "ConnectionUp: ", dpidToStr(event.connection.dpid) for m in event.connection.features.ports: if m.name == "s0-eth0": src_dpid = event.connection.dpid elif m.name == "s1-eth0": dst_dpid = event.connection.dpid msg = of.ofp_flow_mod() msg.priority =1 msg.idle_timeout = 0 msg.match.in_port =1 msg.actions.append(of.ofp_action_output(port = of.OFPP_ALL)) event.connection.send(msg) msg = of.ofp_flow_mod() msg.priority =1 msg.idle_timeout = 0 msg.match.in_port =2 msg.actions.append(of.ofp_action_output(port = of.OFPP_ALL)) event.connection.send(msg) msg = of.ofp_flow_mod() msg.priority =10 msg.idle_timeout = 0 msg.hard_timeout = 0 msg.match.dl_type = 0x0800 msg.match.nw_tos = 0x64 msg.match.in_port=1 msg.match.nw_dst = "192.168.123.2" msg.actions.append(of.ofp_action_output(port = 2)) event.connection.send(msg) msg = of.ofp_flow_mod() msg.priority =10 msg.idle_timeout = 0 msg.hard_timeout = 0 msg.match.dl_type = 0x0800 msg.match.nw_tos = 0x64 msg.match.nw_dst = "192.168.123.1" msg.actions.append(of.ofp_action_output(port = 1)) event.connection.send(msg) # main functiont to launch the module def launch (): # attach handsers to listners core.openflow.addListenerByName("FlowStatsReceived", _handle_flowstats_received) core.openflow.addListenerByName("PortStatsReceived", _handle_portstats_received) core.openflow.addListenerByName("ConnectionUp", _handle_ConnectionUp) # timer set to execute every five seconds Timer(1, _timer_func, recurring=True)
步骤3:运行脚本flow_stats.py。
#./pox.py flow_stats
步骤4:运行脚本mymininet.py。
#chmod +x mymininet.py #./mymininet.py
查看POX控制台信息:
可以看到数据传输路径的平均损耗速率在10%,与在mymininet.py脚本中设置的s0与s1之间的损耗速率相吻合。
5.实验结论
基于Mininet的脚本设计拓扑,主要是对交换机间传输数据包的路径损耗速率进行设置,并且通过控制器POX进行测量。