前面的几篇博客介绍了hub、流表的操作、数据包的解析等知识(以下若有不明白之处,建议先把前几篇博客看完)。接下来,根据这些知识就可以编写自学习交换机的实例了。

第一部分:相关知识

转发表、路由表、ARP表之间的关系需要先行了解(https://cloud.tencent.com/developer/article/1173761

参考ryubook(https://github.com/Yang-Jianlin/ryu/tree/master/spec_book

OpenFlow 交换器会接受来自于 controller 的指令并达到以下功能:

  • 对于接收到的封包进行修改或针对指定的端口进行转送
  • 对于接收到的封包进行转送到 Controller 的动作( Packet-In )
  • 对于接收到来自 Controller 的封包转送到指定的端口( Packet-Out )。

上述的功能所组合起来的就是一台交换器的实现。

首先,利用 Packet-In 的功能来达到 MAC 地址的学习。 Controller 使用 Packet-In 接收来自交换器的封包之后进行分析,得到端口相关数据以及所连接的 host的 MAC 地址。

在学习之后,对所收到的封包进行转送。将封包的目的地址,在已经学习的 host 数据中进行检索,根据检索的结果会进行下列处理。

  • 如果是已经存在记录中的 host:使用 Packet-Out 功能转送至先前所对应的端口
  • 如果是尚未存在记录中的 host:使用 Packet-Out 功能来达到 Flooding

具体步骤如下图所示。

1. 初始状态

此时的Flow table 为空白。将 host A 接到端口 1, host B 接到端口 4, host C 接到端口 3。

2. hostA —> hostB

当 host A 向 host B 发送封包。这时后会触发 Packet-In 讯息。 host A 的 MAC地址会被端口 1 给记录下来。由于 host B 的 MAC 地址尚未被学习,因此会进行 Flooding 并将封包往 host B 和 host C 发送。

3. host B → host A

封包从 host B 向 host A 返回时,在 Flow table 中新增一笔 Flow Entry,并将封包转送到端口 1。因此该封包并不会被 host C收到。

4. host A → host B

再一次, host A 向 host B 发送封包,在 Flow table 中新增一个 Flow Entry 接着转送封包到端口 4。

第二部分:代码

接下来,重点介绍以下代码的编写。

新建一个类Switch,内容如下:

from ryu.base import app_managerclass Switch(app_manager.RyuApp):OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)self.mac_table = {}  # mac表,即转发表,初始化为空

这里我使用v1.3版本的openflow,并且初始化一个字典,用来当作转发表。当然,由于现在尚未添加任何处理代码,所以这段程序什么也做不了。

接下来,接需要继续向类中添加代码以完成添加流表功能的开发。

定义一个发送流表的方法,内容如下:

# 流表的操作函数
# 详细参见:https://blog.csdn.net/weixin_40042248/article/details/115832995?spm=1001.2014.3001.5501def doflow(self, datapath, command, priority, match, actions):ofp = datapath.ofprotoofp_parser = datapath.ofproto_parserinst = [ofp_parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]req = ofp_parser.OFPFlowMod(datapath=datapath, command=command,priority=priority, match=match, instructions=inst)datapath.send_msg(req)

这段代码就是发送流表的方法,写完之后,我们只需要在想要修改流表的时候调用就可以了。

控制器和交换机在最开始的时候进行握手,也就是features信息的请求和返回,当控制器收到features消息之后,我们希望controller会向交换机下发一条默认流表项(table-miss),用来处理没有流表匹配时,交换机将信息发送到控制器。

 # 当控制器和交换机开始的握手动作完成后,进行table-miss(默认流表)的添加# 关于这一段代码的详细解析,参见:https://blog.csdn.net/weixin_40042248/article/details/115749340@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)def switch_features_handler(self, ev):msg = ev.msgdatapath = msg.datapathofp = datapath.ofprotoofp_parser = datapath.ofproto_parser# add table-misscommand = ofp.OFPFC_ADDmatch = ofp_parser.OFPMatch()actions = [ofp_parser.OFPActionOutput(ofp.OFPP_CONTROLLER, ofp.OFPCML_NO_BUFFER)]self.doflow(datapath, command, 0, match, actions)

接下来,就是需要对packet_in消息进行处理,控制器根据收到的交换机的消息进行转发表的学习和流表的下发等操作。具体的解释,看代码注释。

# 关键部分,转发表的学习,流表的下发,控制器的指令等@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)def packet_in_handler(self, ev):global src, dstmsg = ev.msgdatapath = msg.datapathofp = datapath.ofprotoofp_parser = datapath.ofproto_parserdpid = datapath.id# msg实际上是json格式的数据,通过解析,找出in_port# 可用print(msg)查看详细数据in_port = msg.match['in_port']# 接下来,主要是解析出源mac地址和目的mac地址pkt = packet.Packet(msg.data)for p in pkt.protocols:if p.protocol_name == 'ethernet':src = p.srcdst = p.dstprint('src:{0}  dst:{1}'.format(src, dst))# 字典的样式如下# {'dpid':{'src':in_port, 'dst':out_port}}self.mac_table.setdefault(dpid, {})# 转发表的每一项就是mac地址和端口,所以在这里不需要额外的加上dst,port的对应关系,其实返回的时候目的就是源self.mac_table[dpid][src] = in_port# 若转发表存在对应关系,就按照转发表进行;没有就需要广播得到目的ip对应的mac地址if dst in self.mac_table[dpid]:out_port = self.mac_table[dpid][dst]else:out_port = ofp.OFPP_FLOODactions = [ofp_parser.OFPActionOutput(out_port)]# 如果执行的动作不是flood,那么此时应该依据流表项进行转发操作,所以需要添加流表到交换机if out_port != ofp.OFPP_FLOOD:match = ofp_parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)command = ofp.OFPFC_ADDself.doflow(datapath=datapath, command=command, priority=1,match=match, actions=actions)data = Noneif msg.buffer_id == ofp.OFP_NO_BUFFER:data = msg.data# 控制器指导执行的命令out = ofp_parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,in_port=in_port, actions=actions, data=data)datapath.send_msg(out)

第三部分:实验

首先,利用mininet构建网络拓扑,如下所示。

拓扑设置完成,运行拓扑。然后在Ubuntu命令行运行ryu程序,命令如下。

root@yang-VirtualBox:/home/yang/ryu/ryu/app# ryu-manager switch_yjl.py

ryu程序运行后,可用查看s1和s2的流表,可用发现s1s2中已经添加了默认的流表项。

接下来,在mininet命令行输入h1 ping h2,h2 ping h3,h1 ping h3,结果如下。

结果表明,所有主机之间都可以相互ping通,接下来,查看流表项。

下面附上全部代码

from ryu.base import app_manager
from ryu.ofproto import ofproto_v1_3
from ryu.controller.handler import set_ev_cls
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.controller import ofp_event
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
'''
自学习交换机的实现
结合了握手数据解析、流表下发、转发表学习等操作
'''class Switch(app_manager.RyuApp):OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)self.mac_table = {}  # mac表,即转发表,初始化为空# 流表的操作函数# 详细参见:https://blog.csdn.net/weixin_40042248/article/details/115832995?spm=1001.2014.3001.5501def doflow(self, datapath, command, priority, match, actions):ofp = datapath.ofprotoofp_parser = datapath.ofproto_parserinst = [ofp_parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]req = ofp_parser.OFPFlowMod(datapath=datapath, command=command,priority=priority, match=match, instructions=inst)datapath.send_msg(req)# 当控制器和交换机开始的握手动作完成后,进行table-miss(默认流表)的添加# 关于这一段代码的详细解析,参见:https://blog.csdn.net/weixin_40042248/article/details/115749340@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)def switch_features_handler(self, ev):msg = ev.msgdatapath = msg.datapathofp = datapath.ofprotoofp_parser = datapath.ofproto_parser# add table-misscommand = ofp.OFPFC_ADDmatch = ofp_parser.OFPMatch()actions = [ofp_parser.OFPActionOutput(ofp.OFPP_CONTROLLER, ofp.OFPCML_NO_BUFFER)]self.doflow(datapath, command, 0, match, actions)# 关键部分,转发表的学习,流表的下发,控制器的指令等@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)def packet_in_handler(self, ev):global src, dstmsg = ev.msgdatapath = msg.datapathofp = datapath.ofprotoofp_parser = datapath.ofproto_parserdpid = datapath.id# msg实际上是json格式的数据,通过解析,找出in_port# 可用print(msg)查看详细数据in_port = msg.match['in_port']# 接下来,主要是解析出源mac地址和目的mac地址pkt = packet.Packet(msg.data)for p in pkt.protocols:if p.protocol_name == 'ethernet':src = p.srcdst = p.dstprint('src:{0}  dst:{1}'.format(src, dst))# 字典的样式如下# {'dpid':{'src':in_port, 'dst':out_port}}self.mac_table.setdefault(dpid, {})# 转发表的每一项就是mac地址和端口,所以在这里不需要额外的加上dst,port的对应关系,其实返回的时候目的就是源self.mac_table[dpid][src] = in_port# 若转发表存在对应关系,就按照转发表进行;没有就需要广播得到目的ip对应的mac地址if dst in self.mac_table[dpid]:out_port = self.mac_table[dpid][dst]else:out_port = ofp.OFPP_FLOODactions = [ofp_parser.OFPActionOutput(out_port)]# 如果执行的动作不是flood,那么此时应该依据流表项进行转发操作,所以需要添加流表到交换机if out_port != ofp.OFPP_FLOOD:match = ofp_parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)command = ofp.OFPFC_ADDself.doflow(datapath=datapath, command=command, priority=1,match=match, actions=actions)data = Noneif msg.buffer_id == ofp.OFP_NO_BUFFER:data = msg.data# 控制器指导执行的命令out = ofp_parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,in_port=in_port, actions=actions, data=data)datapath.send_msg(out)

