目錄
1.Ryu+Mininet應用案例一
1.1Hub+Learning
1.2結果顯示
2.Ryu+Mininet應用案例二
2.1Learning Switch/自學習交換機
2.2案例實作
2.3結果顯示
3.Ryu+Mininet應用案例三
3.1流量監控
3.1.1流量監控原理
3.2案例實作
3.3結果顯示
1.Ryu+Mininet應用案例一
1.1Hub+Learning
通過控制器來實作集線器算法(泛洪),然後指導資料平面實作集線器操作。
在Ryu控制器源碼的ryu/app目錄下建立hub.py檔案。代碼如下:
#集線器的應用
class Hub(app_manager.RyuApp):
""" 集線器一個端口輸入,其餘端口輸出 """
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] #說明OpenFlow協定版本為1.3
def __init__(self,*args,**kwargs):
super(Hub, self).__init__(*args,**kwargs)
@set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER) # 注冊:在配置狀态下(CONFIG_DISPATCHER)監聽事件(EventOFPSwitchFeatures)
def switch_features_handler(self,event): #處理交換機的連接配接,也就是處理上面所監聽到的事件
#當接收到Switch Features(Features Reply)消息時(在交換機與控制器握手時接收),Table-miss流表項被添加。
#當交換機握手(handshake)完成後,Table-miss流表項被添加到流表中,準備接收Packet-In消息
#解析
datapath=event.msg.datapath #datapath在OpenFlow協定中定義,等同于資料平面的通道或Bridge網橋
ofproto=datapath.ofproto
ofp_parser=datapath.ofproto_parser
#install the table-miss flow entry 安裝table-miss流表項
match =ofp_parser.OFPMatch() #指明Match域
actions=[ofp_parser.OFPActionOutput( #指明動作集
ofproto.OFPP_CONTROLLER, #說明發送端口為CONTROLLER
ofproto.OFPCML_NO_BUFFER)] #資料包在Buffer中存入的Buffer_id,此處不存放Buffer_id
self.add_flow(datapath,0,match,actions)
def add_flow(self,datapath,priority,match,actions): #添加流表
#add a flow entry,and install it into datapath
ofproto=datapath.ofproto
ofp_parser=datapath.ofproto_parser
#construct a flow_mod msg and sent it (通過Flow-mod消息增删交換機的流表項)
inst=[ofp_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] #添加指令 #指令的動作是:當執行當此指令集時,就執行其動作Actions #執行的對象
flow_mod=ofp_parser.OFPFlowMod(datapath=datapath,priority=priority,match=match,instructions=inst)
datapath.send_msg(flow_mod) #發送資訊 使用Ryu,當接收到OpenFlow消息時,将生成與該消息對應的事件
@set_ev_cls(ofp_event.EventOFPPacketIn,MAIN_DISPATCHER) #注冊:在主狀态下(MAIN_DISPATCHER)監聽事件(EventOFPPacketIn)
def packet_in_handler(self,event):
msg=event.msg
datapath=msg.datapath
ofproto=datapath.ofproto
ofp_parser=datapath.ofproto_parser
in_port=msg.match['in_port']
#sent packetIn
#首先construct a flow entry
match=ofp_parser.OFPMatch()
actions=[ofp_parser.OFPActionOutput(ofproto.OFPP_FLOOD)] #對比對到流表項的資料包,發送到OutPort的Flood端口
#install flow_mod to avoid PacketIn next time
self.add_flow(datapath,1,match,actions)
out=ofp_parser.OFPPacketOut(datapath=datapath,buffer_id=msg.buffer_id,in_port=in_port,actions=actions)
datapath.send_msg(out)
注:ryu控制器基礎内容說明:
①使用Ryu,當接收到OpenFlow消息時,将生成與該消息對應的事件。
②事件處理(Event handler)程式定義了一個函數,該函數的參數為事件對象,并使用ryu.controller.handler.set_ev_cls 裝飾器來裝飾。
③事件(Event)的類名是:
ryu.controller.ofp_event.EventOFP
+ <OpenFlow message name> 例如Packet-in消息,其事件的類名:EventOFPPacketIn。
④事件狀态(Event State):
ryu.controller.handler.HANDSHAKE_DISPATCHER :交換HELLO消息
ryu.controller.handler.CONFIG_DISPATCHER :等待接收SwitchFeatures消息
ryu.controller.handler.MAIN_DISPATCHER :正常狀态
ryu.controller.handler.DEAD_DISPATCHER :斷開連接配接
1.2結果顯示
在終端(Terminal)啟動Ryu,輸入指令ryu-manager hub.py --verbose,會出現如下結果:

