【mininet 0x05】使用ryu作为mininet的controller完成L2Switch功能

【mininet 0x05】使用ryu作为mininet的controller完成L2Switch功能,第1张

mininet 系列文章链接归类:

【mininet 0x01】mininet环境搭建
【mininet 0x02】如何使用mn工具来 *** 作mininet
【mininet 0x03】如何使用Python API来 *** 作mininet
【mininet 0x04】使用ryu作为mininet的controller完成HUB功能
【mininet 0x05】使用ryu作为mininet的controller完成L2Switch功能


文章目录
  • mininet 系列文章链接归类:
  • 前言
  • 一、安装ryu
  • 二、 实现一个简单的Switch
    • 关键代码
  • 三、实际效果
  • 总结
  • 参考


前言

这一节我们将接触controller,使用开源的框架ryu,制作一个简单的二层带学习MAC-Port的Switch功能。


一、安装ryu

我的环境是python2.7,如果环境相同的读者可以按照下面的链接进行安装:
【RYU】python2.7环境安装RYU的方法及遇到的坑

二、 实现一个简单的Switch
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from ryu.lib.packet import packet, ether_types
from ryu.lib.packet import ethernet

class L2Switch(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(L2Switch, self).__init__(self, *args, **kwargs)
        # {dpid : {mac: port]}
        self.mac_to_port = {}

    @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

        # 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.
        match = parser.OFPMatch()
        actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
                                          ofproto.OFPCML_NO_BUFFER)]
        self.add_flow(datapath, 0, match, actions)

    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.EventOFPPacketIn, MAIN_DISPATCHER)
    def _packet_in_handler(self, ev):
        msg = ev.msg  # ev = PacketIn'data structure  msg = openflow message
        data = None
        dp = msg.datapath  # dp=switch'id
        ofp = dp.ofproto
        ofp_parser = dp.ofproto_parser
        in_port = msg.match['in_port']
        self.logger.info("switch is %s,buffer_id is %s,in_port is %s.", dp.id, msg.buffer_id, in_port)

        pkg = packet.Packet(msg.data)
        eth = pkg.get_protocols(ethernet.ethernet)[0]
        print eth
        src_MAC = eth.src
        dst_MAC = eth.dst

        if eth.ethertype == ether_types.ETH_TYPE_LLDP:
            # ignore lldp packet
            self.logger.info("filter out LLDP msg.")
            return

        dpid = format(dp.id, "d").zfill(16)
        self.mac_to_port.setdefault(dpid, {})

        # learn a mac address to avoid FLOOD next time.
        self.mac_to_port[dpid][src_MAC] = in_port
        # find dst MAC in mac_to_port dict,
        # send the pkg to the port, if we find out the dst port in mac_to_port by dst MAC.
        # send to all port in the switch, if we can not find the dst port in mac_to_port by dst MAC.
        if dst_MAC in self.mac_to_port[dpid]:
            out_port = self.mac_to_port[dpid][dst_MAC]
            actions = [ofp_parser.OFPActionOutput(out_port)]
            match = ofp_parser.OFPMatch(in_port=in_port, eth_dst=dst_MAC, eth_src=src_MAC)
            if msg.buffer_id != ofp.OFP_NO_BUFFER:
                self.add_flow(datapath=dp, priority=100, match=match, actions=actions, buffer_id=msg.buffer_id)
                return
            else:
                self.add_flow(datapath=dp, priority=100, match=match, actions=actions)
        else:
            out_port = ofp.OFPP_FLOOD
            actions = [ofp_parser.OFPActionOutput(out_port)]

        if msg.buffer_id == ofp.OFP_NO_BUFFER:
            data = msg.data
        out = ofp_parser.OFPPacketOut(
            datapath=dp, buffer_id=msg.buffer_id, in_port=in_port,
            actions=actions, data=data)
        dp.send_msg(out)
        self.logger.info("send success!")

  • ev.msg:每一个事件类ev中都有msg成员,用于携带触发事件的数据包。
  • msg.datapath:已经格式化的msg其实就是一个packet_in报文,msg.datapath直接可以获得packet_in报文的datapath结构。datapath用于描述一个交换网桥。也是和控制器通信的实体单元。datapath.send_msg()函数用于发送数据到指定datapath。通过datapath.id可获得dpid数据,在后续的教程中会有使用。
  • datapath.ofproto对象是一个OpenFlow协议数据结构的对象,成员包含OpenFlow协议的数据结构,如动作类型OFPP_FLOOD。
  • datapath.ofp_parser则是一个按照OpenFlow解析的数据结构。
  • actions是一个列表,用于存放action list,可在其中添加动作。
  • 通过ofp_parser类,可以构造构造packet_out数据结构。括弧中填写对应字段的赋值即可。
关键代码
        # learn a mac address to avoid FLOOD next time.
        self.mac_to_port[dpid][src_MAC] = in_port

  对于来的所有包,源MAC 进行一个学习,记录到mac_to_port字典中,
  mac_to_port的结构为:{dpid : {mac: port]}

        # find dst MAC in mac_to_port dict,
        # send the pkg to the port, if we find out the dst port in mac_to_port by dst MAC.
        # send to all port in the switch, if we can not find the dst port in mac_to_port by dst MAC.
        if dst_MAC in self.mac_to_port[dpid]:
            out_port = self.mac_to_port[dpid][dst_MAC]
            actions = [ofp_parser.OFPActionOutput(out_port)]
            match = ofp_parser.OFPMatch(in_port=in_port, eth_dst=dst_MAC, eth_src=src_MAC)
            if msg.buffer_id != ofp.OFP_NO_BUFFER:
                self.add_flow(datapath=dp, priority=100, match=match, actions=actions, buffer_id=msg.buffer_id)
                return
            else:
                self.add_flow(datapath=dp, priority=100, match=match, actions=actions)
        else:
            out_port = ofp.OFPP_FLOOD
            actions = [ofp_parser.OFPActionOutput(out_port)]

  对于目的MAC在mac_to_port列表中的包,我们直接进行单播就好了,
  对于目的MAC接口未知的情况,我们选择进行FLOOD处理,发往switch上所有的接口。

三、实际效果

1、用命令 mn --topo linear,2 --mac --switch ovsk --controller remote 创建如下拓扑:

能够看到互ping已经通了

ryu的日志:

流表状态:


2、再创建如下拓扑:

能够看到互ping已经通了

ryu日志:

ovs流表内容:

总结

使用ryu作为mininet的controller,完成了switch功能。

参考

http://www.muzixing.com/pages/2014/09/20/ryuru-men-jiao-cheng.html

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/724601.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存