github地址:https://github.com/Yang-Jianlin/ryu/blob/master/ryu/app/switch_yjl.py

ryu实例---自学习交换机相关推荐

  1. ryu实例---基于跳数的最短路径转发

    参考网上的一些知识,本篇内容主要介绍如何利用networkx实现最短路径转发,同时介绍ryu如何获取链路拓扑. 一.获取拓扑 对于ryu控制器而言,获取链路拓扑的主要模块在ryu/topology目录 ...

  2. SDN实验(四)——Learning Switch自学习交换机

    SDN实验(四)--Learning Switch自学习交换机 一.自学习交换机原理 (一)普通交换机实现 (二)SDN交换机实现 二.自学习交换机代码实现 (一)代码 (二)代码讲解 (三)实验 三 ...

  3. ryu实例---网络时延探测

    一.前言 前面的博文写到了基于跳数的最短路径的案例实现(ryu实例---基于跳数的最短路径转发_北风-CSDN博客),然而基于跳数的最短路径转发并未考虑网络链路的质量(时延.可用带宽.丢包率...), ...

  4. openflow交换机 ryu_在RYU中实现交换机的功能

    首先源码,解析部分如下,同时可以参考RYU_BOOK上的解释说明 原文链接参考:https://blog.csdn.net/qq_34099967/article/details/89047741 f ...

  5. ryu实例---数据包解析

    本文章基于https://ryu.readthedocs.io/en/latest/library_packet.html里面的Packet library的内容进行讲解的,即数据包解析的简单实现.r ...

  6. 中小型企业网络局域网实例-核心交换机配置2

    配置核心交换机STP 配置核心交换机生成树协议模式为MSTP,配置VLAN 10 1024 对应实例1,配置VLAN20 对应实例2,域名huawei,配置核心交换机1为MSTP 实例1的根桥.实例2 ...

  7. ryu实例---ECMP的rr(轮询)算法实现

    最近,做到一个实验,需要每隔一段时间,就改变数据的传输路径,如下图所示,主机之间的数据传输需要经过s1和s2两个交换机,交换机之间的链路有两条,实验是在这两条链路之间每隔10秒切换一次,也就是相当于E ...

  8. (五)Cisco dhcp snooping实例3-多交换机环境(DHCP服务器和DHCP客户端位于同VLAN)...

    试验拓扑 环境:dhcp server和dhcp客户端属于同vlan,但是客户端属于不同的交换机,在L2和L3交换机开启dhcp snooping后得出如下结论 L3交换机的配置 ip dhcp po ...

  9. 思科vrrp实例_Cisco 交换机 vrrp+mstp 配置实例

    一.        组网需求 1.switch a .switch b选用两台锐捷的s5750 :switch c .shwich d 选 用锐捷的 s3750和s3760 2.全网共有两个业务vla ...

