生动的SDN基础内容介绍(三)--Ryu控制器

这篇具有很好参考价值的文章主要介绍了生动的SDN基础内容介绍(三)--Ryu控制器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

控制器

之前介绍完了南向协议OpenFlow,这次说一说Ryu。因为毕设的时候师兄推荐了Ryu,再考虑到Python方便开发,我也就继续用Ryu了。但是后续发现好像支持Ryu开发的框架相较Floodlight、OpenDaylight、ONOS没那么多(但也可能只是我没找到)。

首先非常强烈推荐这位大哥的博客:
https://www.cnblogs.com/ssyfj/p/11730362.html
当时学习的时候多亏了这位大哥,大哥博客里面有很多关于SDN的实验,讲解的很细致。

其次要强烈推荐Ryu官方的手册:
https://ryu.readthedocs.io/en/latest/ofproto_v1_3_ref.html
有不懂的就翻手册。

Ryu的目录

Ryu的安装网上有很多介绍了,这里就不说了。

我用的是Ubuntu20.04,Ryu4.34的目录(ryu/ryu)如下所示:

生动的SDN基础内容介绍(三)--Ryu控制器
1、base:
里面有一个很重要的文件:app_manager.py,其作用是Ryu应用的管理中心。用于加载RYU应用程序,接受从APP发送过来的信息,同时也完成消息的路由。其主要的函数有app注册、注销、查找、并定义了Ryu APP基类,定义了Ryu APP的基本属性。

Ryu APP是啥,其实就是用Ryu控制器编写的应用代码,我们在Ryu框架的基础上进行开发,注意这里APP与应用平面不同,我们现在讨论的都是控制平面。

2、controller:
实现控制器和交换机之间的连接和事件处理。

3、lib:
实现了网络的基本协议以及基本的数据结构。
ofctl_v1_3.py中的代码有匹配的函数,可对照之前OpenFlow介绍中显示的匹配域:
生动的SDN基础内容介绍(三)--Ryu控制器

4、ofproto:
这里有两类文件,一类是协议的数据结构定义,另外一类是协议的解析,即处理的函数。
如ofproto_v1_3.py是1.3版本的OpenFlow协议数据结构的定义,而ofproto_v1_3_parser.py则定义了1.3版本的协议编码和解码。

5、topology:
这里定义了交换机的数据结构和一些event。
可以看到这里的Switches class将交换机基本的内容已经定义好了:
生动的SDN基础内容介绍(三)--Ryu控制器
定义了可支持的OpenFlow的版本、时间、链路、主机、端口等。

6、contrib:
存放了开源社区贡献者的代码。

7、cmd:
为控制器的执行创建环境,执行命令行的命令。

Ryu的学习

Ryu的目录里有一个app目录,这里存放了开发者给我们写好了的一些app。

simple_switch_13.py

这里最要关注的是simple_switch_13.py,这个文件很适合刚接触Ryu的人来熟悉Ryu。
它基于OpenFlow1.3协议实现了简单的学习交换机,为啥叫学习呢,因为最开始交换机不知道去往MAC1的数据包该往哪儿发,然后接受到了一次之后就把要转发的端口记录下来了,下次就知道去MAC1的数据包要从哪个端口走了。其实就是实现了平常咱们用的二层交换机的功能。

关于这个文件的源码讲解,这篇博客还是很好的:
https://www.cnblogs.com/kl107/p/13138568.html
当然还有大哥的这篇:
https://www.cnblogs.com/ssyfj/p/11748465.html

我把我自己的一些注释写在下面的代码里

# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ryu.base import app_manager #app的基类
from ryu.controller import ofp_event #OpenFlow的事件
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3#应用的是OpenFlow1.3协议
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet #引入了基本的网络协议
from ryu.lib.packet import ether_types


