实验目的
利用ryu提供的API,实现二层自学习交换机。避免数据包的洪泛。
实验过程
首先需要了解二层自学习交换机的学习原理。
任何桢发送到交换机上时至少包含源MAC,因此交换机就可以记录一条(端口,MAC)记录到“端口MAC映射表”,针对那些交换机无法找到目标端口的目标MAC地址(可能是ARP桢的6字节全0目标MAC),交换机则将该桢转发到除接收端口外的其他全部端口,该过程叫泛洪,而目标主机收到后就会回复该桢,而交换机就可以记录该回复桢的源MAC(即上面的目标MAC)跟端口的对应关系以备下次使用。
在了解交换机的学习原理之后我们就可以很快的来添加需要的代码。
首先我们需要一个全局的哈希表来存储已经学习到规则。然后我们需要向交换机中添加当一个流表项就是将收到的数据包向控制器转发。当控制器收到相关的数包之后,会在这个交换机对应得哈希表中查找是否有相应得记录。如果找到了,则向交换机中下发相应得流表。这样交换机就能从对应得端口转发数据包。如果没有匹配得项得话,就从除了入端口以外得所有端口转发出去。
以下是完成得代码部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_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 class LearningSwitch(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] def __init__(self, *args, **kwargs): super(LearningSwitch, self).__init__(*args, **kwargs) # the dict for mac and port self.Map = {} def add_flow(self, datapath, priority, match, actions): dp = datapath ofp = dp.ofproto parser = dp.ofproto_parser inst = [parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)] mod = parser.OFPFlowMod(datapath=dp, priority=priority, match=match,instructions=inst) dp.send_msg(mod) @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): msg = ev.msg dp = msg.datapath ofp = dp.ofproto parser = dp.ofproto_parser match = parser.OFPMatch() actions = [parser.OFPActionOutput(ofp.OFPP_CONTROLLER,ofp.OFPCML_NO_BUFFER)] self.add_flow(dp, 0, match, actions) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def packet_in_handler(self, ev): msg = ev.msg dp = msg.datapath ofp = dp.ofproto parser = dp.ofproto_parser # the identity of switch dpid = dp.id dpid = dp.id self.Map.setdefault(dpid,{}) in_port = msg.match['in_port'] # the port that receive the packet pkt = packet.Packet(msg.data) eth_pkt = pkt.get_protocol(ethernet.ethernet) # get the mac dst = eth_pkt.dst src = eth_pkt.src dst = eth_pkt.dst src = eth_pkt.src # print logger self.logger.info('packet: %s %s %s %s', dpid, src, dst, in_port) self.Map[dpid][src] = in_port # we can use the logger to print some useful information # you need to code here to avoid the direct flooding # having fun # :) if dst in self.Map[dpid]: out_port = self.Map[dict][dst] else: out_port = ofp.OFPP_FLOOD actions = [parser.OFPActionOutput(out_port)] # install flow to avoid packet_in next time if out_port != ofp.OFPP_FLOOD: match = parser.OFPMatch(in_port = in_port,eth_dst = dst) self.add_flow(datapath, 1, match, actions) out = parser.OFPPacketOut( datapath=dp, buffer_id=ofp.OFP_NO_BUFFER,in_port=in_port,actions=actions, data=msg.data) dp.send_msg(out) |
实验结果
未改进时采用洪泛方法转发得效果图
从图中我们可以看出,虽然h1 ping h2并不需要s3的转发,但是s3还是收到了相应的数据包,原因是s2收到数据包后,采用洪泛的方式转发。导致s3也收到了相应的数据包。显然这不是我们需要的。于是有了以下的改进。
可以看到,经过改进之后,s3并没有收到他不需要的数据报。我们可以查看一下各个交换机的流表项。
可以看到,每个交换机收到相应的数据包都可以按照流表进行转发。这样就不会出现未改进时的情况。
但是这种策略也是存在问题的。会形成广播风暴。导致链路中有大量的数据包存在。我们可以采用上次作业实现的FatTree拓扑来观察。通过运行相应的拓扑,我们可以观察到,当交换机在学习的过程中,会有大量数据包在链路中传播,而且好像是无限循环的,最终可能导致控制器崩溃。
实验中遇到的问题
1. 运行simple_switch.py的时候出现了
通过在网上查阅相关资料后,发现是代码中一个参数的问题,参考连接如下:
https://sourceforge.net/p/ryu/mailman/message/34831211/
将代码的到数第二行中的参数buffer_id=msg.buffer_id,改为buffer_id=ofp.OFP_NO_BUFFER。之后程序能够正常运行。
2.正确执行ryu-manager exp_1.py之后,报错显示并没有发现这个app,原因可能是exp_1.py没有在当前终端所在的目录,另一个原因可能是exp_1.py文件中代码有问题。对于第二种情况,我们可以使用python exp_1.py来执行一下这个文件,看有没有一些基础的语法错误,当然逻辑错误就不能通过这种方式排除了。