Ryu利用组表实现组播

组播是现代网络中非常重要的组成部分,当我们需要发送数据给多台主机的时候,如果采用单播的方式,我们需要发送多个数据包,而采用广播又会使得网络中的每个终端都必须接收数据,所以组播应运而生,组播的特点就是组播源只需要发送一次数据包,而且只有一组特定的主机会接收数据包,不想接收的主机是收不到的。

要利用Ryu实现组播,需要考虑以下几点:
1.获取,管理组播组成员的信息。
2.寻找组播源去往组播组成员的最佳路径。
在Ryu里已经提供了用于管理组成员和寻路的库ryu/lib/igmplib.py
本文将基于这个库来使用组表实现无环路拓扑的组播

igmplib

这个库集成了有关组的管理,组成员管理,流表的管理等等组播要考虑的绝大部分功能,实现组播的时候只需要把它作为一个线程启动即可,app/simple_switch_igmp_13.py就是这么一个例子。也正是因为组播的功能都在这里面实现,我们用组表实现组播关键就是改进这个库。
先简单介绍一下这个库里的6个类:

  • EventPacketIn:

处理非IGMP的Packet-In报文的。

  • EventMulticastGroupStateChanged

描述组状态改变的。

  • IgmpLibIGMP

IGMP核心类,负责线程的启动,停止,交换机角色设定,处理IGMP的Packet-In报文等。

  • IgmpBase

在IGMPv2里面交换机有两种角色:querier和snooper。这是他俩的基类,负责流表下发,删除等。

  • IgmpQuerier

这是querier类。作为一个网络的组管理者,定期下发query消息,处理组成员的加入,删除,存储组成员和接口相关信息。

  • IgmpSnooper

这是snooper类。协助querier进行组管理,收到query消息进行广播,处理组成员的加入,删除,有组成员离开时代表querier发送query消息进行查询,存储组成员和接口相关信息。

组管理

主机在加入组的时候会发送report消息,离开组的时候会发送leave消息。querier和snooper就是根据这两种消息来修改组的信息。
以querier为例,下面是它收到report消息进行的处理:

    def _do_report(self, report, in_port, msg):
        """the process when the querier received a REPORT message."""
        datapath = msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        if ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION:
            size = 65535
        else:
            size = ofproto.OFPCML_MAX

        update = False
        self._mcast.setdefault(report.address, {})
        if in_port not in self._mcast[report.address]:
            update = True
        self._mcast[report.address][in_port] = True

        if update:
            group_id = self._ipv4_text_to_int(report.address)
            actions = [parser.OFPActionGroup(group_id=group_id)]
            self._set_flow_entry(datapath, actions, self.server_port, report.address)
            buckets = []
            for port in self._mcast[report.address]:
                buckets.append(parser.OFPBucket(actions = [parser.OFPActionOutput(port)]))
            if len(buckets)==1:
                req = parser.OFPGroupMod(datapath, ofproto.OFPFC_ADD,ofproto.OFPGT_ALL, group_id, buckets)
            else:
                req = parser.OFPGroupMod(datapath, ofproto.OFPGC_MODIFY,ofproto.OFPGT_ALL, group_id, buckets)
            datapath.send_msg(req)
            self._set_flow_entry(
                datapath,
                [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, size)],
                in_port, report.address)

这里首先把接口加入到组里面,如果是第一次收到report消息就会下发流表项和组表项,流表项分为两种:把之后的report或者leave消息交给控制器处理,把匹配的组播流交给组表处理。

在收到leave消息之后:

    def _do_leave(self, leave, in_port, msg):
        """the process when the querier received a LEAVE message."""
        datapath = msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser

        self._mcast.setdefault(leave.address, {})
        if in_port in self._mcast[leave.address]:
            group_id = self._ipv4_text_to_int(leave.address)
            self._del_flow_entry(datapath, in_port, leave.address)
            del self._mcast[leave.address][in_port]
            buckets = []
            for port in self._mcast[leave.address]:
                buckets.append(parser.OFPBucket(actions = [parser.OFPActionOutput(port)]))
            if len(buckets):
                actions = [parser.OFPActionGroup(group_id=group_id)]
                self._set_flow_entry(datapath, actions, self.server_port, leave.address)
                req = parser.OFPGroupMod(datapath, ofproto.OFPGC_MODIFY,ofproto.OFPGT_ALL, group_id, buckets)
                datapath.send_msg(req)
            else:
                self._del_flow_entry(datapath, self.server_port, leave.address)
                req = parser.OFPGroupMod(datapath, ofproto.OFPGC_DELETE,ofproto.OFPGT_ALL, group_id)
                datapath.send_msg(req)

因为不确定是不是还有组成员连接到这个交换机,所以对流表项和组表项的操作要看组成员接口,会删除或者修改相关接口的流表项,然后修改或者删除现有的组表项。

设置完对流表项的操作,这个库就算设置完了,我们需要一个app对其进行调用,可以直接使用Ryu提供的igmp_13,它继承自simple_switch_13,在这里面处理非IGMP报文,也可以自己编写一个具有交换功能的代码,初始化的时候创建igmplib的线程即可。不过在这里面也应该包括两个函数,来处理普通Packet-In事件和EventMulticastGroupStateChanged事件。
写完app就可以进行组播测试了。
源代码已经放至Github,具体使用方法在Readme,还不是很完善,后续会进行修改。

这里推荐使用vlc进行组播测试,它是一个播放器,可以模拟udp组播流。
在Ubuntu下安装命令

apt-get install vlc

即可安装,安装完毕输入vlc即可运行。
vlc有两种运行方法,命令行和图形化界面,具体使用方法这里不再赘述。

下面展示组播测试结果。
组成员接收组播视频流如图1所示,中间交换机的流表项如图2所示,图3是同一台交换机的组表项。

组播测试

图1 主机收到了组播流



流表项

图2 中间一台交换机的流表项



组表项

图3 中间一台交换机的组表项

总结

原本Ryu是以流表项添加多个output实现的组播,我改为用组表实现,用组表可能会比流表更慢一些,因为流表只需要查找一次,组表要查找两次,仅仅是为了使用组表。实验效果可能因电脑配置而异,视频不流畅不一定是网络有环路,毕竟是在虚拟机播放视频,对硬件还有要求的。
这只是一个非常简单的无环组播,如果考虑环路的话还不仅仅是广播风暴,因为IGMP的report和leave报文也可能会到达同一交换机的不同接口,而无法确认这个报文到底是不是真正应该起作用,我会继续研究,如果我的内容有什么不对的地方,还请各位读者批评指正。


发表评论

  • OωO
  • |´・ω・)ノ
  • ヾ(≧∇≦*)ゝ
  • (☆ω☆)
  • (╯‵□′)╯︵┴─┴
  •  ̄﹃ ̄
  • (/ω\)
  • ∠(ᐛ」∠)_
  • (๑•̀ㅁ•́ฅ)
  • →_→
  • ୧(๑•̀⌄•́๑)૭
  • ٩(ˊᗜˋ*)و
  • (ノ°ο°)ノ
  • (´இ皿இ`)
  • ⌇●﹏●⌇
  • (ฅ´ω`ฅ)
  • (╯°A°)╯︵○○○
  • φ( ̄∇ ̄o)
  • (งᵒ̌皿ᵒ̌)ง⁼³₌₃
  • (ó﹏ò。)
  • Σ(っ°Д°;)っ
  • ╮(╯▽╰)╭
  • o(*
  • >﹏<
  • (。•ˇ‸ˇ•。)
  • 泡泡
  • 颜文字

*

已有 1 条评论