• RYU 灭龙战 third day


    RYU 灭龙战 third day

    前言

    传统的交换机有自学习能力。然而你知道在SDN的世界里,脑子空空的OpenFlow交换机是如何学习的吗?今日说法带你领略SDN的mac学习能力。

    RYUBook从中学习

    场景描述

    传统交换机原理

    • 学习连接到传统交换机的主机的mac地址,并把其存在mac地址表中
    • 对于已经记录下来的mac地址,若是收到送往该mac地址的数据包时,就往对应的端口进行转发
    • 对于mac地址表中没有的数据包,则进行flooding

    OpenFlow交换机实现传统交换机功能

    • 对于接收到的数据包针对指定的端口转发
    • 把接收到的数据包发送给控制器(Packet-In)
    • 把从控制器接收到的数据包转发到指定的端口(Packet-Out)

    图示

    1.初始状态

    mac地址表和交换机的流表均为空的表项

    2.Host A -> Host B

    当Host A 向 Host B 发送数据包时。这个时候会出发PacketIn消息。Host A的mac地址以及对应的端口会记录到mac地址表内。然后由于Host B的mac不在mac地址表内,此时会flooding

    3.Host B -> Host A

    数据包从host B回复给Host B时,在Flow table上新增一条流表,讲数据包转发给端口1

    4.Host A -> Host B

    再次由主机A向主机B发送数据包,新增流表,将数据包转发到端口4

    场景实现

    代码附录(附加注释)simple_switch_13.py

    # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #    http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    # implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    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
    
    
    class SimpleSwitch13(app_manager.RyuApp):
        #OF版本为1.3
        OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
    
        #初始化函数
        def __init__(self, *args, **kwargs):
            super(SimpleSwitch13, self).__init__(*args, **kwargs)
            #MAC地址表的定义
            self.mac_to_port = {}
    
        #"ryu.controller.handler.set_ev_cls作为修饰器,参数为指定事件类别的接受信息,以及交换机的状态
        #此时接收到的是SwitchFeatures,即交换机的功能,CONFIG_DISPATCHER则是交换机的状态为接收SwitchFeatures消息
        #每一个事件管理(Envent Handler)都需要有一个事件event作为参数
        @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
        def switch_features_handler(self, ev):
            #ev.msg是用来存对应事件的OpenFlow消息类别实体,这里指的是OFPSwitchFeatures
            #datapath是用来处理OpenFlow交换机重要的消息,比如与交换机的通讯和触发接收消息相关的实践
            datapath = ev.msg.datapath
            ofproto = datapath.ofproto
            parser = datapath.ofproto_parser
    
            # install table-miss flow entry
            #
            # We specify NO BUFFER to max_len of the output action due to
            # OVS bug. At this moment, if we specify a lesser number, e.g.,
            # 128, OVS will send Packet-In with invalid buffer_id and
            # truncated packet data. In that case, we cannot output packets
            # correctly.  The bug has been fixed in OVS v2.1.0.
    
            #下发Table-miss Flow Entry优先级为0的流表,即如果报文都没有匹配的话,则匹配该报文,并将其发送给控制器
            match = parser.OFPMatch()
            actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
                                              ofproto.OFPCML_NO_BUFFER)]
            self.add_flow(datapath, 0, match, actions)
    
    
        #add_flow方法用来发送Flow Mod消息
        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)
            #用FlowMod消息去更新,增加,删除流表
            datapath.send_msg(mod)
    
    
        #处理Packet-in数据包,交换机状态为一般状态
        @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)
            msg = ev.msg
            datapath = msg.datapath
            ofproto = datapath.ofproto
            parser = datapath.ofproto_parser
            #match用来存储数据包的Meta元数据
            in_port = msg.match['in_port']
    
            #data接受数据包本身的消息
            pkt = packet.Packet(msg.data)
            eth = pkt.get_protocols(ethernet.ethernet)[0]
    
            #忽略LLDP数据包
            if eth.ethertype == ether_types.ETH_TYPE_LLDP:
                # ignore lldp packet
                return
            #源目mac
            dst = eth.dst
            src = eth.src
            #id为交换机的id
            dpid = datapath.id
            #更新mac地址表,每台交换机独立的dpid对应一个表
            self.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.
            #学习mac地址表,源mac和端口对应起来
            self.mac_to_port[dpid][src] = in_port
    
            #如果目的mac在mac地址表里面,则将出端口置位对应目的mac对应的端口,否则就泛洪flooding
            if dst in self.mac_to_port[dpid]:
                out_port = self.mac_to_port[dpid][dst]
            else:
                out_port = ofproto.OFPP_FLOOD
    
            actions = [parser.OFPActionOutput(out_port)]
    
            # install a flow to avoid packet_in next time
            #下发对应的目的端口到目的mac的流表,优先级为1,比之前的table_miss的优先级高
            if out_port != ofproto.OFPP_FLOOD:
                match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
                # 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)
            data = None
            if msg.buffer_id == ofproto.OFP_NO_BUFFER:
                data = msg.data
    
            #将经过上述处理的消息通过PacketOut数据包发送给交换机
            out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
                                      in_port=in_port, actions=actions, data=data)
            datapath.send_msg(out)
    
    

    运行测试

    • 一个终端执行
    sudo mn --topo single,3 --mac --switch ovsk,protocols=OpenFlow13 --controller remote
    

    • 另一终端执行
    sudo ovs-vsctl show
    

    继续执行,查看交换机流表

    sudo ovs-ofctl -O OpenFlow13 dump-flows s1 
    

    • 在mininet端执行pingall,结果可想肯定是丢包

    • 再开一个终端,开启RYU

    进入到RYU目录下,执行

    ryu-manager --verbose ryu.app.simple_switch_13
    

    • 在ovs端查看流表
    sudo ovs-ofctl -O OpenFlow13 dump-flows s1 
    

    如今正如代码所示 Table-miss Flow Entry 加入OVS

    • mininet端
    h1 ping -c1 h2
    

    ping通

    • 再次查看OVS流表
    sudo ovs-ofctl -O OpenFlow13 dump-flows s1 
    

    • 查看ryu端的新增输出

    共发出三次PacketIn,下发三次流表

    总结

    1、相比与传统交换机,OpenFlow交换机的mac地址表维护都是在控制器内部。控制器的压力实在是太大了,毕竟可能要管理多台交换机;

    2、借鉴该应用中,处理对应OpenFlow数据包的方法,接下来可以做一些相应的尝试。

  • 相关阅读:
    高级开发必须理解的Java中SPI机制
    希尔排序--python
    SpringContextAware使用详解
    visio professional 2013 密钥
    二分查找--python
    [Oracle]单行字符函数
    [Oracle]sqlplus调整列宽
    [Oracle]MacOS sqlplus上下选择命令
    [Oracle]开启SCOTT账户
    [Oracle]Macos 安装Oracle Client 11g 11.2.0.4
  • 原文地址:https://www.cnblogs.com/wpqwpq/p/6488838.html
Copyright © 2020-2023  润新知