SDN Expriment1——实现二层自学习交换机

二层自学习交换机的学习策略

对于每个交换机,我们可以学习收到的数据包的mac和交换机port的映射,进而下发流表以指导包的转发,从而避免向所有port洪泛。

  • 实验资料下载
    https://www.aliyundrive.com/s/XRfT5Qu6pAy

code

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

class L2LearningSwitch(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] # 协议版本1.3

    def __init__(self, *args, **kwargs):
        super(L2LearningSwitch, self).__init__(*args, **kwargs)
        #maybe you need a global data structure to save the mapping
        self.mac_to_port = {} # {dpid:{src_mac:port}}

    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)

    @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)

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def _packet_in_handler(self, ev):
        '''
        学习阶段
        * 主机A首先洪泛,将包发送至所有的端口
        * 假设主机B的端口in_port1接收到来自主机A的包,无法匹配流表,packet_in报文至控制器
        * 控制器解析报文,记录mac_to_port[dpid_B][A_mac]=in_port1,指示B洪泛此报文
        * 假设主机C的端口in_port2接收到来自主机B的包,无法匹配流表,packet_in报文至控制器
        * 控制器解析报文,记录mac_to_port[dpid_C][A_mac]=in_port2,指示C洪泛此报文
        * ......
        '''

        '''
        学习结束后:
        * 假设主机C想要发送一个报文至主机A
        * 主机C首先将packet_in报文至控制器,控制器查询mac_to_port[dpidC][A_mac]=port2,指示将此报文通过port2转发
        * 报文转发至主机B
        * 主机B首先将packet_in报文至控制器,控制器查询mac_to_port[dpidB][A_mac]=port1,指示将此报文通过port1转发
        * 主机A接收到报文
        '''
        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 # 每一个事件类ev中都有msg成员,用于携带触发时间的数据包
        datapath = msg.datapath # datapath用于描述一个交换网桥
        ofproto = datapath.ofproto # 定义了OpenFlow协议数据结构的对象
        parser = datapath.ofproto_parser # 可以构造OPF的数据包
        # the port that receive the packet
        in_port = msg.match['in_port']

        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]
        # get mac
        dst = eth.dst
        src = eth.src

        dpid = format(datapath.id, "d").zfill(16)
        self.mac_to_port.setdefault(dpid, {})
        # we can use the logger to print some useful information
        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

        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
        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)
        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)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值