实验目标
- 在Ryu上开发二层MAC自学习交换(以下简称二层交换)应用
二层交换应用是最基础的计算机网络应用,通过开发二层交换应用,使用Ryu控制器开发网络应用
实验原理
模拟实现了二层交换机的运作原理:
- 交换机具有自学习能力,能记录源MAC地址和入端口的对应关系。
- 当一个数据包从某端口进入交换机,交换机将记录其源MAC地址和入端口的对应关系,然后查询对应的目的MAC地址对应的端口记录是否存在。
- 若查询成功,则将数据包转发到查询所得端口,若查询失败,则进行泛洪处理。
二层交换应用被广泛应用于局域网环境,是基础的计算机网络应用。
- 但是二层交换应用无法单独应用在有回环的网络拓扑中,还需要其他协议或者算法来辅助,解决广播数据包在环路中泛洪产生的广播风暴。
- 所以在测试本应用时,需搭建无环拓扑。
- 若搭建有环拓扑,则需运行STP(Spanning Tree Protocol)协议或者其他算法来解决环路风暴问题。
- 示例应用使用OpenFlow1.3版本协议,详细代码如下:
#coding:utf-8from ryu.base import app_managerfrom ryu.controller import ofp_eventfrom ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHERfrom ryu.controller.handler import set_ev_clsfrom ryu.ofproto import ofproto_v1_3from ryu.lib.packet import packetfrom ryu.lib.packet import ethernetclass SimpleSwitch13(app_manager.RyuApp):# 继承RyuApp父类,RyuApp是Ryu应用的抽象功能描述类。RyuApp类完成了Ryu应用所需要完成的基本操作# 从而使得开发者在开发应用时专注于业务逻辑的实现OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]def __init__(self, *args, **kwargs):super(SimpleSwitch13, self).__init__(*args,**kwargs)# mac_to_port字典用于保存MAC地址和交换机端口的对应关系self.mac_to_port = {}@set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER)def switch_features_handler(self, ev):# 当datapath(此处的datapath等同于交换网桥,可以大致理解为一台交换机)与控制器建立连接之后,会通过features消息回复datapath的特性。# switch_features_handler方法通过@set_ev_cls装饰器注册了EventOFPSwitchFeatures 事件。# 当EventOFPSwitchFeatures事件在CONFIG_DISPATCHER阶段生成时,就会被分发到switch_features_handler方法,# 从而执行处理方法,完成事件处理。# @set_ev_cls装饰器的第一个参数是事件类型,第二个参数为产生事件的状态。# Ryu的ofp_event模块定义了系统使用到的事件,包括OpenFlow事件和一些内部使用的自定义事件。# Ryu定义了HANDSHAKE_DISPATCHER、CONFIG_DISPATCHER、MAIN_DISPATCHER和DEAD_DISPATCHER四种状态,# 用于描述datapath 的OpenFlow 通道连接的通信状态。# switch_features_handler方法给datapath下发一条table-miss流表项# 用于指导交换机将匹配流表项失败的数据包用Packet-in的方式发送到控制器。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)def add_flow(self, datapath, priority,match, actions):ofproto = datapath.ofprotoparser = datapath.ofproto_parserinst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)]mod = parser.OFPFlowMod(datapath=datapath, priority=priority,idle_timeout=0, hard_timeout=0,match=match, instructions=inst)datapath.send_msg(mod)@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)def _packet_in_handler(self, ev):# _packet_in_handler 方法注册了EventOFPPacketIn 事件# 当EventOFPPacketIn事件在MAIN_DISPATCHER状态下产生时,就会被分发给_packet_in_handler方法处理。# 处理方法首先对数据包进行必要的解析# 然后按照交换机dpid的入端口号为键值,将源MAC地址记录到mac_to_port字典中:mac_to_port[dpid][in_port]=SRC_MAC。# 然后查看其目的MAC地址是否在mac_to_port字典数据项的值中。# 若目的MAC地址不在表中,则将出端口设置为ofproto.OFPP_FLOOD,并通过Packetout报文指示交换机将数据包泛洪;# 若查询成功,则将出端口设置为查询所得端口;最后调用add_flow方法将流表项下发到datapath,从而完成二层交换应用。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]dst = 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)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)
运行实验
- 写完以上代码之后将其保存为Python 文件, 如
simple_switch_13_test.py,然后保存到ryu/app目录下 - 由于这个文件是新加的,Ryu编译文件中并没有,所以还需要将Ryu重新编译安装
- 重新安装之后,通过ryu-manager命令启动Ryu控制器并加载新写的应用即可
具体命令如下
cd ryupython setup.py installryu-manager ryu/app/simple_switch_13_test.py
启动Ryu之后,通过Mininet启动任意无环拓扑并连接到控制器。
sudo mn --controller remote
连接成功之后,在Mininet中进行pingall测试
- 具体测试结果如图所示