最新文章

  1. TSNE 有代码实现有附3D 2D效果图
  2. 【Python】随机划分数据集并生成VOC格式列表
  3. python中os.listdir()的使用和文件批量重命名方法
  4. kubectl查看node状态_适用于初学者的基本 kubectl 和 Helm 命令 | Linux 中国
  5. Android音频开发(五)如何存储和解析最简单的音频wav文件
  6. 内含福利 | 我们“偷”来了一座航站楼。9.21, HAY!18等你来候机!
  7. JavaScript的new关键字执行过程(1)
  8. 为什么大部分的程序员成不了架构师?为什么?
  9. 人工智能火灾报警器_使用AI进行准确的火灾预测
  10. android widget包说明与应用
  11. 零基础怎么自学日语?
  12. 2023CAEE安规测试与环境试验技术设备展览会
  13. 华为手机隐藏app图标_华为手机怎么隐藏应用图标
  14. vux页面转换html,Vue 初学者使用 Vux 前端开发框架 - 文章教程
  15. 一个牛人的经历---北京八年——从极度贫困到财务自由
  16. 用java代码编写出喜字_喜字是怎么写的
  17. 《管理学》课堂笔记(领导)
  18. 台式电脑网络连接配置异常_专为电竞和内容创作者而生!体验惠普 ENVY TE01台式机...
  19. html实践手机调试
  20. 软件定义网络(Software Defined Network,SDN)简介

热门文章

  1. break语句和continue语句课后习题
  2. 糖尿病最新研究进展(2022年4月)
  3. Github每日精选(第57期):用于绘制手绘图的虚拟白板excalidraw
  4. 【对讲机的那点事】手置频麻烦?通过ADMS-7给YAESU FTM-400D写频
  5. 顺推归纳法_采用顺推归纳法求解的适用范围是( )
  6. virtualbox 硬件加速配置页中已启用硬件虚拟化,但主机并不支持。需要禁用硬件虚拟化才能启动虚拟机
  7. 大数据岗位的面试总结
  8. Web Services教程
  9. 80个白色图标素材 psd
  10. java中的垃圾回收算法与垃圾回收器