zoukankan      html  css  js  c++  java
  • SDN控制器拓扑发现(一)

    参考链接:http://www.sdnlab.com/15425.html

       SDN LAB3 — Ryu train

    https://medium.com/@kweisamx0322/sdn-lab3-ryu-train-f8fe13b03548

      OpenFlow协议的SDN控制器通过LLDP(Link Layer Discovery Protocol,链路发现协议)协议进行链路发现,并根据收集的链路信息来识别网络结构,生成网络拓扑。

    LLDP协议
        LLDP协议为二层协议,通过在本地子网中通告自己的设备标识以及相关接口信息来实现链路发现的功能,其基于Ethernet II格式封装的帧格式如下:

    DMAC:目的MAC地址,为固定组播地址,0x0180-C200-000E

    SMAC:源MAC地址,为端口MAC或设备MAC地址

    Ether_Type:以太网类型,为0x88CC

    Classis_ID TLV:用于描述设备信息

    Port_ID TLV:用于描述发送的端口信息

    TTL TLV:用于描述生存时间信息

    END of LLDPDU TLV作为LLDPDU结尾

    FCS:为帧校验序列

     

    • ryu.controller.handler.HANDSHAKE_DISPATCHER: 交换hello消息
      ryu.controller.handler.CONFIG_DISPATCHER: openflow 版本协商,传送 SwitchFeatures 消息
      ryu.controller.handler.MAIN_DISPATCHER: 接收 SwitchFeatures 消息,传送
       set-config message
      ryu.controller.handler.DEAD_DISPATCHER: 連線中斷
        # Send the lldp packet
        def send_lldp_packet(self, datapath, port, hw_addr, ttl):
            ofproto = datapath.ofproto
            ofp_parser = datapath.ofproto_parser
    
            pkt = packet.Packet()
            pkt.add_protocol(ethernet.ethernet(ethertype=ether_types.ETH_TYPE_LLDP,src=hw_addr ,dst=lldp.LLDP_MAC_NEAREST_BRIDGE))
    
            chassis_id = lldp.ChassisID(subtype=lldp.ChassisID.SUB_LOCALLY_ASSIGNED, chassis_id=str(datapath.id).encode('utf-8'))
            #port_id = lldp.PortID(subtype=lldp.PortID.SUB_LOCALLY_ASSIGNED, port_id=str(port))
            port_id = lldp.PortID(subtype=lldp.PortID.SUB_LOCALLY_ASSIGNED, port_id=str(port).encode('utf-8'))
            #port_id = lldp.PortID(subtype=lldp.PortID.SUB_LOCALLY_ASSIGNED, port_id=b'1/3')
            ttl = lldp.TTL(ttl=1)
            end = lldp.End()
            tlvs = (chassis_id,port_id,ttl,end)
            pkt.add_protocol(lldp.lldp(tlvs))
            pkt.serialize()
            self.logger.info("packet-out %s" % pkt)
            data = pkt.data
            actions = [ofp_parser.OFPActionOutput(port=port)]
            out = ofp_parser.OFPPacketOut(datapath=datapath,
                                      buffer_id=ofproto.OFP_NO_BUFFER,
                                      in_port=ofproto.OFPP_CONTROLLER,
                                      actions=actions,
                                      data=data)
            datapath.send_msg(out)
    @set_ev_cls(EventOFPSwitchFeatures, CONFIG_DISPATCHER)
        def switch_features_handler(self, ev):
            """Handle switch features reply to install table miss flow entries."""
            datapath = ev.msg.datapath
            self._register(datapath)
            self.install_table_miss(datapath, 0)
    
        def install_table_miss(self, datapath, table_id):
        """Create and install table miss flow entries."""
            parser = datapath.ofproto_parser
            ofproto = datapath.ofproto
            match = parser.OFPMatch()
            match.set_dl_type(ETH_TYPE_LLDP)
            output = parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
                                            self.LLDP_PACKET_LEN)
            write = parser.OFPInstructionActions(ofproto.OFPIT_WRITE_ACTIONS,
                                                 [output])
            instructions = [write]
            flow_mod = self.create_flow_mod(datapath, 0, table_id,
                                            match, instructions)
            datapath.send_msg(flow_mod)
            self.send_barrier_request(datapath)
      @set_ev_cls(EventOFPBarrierReply, MAIN_DISPATCHER)
        def barrier_reply_handler(self, ev):
            msg = ev.msg
            datapath = msg.datapath
            self.send_port_desc_stats_request(datapath)
        def send_lldp_packet(self, datapath, port_no, dl_addr):
            ofproto = datapath.ofproto
            parser = datapath.ofproto_parser
            lldp_data = LLDPPacket.lldp_packet(datapath.id,
                            port_no,
                            dl_addr,
                            self.DEFAULT_TTL)
            output_port = parser.OFPActionOutput(port_no,
                                                ofproto.OFPCML_NO_BUFFER)
            packet_out = parser.OFPPacketOut(datapath, ofproto.OFPP_ANY,
                                              ofproto.OFPP_CONTROLLER,
                                              [output_port], lldp_data)
            datapath.send_msg(packet_out)
    
        @set_ev_cls(EventOFPPortDescStatsReply, MAIN_DISPATCHER)
        def port_desc_stats_reply_handler(self, ev):
            msg = ev.msg
            datapath = msg.datapath
            self.show_port_desc(msg.body, datapath)
    
        def show_port_desc(self, body, datapath):
            self.port_state[datapath.id] = PortState()
            for p in body:
                self.port_state[datapath.id].add(p.port_no, p)
                self.send_lldp_packet(datapath, p.port_no, haddr_to_str(p.hw_addr))
                port = self._get_port(datapath.id, p.port_no)
                if port and not port.is_reserved():
                    self._port_added(port)
                    self.lldp_event.set()
    
        def send_port_desc_stats_request(self, datapath):
            ofp_parser = datapath.ofproto_parser
            req = ofp_parser.OFPPortDescStatsRequest(datapath, 0)
            datapath.send_msg(req)
    from ryu.base import app_manager
    from ryu.ofproto import ofproto_v1_3
    from ryu.controller.handler import set_ev_cls
    from ryu.controller import ofp_event
    from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
    from ryu.lib.packet import ether_types,lldp,packet,ethernet
    
    
    class MySwitch(app_manager.RyuApp):
        OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
        link = []
    
        def __init__(self, *args,**kwargs):
            super(MySwitch,self).__init__(*args,**kwargs)
            self.mac_to_port = {} # Mac address is defined
        @set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER)
        def switch_features_handler(self, ev):
            datapath = ev.msg.datapath
            ofproto = datapath.ofproto
            parser = datapath.ofproto_parser
    
            #set if packet is lldp, send to controller
            actions = [parser.OFPActionOutput(port=ofproto.OFPP_CONTROLLER,
                                              max_len=ofproto.OFPCML_NO_BUFFER)]
            inst = [parser.OFPInstructionActions(type_=ofproto.OFPIT_APPLY_ACTIONS,actions=actions)]
            match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_LLDP)
            
            mod = parser.OFPFlowMod(datapath=datapath,
                                    priority=1,
                                    match=match,
                                    instructions=inst)
            datapath.send_msg(mod)
    
            self.send_port_desc_stats_request(datapath)# send the request
    
    
        def add_flow(self, datapath, priority, match, actions):
            ofproto = datapath.ofproto
            parser = datapath.ofproto_parser
    
            inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,actions)]
    
            mod = parser.OFPFlowMod(datapath=datapath,priority=priority,match=match,instructions=inst)
            datapath.send_msg(mod)
    
    
        def send_port_desc_stats_request(self, datapath):
            ofproto = datapath.ofproto
            ofp_parser = datapath.ofproto_parser
        
            req = ofp_parser.OFPPortDescStatsRequest(datapath, 0)
            datapath.send_msg(req)
    
    
        # Send the lldp packet
        def send_lldp_packet(self, datapath, port, hw_addr, ttl):
            ofproto = datapath.ofproto
            ofp_parser = datapath.ofproto_parser
    
            pkt = packet.Packet()
            pkt.add_protocol(ethernet.ethernet(ethertype=ether_types.ETH_TYPE_LLDP,src=hw_addr ,dst=lldp.LLDP_MAC_NEAREST_BRIDGE))
    
            #chassis_id = lldp.ChassisID(subtype=lldp.ChassisID.SUB_LOCALLY_ASSIGNED, chassis_id=str(datapath.id))
            chassis_id = lldp.ChassisID(subtype=lldp.ChassisID.SUB_LOCALLY_ASSIGNED, chassis_id=str(datapath.id).encode('utf-8'))
            #port_id = lldp.PortID(subtype=lldp.PortID.SUB_LOCALLY_ASSIGNED, port_id=str(port))
            port_id = lldp.PortID(subtype=lldp.PortID.SUB_LOCALLY_ASSIGNED, port_id=str(port).encode('utf-8'))
            #port_id = lldp.PortID(subtype=lldp.PortID.SUB_LOCALLY_ASSIGNED, port_id=b'1/3')
            ttl = lldp.TTL(ttl=1)
            end = lldp.End()
            tlvs = (chassis_id,port_id,ttl,end)
            pkt.add_protocol(lldp.lldp(tlvs))
            pkt.serialize()
            self.logger.info("packet-out %s" % pkt)
            data = pkt.data
            actions = [ofp_parser.OFPActionOutput(port=port)]
            out = ofp_parser.OFPPacketOut(datapath=datapath,
                                      buffer_id=ofproto.OFP_NO_BUFFER,
                                      in_port=ofproto.OFPP_CONTROLLER,
                                      actions=actions,
                                      data=data)
            datapath.send_msg(out)
    
        @set_ev_cls(ofp_event.EventOFPPortDescStatsReply, MAIN_DISPATCHER)
        def port_desc_stats_reply_handler(self, ev):
            ports = []
            for p in ev.msg.body:
                ports.append('port_no=%d hw_addr=%s name=%s config=0x%08x '
                         'state=0x%08x curr=0x%08x advertised=0x%08x '
                         'supported=0x%08x peer=0x%08x curr_speed=%d '
                         'max_speed=%d' %
                         (p.port_no, p.hw_addr,
                          p.name, p.config,
                          p.state, p.curr, p.advertised,
                          p.supported, p.peer, p.curr_speed,
                          p.max_speed))
                self.logger.debug('OFPPortDescStatsReply received: %s', ports)
        '''
        @set_ev_cls(ofp_event.EventOFPPortDescStatsReply, MAIN_DISPATCHER)
        def port_desc_stats_reply_handler(self, ev):
            datapath = ev.msg.datapath
            ofproto = datapath.ofproto
            ofp_parser = datapath.ofproto_parser
            ports = []
            for stat in ev.msg.body:
                if stat.port_no <=ofproto.OFPP_MAX: 
                    ports.append({'port_no':stat.port_no,'hw_addr':stat.hw_addr})
            for no in ports:
                in_port = no['port_no']
                match = ofp_parser.OFPMatch(in_port = in_port)
                for other_no in ports:
                    if other_no['port_no'] != in_port:
                        out_port = other_no['port_no']
                self.send_lldp_packet(datapath,no['port_no'],no['hw_addr'],10)
                actions = [ofp_parser.OFPActionOutput(out_port)]
                self.add_flow(datapath, 1, match, actions)
    
        '''
        @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
        def packet_in_handler(self, ev):
            msg = ev.msg
            datapath = msg.datapath
            ofproto = datapath.ofproto
            parser = datapath.ofproto_parser
    
    
            pkt = packet.Packet(data=msg.data)
            dpid = datapath.id # switch id which send the packetin
            in_port  = msg.match['in_port']
    
            pkt_ethernet = pkt.get_protocol(ethernet.ethernet)
            pkt_lldp = pkt.get_protocol(lldp.lldp)
            if not pkt_ethernet:
                return 
            #print(pkt_lldp)
            if pkt_lldp:
                self.handle_lldp(dpid,in_port,pkt_lldp.tlvs[0].chassis_id,pkt_lldp.tlvs[1].port_id)
    
    
            #self.logger.info("packet-in %s" % (pkt,))
    
        # Link two switch
        def switch_link(self,s_a,s_b):
            return s_a + '<--->' + s_b
                
        def handle_lldp(self,dpid,in_port,lldp_dpid,lldp_in_port):
            switch_a = 'switch'+str(dpid)+', port'+str(in_port)
            switch_b = 'switch'+lldp_dpid.decode('utf-8')+', port'+lldp_in_port.decode('utf-8')
            link = self.switch_link(switch_a,switch_b)
    
            # Check the switch link is existed
            if not any(self.switch_link(switch_b,switch_a) == search for search in self.link):
                self.link.append(link)
    
    
            print(self.link)

    生成拓扑

    from mininet.log import setLogLevel,info
    from mininet.node import RemoteController
    from mininet.net import Mininet
    from mininet.cli import CLI
    from optparse import OptionParser
    def SetParse():
        parser = OptionParser()
        parser.add_option("-n","--number",type="int",dest="switch_num",help="write the switch number",default=1 )
        return parser.parse_args()
    
    def MininetTopo(switch_num):
        net = Mininet()
        info("Create host nodes.\n")
        h1 = net.addHost("h1")
        h2 = net.addHost("h2")
        
    
    
        info("Create switch node.\n")
        #s1 = net.addSwitch("s1",failMode = 'standalone')
        #s1 = net.addSwitch("s1",failMode = 'secure',protocols = 'OpenFlow13')
        for sw in range(1,switch_num+1):
            name = "s"+str(sw)
            net.addSwitch(name,failMode = 'secure',protocols = 'OpenFlow13')
    
        info("Create Links. \n")
        for link in range(0,switch_num+1):
            if link is 0:
                net.addLink(h1,"s"+str(link+1))
            elif link is switch_num:
                net.addLink(h2,"s"+str(link))
            else:
                net.addLink("s"+str(link),"s"+str(link+1))
    
        info("Create controller ot switch. \n")
        net.addController(controller=RemoteController,ip='127.0.0.1',port=6633)
    
        info("Build and start network.\n")
        net.build()
        net.start()
        info("Run the mininet CLI")
        CLI(net)
    
    
    if __name__ == '__main__':
        setLogLevel('info')
        # Set Parse
        (options, args) = SetParse()
        print(options.switch_num)
    
        MininetTopo(options.switch_num)
    mininet> net
    h1 h1-eth0:s1-eth1
    h2 h2-eth0:s3-eth2
    s1 lo:  s1-eth1:h1-eth0 s1-eth2:s2-eth1
    s2 lo:  s2-eth1:s1-eth2 s2-eth2:s3-eth1
    s3 lo:  s3-eth1:s2-eth2 s3-eth2:h2-eth0
    c0
    mininet> node
    *** Unknown command: node
    mininet> nodes
    available nodes are: 
    c0 h1 h2 s1 s2 s3
    mininet> h1 ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host 
           valid_lft forever preferred_lft forever
    2: h1-eth0@if116: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
        link/ether b2:fe:b0:b5:c8:47 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 10.0.0.1/8 brd 10.255.255.255 scope global h1-eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::b0fe:b0ff:feb5:c847/64 scope link 
           valid_lft forever preferred_lft forever
    mininet> h2 ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host 
           valid_lft forever preferred_lft forever
    2: h2-eth0@if121: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
        link/ether 7a:73:94:49:38:bb brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 10.0.0.2/8 brd 10.255.255.255 scope global h2-eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::7873:94ff:fe49:38bb/64 scope link 
           valid_lft forever preferred_lft forever
    mininet> 
    EVENT ofp_event->MySwitch EventOFPPortDescStatsReply
    move onto main mode
    EVENT ofp_event->MySwitch EventOFPPortDescStatsReply
    move onto main mode
    EVENT ofp_event->MySwitch EventOFPPortDescStatsReply
    OFPPortDescStatsReply received: ["port_no=4294967294 hw_addr=b6:ea:b2:d3:71:43 name=b's1' config=0x00000001 state=0x00000001 curr=0x00000000 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=0 max_speed=0", "port_no=1 hw_addr=9e:48:6a:42:e7:e6 name=b's1-eth1' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0", "port_no=2 hw_addr=26:fe:ef:c1:d1:cd name=b's1-eth2' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0"]
    OFPPortDescStatsReply received: ["port_no=4294967294 hw_addr=66:17:2e:64:b0:49 name=b's3' config=0x00000001 state=0x00000001 curr=0x00000000 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=0 max_speed=0", "port_no=1 hw_addr=1a:d2:d5:c9:3c:5e name=b's3-eth1' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0", "port_no=2 hw_addr=8a:d2:8d:2f:4c:21 name=b's3-eth2' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0"]
    OFPPortDescStatsReply received: ["port_no=4294967294 hw_addr=ba:69:a7:ba:36:4f name=b's2' config=0x00000001 state=0x00000001 curr=0x00000000 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=0 max_speed=0", "port_no=1 hw_addr=d6:63:a2:0c:7f:70 name=b's2-eth1' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0", "port_no=2 hw_addr=d2:3e:f9:67:b5:a0 name=b's2-eth2' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0"]
    OFPPortDescStatsReply received没有h1 和h2

    程序代码改为

    OFPPortDescStatsReply received: ["port_no=1 hw_addr=9e:48:6a:42:e7:e6 name=b's1-eth1' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0", "port_no=2 hw_addr=26:fe:ef:c1:d1:cd name=b's1-eth2' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0"]
    OFPPortDescStatsReply received: ["port_no=1 hw_addr=d6:63:a2:0c:7f:70 name=b's2-eth1' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0", "port_no=2 hw_addr=d2:3e:f9:67:b5:a0 name=b's2-eth2' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0"]
    move onto main mode
    EVENT ofp_event->MySwitch EventOFPPortDescStatsReply
    OFPPortDescStatsReply received: ["port_no=1 hw_addr=1a:d2:d5:c9:3c:5e name=b's3-eth1' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0", "port_no=2 hw_addr=8a:d2:8d:2f:4c:21 name=b's3-eth2' config=0x00000000 state=0x00000004 curr=0x00000840 advertised=0x00000000 supported=0x00000000 peer=0x00000000 curr_speed=10000000 max_speed=0"]
    123: s1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
        link/ether b6:ea:b2:d3:71:43 brd ff:ff:ff:ff:ff:ff
    124: s2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
        link/ether ba:69:a7:ba:36:4f brd ff:ff:ff:ff:ff:ff
    125: s3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
        link/ether 66:17:2e:64:b0:49 brd ff:ff:ff:ff:ff:ff
     
    OFPPortDescStatsReply received没有s1 和s2、s3


    s1-eth1      s1-eth2
    s3-eth1      s3-eth2
    s2-eth1      s2-eth2
    mininet> net
    h1 h1-eth0:s1-eth1
    h2 h2-eth0:s3-eth2
    s1 lo:  s1-eth1:h1-eth0 s1-eth2:s2-eth1
    s2 lo:  s2-eth1:s1-eth2 s2-eth2:s3-eth1
    s3 lo:  s3-eth1:s2-eth2 s3-eth2:h2-eth0

     

     
  • 相关阅读:
    dos
    jsf session的获取和添加
    tomcat多开造成的端口占用
    myeclipse中tomcat7内存大小的设置
    中文传值乱码过滤
    java定时器
    jsf中jstl标签<c:forEach/>打印信息
    python基础python函数any()与all()的区别
    C# GetManifestResourceStream获取资源为null
    Linux kill 9 和 kill 15 的区别
  • 原文地址:https://www.cnblogs.com/dream397/p/13156211.html
Copyright © 2011-2022 走看看