首先我们欣赏一下simple_switch_13.py,它在ryu/app下面

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
from ryu.lib.packet import ether_typesclass SimpleSwitch13(app_manager.RyuApp):OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]def __init__(self, *args, **kwargs):super(SimpleSwitch13, 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.datapathofproto = datapath.ofprotoparser = 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)]self.add_flow(datapath, 0, match, actions)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)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.dstsrc = eth.srcdpid = datapath.idself.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.self.mac_to_port[dpid][src] = in_portif dst in self.mac_to_port[dpid]:out_port = self.mac_to_port[dpid][dst]else:out_port = ofproto.OFPP_FLOODactions = [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.dataout = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,in_port=in_port, actions=actions, data=data)datapath.send_msg(out)

其实完全理解这个代码还是非常困难的,尤其对于我们这种入门级的学生。不过没有关系。在写自己的ryu app时,在网上都能找到很多借鉴,改动其中一些代码,便可推陈出新。

接着让我们看一下文件的名称,叫simple_switch_13.py,顾名思义,它实现的是一个简单交换机的功能。其中的"13"意思是适用于openflow1.3协议。
然后,注意到类继承关系,继承了ryu.base.app_manager这个类:

class SimpleSwitch13(app_manager.RyuApp):

接着,是

 OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]def __init__(self, *args, **kwargs):super(SimpleSwitch13, self).__init__(*args, **kwargs)self.mac_to_port = {}

其中设置的OFP_VERSIONS这个变量到底有什么用呢,有待考证。然后是初始化方法。其中self.mac_to_port是一个保存(交换机id, mac地址)到转发端口的字典。
然后是这样的一个方法:

 @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)def switch_features_handler(self, ev):datapath = ev.msg.datapathofproto = datapath.ofprotoparser = datapath.ofproto_parsermatch = parser.OFPMatch()actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,ofproto.OFPCML_NO_BUFFER)]self.add_flow(datapath, 0, match, actions)

首先注意装饰器。这里装饰器的用处是,当ofp_event.EventOFPSwitchFeatures这样的一个事件来临,且处于CONFIG_DISPATCHER这样一个阶段时,触发这个方法switch_features_handler(self, ev),其中ev参数是ofp_event.EventOFPSwitchFeatures的这样一个事件类的对象。

在介绍EventOFPSwitchFeatures这样一个类之前,先介绍一下CONFIG_DISPATCHER这样一个阶段。

ryu控制器和交换机的连接有4个阶段,分别是
ryu.controller.handler.HANDSHAKE_DISPATCHER
ryu.controller.handler.CONFIG_DISPATCHER
ryu.controller.handler.MAIN_DISPATCHER
ryu.controller.handler.DEAD_DISPATCHER
其中,CONFIG_DISPATCHER代表的是协议版本确认后向交换机发送特性请求消息的阶段。
MAIN_DISPATCHER代表的是特性消息接受到后到断开连接前的阶段。
当启动控制器再启动交换机后,先经历握手阶段,再经历CONFIG_DISPATCHER这一个阶段。进入CONFIG_DISPATCHER这个阶段后,控制器自动发送特性请求消息。交换机接受到特性请求消息后会回复消息,就是EventOFPSwitchFeatures。当控制器接受到EventOFPSwitchFeatures这个对象且还未转换到MAIN_DISPATCHER这个状态时,就触发这个方法。
再来看看这个方法内容有哪些。
调用了self.add_flow()这样的一个方法。

 datapath = ev.msg.datapathofproto = datapath.ofprotoparser = datapath.ofproto_parsermatch = parser.OFPMatch()actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,ofproto.OFPCML_NO_BUFFER)]self.add_flow(datapath, 0, match, actions)

这个方法在这里配置的参数有datapath, priority,match,actions
datapath存储交换机的基本信息,如id,端口信息。priority越高,优先级就越高。match这里构造的parser.OFPMatch()代表没有匹配任何东西。actions这里构造的意思是发送给控制器。意思是,当没有匹配任何其它流表时,发送请求给控制器。(因为它的优先级最低是0)

接下来是重点。

@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)def _packet_in_handler(self, ev):

这个类呢,代表交换机发送数据包给控制器引发的类,且一定是在MAIN_DISPATCHER这个阶段才触发。
下面这句,看注释都能明白,如果传输出错,直接丢弃。

 # 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.dstsrc = eth.srcdpid = datapath.idself.mac_to_port.setdefault(dpid, {})self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)

其中,根据注释,如果接受到了lldp包,就直接丢弃。

关键来了,接下来是自学习的一句:

 # learn a mac address to avoid FLOOD next time.self.mac_to_port[dpid][src] = in_port

其中,dpid是交换机的id,src是数据包的源mac地址,in_port是交换机接受到包的端口。
自学习后,检验目的地址是否已经学习,如果已经学习到,则向交换机下发流表,并让交换机向相应端口转发包。如果没有,则无法下发流表,让交换机洪范地转发包。
注意,这里有个buffer_id的概念。buffer是交换机的一项功能。这里我们不去讨论有没有这项功能,我们写控制器只关心交换机用没有用这项功能。如果msg.buffer_id不为None,即交换机把数据包存储在缓冲区,那么控制器在下发parser.OFPPacketOut的命令时,只需指定buffer_id就行。如果没有把数据包存储在缓冲区,那么控制器在下发命令时,又要把数据包的信息传给交换机让它转发。
这里,当目的地址对于交换机来说已经学习到时,如果buffer_id不为None,那么只需控制器下发流表的命令即可;交换机增加了流表项后,位于缓冲区的数据包自动转发出去。如果没有buffer,那么控制器不光要更改交换机的流表项,还要下发命令让交换机把数据包转发出去。当然如果目的地址对于交换机还没有学习到的话,控制器就只下发命令让交换机洪范地转发数据包出去即可。

     if dst in self.mac_to_port[dpid]:out_port = self.mac_to_port[dpid][dst]else:out_port = ofproto.OFPP_FLOODactions = [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.dataout = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,in_port=in_port, actions=actions, data=data)datapath.send_msg(out)

