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中,差值就是丟失的數據包數量。
搭建環境:本實驗需要安裝POX和支持OpenFlow1.3協議的Mininet。POX的安裝教程如下:
Https://blog.csdn.net/shallynever/article/details/48522941
關於python中的logging以及相關函數的使用說明:
https://www.jb51.net/article/126681.htm
1.1 在裝有mininet的虛擬機上創建腳本mymininet.py
cd mininet
gedit mymininet.py
1.2 編輯腳本(稍微解釋了一下)
#!/usr/bin/python
#coding:utf-8
# 調用關於mininet和time的一些模塊
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
#定義mynet函數
def myNet(cname='controller', cargs='-v ptcp:'):
#通過使用OVS抓取來創建網絡
#診斷信息然後開始創建節點,其中有控制器C0和交換機s0,s1,還有主機h0,h1
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)
#鏈路0,1,2分別表示h0和s0,s0和s1,h1和s1的鏈路並調用以上兩行的參數
link0=TCLink( h0, switch, **linkopts0)
link1 = TCLink( switch, switch1, **linkopts1)
link2 = TCLink( h1, switch1, **linkopts0)
#MAC地址設置:按照拓撲圖來設置的,這一塊有些不理解,個人理解爲,鏈路0,2只需要設置一個能連上
#s0/s1的端口就行,而1需要左邊的s0端口和右邊的s1端口
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")
#診斷後設置主機ip
info( "*** Configuring hosts\n" )
h0.setIP( '192.168.123.1/24' )
h1.setIP( '192.168.123.2/24' )
#通過使用OVS開始網絡連接,然後單引號裏的意思是搭建和刪除橋樑
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 )
# 控制器和交換機同屬根命名空間所以我們可以通過環回接口連接,對了,10.0.0.13是別的up主的主機
# ip,需要用自己的,因爲控制器是你自己的
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' )
#運行測試20次h0和h1的傳輸情況
#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' )
#主函數,看不懂,意思應當是如果是個主函數就設定info的等級然後開始獲取網絡demo然後運行
#mynet()函數
if __name__ == '__main__':
setLogLevel( 'info' )
info( '*** Scratch network demo (kernel datapath)\n' )
Mininet.init()
myNet()
2.1 我們在pox文件裏面創建腳本
cd pox
gedit flow_stats.py
2.2 編輯腳本 (中文部分是對程序的解釋,未翻譯部分是對具體代碼的代碼解釋)
#!/usr/bin/python
# Copyright 2012 William Yu
# [email protected]
#
# 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()
#初始化網絡的參數0
src_dpid = 0
dst_dpid = 0
input_pkts = 0
output_pkts = 0
def getTheTime():
#設定當地時間的函數
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
# 用於將請求發送到連接到控制器的所有交換機的定時器功能的處理程序
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))
# 顯示在事件的JSON格式結構中接收到的流統計信息的處理程序由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, "%"
# 處理程序以顯示JSON格式接收的端口統計信息
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)
# 啓動模塊的主函數
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)
# 定時器每5秒執行一次
Timer(1, _timer_func, recurring=True)
3 執行兩個腳本
此處爲mymininet的腳本
此處爲flow_stats腳本
總結:
1.能看懂2/3的腳本內容,以及腳本的程序意義
2.明白了pox的安裝
3.對網絡的瞭解還是不夠深刻