SDN Experiment 3

SDN Experiment 3


实验内容


第三次实验主要为设计性实验,要求各位在熟悉SDN的基本原理和RYU API的基础上解决下⾯问题

题⽬

假如你有⼀个笔友遍天下爱写信的朋友叫李华,她⽣活在1972年的UCLA,希望通过ARPANET(世界第⼀个数据包交换⽹络,互联⽹的⿐祖,接⼊了25个研究机构,共计55条链路。具体拓扑⻅下图)发送⼀封Email给位于MIT的李明同学,现在需要你借助Ryu控制器编写Ryu APP帮助她

  1. 为减少⽹络中节点的中转,希望找到⼀条从UCLA到MIT跳数最少的连接,输出经过的路线
  2. 为了尽快发送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
遇到错误:

  1. python版本问题,将topo代码改为python3代码后解决
  2. 遇到如下图问题在这里插入图片描述
    参考链接 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 nx

ETHERNET = 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 = self
        self.switch_host_port = {} #交换机和主机连接的端口
        self.datapath_switch = {} #交换机及其对应的 datapath
    def add_flow(self, datapath, priority, match, actions):
        dp = datapath
        ofp = dp.ofproto
        parser = dp.ofproto_parser
        inst = [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.msg
        dp = msg.datapath
        ofp = dp.ofproto
        parser = dp.ofproto_parser
        match = 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.msg
        dp = msg.datapath
        ofp = dp.ofproto
        parser = dp.ofproto_parser
        pkt = packet.Packet(msg.data)
        dpid = dp.id
        in_port = msg.match['in_port']
        eth_pkt = pkt.get_protocol(ethernet.ethernet)
        dst = eth_pkt.dst
        src = eth_pkt.src
        if dpid not in self.datapath_switch:
            self.datapath_switch[dpid] = dp
            
        #去除不需要的协议
        if eth_pkt.ethertype == ether_types.ETH_TYPE_LLDP:
            return None
            
        if eth_pkt.ethertype == ether_types.ETH_TYPE_IPV6:
            return None
            
        header_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] = src
            arp_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 None
                
            nxt = path[path.index(dpid) + 1]
            out_port = self.graph[dpid][nxt]['port']
            self.mac_table[dpid][dst] = out_port
            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)
            
            #到达目的地则输出路径
            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 #交换机 id
            flag = False
            for port in switch.ports:
                if flag:
                    switch_all_port[dpid].add(port.port_no) #获得端口            号
                    continue
                if dpid not in switch_all_port:
                    switch_all_port[dpid] = {port.port_no}
                    flag = True
        #去除交换机之间连接的端口
            for link in link_list:
                Src = link.src
                Dst = link.dst
                if 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

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李汪才

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值