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

  • 控制器
  • Ryu的目录
  • Ryu的学习
    • simple_switch_13.py
    • simple_switch_rest_13.py
    • 交换机信息及流表项的查询
  • 总结

控制器

之前介绍完了南向协议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)如下所示:


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介绍中显示的匹配域:

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

5、topology:
这里定义了交换机的数据结构和一些event。
可以看到这里的Switches class将交换机基本的内容已经定义好了:

定义了可支持的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_typesclass SimpleSwitch13(app_manager.RyuApp):OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] #应用的版本是OpenFlow1.3def __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.ofprotoparser = datapath.ofproto_parserinst = [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 switchif 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.msgdatapath = msg.datapathofproto = datapath.ofprotoparser = datapath.ofproto_parserin_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 packetreturndst = 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 timeif 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_outif msg.buffer_id != ofproto.OFP_NO_BUFFER:self.add_flow(datapath, 1, match, actions, msg.buffer_id)returnelse:self.add_flow(datapath, 1, match, actions)data = Noneif 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.bodyfor port_stat in sorted(body,key=attrgetter('port_no')):datapathid=ev.msg.datapath.idno=port_stat.port_notx=port_stat.tx_bytesp=self.port_tx[datapathid]if no==4294967294:continueif no in p.keys():self.port_tx1[datapathid][no]=txelse:self.port_tx[datapathid][no]=txdef send_port_stats_request(self, datapath):#执行端口信息的查询ofproto = datapath.ofprotoparser = datapath.ofproto_parserreq = 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

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

  1. 生动的SDN基础内容介绍(五)--SDN北向协议/接口和意图驱动

    生动的SDN基础内容介绍(五)--SDN北向协议/接口和意图驱动 背景 北向协议 REST API REST API在Ryu中的实现 意图驱动 总结 背景 经过前几章的讲解,现在回过头来再看看SDN的 ...

  2. 生动的SDN基础内容介绍(二)--OpenFlow协议

    生动的SDN基础内容介绍(二)--OpenFlow协议 背景 OpenFlow OpenFlow的基础概念 流表项 流表 漏表 流表项的下发 流表项的删除 计量表 组表 控制器与交换机的通信 总结 背 ...

  3. 生动的SDN基础内容介绍(六)--SDN应用平面和网络测量

    生动的SDN基础内容介绍(六)--SDN应用平面和网络测量 背景 网络测量 网络测量的定义 网络测量的对象 网络状态参数 网络性能参数 网络流量参数 Heavy Hitter和Hierarchical ...

  4. folium基础内容介绍

    folium基础内容介绍 1. 简介 ​ folium是js上著名的地理信息可视化库leafet.js为Python提供的接口,通过它,我们可以通过在Python端编写代码操纵数据,来调用leafle ...

  5. linux查找目录下含有xx的文件,linux基础命令介绍三:文件搜索及其它

    1.linux中包含大量的文件,对于文件查找,linux提供了find命令. find是一个非常有效的工具,它可以遍历目标目录甚至整个文件系统来查找某些文件或目录: find [path...] [e ...

  6. 工赋开发者社区 | 当PLC与见“IT”:MES/MOM标准之ISA-95基础内容介绍

    ISA-95 简称S95,也有称作SP95.ISA-95 是企业系统与控制系统集成国际标准,由国际自动化学会(ISA,International Society of Automation) 在199 ...

  7. 图像标注的基础内容介绍

    点击上方"AI公园",关注公众号,选择加"星标"或"置顶" 作者:Surya Remanan 编译:ronghuaiyang 导读 给大家介 ...

  8. 收藏 | 图像标注的基础内容介绍

    作者:Surya Remanan,来源:AI公园 介绍 "如果没有数据分析,公司就会变得既盲又聋,就像高速公路上的鹿一样在网络上游荡." - Geoffrey Moore 每个数据 ...

  9. _linux文本过滤grep基础命令介绍

    在linux中经常需要对文本或输出内容进行过滤,最常用的过滤命令是grep grep [OPTIONS] PATTERN [FILE...] grep按行检索输入的每一行,如果输入行包含模式PATTE ...

最新文章

  1. android route命令详解,route cmd命令详解
  2. 安装您的Sbo Add-on插件
  3. 【Python-ML】神经网络-深度学习库Keras
  4. 关于ALTERA的FPGA的弱上拉问题
  5. 强java_Java (强/弱/软/虚)引用
  6. 欢迎使用CSDN-markdown编辑器11111
  7. android长按加入购物车,《Android APP可能有的东西》之UI篇:加入购物车动画
  8. 通过Python实现简单的计算器
  9. mysql查看执行计划任务_学习计划 mysql explain执行计划任务详解
  10. MagicDraw UML 16.8 安装教程(2)-破解补丁
  11. (Foxit PDF阅读器设置)禁止改变当前缩放比例,改善书签跳转阅读体验
  12. pe_xscan作了3点更新
  13. WINDOWSXP主题风格美化教程
  14. adadelta算法_(学习率自适应的梯度下降算法)ADADELTA: AN ADAPTIVE LEARNING RATE METHOD(2012)...
  15. 计算机专业的入门书籍(第一篇博客)
  16. 50个ospf经典问题
  17. Android:根据GPS信息在地图上定位
  18. 【密码学】——初识JAVA加密体系(JCA)
  19. 潭州课堂25班:Ph201805201 WEB 之 Ajax第八课 (课堂笔记)
  20. pyalgotrade量化回测框架简单试用

热门文章

  1. Docker学习笔记之四,构建一个Redis as a Service(RAAS)
  2. 表设计中冗余字段的思考
  3. 2020年出生人口会大跌吗?解读人口数据
  4. JAVA高并发(一)——了解并行世界
  5. linux下电路图软件下载,2020年最新最好用的3款电路图软件
  6. hexo主题切换可能出现的错误
  7. 钉钉官方接口调用过程
  8. VDA 6.3 难点之“过程划分”
  9. xshell使用SSH密钥登录Linux实例
  10. 网络原理实验2 路由器的基本配置