ONOS实现SDN网络组播
组表效果实现
0x00 OpenFlow组表
功能
实现更高级的数据包转发特性,如:组播、负载均衡、容灾备份和聚合
组表项
- 组表号
groupId:标识组表,用于组表区分 - 组表类型
type:与流表转发类型相似,表示对应的动作(action):Indirect、All、Select、Indirect、Fast failover-
Inidirct:执行该group中一个已定义的bucket, 该组仅支持一个bucket。 允许多个流表项或组表项指向一个公共的组(例如IP转发的下一跳)。 这是最简单的group类型,交换机通常比较支持这种类型的group。 -
All:执行该group中所有的bucket。这种类型的group用来进行multicast和broadcast。为每个bucket克隆一份数据包,然后分别执行每个bucket中的actions。 -
Select:执行该group中的一个bucket。基于一种选择算法(用户定义的哈希算法或者轮询算法)选择group中的一个bucket对数据包执行actions。这种选择算法应该尽量支持负载均衡并且为每个bucket提供一个权重用于分配。当一个bucket指定的端口down掉,交换机应该将选择限制在剩下的正常的bucket中而不是丢掉,这是为了减少链路中断。 -
Fast failover:执行第一个活动的bucket。 每个action bucket都与控制其活动性的特定端口和/或组相关联。 按照group定义的顺序评估bucket,并选择与活动端口/组关联的第一个bucket。 这个group类型使交换机可以更改转发行为而无需往返于控制器。 如果没有bucket,则丢弃数据包。
-
- 计数器
counters:记录处理过的报文数 - 动作桶
action buckets:动作桶(action bucket)列表,每个动作桶包含相关动作和参数

0x01 实验拓扑
目的
组播组外的主机向任一组播主机发送的数据包,能够被组播组所有主机接收。
组成
h0为非组播主机,负责发送数据包到组播段。h1h2h3为组播段主机,到任一主机的数据包会被复制到其他主机。s7为组播交换机,负责连接组播段和网络拓扑。
效果

0x02 脚本编写
处理拓扑信息
-
找到重复挂载交换机
# get device for i in resp_port["hosts"]: if i["ipAddresses"][0] == host_ip: group_deviceId = i["locations"][0]["elementId"] -
找到组播交换机对应端口
# get ports for i in resp_port["hosts"]: if i["locations"][0]["elementId"] == group_deviceId: # get multicast host group_ports.append(i["locations"][0]["port"]) else: # get link port link_port = i["locations"][0]["port"] return group_deviceId, group_ports, link_port
流表组表内容
-
组表项
data = { "type": "ALL", "appCookie": "0x1234abcd", "groupId": 1, "buckets": [] } for i in range(len(host_ports)): instruction = [{"type": "OUTPUT"}] instruction[0]["port"]= str(host_ports[i]) treatment = {"instructions": instruction} data["buckets"].append({"weight": 1,"treatment": treatment}) -
流表项
params = {"appId": "myApp"} data = { "priority": 10, "timeout": 0, "isPermanent": True, "deviceId": deviceId, "treatment": { "instructions": [ { "type": "GROUP", "groupId": 1 } ] }, "selector": { "criteria": [ { "type": "ETH_TYPE", "ethType": "0x800" }, { "type": "IN_PORT", "port": link_port } ] } }
0x03 存在问题
和组播实现有一定距离
- 只是简单完成了数据包的复制转发,没有落实组播段的真实意义。
- 组播的目的仍为单播形式,目的地为单个主机
ip。
拓扑过于理想
- 所有组播主机连在同一个交换机上,端口处理固定化。
- 组播交换机上没有非组播主机。
0x04 解决方案
建立实际拓扑
- 将组播主机分布在网络拓扑的各个分支。
- 组播主机需和至少一个非组播主机位于同一个分支。
基于图生成组播树
- 在
dijkstra算法选路的基础上,寻找组播树的上确界。
修改组表
- 将转发命令精确到每个组播主机。
0x05 方案实践
添加组播组
-
将组播组按照规定格式存入
GROUPS组GROUPS = [{"deviceId":"","port":"","vlan":"192.168.0.1","ip":["10.0.1.1","10.0.1.2"],"HostPort":[],"mac": {}}] -
在获取拓扑过程中完善
GROUPS中信息
修改数据目的信息
-
通过组表的动作桶修改数据包目的
IP和MAC,使得转发后目的主机能够接收响应instruction = [{"type": "L2MODIFICATION","subtype": "ETH_DST","mac": GROUPS[groupId]["mac"][GROUPS[groupId]["ip"][i]]}, {"type": "L3MODIFICATION","subtype":"IPV4_DST","ip":GROUPS[groupId]["ip"][i]}, {"type": "OUTPUT","port":GROUPS[groupId]["HostPort"][i]},]
设置vlan
-
要
ping的组播段IP与GROUPS中组播段匹配vlanIp = "192.168.0.1" -
vlan默认挂载在组播组第一个主机直连的SDN交换机端口,组播的信息需要经过该端口进行复制转发# Source to vlan dstPort = GROUPS[groupId]["deviceId"]+" "+GROUPS[groupId]["port"] path = sn.get_path(srcIp, dstPort)
0x06 改进效果展示
ping组播段内主机
-
得到重复回复

-
说明
10.0.1.2能够接收10.0.0.1发送给10.0.1.1的报文
基于VLC实现视频流转发
-
源(
10.0.0.1)发送视频流至目的(10.0.1.1)
-
组播段其余主机(
10.0.1.2)也收到视频流,效果实现

浙公网安备 33010602011771号