52

基于ONOS的路径反转实现

 6 years ago
source link: https://www.sdnlab.com/21019.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

作者简介:周正强,北京邮电大学未来网络实验室在读研究生,个人邮箱:[email protected]

参加了第五届SDN比赛,我也很幸运的进了复赛,本来也想着通过这种方式学习ONOS,所以想分享关于初赛第四题自己的一些解法思路,仅供大家参考。

一. 实验任务:

基于北向API开发一个简单的路由控制应用,实现动态转发路径规则设置。假设H1 ping H4,初始的路由规则是S1-S2-S5,30秒后,路由转发规则变为S1-S3-S5,再过30秒,规则变为S1-S4-S5,然后再回到最初的转发规则S1-S2-S5。通过这个循环调度的例子动态地改变交换机的转发规则。开发验证程序,使得为程序输入源IP地址和目的IP地址时,能够根据当前的流表信息分析出传输路径,并输出路径结果。

network.png

图1 网络拓扑示意图

二. 实验步骤:

2.1 仿真网络环境

采用的环境和基础题的环境一致,如下所述:

  • Ubuntu14.04LTS虚机中安装2.2.1版本的mininet网络仿真软件(IP地址为10.108.144.145);
  • 1台ubuntu16.04LTS虚机分别安装ONOS1.10.13版本的控制器(IP地址分别为10.108.147.10);
  • 1台Windows10 主机,安装Node.JS和NPM包管理器(IP地址为10.112.9.139)。

2.2 程序设计方案

2.2.1 路径动态反转方案设计

根据题目要求需要开发路由控制应用,可以实现30s的动态转发路径规则设置,其利用的基本原理是给交换机下发流表时设置hard_timeout为30s,表示到了30s后自动删除流表,这时候会重新发送报文给控制器请求新的路径。同时,我们在设计过程中用了给路径打标签的方法(具体见3中所述),其路径动态反转大致流程图如下图中所示:

2path.PNG

图2 路径动态反转思路

1) 在控制器通过API获取全局拓扑后,控制器等待接收Packet_in消息对底层转发规则进行更改。
2) 此时解析数据包,获取数据包中的srcId和dstId,并且通过全局拓扑计算源目的IP之间的所有路径
3) 解析所有路径,从所有路径中获取到我们需要的path。
4) 在获得路径后,给该路径上的所有交换机下发对应的流表,并且设置流表中hard_timeout为30s,匹配域为源IP地址和目的IP地址,这也是为方便之后根据流表解析路径所做的准备。

5) 待30s结束流表会被清空,此时新的数据包包在进入第一个交换机时会再次packet_in给controller,此时利用上述中选定不同的路径将数据包转发,可以实现30s的动态反转。

2.2.2 IP验证程序方案设计

IP验证程序利用拓扑获取API,使用JavaScript实现,其流程图如下图中所示:

3%20IP.png

图3 IP验证程序设计实现

1) 首先会使用API获取链接和主机,并生成虚拟网络拓扑结构
2) 获取用户输入的源和目的IP地址,并获取当前网络中没给交换机中的流表
3) 匹配完成后采用DFS搜索,可以获得当前路径并显示在前端上
4) 为实现其路径反转效果,我们在程序旁边加入了当前系统时间,在一定时间内重新获取交换机中的流表,并重新计算路径,作为前后对比可以得出其路径反转效果。

2.3 具体反转解题思路

  • 首先根据题目中所示拓扑,左边的主机和右边的主机通信的时候就只有三条路径,分别是S1-S2-S5, S1-S3-S5, S1-S4-S5,这三条路中唯一不同的只有中间的交换机S2,S3,S4,因此我给每条路径都打上标签,S1-S2-S5设置的标签为数字1, S1-S3-S5设置的标签为数字2,S1-S4-S5设置的标签为数字3,在选择路径匹配时用该条路径的数字标签和对应的中间交换机进行比对即可判断是否符合路径的选择判断。
  • 根据以上想法,我设计两个Map的键值对变量。其中map用于存储的键是<源主机ID,目的主机ID>,值为对应的路径标签1-3中的其中一个,表示当前源主机到目的主机应该选择的路径。mapDevice则是根据当前路径标签去筛选出对应的中间交换机的DeviceId, 通过这个DeviceId去匹配当前算法得到的路径是否符合要求,并在activate函数中将此变量初始化备用。
