控制器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,会出现如下结果:

可以看到,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数据包得到相应数据。

 

  • 7
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一份示例代码,但需要注意的是,这份代码只是一个简单的示例,仅能实现基本的交换机功能,您需要根据实际情况进行修改和完善。 ```python 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_0 from ryu.ofproto import ofproto_v1_0_parser from ryu.lib.packet import packet from ryu.lib.packet import ethernet class SimpleSwitch(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION] def __init__(self, *args, **kwargs): super(SimpleSwitch, self).__init__(*args, **kwargs) self.mac_to_port = {} @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): datapath = ev.msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser # 添加默认流表,将所有流量转发至控制器 match = parser.OFPMatch() actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] self.add_flow(datapath, 0, match, actions) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def packet_in_handler(self, ev): msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser in_port = msg.in_port pkt = packet.Packet(msg.data) eth = pkt.get_protocols(ethernet.ethernet)[0] dst = eth.dst src = eth.src dpid = datapath.id self.mac_to_port.setdefault(dpid, {}) # 记录源 MAC 地址和端口号 self.mac_to_port[dpid][src] = in_port # 如果目的 MAC 地址在交换机已知主机列表中,则直接转发 if dst in self.mac_to_port[dpid]: out_port = self.mac_to_port[dpid][dst] else: # 如果目的 MAC 地址未知,则广播到所有端口 out_port = ofproto_v1_0.OFPP_FLOOD actions = [parser.OFPActionOutput(out_port)] # 发送流表 if out_port != ofproto_v1_0.OFPP_FLOOD: match = parser.OFPMatch(in_port=in_port, dl_dst=dst) self.add_flow(datapath, 1, match, actions) # 发送数据包 out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port, actions=actions) datapath.send_msg(out) def add_flow(self, datapath, priority, match, actions): ofproto = datapath.ofproto parser = datapath.ofproto_parser # 构造流表 inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] mod = parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst) datapath.send_msg(mod) ``` 这是一个基于 OpenFlow 协议的简单交换机实现。当交换机收到数据包时,会将源 MAC 地址和端口号记录在一个字典中,然后查找目的 MAC 地址所对应的端口号并转发数据包。如果目的 MAC 地址未知,则广播到所有端口。 请注意,这个示例代码只适用于 OpenFlow1.0,如果您使用的是其他版本的 OpenFlow 协议,需要对代码进行相应的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值