1. 程式人生 > 實用技巧 >實驗三

實驗三

一、實驗目的
在實驗 2 的基礎上進一步熟悉 Mininet 自定義拓撲指令碼,以及與損耗率相關的設
定;初步瞭解 Mininet 安裝時自帶的 POX 控制器指令碼編寫,測試路徑損耗率。
二、實驗任務


h0 向 h1 傳送資料包,由於在 Mininet 指令碼中設定了連線損耗率,在傳輸過程中
會丟失一些包,本次實驗的目的是展示如何通過控制器計算路徑損耗速率(h0
s0-s1-h1)。這裡假設控制器預先知道網路拓撲。控制器將向 s0 和 s1 傳送
flow_stats_request,當控制器接收到來自 s0 的 response 時,將特定流的資料包
數儲存在 input_pkts 中,當控制器接收到來自 s1 的 response 時,將接收到特定
流的資料包數儲存在 output_pkts 中,差值就是丟失的資料包數量。
基於上述拓撲,編寫 Mininet 指令碼,設定特定的交換機間的路徑損耗速率,然後
編寫 POX 控制器指令碼,實現對路徑的損耗率的測量。
三、實驗步驟


1. 實驗環境
安裝了 Ubuntu 18.04.5 Desktop amd64 的虛擬機器
2. 實驗過程
SDNLAB 實驗參考資料:https://www.sdnlab.com/15100.html
(1)新建並編輯 pox 指令碼 flowstat.py:
在 pox 安裝目錄下(Mininet 完整安裝包含了 pox)執行以下命令執行 pox 指令碼
$ ./pox.py flowstat

現在一起看下 flowstat.py 的關鍵程式碼:
第 7 行開始,讓 h0 ping h1,監測 s0 和 s1 之間的鏈路。

如果匹配到乙太網型別的包頭(0x0800),並且資料包的目的 IP 地址是192.168.123.2(對照後面 Mininet 的指令碼發現是 h1), 並且連線到控制器的資料平面裝置 id 是 s0(h0 ping h1,鏈路 s0-s1 上資料包是從 s0 流向 s1,s0 為源,s1 為目的地), 執行 input_pkts = f.packet_count,把資料包數量存入input_pkts;

同理,如果連線到控制器的資料平面裝置 id 是 s1,執行 output_pkts =f.packet_count,把資料包數量存入 output_pkts。

最後求 input_pkts 和 output_pkts 的差值。一般情況下差值為正,說明鏈路上資料包有損耗。

#!/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()

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)

再執行命令執行 Mininet 指令碼 mymininet3.py
$ sudo python mymininet3.py

如果修改程式碼中 s0 和 s1 之間鏈路的丟包率為 10。

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)

重新執行 Mininet 指令碼 mymininet3.py,20 秒時間的 ping 過程中有 icmp_seq ping 不通

四、實驗要求

(已在實驗步驟中體現)

  1. 根據實驗步驟重複上述實驗。

  2. 在部落格園發表一篇部落格,記錄主要步驟。

五、實驗總結

這次實驗總體來說較為簡單,遇到的一個最大問題就是在輸入./pox.py flowstat 之後,運行了控制器,但是卻不知道怎麼繼續執行mymininet3.py,經過半小時的冥思苦想,最終我憑藉自己過人的智慧想到了可以新建一個terminal,然後在新的terminal裡面執行mymininet3.py,雖然這個問題非常簡單,但是對於類似我這樣的計算機小白來說,還是略有深度的。這次實驗也是收穫滿滿,期待下次實驗能給我帶來不一樣的驚喜。