作者简介:满明磊,山东科技大学本科生,SDN爱好者
组播是现代网络中非常重要的组成部分,当我们需要发送数据给多台主机的时候,如果采用单播的方式,我们需要发送多个数据包,而采用广播又会使得网络中的每个终端都必须接收数据,所以组播应运而生,组播的特点就是组播源只需要发送一次数据包,而且只有一组特定的主机会接收数据包,不想接收的主机是收不到的。
要利用Ryu实现组播,需要考虑以下几点:
1.获取,管理组播组成员的信息。
2.寻找组播源去往组播组成员的最佳路径。
在Ryu里已经提供了用于管理组成员和寻路的库ryu/lib/igmplib.py
本文将基于这个库来使用组表实现无环路拓扑的组播
一、igmplib
igmplib库集成了有关组管理、组成员管理、流表管理等组播要考虑的绝大部分功能,实现组播时只需要把它作为一个线程启动即可,ryu中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消息进行的处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
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消息之后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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下安装命令
1 |
apt-get install vlc |
即可安装,安装完毕输入vlc即可运行。
vlc有两种运行方法,命令行和图形化界面,具体使用方法这里不再赘述。
三、结果测试
下面展示组播测试结果。
组成员接收组播视频流如图1所示,中间交换机的流表项如图2所示,图3是同一台交换机的组表项。
四、总结
原本Ryu是以流表项添加多个output实现的组播,我改为用组表实现,用组表可能会比流表更慢一些,因为流表只需要匹配一次,组表要匹配两次,仅仅是为了使用组表。实验效果可能因电脑配置而异,视频不流畅不一定是网络有环路,毕竟是在虚拟机播放视频,对硬件还有要求的。
这只是一个非常简单的无环组播,如果考虑环路的话还不仅仅是广播风暴,因为IGMP的report和leave报文也可能会到达同一交换机的不同接口,而无法确认这个报文到底是不是真正应该起作用,我会继续研究,如果我的内容有什么不对的地方,还请各位读者批评指正。