以上就是我对第一个ryu app-simple_switch.py的理解,是不是很简单呢?

软件定义网络入门学习笔记3-学习第一个ryu app-simple_switch_13.py相关推荐

  1. 学习笔记之编程达到一个高的境界就是自制脚本语言(图)

    学习笔记之编程达到一个高的境界就是自制脚本语言(图) 编程达到一个高的境界就是自制脚本语言,通过这可以精通编程里面的高深的技术,如编译原理.语言处理器.编译器与解释器,这些都是代表一个程序员实力的技术 ...

  2. IOS学习笔记04---编写第一个C语言程序-Hello World

    IOS学习笔记04---编写第一个C语言程序-Hello World --------------------------------------------------------         ...

  3. SilverLight学习笔记--进一步学习Isolated Storage独立存储一(理论篇)

    在"silverlight如何在客户端读取文件"以及"silverlight如何在客户端写入文件"两篇文章中我们初步接触了Isolated Storage概念. ...

  4. go 变量在其中一个函数中赋值 另一个函数_go 学习笔记之仅仅需要一个示例就能讲清楚什么闭包...

    本篇文章是 Go 语言学习笔记之函数式编程系列文章的第二篇,上一篇介绍了函数基础,这一篇文章重点介绍函数的重要应用之一: 闭包 空谈误国,实干兴邦,以具体代码示例为基础讲解什么是闭包以及为什么需要闭包 ...

  5. Python3学习笔记之-学习基础(第三篇)

    Python3学习笔记之-学习基础(第三篇) 文章目录 目录 Python3学习笔记之-学习基础(第三篇) 文章目录 一.循环 1.for循环 2.while循环 3.break,continue 二 ...

  6. go 学习笔记之仅仅需要一个示例就能讲清楚什么闭包

    本篇文章是 Go 语言学习笔记之函数式编程系列文章的第二篇,上一篇介绍了函数基础,这一篇文章重点介绍函数的重要应用之一: 闭包 空谈误国,实干兴邦,以具体代码示例为基础讲解什么是闭包以及为什么需要闭包 ...

  7. P4学习笔记(二)一个简单P4交换机实现

    P4学习笔记(一)初始P4 P4学习笔记(二)一个简单P4交换机实现 文章目录 1. 架构模型 2.预定义模块详细描述 2.1 Arbiter 模块 2.2 Parser runtime 模块 2.3 ...

  8. 强化学习笔记-强化学习概述

    强化学习笔记-强化学习概述 机器学习分类 强化学习与监督学习的异同点 强化学习基本原理 强化学习解决的是什么样的问题 强化学习分类 请分别解释随机性策略和确定性策略 回报.值函数.行为值函数三个指标的 ...

  9. UE4入门学习笔记——纪念学习虚幻引擎满一周年

    UE4入门学习笔记 前言: 今天是正式学习ue4一周年.一年前的今天,我结束了PBR流程的学习,怀揣着对游戏制作的热爱,正式开始学习ue4,继续追寻儿时的那个大厂梦.谁也没想到,一年后的今天,我会在T ...

  10. 【DOTS学习笔记】从第一个Jobs程序入门

    目录 前言 Unity Jobs System C# Jobs Systems Blittable Types VS Non-Blittable Types NativeContainers Nati ...

最新文章

  1. java静态方法和非静态方法内存区别_static方法和非static方法的区别(java)
  2. nginx+tomcat的负载均衡
  3. CAS_SSO单点登录实例详细步骤(转)、Tomcat ssl(https) 配置
  4. NameNode和SecondaryNameNode工作机制
  5. LeetCode 649. Dota2 参议院(循环队列)
  6. PYTHON 处理JSON文件(新建key值Findex,并将歌手名字转换为大写首字母作为value值)
  7. POJ1068 LA2338 HDU1361 ZOJ1016 Parencodings【序列处理+括号匹配】
  8. ISO/IEC 27000官方文档
  9. win7使用命令行改计算机名,Win7巧用注册表更改计算机名的实用方法
  10. MarkDown--- 让CSDN的博客更炫丽,添加小图标,调整字体大小和颜色
  11. OC 6702升压型恒流驱动芯片, ESOP8 封装,内置 100V 功率 MOS
  12. CodeFroces gym 100781 A.Adjoin the Networks(贪心)
  13. Ruby‘s Adventrue游戏制作笔记(十四)Unity播放游戏音效
  14. 人工智能数学基础---不定积分3:分部积分法
  15. 怎么用win7系统的电脑录屏
  16. 【手把手带你学习神经机器翻译--代码篇】
  17. 中文字典排序与多音字处理
  18. 培养数字化人才 护航大学生就业 千锋教研院2022年教研战略发布会隆重举行
  19. Windows10如何配置java环境
  20. tar分卷压缩/解压大文件

热门文章

  1. CodeRunner破解
  2. 正则表达式lookahead and lookbehind zero-length assertions
  3. HHL论文第一弹(总结算法基本思想、QRAM制备量子态)
  4. 23种设计模式--桥接模式(Bridge)
  5. 软件开发工具【十】 之 调试程序
  6. 对于拖延症的最好方法
  7. 机器学习实战K-近邻算法
  8. 计算机博士自白:毕业放弃学术去企业,从天之骄子坠落成天生白痴
  9. android调色器 源代码,Android 上的调色板 —— Palette
  10. 可调稳压电源lm317实验报告_LM317可调稳压电源