class SimpleSwitch13(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] #应用的版本是OpenFlow1.3

    def __init__(self, *args, **kwargs):
        super(SimpleSwitch13, self).__init__(*args, **kwargs)
        self.mac_to_port = {} #MAC-端口对

    @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 
    #起到了事件响应的功能
    #当接收到feature-reply报文后执行下面的函数下发table-miss表项
    def switch_features_handler(self, ev):
        datapath = ev.msg.datapath #获取数据通路即交换机
        ofproto = datapath.ofproto #获取数据通路的协议
        parser = datapath.ofproto_parser #对协议进行解析

        # install table-miss flow entry
        #
        # We specify NO BUFFER to max_len of the output action due to
        # OVS bug. At this moment, if we specify a lesser number, e.g.,
        # 128, OVS will send Packet-In with invalid buffer_id and
        # truncated packet data. In that case, we cannot output packets
        # correctly.  The bug has been fixed in OVS v2.1.0.
        match = parser.OFPMatch() #全匹配
        actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
                                          ofproto.OFPCML_NO_BUFFER)]
        #设置table-miss表项的动作:将数据包发至控制器
        self.add_flow(datapath, 0, match, actions)
        #将0优先级、全匹配、动作为“将数据包发至控制器”的table-miss表项下发给交换机。
        #这样以后交换机不知道咋处理的数据包会自动发至控制器。

    def add_flow(self, datapath, priority, match, actions, buffer_id=None):
        #下发流表项
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser

        inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
                                             actions)]
        if buffer_id:
            mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id,
                                    priority=priority, match=match,
                                    instructions=inst)
        else:
            mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
                                    match=match, instructions=inst)
        datapath.send_msg(mod)

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    #在MAIN_DISPATCHER阶段碰到EventOFPPacketIn事件的时候执行下面的函数。
    #即控制器收到数据包的时候执行下面的函数。
    #控制器什么时候会收到数据包呢:就是交换机现有的流表项无法与数据包匹配,利用table-miss表项交给控制器处理。
    def _packet_in_handler(self, ev):
        # If you hit this you might want to increase
        # the "miss_send_length" of your switch
        if ev.msg.msg_len < ev.msg.total_len:
            self.logger.debug("packet truncated: only %s of %s bytes",
                              ev.msg.msg_len, ev.msg.total_len)
        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        in_port = msg.match['in_port']
        #此时能进入这里的都是不知道该被咋处理的数据包。
        #记录下数据包从哪个端口进来

		#提取数据包的信息
        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]

        if eth.ethertype == ether_types.ETH_TYPE_LLDP:
            # ignore lldp packet
            return
        dst = eth.dst #获取数据包的目的MAC地址
        src = eth.src #获取数据包的源MAC地址

        dpid = format(datapath.id, "d").zfill(16)
        self.mac_to_port.setdefault(dpid, {})

        self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)

        # learn a mac address to avoid FLOOD next time.
        #设置该交换机的MAC-端口对。
        #即将数据包的源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:
            out_port = ofproto.OFPP_FLOOD
		
		#给流表项赋予动作:遇到该目的MAC地址的时候从这个端口转发。
        actions = [parser.OFPActionOutput(out_port)]

        # install a flow to avoid packet_in next time
        if out_port != ofproto.OFPP_FLOOD:
        	#创建匹配域:以后再遇到这种情况就知道从哪个端口转发了。
            match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
            # verify if we have a valid buffer_id, if yes avoid to send both
            # flow_mod & packet_out
            if msg.buffer_id != ofproto.OFP_NO_BUFFER:
                self.add_flow(datapath, 1, match, actions, msg.buffer_id)
                return
            else:
                self.add_flow(datapath, 1, match, actions)
        data = None
        if msg.buffer_id == ofproto.OFP_NO_BUFFER:
            data = msg.data
		
		#下发流表项
        out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
                                  in_port=in_port, actions=actions, data=data)
        datapath.send_msg(out)

@set_ev_cls是一个装饰器,当参数表示的事件触发后会执行下面的函数,例:

@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 
    def switch_features_handler(self, ev):

控制器事件event具体见ryu/controller/ofp_event.py,其事件名称是由接收到的报文类型来命名的,名字为Event+报文类型,本例中,控制器收到的是交换机发送的FEATURE_REPLY报文,所以事件名称为EventOFPSwitchFeatures。所以本事件其实就是当控制器接收到FEATURE_REPLY报文触发。