Java
private Map<Map<HostId, HostId>, Integer> map = new HashMap<Map<HostId, HostId>, Integer>(); 2. private Map<Integer, DeviceId> mapDevice = new HashMap<Integer, DeviceId>(); 3. protected void activate() { 4. //first register the appId 5. mapDevice.put(1, DeviceId.deviceId("of:0000000000000002")); 6. mapDevice.put(2, DeviceId.deviceId("of:0000000000000003")); 7. mapDevice.put(3, DeviceId.deviceId("of:0000000000000004")); 8. appId = coreService.registerApplication("org.zhou.ifwd"); 9. packetService.addProcessor(processor, PacketProcessor.director(2)); 10. 11. //create the selector of traffic 12. TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); 13. //match the ethtype of ipv4 14. selector.matchEthType(Ethernet.TYPE_IPV4); 15. packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId); 16. log.info("Hello, this is the first App"); 17. }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
   privateMap<Map<HostId,HostId>,Integer>map=newHashMap<Map<HostId,HostId>,Integer>();  
2.       privateMap<Integer,DeviceId>mapDevice=newHashMap<Integer,DeviceId>();  
3.     protectedvoidactivate(){  
4.         //first register the appId  
5.         mapDevice.put(1,DeviceId.deviceId("of:0000000000000002"));  
6.         mapDevice.put(2,DeviceId.deviceId("of:0000000000000003"));  
7.         mapDevice.put(3,DeviceId.deviceId("of:0000000000000004"));
8.          appId=coreService.registerApplication("org.zhou.ifwd");  
9.         packetService.addProcessor(processor,PacketProcessor.director(2));  
10.   
11.         //create the selector of traffic  
12.         TrafficSelector.Builder selector=DefaultTrafficSelector.builder();  
13.         //match the ethtype of ipv4  
14.         selector.matchEthType(Ethernet.TYPE_IPV4);  
15.         packetService.requestPackets(selector.build(),PacketPriority.REACTIVE,appId);  
16.         log.info("Hello, this is the first App");  
17.     }  
  • 进行数据包的解析操作,需要解析出源和目的IP用于下发流表时备用,arp泛洪等操作和一般的解析文件一样。其中,会先通过onos自带的topologyService获取源目的主机之间所有可经过的路径并存储到paths变量中

    Java
    Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(), pkt.receivedFrom().deviceId(), dst.location().deviceId());
    1
    Set<Path>paths=  topologyService.getPaths(topologyService.currentTopology(),  pkt.receivedFrom().deviceId(),dst.location().deviceId());
  • paths变量传入选择路径函数中,从里面选择出理论上应该走过的路径。其中getPathCenterDeviceId函数是根据当前的源主机ID和目的主机ID去查找理论上应该经过的路径的中间交换机的值,每调用一次map中的值变量就对一个+1,按照1-3的次序循环变化(满足题目中的依次反转),然后通过mapDevice返回对应路径的交换机ID。其中在pickForwardPathIfPossible函数中不知道本身获取的paths出于什么原因,会出现一个bug,部分路径在找寻时会发生函数返回的path为null的情况,导致最后反转的路径不对,因此在返回path为null前对map中源主机和目的主机ID对应的链路值进行减1操作,保证下一次再进入寻找路径时可以正常的反转,而不是出现隔一次路径跳转的情况。

    Java
    1. private Path pickForwardPathIfPossible(Set<Path> paths, PortNumber notToPort, HostId srcId, HostId dstId) { 2. DeviceId matchDeviceId = getPathCenterDeviceId(srcId, dstId); 3. log.info("MatchDeviced ID shoud be {}", matchDeviceId); 4. for (Path path : paths) { 5. log.info("\n *** Link Path Info *** {}", 6. path.links().toString().replace("Default", "\n")); 7. if (!path.src().port().equals(notToPort)) { 8. for (Link link : path.links()) { 9. if (link.src().deviceId().equals(matchDeviceId)) { 10. return path; 11. } 12. } 13. } 14. } 15. this.mapMinusOne(srcId, dstId); 16. return null; 17. } 18. 19. private DeviceId getPathCenterDeviceId(HostId srcId, HostId dstId) { 20. //deviceId:of:0000000000000004 21. Map<HostId, HostId> tempMap = new HashMap<HostId, HostId>(); 22. tempMap.put(srcId, dstId); 23. if (map.containsKey(tempMap)) { 24. Integer result = map.get(tempMap); 25. if (result == 3) { 26. //modify the value of the key 27. map.put(tempMap, 1); 28. return mapDevice.get(1); 29. } else { 30. map.put(tempMap, result + 1); 31. return mapDevice.get(result + 1); 32. } 33. } else { 34. // first time to record the srcId and dstId 35. map.put(tempMap, 1); 36. return mapDevice.get(1); 37. } 38. 39. } 40. 41. private void mapMinusOne(HostId srcId, HostId dstId) { 42. Map<HostId, HostId> tempMap = new HashMap<HostId, HostId>(); 43. tempMap.put(srcId, dstId); 44. Integer result = map.get(tempMap); 45. if (result == 1) { 46. //go back to last time 47. map.put(tempMap, 3); 48. } else { 49. map.put(tempMap, result - 1); 50. } 51. } 52.
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    1.privatePath pickForwardPathIfPossible(Set<Path>paths,PortNumber notToPort,HostId srcId,HostId dstId){  
    2.         DeviceId matchDeviceId=getPathCenterDeviceId(srcId,dstId);  
    3.         log.info("MatchDeviced ID shoud be {}",matchDeviceId);  
    4.         for(Path path:paths){  
    5.             log.info("\n *** Link Path Info *** {}",  
    6.                      path.links().toString().replace("Default","\n"));  
    7.             if(!path.src().port().equals(notToPort)){  
    8.                 for(Link link:path.links()){  
    9.                     if(link.src().deviceId().equals(matchDeviceId)){  
    10.                         returnpath;  
    11.                     }  
    12.                 }  
    13.             }  
    14.         }  
    15.         this.mapMinusOne(srcId,dstId);  
    16.         returnnull;  
    17.     }  
    18.
    19.privateDeviceId getPathCenterDeviceId(HostId srcId,HostId dstId){  
    20.         //deviceId:of:0000000000000004  
    21.         Map<HostId,HostId>tempMap=newHashMap<HostId,HostId>();  
    22.         tempMap.put(srcId,dstId);  
    23.         if(map.containsKey(tempMap)){  
    24.             Integerresult=map.get(tempMap);  
    25.             if(result==3){  
    26.                 //modify the value of the key  
    27.                 map.put(tempMap,1);  
    28.                 returnmapDevice.get(1);  
    29.             }else{  
    30.                 map.put(tempMap,result+1);  
    31.                 returnmapDevice.get(result+1);  
    32.             }  
    33.         }else{  
    34.             // first time to record the srcId and dstId  
    35.             map.put(tempMap,1);  
    36.             returnmapDevice.get(1);  
    37.         }  
    38.   
    39.     }  
    40.
    41.privatevoidmapMinusOne(HostId srcId,HostId dstId){  
    42.         Map<HostId,HostId>tempMap=newHashMap<HostId,HostId>();  
    43.         tempMap.put(srcId,dstId);  
    44.         Integerresult=map.get(tempMap);  
    45.         if(result==1){  
    46.             //go back to last time  
    47.             map.put(tempMap,3);  
    48.         }else{  
    49.             map.put(tempMap,result-1);  
    50.         }  
    51.     }  
    52.
  • 在获取到对应的路径后,通过ONOS中的flowRule接口下发流表到对应的交换机中,其中设置hard_timeout为30s时间,匹配域为源和目的IP地址。我们在主机连接的第一个交换机处就会上传封包到控制器中,此时解析到的path通过迭代解析会下发给后续所有的交换机,数据包从第一个交换机转发到后面交换机之后,就可以直接匹配流表转发。选择flowRule的原因在于intent下发流表中没有设置hard_timeout这个选项,不能达到题目的要求。

    Java
    1. private void installRule(PacketContext context, Path path, HostId srcId, HostId dstId) { 2. Ethernet inPkt = context.inPacket().parsed(); 3. TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); 4. Host src = hostService.getHost(srcId); 5. Host dst = hostService.getHost(dstId); 6. if (src == null || dst == null) { 7. return; 8. } else { 9. //constructure ipv4 packet use payload 10. IPv4 ipPkt = (IPv4) inPkt.getPayload(); 11. selector.matchEthType(Ethernet.TYPE_IPV4).matchIPSrc(IpPrefix.valueOf(ipPkt.getSourceAddress(), IpPrefix.MAX_INET_MASK_LENGTH)) 12. .matchIPDst(IpPrefix.valueOf(ipPkt.getDestinationAddress(), IpPrefix.MAX_INET_MASK_LENGTH)); 13. for (Link link : path.links()) { 14. PortNumber portNumber = link.src().port(); 15. //log.info("\n -***- Device Id -***- \n DeviceId:{}", link.src().deviceId()); 16. TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(portNumber).build(); 17. FlowRule flowRule = DefaultFlowRule.builder() 18. .forDevice(link.src().deviceId()) 19. .withSelector(selector.build()) 20. .withPriority(10) 21. .withTreatment(treatment) 22. .withHardTimeout(30) 23. .fromApp(appId) 24. .build(); 25. flowRuleService.applyFlowRules(flowRule); 26. } 27. packetOut(context, path.src().port()); 28. } 29. }
    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
    1.privatevoidinstallRule(PacketContext context,Path path,HostId srcId,HostId dstId){  
    2.         Ethernet inPkt=context.inPacket().parsed();  
    3.         TrafficSelector.Builder selector=DefaultTrafficSelector.builder();  
    4.         Host src=hostService.getHost(srcId);  
    5.         Host dst=hostService.getHost(dstId);  
    6.         if(src==null||dst==null){  
    7.             return;  
    8.         }else{  
    9.             //constructure ipv4 packet use payload  
    10.             IPv4 ipPkt=(IPv4)inPkt.getPayload();  
    11.             selector.matchEthType(Ethernet.TYPE_IPV4).matchIPSrc(IpPrefix.valueOf(ipPkt.getSourceAddress(),IpPrefix.MAX_INET_MASK_LENGTH))  
    12.                     .matchIPDst(IpPrefix.valueOf(ipPkt.getDestinationAddress(),IpPrefix.MAX_INET_MASK_LENGTH));  
    13.             for(Link link:path.links()){  
    14.                 PortNumber portNumber=link.src().port();  
    15.                 //log.info("\n -***- Device Id -***- \n DeviceId:{}", link.src().deviceId());  
    16.                 TrafficTreatment treatment=DefaultTrafficTreatment.builder().setOutput(portNumber).build();  
    17.                 FlowRule flowRule=DefaultFlowRule.builder()  
    18.                         .forDevice(link.src().deviceId())  
    19.                         .withSelector(selector.build())  
    20.                         .withPriority(10)  
    21.                         .withTreatment(treatment)  
    22.                         .withHardTimeout(30)  
    23.                         .fromApp(appId)  
    24.                         .build();  
    25.                 flowRuleService.applyFlowRules(flowRule);  
    26.             }  
    27.             packetOut(context,path.src().port());  
    28.         }  
    29.     }  

2.4 具体验证操作

1) 本文所述只涉及到后台设计,前端展示实现还请自行实现,可以使用log.info将路径打印在后台显示查看,代码中均已注释
2) 开启ONOS,默认会有fwd的app开启,此时需要关闭此app,并安装开启我自己编写名为ifwd的app,如下图中所示。

3%20onos.png

图4 onos启动

4%20ifwd%20app.png

图5 安装ifwd app

5ifwdapp2.png

图6 激活ifwd app

3) Mininet脚本连接到控制器中,如下图中所示:

6%20Mininet.png

图7 Mininet脚本连接控制器

4) Mininet一侧进行ping操作并且在前端获取显示路径结果(也可以直接在onos后台用log.info命令打印路径输出在控制台查看),其结果显示如下:

7once.png

图8 第一次路径探测结果

8twice.png

图9 第二次路径探测结果

9%20third.png

图10 第三次路径探测

2.5 onos源码链接

onos的源代码链接请见github链接:
https://github.com/zhouwaiqiang/onos_sample/tree/master/ifwd


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK