实验原理
在SDN环境中,控制器可以通过对交换机下发流表操作来控制交换机的转发行为,此外,还可以利用控制器测量路径的损耗率。在本实验中,基于Mininet脚本,设置特定的交换机间的路径损耗速率,然后编写POX脚本,实现对路径的损耗率的测量。
拓扑图:
在该环境下,h0向h1发送数据包,由于在mininet脚本中设置了连接损耗率,在传输过程中会丢失一些包,本次实验的目的是展示如何通过控制器计算路径损耗速率(h0-s0-s1-h1)。这里假设控制器预先知道网络拓扑,所以我没有显示发现网络的代码以及其他相关代码。控制器将向s0和s1发送flowstatsrequest,当控制器接收到来自s0的response时,将特定流的数据包数保存在inputpkts中,当控制器接收到来自s1的response时,将接收到特定流的数据包数保存在outputpkts中,差值就是丢失的数据包数量。
实验操作
编写mininet脚本
1 #!/usr/bin/python 2 3 from mininet.net import Mininet 4 from mininet.node import Node 5 from mininet.link import TCLink 6 from mininet.log import setLogLevel, info 7 from threading import Timer 8 from mininet.util import quietRun 9 from time import sleep 10 11 def myNet(cname='controller', cargs='-v ptcp:'): 12 "Create network from scratch using Open vSwitch." 13 info( "*** Creating nodes " ) 14 controller = Node( 'c0', inNamespace=False ) 15 switch = Node( 's0', inNamespace=False ) 16 switch1 = Node( 's1', inNamespace=False ) 17 h0 = Node( 'h0' ) 18 h1 = Node( 'h1' ) 19 20 info( "*** Creating links " ) 21 linkopts0=dict(bw=100, delay='1ms', loss=0) 22 linkopts1=dict(bw=100, delay='1ms', loss=10) 23 link0=TCLink( h0, switch, **linkopts0) 24 link1 = TCLink( switch, switch1, **linkopts1) 25 link2 = TCLink( h1, switch1, **linkopts0) 26 #print link0.intf1, link0.intf2 27 link0.intf2.setMAC("0:0:0:0:0:1") 28 link1.intf1.setMAC("0:0:0:0:0:2") 29 link1.intf2.setMAC("0:1:0:0:0:1") 30 link2.intf2.setMAC("0:1:0:0:0:2") 31 32 info( "*** Configuring hosts " ) 33 h0.setIP( '192.168.123.1/24' ) 34 h1.setIP( '192.168.123.2/24' ) 35 36 info( "*** Starting network using Open vSwitch " ) 37 switch.cmd( 'ovs-vsctl del-br dp0' ) 38 switch.cmd( 'ovs-vsctl add-br dp0' ) 39 switch1.cmd( 'ovs-vsctl del-br dp1' ) 40 switch1.cmd( 'ovs-vsctl add-br dp1' ) 41 42 controller.cmd( cname + ' ' + cargs + '&' ) 43 for intf in switch.intfs.values(): 44 print intf 45 print switch.cmd( 'ovs-vsctl add-port dp0 %s' % intf ) 46 for intf in switch1.intfs.values(): 47 print intf 48 print switch1.cmd( 'ovs-vsctl add-port dp1 %s' % intf ) 49 50 # Note: controller and switch are in root namespace, and we 51 # can connect via loopback interface 52 switch.cmd( 'ovs-vsctl set-controller dp0 tcp:127.0.0.1:6633' ) 53 switch1.cmd( 'ovs-vsctl set-controller dp1 tcp:127.0.0.1:6633' ) 54 55 info( '*** Waiting for switch to connect to controller' ) 56 while 'is_connected' not in quietRun( 'ovs-vsctl show' ): 57 sleep( 1 ) 58 info( '.' ) 59 info( ' ' ) 60 61 #info( "*** Running test " ) 62 h0.cmdPrint( 'ping -Q 0x64 -c 20 ' + h1.IP() ) 63 64 sleep( 1 ) 65 info( "*** Stopping network " ) 66 controller.cmd( 'kill %' + cname ) 67 switch.cmd( 'ovs-vsctl del-br dp0' ) 68 switch.deleteIntfs() 69 switch1.cmd( 'ovs-vsctl del-br dp1' ) 70 switch1.deleteIntfs() 71 info( ' ' ) 72 73 if __name__ == '__main__': 74 setLogLevel( 'info' ) 75 info( '*** Scratch network demo (kernel datapath) ' ) 76 Mininet.init() 77 myNet()
PS:52,53行的地址是pox所在的ip地址,根据实际修改。不然会链接不上控制器。
编写POX脚本
1 # standard includes 2 from pox.core import core 3 from pox.lib.util import dpidToStr 4 import pox.openflow.libopenflow_01 as of 5 from pox.lib.addresses import IPAddr, EthAddr 6 7 # include as part of the betta branch 8 from pox.openflow.of_json import * 9 from pox.lib.recoco import Timer 10 import time 11 12 log = core.getLogger() 13 14 src_dpid = 0 15 dst_dpid = 0 16 input_pkts = 0 17 output_pkts = 0 18 19 def getTheTime(): #fuction to create a timestamp 20 flock = time.localtime() 21 then = "[%s-%s-%s" %(str(flock.tm_year),str(flock.tm_mon),str(flock.tm_mday)) 22 23 if int(flock.tm_hour)<10: 24 hrs = "0%s" % (str(flock.tm_hour)) 25 else: 26 hrs = str(flock.tm_hour) 27 if int(flock.tm_min)<10: 28 mins = "0%s" % (str(flock.tm_min)) 29 else: 30 mins = str(flock.tm_min) 31 if int(flock.tm_sec)<10: 32 secs = "0%s" % (str(flock.tm_sec)) 33 else: 34 secs = str(flock.tm_sec) 35 then +="]%s.%s.%s" % (hrs,mins,secs) 36 return then 37 38 # handler for timer function that sends the requests to all the 39 # switches connected to the controller. 40 def _timer_func (): 41 for connection in core.openflow._connections.values(): 42 connection.send(of.ofp_stats_request(body=of.ofp_flow_stats_request())) 43 connection.send(of.ofp_stats_request(body=of.ofp_port_stats_request())) 44 log.debug("Sent %i flow/port stats request(s)", len(core.openflow._connections)) 45 46 # handler to display flow statistics received in JSON format 47 # structure of event.stats is defined by ofp_flow_stats() 48 def _handle_flowstats_received (event): 49 #stats = flow_stats_to_list(event.stats) 50 #log.debug("FlowStatsReceived from %s: %s", dpidToStr(event.connection.dpid), stats) 51 global src_dpid, dst_dpid, input_pkts, output_pkts 52 #print "src_dpid=", dpidToStr(src_dpid), "dst_dpid=", dpidToStr(dst_dpid) 53 for f in event.stats: 54 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: 55 #print "input: ", f.byte_count, f.packet_count 56 input_pkts = f.packet_count 57 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: 58 #print "output: ", f.byte_count, f.packet_count 59 output_pkts = f.packet_count 60 if input_pkts !=0: 61 print getTheTime(), "Path Loss Rate =", (input_pkts-output_pkts)*1.0/input_pkts*100, "%" 62 63 # handler to display port statistics received in JSON format 64 def _handle_portstats_received (event): 65 #print " <<<STATS-REPLY: Return PORT stats for Switch", event.connection.dpid,"at ",getTheTime() 66 #for f in event.stats: 67 #if int(f.port_no)<65534: 68 #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 69 #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 70 stats = flow_stats_to_list(event.stats) 71 log.debug("PortStatsReceived from %s: %s", dpidToStr(event.connection.dpid), stats) 72 73 def _handle_ConnectionUp (event): 74 global src_dpid, dst_dpid 75 print "ConnectionUp: ", dpidToStr(event.connection.dpid) 76 for m in event.connection.features.ports: 77 if m.name == "s0-eth0": 78 src_dpid = event.connection.dpid 79 elif m.name == "s1-eth0": 80 dst_dpid = event.connection.dpid 81 82 msg = of.ofp_flow_mod() 83 msg.priority =1 84 msg.idle_timeout = 0 85 msg.match.in_port =1 86 msg.actions.append(of.ofp_action_output(port = of.OFPP_ALL)) 87 event.connection.send(msg) 88 89 msg = of.ofp_flow_mod() 90 msg.priority =1 91 msg.idle_timeout = 0 92 msg.match.in_port =2 93 msg.actions.append(of.ofp_action_output(port = of.OFPP_ALL)) 94 event.connection.send(msg) 95 96 msg = of.ofp_flow_mod() 97 msg.priority =10 98 msg.idle_timeout = 0 99 msg.hard_timeout = 0 100 msg.match.dl_type = 0x0800 101 msg.match.nw_tos = 0x64 102 msg.match.in_port=1 103 msg.match.nw_dst = "192.168.123.2" 104 msg.actions.append(of.ofp_action_output(port = 2)) 105 event.connection.send(msg) 106 107 msg = of.ofp_flow_mod() 108 msg.priority =10 109 msg.idle_timeout = 0 110 msg.hard_timeout = 0 111 msg.match.dl_type = 0x0800 112 msg.match.nw_tos = 0x64 113 msg.match.nw_dst = "192.168.123.1" 114 msg.actions.append(of.ofp_action_output(port = 1)) 115 event.connection.send(msg) 116 117 # main functiont to launch the module 118 def launch (): 119 # attach handsers to listners 120 core.openflow.addListenerByName("FlowStatsReceived", 121 _handle_flowstats_received) 122 core.openflow.addListenerByName("PortStatsReceived", 123 _handle_portstats_received) 124 core.openflow.addListenerByName("ConnectionUp", _handle_ConnectionUp) 125 126 # timer set to execute every five seconds 127 Timer(1, _timer_func, recurring=True)
运行POX脚本flow_stats.py
1 ./pox.py flow_stats
运行mininet脚本
1 sudo ./sckmininet.py
mininet信息:
pox信息:
结果符合预期。