zoukankan      html  css  js  c++  java
  • Ryu控制器学习

    Ryu

    在Mininet环境下实现Ryu为控制器控制ARP报文的实验中学习了Ryu相关的知识,记录如下

    官方文档:http://ryu.readthedocs.io/en/latest/getting_started.html

    李呈:https://www.sdnlab.com/1785.html

    另一篇非常详细的博客:https://www.cnblogs.com/zxqstrong/p/4789105.html

    安装

    • pip安装
    • 源码安装(更推荐)
    git clone git://github.com/osrg/ryu.git
    cd ryu
    sudo pip install -r tools/pip-requires
    sudo python setup.py install
    

    若有更多问题,可参考http://linton.tw/2014/02/15/note-install-ryu-36-sdn-framework/

    源码分析

    介绍ryu/ryu下的主要目录

    • base

    app_manager.py,其作用是RYU应用的管理中心,用于加载RYU应用程序,接受从APP发送过来的信息,同时也完成消息的路由。

    其主要的函数有app注册、注销、查找、并定义了RyuApp基类,定义了RyuApp的基本属性。包含name, threads, events, event_handlers和observers等成员,以及对应的许多基本函数。如:start(), stop()等

    • controller

    controller文件夹中许多非常重要的文件,如events.py, ofp_handler.py, controller.py等。

    其中controller.py中定义了OpenFlowController基类。用于定义OpenFlow的控制器,用于处理交换机和控制器的连接等事件,同时还可以产生事件和路由事件。其事件系统的定义,可以查看events.py和ofp_events.py。

    在ofp_handler.py中定义了基本的handler,完成了基本的如:握手,错误信息处理和keep alive 等功能。更多的如packet_in_handler应该在app中定义。

    在dpset.py文件中,定义了交换机端的一些消息,如端口状态信息等,用于描述和操作交换机。如添加端口,删除端口等操作。

    • lib

    lib中定义了我们需要使用到的基本的数据结构,如dpid, mac和ip等数据结构。在lib/packet目录下,还定义了许多网络协议,如ICMP, DHCP, MPLS和IGMP等协议内容。而每一个数据包的类中都有parser和serialize两个函数。用于解析和序列化数据包。

    lib目录下,还有ovs, netconf目录,对应的目录下有一些定义好的数据类型,不再赘述。

    • ofproto

    基本分为两类文件,一类是协议的数据结构定义,另一类是协议解析,也即数据包处理函数文件。

    如ofproto_v1_0.py是1.0版本的OpenFlow协议数据结构的定义,而ofproto_v1_0_parser.py则定义了1.0版本的协议编码和解码。具体内容不赘述,实现功能与协议相同。

    • topology

    包含了switches.py等文件,基本定义了一套交换机的数据结构。event.py定义了交换上的事件。dumper.py定义了获取网络拓扑的内容。最后api.py向上提供了一套调用topology目录中定义函数的接口。

    • cmd

    ryu的命令系统

    • services

    完成了BGP和vrrp的实现

    • tests

    单元测试

    二次交换机样例

    # 导入ryu的应用管理中心
    from ryu.base import app_manager
    
    # 继承app_manager.RyuApp基类,其中定义了Ryu的App基本的属性,类定义在ryu/ryu/base/app_manager.py
    class L2Switch(app_manager.RyuApp):
        def __init__(self, *args, **kwargs):
        	# 调用父类的构造函数
            super(L2Switch, self).__init__(*args, **kwargs)
    

    样例来自官网。运行

    ryu-manager L2Switch.py
    

    继续添加内容

    from ryu.base import app_manager
    
    # ofp_event完成了事件的定义,从而我们可以在函数中注册handler,监听事件,并作出回应
    from ryu.controller import ofp_event
    
    # OF协议的四个状态
    # MAIN_DISPATCHER 控制器收到feature-reply,下发配置消息,
    # HANDSHAKE_DISPATCHER 发送并等待hello消息
    # CONFIG_DISPATCHER 商议协议版本,发送feature-request
    # DEAD_DISPATCHER 断开连接
    # 在ryu/ryu/controller/handler.py中有详细的注释
    from ryu.controller.handler import MAIN_DISPATCHER
    
    # 装饰器 set_event_class,RyuApp用来声明一个事件handler
    from ryu.controller.handler import set_ev_cls
    
    class L2Switch(app_manager.RyuApp):
        def __init__(self, *args, **kwargs):
            super(L2Switch, self).__init__(*args, **kwargs)
            
    	# 调用修饰器,第一个参数表示事件发生时应该调用的函数,第二个参数告诉交换机只有在握手之后被调用。
        @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
        def packet_in_handler(self, ev):
            msg = ev.msg
            datapath = msg.datapath
            ofp = datapath.ofproto
            ofp_parser = datapath.ofproto_parser
    
            actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
            out = ofp_parser.OFPPacketOut(
                datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port,
                actions=actions)
            datapath.send_msg(out)
    

    分析具体的数据操作:

    • ev.msg:每一个事件类ev中都有msg成员,用于携带触发事件的数据包。
    • msg.datapath:已经格式化的msg其实就是一个packet_in报文,msg.datapath直接可以获得packet_in报文的datapath结构。datapath用于描述一个交换网桥。也是和控制器通信的实体单元。datapath.send_msg()函数用于发送数据到指定datapath。通过datapath.id可获得dpid数据,在后续的教程中会有使用。
    • datapath.ofproto对象是一个OpenFlow协议数据结构的对象,成员包含OpenFlow协议的数据结构,如动作类型OFPP_FLOOD。
    • datapath.ofp_parser则是一个按照OpenFlow解析的数据结构。
    • actions是一个列表,用于存放action list,可在其中添加动作。
    • 通过ofp_parser类,可以构造构造packet_out数据结构。括弧中填写对应字段的赋值即可。

    如果datapath.send_msg()函数发送的是一个OpenFlow的数据结构,RYU将把这个数据发送到对应的datapath。

    继续修改完成二层交换机如下:

    import struct
    import logging
    
    from ryu.base import app_manager
    from ryu.controller import mac_to_port
    from ryu.controller import ofp_event
    from ryu.controller.handler import MAIN_DISPATCHER
    from ryu.controller.handler import set_ev_cls
    from ryu.ofproto import ofproto_v1_0
    from ryu.lib.mac import haddr_to_bin
    from ryu.lib.packet import packet
    from ryu.lib.packet import ethernet
    
    class L2Switch(app_manager.RyuApp):
    
        OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]#define the version of OpenFlow
    
        def __init__(self, *args, **kwargs):
            super(L2Switch, self).__init__(*args, **kwargs)
            self.mac_to_port = {}
    
        def add_flow(self, datapath, in_port, dst, actions):
            ofproto = datapath.ofproto
    
            match = datapath.ofproto_parser.OFPMatch(
                in_port = in_port, dl_dst = haddr_to_bin(dst))
    
            mod = datapath.ofproto_parser.OFPFlowMod(
                datapath = datapath, match = match, cookie = 0,
                command = ofproto.OFPFC_ADD, idle_timeout = 10,hard_timeout = 30,
                priority = ofproto.OFP_DEFAULT_PRIORITY,
                flags =ofproto.OFPFF_SEND_FLOW_REM, actions = actions)
    
            datapath.send_msg(mod)
    
        @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
        def packet_in_handler(self, ev):
            msg = ev.msg
            datapath = msg.datapath
            ofproto = datapath.ofproto
    
            pkt = packet.Packet(msg.data)
            eth = pkt.get_protocol(ethernet.ethernet)
    
            dst = eth.dst
            src = eth.src
    
            dpid = datapath.id    #get the dpid
            self.mac_to_port.setdefault(dpid, {})
    
            self.logger.info("packet in %s %s %s %s", dpid, src, dst , msg.in_port)
            #To learn a mac address to avoid FLOOD next time.
    
            self.mac_to_port[dpid][src] = msg.in_port
    
    
            out_port = ofproto.OFPP_FLOOD
    
            #Look up the out_port 
            if dst in self.mac_to_port[dpid]:
                out_port = self.mac_to_port[dpid][dst]
    
            ofp_parser = datapath.ofproto_parser
    
            actions = [ofp_parser.OFPActionOutput(out_port)]
    
            if out_port != ofproto.OFPP_FLOOD:
                self.add_flow(datapath, msg.in_port, dst, actions)
    
    
            #We always send the packet_out to handle the first packet.
            packet_out = ofp_parser.OFPPacketOut(datapath = datapath, buffer_id = msg.buffer_id,
                in_port = msg.in_port, actions = actions)
            datapath.send_msg(packet_out)
        #To show the message of ports' status.
        @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
        def _port_status_handler(self, ev):
            msg = ev.msg
            reason = msg.reason
            port_no = msg.desc.port_no
    
            ofproto = msg.datapath.ofproto
    
            if reason == ofproto.OFPPR_ADD:
                self.logger.info("port added %s", port_no)
            elif reason == ofproto.OFPPR_DELETE:
                self.logger.info("port deleted %s", port_no)
            elif reason == ofproto.OFPPR_MODIFY:
                self.logger.info("port modified %s", port_no)
            else:
                self.logger.info("Illeagal port state %s %s", port_no, reason)
    

    运行

    在mininet环境下搭建网络拓扑:

    运行RyuAPP

    ryu-manager simple_switch.py
    

    h1 ping h2结果如下

  • 相关阅读:
    Springboot整合dubbo搭建基本的消费、提供和负载均衡
    SpringBoot与Dubbo整合的三种方式
    Dubbo整合SpringBoot
    Java 如何有效地避免OOM:善于利用软引用和弱引用
    finalize的作用
    垃圾回收
    不同JDK版本之间的intern()方法的区别-JDK6 VS JDK6+
    Java8内存模型—永久代(PermGen)和元空间(Metaspace)
    to meet you
    Atomic long 和long的区别
  • 原文地址:https://www.cnblogs.com/yueshangzuo/p/8759715.html
Copyright © 2011-2022 走看看