a) 代码当中的mac_to_port的作用是什么?
b) simple_switch和simple_switch_13在dpid的输出上有何不同?
c) 相比simple_switch,simple_switch_13增加的switch_feature_handler实现了什么功能?
d) simple_switch_13是如何实现流规则下发的?
e) switch_features_handler和_packet_in_handler两个事件在发送流规则的优先级上有何不同?
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_types # 继承ryu.base.app_manager.RyuApp # 基类ryu.base.app_manager.RyuAPP是所有开发APP必备继承的类, # 可以理解成开发APP的环境,而且有了它都不用注册,非常方便 class SimpleSwitch13(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] # 指定OpenFlow 1.3版本 def __init__(self, *args, **kwargs): super(SimpleSwitch13, self).__init__(*args, **kwargs) self.mac_to_port = {} # 定义MAC地址列表,这里的mac_to_port表就是对应的交换机二层通信查询表 # set_ev_cls指定事件类别得以接受事件消息和交换机状态作为参数 # set_ev_cls第一个参数表示事件发生时应该调用的函数,第二个参数告诉交换机只有在交换机握手完成之后,才可以被调用。 # ofp_event完成了事件的定义,从而我们可以在函数中注册handler,监听事件,并作出回应 # packet_in_handler方法用于处理packet_in事件。 # @set_ev_cls修饰符用于告知RYU,被修饰的函数应该被调用。 @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): # ev.msg 是用来存储对应事件的 OpenFlow 消息类别实体 # msg.datapath是用来存储OpenFlow交换机ryu.controller.controller.Datapath 类别所对应的实体 datapath = ev.msg.datapath ofproto = datapath.ofproto # ofproto表示使用的OpenFlow版本所对应的ryu.ofproto.ofproto_v1_3 parser = datapath.ofproto_parser # 和ofproto一样,有对应版本ryu.ofproto.ofproto_v1_3_parser,解析协议才能使用 @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 switch if 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) # 送往 Controller 的封包可以僅只傳送 header 部分( Ethernet header ),剩下的則存在緩衝區間中以增加效率。 #但目前( 2014年1月 )Open vSwitch 存在臭蟲的關係,會將所有的封包都傳送,並不會只傳送 header。 #这条logger.debug日志是为了提示送往controller的packet_in包是截取header 部分,这里我是这么理解的,有错误请大家告知 # 为了接收处理未知目的地的数据包,需要执行Packet-In 事件管理 msg = ev.msg # 每一个事件类ev中都有msg成员,用于携带触发事件的数据包 datapath = msg.datapath # 已经格式化的msg其实就是一个packet_in报文,msg.datapath直接可以获得packet_in报文的datapath结构 # datapath用于描述一个交换网桥,也是和控制器通信的实体单元。 # datapath.send_msg()函数用于发送数据到指定datapath。 # 通过datapath.id可获得dpid数据。 ofproto = datapath.ofproto # datapath.ofproto对象是一个OpenFlow协议数据结构的对象,成员包含OpenFlow协议的数据结构,如动作类型OFPP_FLOOD parser = datapath.ofproto_parser # datapath.ofp_parser则是一个按照OpenFlow解析的数据结构。 # 更新Mac地址表 in_port = msg.match['in_port'] pkt = packet.Packet(msg.data) eth = pkt.get_protocols(ethernet.ethernet)[0] # pkt.get_protocols 传入的是 协议类 参数 # 得到协议的ethernet.ethernet类实例的协议列表,看源码知道了lib.packet.packet/ethernet if eth.ethertype == ether_types.ETH_TYPE_LLDP: # ignore lldp packet return dst = eth.dst src = eth.src dpid = datapath.id self.mac_to_port.setdefault(dpid, {}) # 指定交换机dpid,默认mac_to_port表为空 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_port # 判断转发的数据包的连接端口 # 目的 MAC 位址若存在于 MAC 地址表,则判断该连接端口号码为输出。 # 反之若不存在于 MAC 地址表则 OUTPUT action 类别的实体并生成 flooding( OFPP_FLOOD )给目的连接端口使用。 if dst in self.mac_to_port[dpid]: out_port = self.mac_to_port[dpid][dst] # 有目的地址寻找端口号,否则泛洪 else: out_port = ofproto.OFPP_FLOOD # 准备泛洪的packet_out指令给后面的if判断语句最后的else actions = [parser.OFPActionOutput(out_port)] # install a flow to avoid packet_in next time if 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_out if msg.buffer_id != ofproto.OFP_NO_BUFFER: self.add_flow(datapath, 1, match, actions, msg.buffer_id) return # 不用泛洪退出函数 else: self.add_flow(datapath, 1, match, actions) # 在 MAC 地址表中找寻目的 MAC 地址,若是有找到则发送 Packet-Out 讯息,并且转送数据包。 data = None if msg.buffer_id == ofproto.OFP_NO_BUFFER: # 表示要泛洪发送Packet-Out,把本身的二进制数据取出来 data = msg.data #需要Flooding 的actions和msg。buffer_id已经准备好了 out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port, actions=actions, data=data) datapath.send_msg(out)
a) 代码当中的mac_to_port的作用是什么?
答:对应的交换机二层通信查询表 。
b) simple_switch和simple_switch_13在dpid的输出上有何不同?
答:simple_switch_13对dpid填充为16位数字。
c) 相比simple_switch,simple_switch_13增加的switch_feature_handler实现了什么功能?
答:安装无目标的流表条目
d) simple_switch_13是如何实现流规则下发的?
答:接收到包后学习,存在mac地址就进行转发,不存在就洪泛转发。
e) switch_features_handler和packet_in_handler两个事件在发送流规则的优先级上有何不同?
答:switch_features_handler发送的priority=0,packet_in_handler发送的流表的priority设置为1,switch_features_handler优先级更高。
个人感想:前面的基础任务我想还是ok的吧,安装那么多东西,也不差一个ryu。能碰到的问题也相差无几。但安装的时候好像把python升级了?删除了一下旧链接,重新再建立新连接也就搞定了,后面使用 tcpdump 验证L2Switch的过程跟上次的实验步骤都差不多。
这次的进阶,查资料就完事了,勉勉强强看懂一些功能,但大部分的代码就是我虽然看的不是很懂,但我大受震撼。感觉其中暗含的技术真是让人摸不着头,可能以后再学深入一些可能就会再明白些吧。