天天看點

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三

目錄

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,會出現如下結果:

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三

可以看到,ofp_event事件提供EventOFPPacketIn和EventOFPSwitchFeatures,而Hub消費到EventOFPPacketIn和EventOFPSwitchFeatures,表示集線器(Hub)功能實作。

啟動Mininet,輸入指令sudo mn --controller=remote,ip=xx.xx.xx.xx,port=6633,連接配接控制器,應用系統自帶拓撲結構。

進入Mininet後,輸入pingall,Ryu控制器終端會有如下結果顯示:

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三

2.Ryu+Mininet應用案例二

2.1Learning Switch/自學習交換機

注:本執行個體來自Ryubook

通過控制器來實作自學習交換算法,然後指導資料平面實作交換機操作。

自學習交換機原理(4歩):

①初始狀态

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三

初始狀态下流表為空,主機A(host A)連接配接端口1(port 1),主機B連接配接端口4,主機C連接配接端口3。

②Host A ->Host B

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三

當資料包(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

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三

當主機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

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三

當資料包再次從主機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,會出現如下結果:

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三

當開啟Mininet後(控制平面與資料平面連接配接上後),有如下結果:

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三

在Mininet中使用pingall/h1 ping h2(使用Mininet預設拓撲2個主機)指令後,有如下結果:

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三

從結果中可以看出,主機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

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三

然後控制器主動下發請求request;最後通過處理Reply資料包得到相應資料。

控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三
控制器Ryu+Mininet完成集線器、自學習交換機、流量監控執行個體開發1.Ryu+Mininet應用案例一2.Ryu+Mininet應用案例二3.Ryu+Mininet應用案例三