控制器有以下四个状态:
1、HANDSHAKE_DISPATCHER:发送Hello报文并等待对端Hello报文。
2、CONFIG_DISPATCHER:协商版本并发送FEATURE-REQUEST报文。
3、MAIN_DISPATCHER:已收到FEATURE-REPLY报文并发送SET-CONFIG报文。
4、DEAD_DISPATCHER:与对端断开连接。
综上,以上代码说明了当控制器处于CONFIG_DISPATCHER状态并且接受到FEATURE_REPLY报文时,执行switch_features_handler()函数。

Ryu控制器一般是配合Mininet进行网络的实验,在后续讲到Mininet的时候再说。

simple_switch_rest_13.py

除了simple_switch_13.py,有一个和它长得很像的文件,simple_switch_rest_13.py。

唯一的区别就是多了个rest_,rest是啥,我当时第一反应是放松。后来做毕设的时候才意识到这是RESTful软件架构风格。

REST (Representational State Transfer) API,这是Roy Fielding在2000年提出的一种架构风格或设计原则。REST的原则是将系统中的所有事物抽象为URL表示的资源。客户端可以通过 HTTP 请求(如 GET、POST、PUT、DELETE等)来操作资源。服务器收到请求后会根据不同的方法采取不同的响应,最终将数据以XML、JSON等格式返回给客户端。

听起来有点云里雾里的,简单点说其实就是用url的格式来进行资源的访问/进程间的通信。这种设计原则在现在已经很常见了,Ryu也采用的是这样的交互设计原则。

所以说了这么多,这玩意儿是干啥的,它其实就是之前在(一)里面提到的甲方与领导沟通的那套规则–北向协议。

老说南向协议和北向协议啥的,听着很玄乎。
南向协议就是控制器和数据平面沟通的规则。
北向协议就是应用平面和控制平面沟通的规则。

用户可以通过url对流表项进行查询、下发、删除等操作。

simple_switch_rest_13.py可以通过REST API干两件事:
1、获取MAC表
2、注册MAC表
具体的请看
https://blog.csdn.net/weixin_46239293/article/details/115607828

关于北向接口和意图映射的事情会在后续讲到。

交换机信息及流表项的查询

Ryu提供了两种方式来查询交换机信息以及流表项信息:
1、REST API主动查询:
后续讲到北向接口的时候会详细讲述。
2、事件响应查询:
Ryu中提供了很多的事件,如EventOFPPortStatsReply、EventOFPFlowStatsReply等。
EventOFPPortStatsReply在端口查询返回结果时触发。
EventOFPFlowStatsReply在流量查询返回结果时触发。

simple_monitor_13.py中展示了这两个事件的应用:
详细的代码讲解可以看大哥的博客:
https://www.cnblogs.com/ssyfj/p/11755773.html

我这里以我毕设的部分代码为例:

@set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER)
    def _port_stats_reply_handler(self, ev):#执行完send_port_stats_request后会调用该函数对查询完的端口信息进行处理
        body = ev.msg.body
        for port_stat in sorted(body,key=attrgetter('port_no')):
            datapathid=ev.msg.datapath.id
            no=port_stat.port_no
            tx=port_stat.tx_bytes
            p=self.port_tx[datapathid]
            if no==4294967294:
                continue
            if no in p.keys():
                self.port_tx1[datapathid][no]=tx
            else:
                self.port_tx[datapathid][no]=tx
    
    def send_port_stats_request(self, datapath):#执行端口信息的查询
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        req = parser.OFPPortStatsRequest(datapath)
        datapath.send_msg(req)

如果相对端口的信息进行查询可以调用send_port_stats_request()函数,该函数向指定的交换机发起端口的查询请求,也就是OFPPortStatsRequest。

交换机收到查询请求后进行相应的查询,然后将查询结果返回给控制器,此时会触发事件EventOFPPortStatsReply。开始执行_port_stats_reply_handler()函数,该函数内部的处理是specific的,可以忽略,换成自己的代码就可以,其实就是对查询完的端口信息进行处理。

总结

学习Ryu主要是还得多看app的源码,可以配合大哥的博客学习。

但其实说到这里,Ryu咋用还是没说到,这个得配合后面介绍的Mininet了。

