本文主要是介绍SDN Experiment 3,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
SDN Experiment 3
实验内容
第三次实验主要为设计性实验,要求各位在熟悉SDN的基本原理和RYU API的基础上解决下⾯问题
题⽬
假如你有⼀个笔友遍天下爱写信的朋友叫李华,她⽣活在1972年的UCLA,希望通过ARPANET(世界第⼀个数据包交换⽹络,互联⽹的⿐祖,接⼊了25个研究机构,共计55条链路。具体拓扑⻅下图)发送⼀封Email给位于MIT的李明同学,现在需要你借助Ryu控制器编写Ryu APP帮助她
- 为减少⽹络中节点的中转,希望找到⼀条从UCLA到MIT跳数最少的连接,输出经过的路线
- 为了尽快发送Email,希望能找到⼀条从UCLA到MIT时延最短的连接,输出经过的路线及总的时延,利⽤Ping包的RTT验证你的结果(此问题选做)
实验过程
1.建立topo
利用已经给出的Arpanet19723.py文件
sudo mn --custom Arpanet19723.py --controller=remote,ip=127.0.0.1,port=6633 --topo topoexp3
遇到错误:
- python版本问题,将topo代码改为python3代码后解决
- 遇到如下图问题
参考链接 MININET和PYTHON脚本运行出错:INVALID TOPO NAME
在class RouterTopo定义前添加一行TOPOS = {'topoexp3':(lambda:GeneratedTopo())}
将问题解决
正确截图如下:
ryu-manager test.py --observe-links
ryu-manager exp3_1.py --observe-links
查看链路信息
2.启动RYU控制器
ryu-manager exp3_1.py --ofp-tcp-listen-port 6633 --observe-links
交换机及其之间的链路
3.ULCA ping MIT
ULCA ping MIT
最短路径:
如上图所示,在“UCLA” 主机 ping“ MIT”主机 10 次之后,RYU 打印出了
两者之间跳数最小的路径,该路径共经过 7 台交换机,与实验指导书中给出的例子不同,但经过的交换机数目是一样的。下面对显示内容进行说明:
上图中共有两组内容,上方是从 UCLA 到 MIT,下方是由 MIT 到 UCLA。显然两者是完全相反的关系。每组数据分为三行:
[1]第一行为路径的源地址和目的地址
[2]第二行为所经过的主机与交换机的个数
[3]第三行为具体的路径信息,首尾分别为源地址和目的地址,中间”a : b : c”部分的含义为:“in_port:switch_id:out_port”
代码
from ryu.base import app_manager
from ryu.ofproto import ofproto_v1_3
from ryu.controller.handler import set_ev_cls
from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER
from ryu.controller import ofp_event
from ryu.lib import hub
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import arp
from ryu.lib.packet import icmp
from ryu.lib.packet import ether_types
from ryu.lib import mac
from ryu.topology import event, switches
from ryu.topology.api import get_switch, get_link
from ryu.topology.api import get_all_host, get_all_link, get_all_switch
from ryu.app.wsgi import ControllerBase
import networkx as nxETHERNET = ethernet.ethernet.__name_ETHERNET_MULTICAST = "ff:ff:ff:ff:ff:ff" #ARP 请求的地址ARP = arp.arp.__name__class GETSHORT_1(app_manager.RyuApp):OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]#初始化类变量:def __init__(self, *args, **kwargs):super(GETSHORT_1, self).__init__(*args, **kwargs)self.mac_table = {} #交换机的 MAC 表self.arp_table = {} #arp 表self.topo_thread = hub.spawn(self._get_topology) #使用绿色线程来 执行_get_topology 函数self.graph = nx.DiGraph() #使用 networkx 创建一个图self.topology_api_app = selfself.switch_host_port = {} #交换机和主机连接的端口self.datapath_switch = {} #交换机及其对应的 datapathdef add_flow(self, datapath, priority, match, actions):dp = datapathofp = dp.ofprotoparser = dp.ofproto_parserinst = [parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]mod = parser.OFPFlowMod(datapath=dp, priority=priority, match=match, instructions=inst)dp.send_msg(mod)@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)def switch_features_handler(self, ev):msg = ev.msgdp = msg.datapathofp = dp.ofprotoparser = dp.ofproto_parsermatch = parser.OFPMatch()actions = [parser.OFPActionOutput(ofp.OFPP_CONTROLLER, ofp.OFPCML_NO_BUFFER)]self.add_flow(dp, 0, match, actions)@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)def packet_in_handler(self, ev):msg = ev.msgdp = msg.datapathofp = dp.ofprotoparser = dp.ofproto_parserpkt = packet.Packet(msg.data)dpid = dp.idin_port = msg.match['in_port']eth_pkt = pkt.get_protocol(ethernet.ethernet)dst = eth_pkt.dstsrc = eth_pkt.srcif dpid not in self.datapath_switch:self.datapath_switch[dpid] = dp#去除不需要的协议if eth_pkt.ethertype == ether_types.ETH_TYPE_LLDP:return Noneif eth_pkt.ethertype == ether_types.ETH_TYPE_IPV6:return Noneheader_list = dict((p.protocol_name, p) for p in pkt.protocols if type(p) != str)#将未学习的 ARP 包向所有交换机与主机相连的端口转发if dst == ETHERNET_MULTICAST and ARP in header_list:self.arp_table[header_list[ARP].src_ip] = srcarp_dst_ip = header_list[ARP].dst_ip#未被学习过if arp_dst_ip not in self.arp_table:#向其他所有交换机和主机相连接的端口转发for key in self.switch_host_port:if key != dpid:dp = self.datapath_switch[key]for out_port in self.switch_host_port[key]:out = parser.OFPPacketOut(datapath=dp,buffer_id=ofp.OFP_NO_BUFFER,in_port=ofp.OFPP_CONTROLLER,actions=[parser.OFPActionOutput(out_port)], data=msg.data)dp.send_msg(out)#已经学习过else:dst = self.arp_table[arp_dst_ip]self.mac_table.setdefault(dpid, {})if dst in self.mac_table[dpid]:out_port = self.mac_table[dpid][dst]else:out_port = ofp.OFPP_FLOOD#将主机与交换机之间的连接加入到图中if src not in self.graph:self.graph.add_node(src)self.graph.add_edge(dpid, src, weight=0, port=in_port)self.graph.add_edge(src, dpid, weight=0)#得到跳数最小的路径if src in self.graph and dst in self.graph and dpid in self.graph:#直接使用 networkx 得到最短路径path = nx.shortest_path(self.graph, src, dst, weight="weight")#如果当前的交换机不在最短路径上,丢弃该数据包if dpid not in path:return Nonenxt = path[path.index(dpid) + 1]out_port = self.graph[dpid][nxt]['port']self.mac_table[dpid][dst] = out_portactions = [parser.OFPActionOutput(out_port)]out = parser.OFPPacketOut(datapath=dp, buffer_id=ofp.OFP_NO_BUFFER, in_port=in_port, actions=actions, data=msg.data)dp.send_msg(out)#到达目的地则输出路径if nxt == dst and dpid == path[-2]:print( "path:",src,"->",dst)print("the length of the path is {}".format(len(path)))print(path[0],"->",end='')for item in path[1:-1]:index = path.index(item)print("{}:s{}:{}".format(self.graph[item][path[index - 1]]['port'],item,self.graph[item][path[index + 1]]['port']),end='')print("->",end='')print(path[-1])print('\n')#print('switch_host_port')#print(self.switch_host_port)#print('datapath_switch')#print(self.datapath_switch)#print('header_list')#print(header_list)#print(path)print('\n')return None#else:#actions = [parser.OFPActionOutput(out_port)]#out = parser.OFPPacketOut(#datapath=dp, buffer_id=ofp.OFP_NO_BUFFER, in_port=in_port, actions=actions, data=msg.data)#dp.send_msg(out)def _get_topology(self):hub.sleep(2) #模仿网络请求等待#获得整个网络的拓扑switch_list = get_switch(self.topology_api_app, None)switches = [switch.dp.id for switch in switch_list]self.graph.add_nodes_from(switches)link_list = get_link(self.topology_api_app, None)for link in link_list:self.graph.add_edge(link.src.dpid, link.dst.dpid, weight=1,port=link.src.port_no)self.graph.add_edge(link.dst.dpid, link.src.dpid, weight=1,port=link.dst.port_no)switch_all_port = {}for switch in switch_list:dpid = switch.dp.id #交换机 idflag = Falsefor port in switch.ports:if flag:switch_all_port[dpid].add(port.port_no) #获得端口 号continueif dpid not in switch_all_port:switch_all_port[dpid] = {port.port_no}flag = True#去除交换机之间连接的端口for link in link_list:Src = link.srcDst = link.dstif Src.dpid in switch_all_port:switch_all_port[Src.dpid].discard(Src.port_no)if Dst.dpid in switch_all_port:switch_all_port[Dst.dpid].discard(Dst.port_no)self.switch_host_port = switch_all_port#打印拓扑信息print("nodes:")print(self.graph.nodes())print("links:")print(self.graph.edges())print("topo:")print("node1 node2")for u, adj_u in self.graph.adj.items():for v, eattr in adj_u.items():if u < v:self.logger.info('s%2s s%2s', u, v)print("--------------------------------")
参考文献
Ryu中基于时延的最短路径转发算法(SPF)
RYU基于跳数的最短路径转发
Ryu应用开发(流量监控原理与基于跳数的最短路转发
通信网系列实验(三)——基于Dijkstra算法的Ryu+Mininet应用
projects based on Mininet and Ryu
这篇关于SDN Experiment 3的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!