RYU功能开发(一)从simple switch开始

        对于任意一款控制器,想要快速了解其开发机制,从转发模块入手无疑是最佳的学习方式。RYU通过App的形式提供了一系列功能模块,其中包括使用了OpenFlow作为控制协议的二层交换机控制模块simple_switch_13.py。

        想要理解simple switch的控制逻辑,首先要掌握传统网络下二层交换机的转发学习表工作原理。

        对于每个二层交换机,都会维护一个mac地址表,用于记录mac地址和物理端口的映射关系。默认状态下,mac地址表为空,当交换机在端口A收到一个数据帧时,首先检查帧头中的源mac地址(mac_src),记录mac_src到端口A的映射关系,表示交换机可以通过端口A找到mac_src对应的主机。对于帧头中的目的mac地址(mac_dst),如果没有记录mac_dst与端口的映射关系,则会通过广播的方式将数据帧转发出去。

        假如每个交换机都通过一个唯一的switch_id来标识,那么对于整个二层网络,我们可以得到一个由(switch_id,mac)到端口号(port_num)的映射表。实例:如果存在映射关系(sw1,mac1)→(port_1),那么当sw1收到目的mac地址为mac1的数据帧时,交换机将会从port_1把数据帧转发出去。simple switch模块的核心就是实现了上述映射表的记录,记录在mac_to_port字典中。

        switch_feature_handler函数:

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

        该函数用于对交换机配置table-miss流表项,实现交换机初始化,table-miss流表项匹配所有数据包,优先级为0,交换机会将匹配到table-miss流表项的数据包发送给控制器,形成packet_in事件。代码中,OFPMatch和OFPActionOutput对应了OpenFlow交换机流表规定的匹配字段和指定端口转发的动作字段,具体实现详见ofproto_v1_3_parser.py

        add_flow函数:

    def add_flow(self, datapath, priority, match, actions, buffer_id=None):
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser

        inst = [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)

        通过向交换机发送flow_mod消息配置流表。其中datapath表示交换机实例,priority是优先级,注意flow_mod消息中hard_timeout和idle_timeout缺省值为0,表示流表永久存活,可以自行设置其他整数值来为流表项设置生存时间,单位是秒。

        packet_in_handler函数:

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def _packet_in_handler(self, ev):
        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)
        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        in_port = msg.match['in_port']                # 从in_port得到的该数据帧
        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0] # 获取二层帧头

        if eth.ethertype == ether_types.ETH_TYPE_LLDP:
            return
        dst = eth.dst
        src = eth.src
        dpid = datapath.id                            # 交换机的id
        self.mac_to_port.setdefault(dpid, {})         # 数据结构格式:{dpid: {mac: port}}
        self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
        self.mac_to_port[dpid][src] = in_port         # 记录映射信息

        if dst in self.mac_to_port[dpid]:
            out_port = self.mac_to_port[dpid][dst]    # 查找目的mac对应的端口号
        else:
            out_port = ofproto.OFPP_FLOOD             # 如果没有找到,则广播

        actions = [parser.OFPActionOutput(out_port)]

        if out_port != ofproto.OFPP_FLOOD:
            match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
            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)
        data = None
        if msg.buffer_id == ofproto.OFP_NO_BUFFER:
            data = msg.data

        out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
                                  in_port=in_port, actions=actions, data=data)
        datapath.send_msg(out)

        packet_in_handler函数通过@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)标签来注册对packet_in事件的监听。对于SDN控制器应用来说,packet_in是最常见的程序入口,通过解析包头字段,获取地址、端口号、包类型等信息。simple_switch的packet_in_handler函数中解析了以太网帧头,得到了源、目的mac地址,记录交换机id、源mac地址到收包端口号。如果交换机记录了到目的mac地址的端口映射,则从对应端口转发出去,否则指定转发端口为FLOOD。

 

有关buffer_id

Packetin消息:用于标记缓存在交换机中的数据报文id,如报文被action上送到控制器中maxlen字段或者table_miss消息限制长度,而通过bufferid将报文缓存在交换机中,以便被另外两种消息来调用;

Packetout消息:用于控制器将原先buffer在交换机中的报文,通过Packetout个形式从交换机的某个物理口送出去;

Flowmod消息:如果flowmod中带有bufferid,那么说明这个flowmod需要做两件事情,第一是正常下发一条flow,其次是把交换机中先前buffer的那个数据报文,Packetout到table来匹配一次下的这条flow;注意以上两个指令都是通过这个带有bufferid的消息执行的,不需要控制器另外下packet_out消息,这种设计思路是非常巧妙的。

 

大家如果有希望了解的控制器源码,欢迎在评论区进行推荐。

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值