下章会介绍Mininet、OVS,以及Ryu的使用。
https://blog.csdn.net/weixin_44480014/article/details/123261925文章来源地址https://www.toymoban.com/news/detail-414104.html

到了这里,关于生动的SDN基础内容介绍(三)--Ryu控制器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【李群李代数】李群控制器(lie-group-controllers)介绍——控制 SO(3) 空间中的系统的比例控制器Demo...

    李群控制器SO(3)测试 测试代码是一个用于控制 SO(3) 空间中的系统的比例控制器。它通过计算控制策略来使当前状态逼近期望状态。该控制器使用比例增益 kp 进行参数化,然后进行一系列迭代以更新系统状态,最终检查状态误差是否小于给定的阈值。这个控制器用于姿态控制

    2024年02月12日
    浏览(51)
  • 【ARM CoreLink 系列 5 -- CI-700 控制器介绍 】

    请阅读 【ARM CoreLink 文章专栏导读】

    2024年02月07日
    浏览(51)
  • 【ARM CoreLink 系列 3 -- CCI-550 控制器介绍 】

    请阅读 【ARM AMBA 总线 文章专栏导读】 上篇文章:ARM CoreLink 系列 2 – CCI-400 控制器简介

    2024年02月09日
    浏览(49)
  • 【ARM CoreLink 系列 4 -- NIC-400 控制器详细介绍】

    请阅读 【ARM AMBA 总线 文章专栏导读】

    2024年02月05日
    浏览(81)
  • CAN学习笔记3:STM32 CAN控制器介绍

    STM32 CAN控制器(bxCAN),支持CAN 2.0A 和 CAN 2.0B Active版本协议。CAN 2.0A 只能处理标准数据帧且扩展帧的内容会识别错误,而CAN 2.0B Active 可以处理标准数据帧和扩展数据帧。 波特率最高可达1M bps 支持时间触发通信(CAN的硬件内部定时器可以在TX/RX的帧起始位的采样点位置生成时

    2024年02月15日
    浏览(52)
  • 【Mininet】基础篇:开源控制器POX

    大家好,我是文思月! 每文一言:时间不在于你拥有多少,而在于你如何使用! 本篇文章主要是基于POX控制器进行的两个实验:一个是使交换机模拟hub(集线器)的功能,一个是让交换机实现L2自学习。POX是一个SDN控制器,编程语言是python。 在开始之前,首先需要在ubuntu中

    2024年02月08日
    浏览(38)
  • 固定时间收敛的控制器设计(基础知识)

    本篇文章主要描述在设计固定时间控制器时,所采用的一些引理。 考虑如下非线性自治系统 x ˙ = f ( x ) , x ( 0 ) = x 0 dot{x}=f(x),quad x(0)=x_0 x ˙ = f ( x ) , x ( 0 ) = x 0 ​ 其中 x ∈ R n xinmathbb{R}^n x ∈ R n 表示状态; f : D → R n f:mathbb{D}tomathbb{R}^n f : D → R n 表示在原点的开邻域

    2023年04月10日
    浏览(73)
  • Django基础讲解-路由控制器和视图(Django-02)

    参考链接: Django源码阅读:路由(二) - 知乎 Route路由, 是一种映射关系!路由是把客户端请求的  url路径与视图进行绑定  映射的一种关系。 这个/timer通过路由控制器最终匹配到myapp.views中的视图函数 timer 。 在django中所有的路由最终都被保存到一个变量 urlpatterns , urlpatt

    2024年02月07日
    浏览(35)
  • 飞天使-k8s基础组件分析-控制器

    控制器含义解释 pod的标签与注释 ReplicaController ReplicaSet Deployments DaemonSet Job Cronjob 参考文档

    2024年02月11日
    浏览(47)
  • 【Three.js基础】创建场景、渲染场景、创建轨道控制器(一)

    🐱 个人主页: 不叫猫先生 🙋‍♂️ 作者简介:前端领域新星创作者、阿里云专家博主,专注于前端各领域技术,共同学习共同进步,一起加油呀! 💫系列专栏:vue3从入门到精通、TypeScript从入门到实践 📢 资料领取:前端进阶资料以及文中源码可以找我免费领取 🔥 前端

    2024年02月01日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包