可以看到,ofp_event事件提供EventOFPPacketIn和EventOFPSwitchFeatures,而Hub消費到EventOFPPacketIn和EventOFPSwitchFeatures,表示集線器(Hub)功能實作。
啟動Mininet,輸入指令sudo mn --controller=remote,ip=xx.xx.xx.xx,port=6633,連接配接控制器,應用系統自帶拓撲結構。
進入Mininet後,輸入pingall,Ryu控制器終端會有如下結果顯示:
2.Ryu+Mininet應用案例二
2.1Learning Switch/自學習交換機
注:本執行個體來自Ryubook
通過控制器來實作自學習交換算法,然後指導資料平面實作交換機操作。
自學習交換機原理(4歩):
①初始狀态
初始狀态下流表為空,主機A(host A)連接配接端口1(port 1),主機B連接配接端口4,主機C連接配接端口3。
②Host A ->Host B
當資料包(Packets)要從主機A發送給主機B時,一條Packet-In消息被發送(由交換機發送)并且主機A的MAC位址被端口1擷取到,因為主機B的端口還沒有發現主機B的MAC位址,控制器也不知道主機B的位址,是以資料包(Packets)被洪泛(控制器下發的Packet-out:action),則主機B和主機C都會收到資料包。
Packet-In消息内容:
in-port:1
eth-dst:Host B
eth-src:Host A
Packet-out消息内容:
action:OUTPUT:Flooding
注:當控制器需要發送分組到資料平面,這時可以通過Packet-out消息封裝好資料分組傳給OpenFlow,并在該消息中指定特定的動作表指導交換機處理這個資料分組,而不再進行流表的比對(除非動作表中包含轉發到Table的動作)。
③Host B -> Host A
當主機B收到資料包後會傳回(reply)消息,此時交換機會給控制器發送Packet-In,控制器會下發packet-out,此時一條流表項(Entry)下發被添加到流表中,資料包(Packets)從主機B傳回到主機A。由于主機C并不是目的主機,是以之前主機C收到的資料包後不會傳回到主機A(不會發送Reply)會丢棄掉,流表中也不會添加别的流表項。
Packet-In:
in-port:1
eth-dst:Host A
eth-src:Host B
Packet-Out:
action:OUTPUT:Port 1
④Host A ->Host B
當資料包再次從主機A發送到主機B時,交換機上發一條Packet-in消息給控制器,此時控制器知道怎麼到達主機B,是以控制器發送Packet-out消息,此時一條流表項被添加到流表中,交換機收到Packet-out後就會将資料包(Packets)發送到資料B。
Packet-In:
in-port:1
eth-dst:Host B
eth-src:Host A
Packet-Out:
action:OUTPUT:Port 4
2.2案例實作
在Ryu控制器源碼的ryu/app目錄下建立example_switch.py檔案,代碼如下。
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
"""
Base元件隻包含子產品管理器(app_manager)單個元件,它是Ryu應用元件的管理中樞,
具有加載Ryu應用元件、為元件提供上下文(Context)及傳遞元件消息的作用
"""
class ExampleSwitch(app_manager.RyuApp):
"""
app_manager中定義的RyuApp類是Ryu應用元件的基礎類,
Ryu應用元件需要定義繼承該類的子類。
"""
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(ExampleSwitch, self).__init__(*args, **kwargs)
# initialize mac address table.
self.mac_to_port = {} #mac位址到port的對應關系
@set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER) # 注冊:在配置狀态下(CONFIG_DISPATCHER)監聽事件(EventOFPSwitchFeatures)
def switch_features_handler(self, event): # 處理交換機的連接配接,也就是處理上面所監聽到的事件
# 解析
datapath = event.msg.datapath # datapath在OpenFlow協定中定義,等同于資料平面的通道或Bridge網橋
ofproto = datapath.ofproto
ofp_parser = datapath.ofproto_parser
# install the table-miss flow entry 安裝table-miss流表項
match = ofp_parser.OFPMatch() # 指明Match域
actions = [ofp_parser.OFPActionOutput( # 指明動作集
ofproto.OFPP_CONTROLLER, # 說明發送端口為CONTROLLER
ofproto.OFPCML_NO_BUFFER)] # 資料包在Buffer中存入的Buffer_id,此處不存放Buffer_id
self.add_flow(datapath, 0, match, actions)
def add_flow(self, datapath, priority, match, actions): # 添加流表
# add a flow entry,and install it into datapath
ofproto = datapath.ofproto
ofp_parser = datapath.ofproto_parser
# construct a flow_mod msg and sent it (通過Flow-mod消息增删交換機的流表項)
inst = [ofp_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
actions)] # 添加指令 #指令的動作是:當執行當此指令集時,就執行其動作Actions #執行的對象
flow_mod = ofp_parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst)
datapath.send_msg(flow_mod) # 發送資訊
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) # 注冊:在主狀态下(MAIN_DISPATCHER)監聽事件(EventOFPPacketIn)
def packet_in_handler(self, event):
msg = event.msg
datapath = msg.datapath
ofproto = datapath.ofproto
ofp_parser = datapath.ofproto_parser
#邏輯:實作自學習算法
#1.擷取datapath id 來定位或識别OpenFlow交換機
dpid=datapath.id
self.mac_to_port.setdefault(dpid,{})
#2.儲存從OpenFlow交換機擷取到的資訊
#3.解析收到的packets資訊 analyse the received packets using the packet library
pkt=packet.Packet(msg.data)
eth_pkt=pkt.get_protocol(ethernet.ethernet) #以太網資料包
dst=eth_pkt.dst #目的mac位址
src=eth_pkt.src #源mac位址
in_port=msg.match['in_port'] #從packet-in消息中收到端口号
self.logger.info("--- packet in %s %s %s %s",dpid,src,dst,in_port)
#4.學會源mac位址到port端口的映射資訊,進而避免下一次出現FLOOD操作
self.mac_to_port[dpid][src]=in_port
#5.如果目的mac位址已經學到,就下發流表項指定端口發送資料包(packets);若果目的mac位址沒有學到,則下發流表項的action為flooding(泛洪)
if dst in self.mac_to_port[dpid]:
out_port=self.mac_to_port[dpid][dst]
else:
out_port=ofproto.OFPP_FLOOD
#6.構造一個 actions
actions=[ofp_parser. OFPActionOutput(out_port)]
#7.安裝一個flow消息 install a flow to avoid packet_in next time
if out_port !=ofproto.OFPP_FLOOD:
match=ofp_parser.OFPMatch(in_port=in_port,eth_dst=dst)
self.add_flow(datapath,1,match,actions) #下發流表
#8.構造packet_out消息并且發送packet-out
packetOut=ofp_parser.OFPPacketOut(datapath=datapath,buffer_id=msg.buffer_id,
in_port=in_port,actions=actions,data=msg.data)
datapath.send_msg(packetOut)
2.3結果顯示
在終端(Terminal)啟動Ryu,輸入指令ryu-manager example_switch.py --verbose,會出現如下結果:
當開啟Mininet後(控制平面與資料平面連接配接上後),有如下結果:
在Mininet中使用pingall/h1 ping h2(使用Mininet預設拓撲2個主機)指令後,有如下結果:
從結果中可以看出,主機1向主機2發出資料包(packets),首先主機1發出資料包後,交換機發送packet-in消息給控制器,由于控制器不知道主機B的位址,是以下發packet-out消息,action為OFPP_FLOOD,從上圖可以看出第一次的目的mac位址為ff:ff:ff:ff:ff:ff(ARP請求);然後主機B收到後會向主機A發送packetReply消息,此時控制器就知道主機B的位址,然後下發流表項;最後當主機A再次向主機B發送資料包時,此時控制器就可以找到主機B,直接發送資料包。
3.Ryu+Mininet應用案例三
3.1流量監控
3.1.1流量監控原理:
1)控制器向交換機周期下發擷取統計消息,請求交換機資訊
①端口流量統計資訊
②請求流表項統計資訊
2)根據交換機統計資訊計算流量資訊
①流速公式:speed=(s(t1)-s(t0))/(t1-t0)
②端口/鍊路剩餘帶寬公式:free_bw=capability-speed
3.2案例實作
在Ryu控制器源碼的ryu/app目錄下建立 MyMonitor13.py檔案,代碼如下。
# 編碼時間: 2021/3/3 17:25
# @File : my_monitor_13.py
# @software : PyCharm
from operator import attrgetter
from ryu.app import simple_switch_13
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.lib import hub
class MyMonitor13(simple_switch_13.SimpleSwitch13):
def __init__(self, *args, **kwargs): #初始化函數
super(MyMonitor13, self).__init__(*args, **kwargs)
self.datapaths = {} #初始化成員變量,用來存儲資料
self.monitor_thread = hub.spawn(self._monitor) #用協程方法執行_monitor方法,這樣其他方法可以被其他協程執行。 hub.spawn()建立協程
"""
Controller元件主要由OpenFlowController和Datapath兩類構成,其中,OpenFlowController負責監聽與Ryu連接配接的OpenFlow網絡中的事件,
一旦有事件發生,會建立一個Datapath對象負責接收來自該事件的連接配接和資料,并将這些事件的資料分組進行解析,封裝成Ryu的事件對象,然後派發。
"""
#get datapath info 擷取datapath資訊
#EventOFPStateChange事件用于檢測連接配接和斷開。
@set_ev_cls(ofp_event.EventOFPStateChange,[MAIN_DISPATCHER,DEAD_DISPATCHER])#通過ryu.controller.handler.set_ev_cls裝飾器(decorator)進行注冊,在運作時,ryu控制器就能知道MyMonitor13這個子產品的函數_state_change_handler監聽了一個事件
def _state_change_handler(self,event): #交換機狀态發生變化後,讓控制器資料于交換機一緻
datapath=event.datapath
if event.state == MAIN_DISPATCHER: # 在MAIN_DISPATCHER狀态下,交換機處于上線狀态
if datapath.id not in self.datapaths:
self.logger.debug('register datapath: %016x',datapath.id)
self.datapaths[datapath.id]=datapath #datapath用字典來儲存,key為id,value為datapath
elif event.state == DEAD_DISPATCHER: #在DEAD_DISPATCHER狀态下
if datapath.id in self.datapaths:
self.logger.debug('unregister datapath:%016x',datapath.id)
del self.datapaths[datapath.id]
#send request msg periodically
def _monitor(self):
while True: #對已注冊交換機發出統計資訊擷取請求每10秒無限地重複一次
for dp in self.datapaths.values(): #周遊所有的交換機或網橋
self._request_stats(dp)
hub.sleep(10) #休眠
#send stats request msg to datapath (完成控制器主動下發邏輯)
def _request_stats(self,datapath):
self.logger.debug('send stats request:%016x',datapath.id)
ofproto=datapath.ofproto
ofp_parser=datapath.ofproto_parser #解析器
# send flow stats request msg
request=ofp_parser.OFPFlowStatsRequest(datapath)
datapath.send_msg(request)
# send port stats request msg
request=ofp_parser.OFPPortStatsRequest(datapath,0,ofproto.OFPP_ANY)
datapath.send_msg(request)
#handle the port stats reply msg (完成交換機被動發送邏輯)
@set_ev_cls(ofp_event.EventOFPPortStatsReply,MAIN_DISPATCHER)
def _port_stats_reply_handler(self,event):
body=event.msg.body #消息體
self.logger.info('datapath port '
'rx-pkts rx-bytes rx-error '
'tx-pkts tx-bytes tx-error ') # rx-pkts:receive packets tx-pks:transmit packets
self.logger.info('---------------- -------- '
'-------- -------- -------- '
'-------- -------- --------')
for stat in sorted(body,key=attrgetter('port_no')): #attrgetter:屬性擷取工具
self.logger.info('%016x %8x %8d %8d %8d %8d %8d %8d',
event.msg.datapath.id, stat.port_no,
stat.rx_packets, stat.rx_bytes, stat.rx_errors,
stat.tx_packets, stat.tx_bytes, stat.tx_errors)
#handle the flow entry stats reply msg
@set_ev_cls(ofp_event.EventOFPFlowStatsReply,MAIN_DISPATCHER)
def _flow_stats_reply_handler(self,event):
body=event.msg.body # body:OFPFlowStats的清單,存儲受FlowStatsRequest影響每個流表項的統計資訊
self.logger.info('datapath '
'in-port eth-dst '
'out-port packets bytes')
self.logger.info('---------------- '
'-------- ----------------- '
'-------- -------- --------')
for stat in sorted([flow for flow in body if flow.priority==1]
,key=lambda flow:(flow.match['in_port'],flow.match['eth_dst'])):
self.logger.info('%016x %8x %17s %8x %8d %8d',
event.msg.datapath.id,stat.match['in_port'],
stat.match['eth_dst'],stat.instructions[0].actions[0].port,
stat.packet_count,stat.byte_count)
3.3結果顯示
流量監控:
交換機連接配接上控制器,首先注冊datapath
然後控制器主動下發請求request;最後通過處理Reply資料包得到相應資料。