예제 #1
0
    def __init__(self, *args, **kwargs):
        super(ShortestPathSwitching, self).__init__(*args, **kwargs)

        self.tm = TopoManager()
        self.graph = Graph(100)  # switch 数量
        self.belong = {
        }  # 每个host属于哪个switch: key, value <- host_mac, (switch_id, switch_port_num)
        self.mac_to_port = {}  # ???
        self.res = {}  # 最短路结果: key, value <- [i][j] = (i-> out1_port)
        self.ip_mac_dict = {}  # 每个ip对应的host是什么:key, value <- ip, host_mac
예제 #2
0
    def __init__(self, *args, **kwargs):
        super(ShortestPathSwitching, self).__init__(*args, **kwargs)

        self.tm = TopoManager()
        self.graph = Graph(100)  # switch 数量
        self.belong = {
        }  # 每个host属于哪个switch: key, value <- host_mac, (switch_id, switch_port_num)
        self.mac_to_port = {}  # ???
        self.res = {}  # 最短路结果: key, value <- [i][j] = (i-> out1_port)
        self.ip_mac_dict = {}  # 每个ip对应的host是什么:key, value <- ip, host_mac
        self.switch_list = []  # 所有switch的list
        # self.datapath_set = {}

        self.spanning_tree = SpanningTree(1000)
        # 用于处理广播包的伸展树 只处理内部switch节点
        self.switch_contain_host = {
        }  # 每个switch有哪些host: key=switch.dp.id, value=[] (host_mac, switch_port_num)
예제 #3
0
 def __init__(self, *args, **kwargs):
     super(ShortestPathSwitching, self).__init__(*args, **kwargs)
     self.mac_table = {}
     self.tm = TopoManager()
예제 #4
0
class ShortestPathSwitching(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(ShortestPathSwitching, self).__init__(*args, **kwargs)
        self.mac_table = {}
        self.tm = TopoManager()

    @set_ev_cls(event.EventSwitchEnter)
    def handle_switch_add(self, ev):
        """
        Event handler indicating a switch has come online.
        """
        switch = ev.switch

        self.logger.warn("Added Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.add_switch(switch)

        self.update_table()

    @set_ev_cls(event.EventSwitchLeave)
    def handle_switch_delete(self, ev):
        """
        Event handler indicating a switch has been removed
        """
        switch = ev.switch

        self.logger.warn("Removed Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.dele_switch(switch)
        self.update_table()

    @set_ev_cls(event.EventHostAdd)
    def handle_host_add(self, ev):
        """
        Event handler indiciating a host has joined the network
        This handler is automatically triggered when a host sends an ARP response.
        """
        host = ev.host
        self.logger.warn("Host Added:  %s (IPs:  %s) on switch%s/%s (%s)",
                         host.mac, host.ipv4, host.port.dpid,
                         host.port.port_no, host.port.hw_addr)
        # TODO:  Update network topology and flow rules
        self.tm.add_host(host)
        # self.add_forwarding_rule(self.my_switch,  host.mac, host.port.port_no)
        self.update_table()
        self.mac_table[host.ipv4[0]] = host.mac

    @set_ev_cls(event.EventLinkAdd)
    def handle_link_add(self, ev):
        """
        Event handler indicating a link between two switches has been added
        """
        # link = ev.link
        src_port = ev.link.src
        dst_port = ev.link.dst
        self.logger.warn("Added Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.add_link(src_port, dst_port)
        # for sw in self.tm.switches:
        # print(sw.neighbors)
        self.update_table()

    @set_ev_cls(event.EventLinkDelete)
    def handle_link_delete(self, ev):
        """
        Event handler indicating when a link between two switches has been deleted
        """
        link = ev.link
        src_port = link.src
        dst_port = link.dst

        self.logger.warn("Deleted Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.dele_link(src_port, dst_port)
        self.update_table()

    @set_ev_cls(event.EventPortModify)
    def handle_port_modify(self, ev):
        """
        Event handler for when any switch port changes state.
        This includes links for hosts as well as links between switches.
        """
        port = ev.port
        self.logger.warn("Port Changed:  switch%s/%s (%s):  %s", port.dpid,
                         port.port_no, port.hw_addr,
                         "UP" if port.is_live() else "DOWN")
        # TODO:  Update network topology and flow rules

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        """
       EventHandler for PacketIn messages
        """
        msg = ev.msg

        # In OpenFlow, switches are called "datapaths".  Each switch gets its own datapath ID.
        # In the controller, we pass around datapath objects with metadata about each switch.
        dp = msg.datapath

        # Use this object to create packets for the given datapath
        ofctl = OfCtl.factory(dp, self.logger)

        in_port = msg.in_port
        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]

        if eth.ethertype == ether_types.ETH_TYPE_ARP:
            arp_msg = pkt.get_protocols(arp.arp)[0]

            if arp_msg.opcode == arp.ARP_REQUEST:

                self.logger.warning(
                    "Received ARP REQUEST on switch%d/%d:  Who has %s?  Tell %s",
                    dp.id, in_port, arp_msg.dst_ip, arp_msg.src_mac)

                # TODO:  Generate a *REPLY* for this request based on your switch state

                # Here is an example way to send an ARP packet using the ofctl utilities
                # ofctl.send_arp(vlan_id=VLANID_NONE,
                #               src_port=ofctl.dp.ofproto.OFPP_CONTROLLER,
                #               . . .)
                if arp_msg.dst_ip in self.mac_table:
                    ofctl.send_arp(arp_opcode=arp.ARP_REPLY,
                                   vlan_id=VLANID_NONE,
                                   dst_mac=self.mac_table[arp_msg.src_ip],
                                   sender_mac=self.mac_table[arp_msg.dst_ip],
                                   sender_ip=arp_msg.dst_ip,
                                   target_ip=arp_msg.src_ip,
                                   target_mac=arp_msg.src_mac,
                                   src_port=ofctl.dp.ofproto.OFPP_CONTROLLER,
                                   output_port=in_port)

    def add_forwarding_rule(self, datapath, dl_dst, port):
        ofctl = OfCtl.factory(datapath, self.logger)
        actions = [datapath.ofproto_parser.OFPActionOutput(port)]
        ofctl.set_flow(cookie=0,
                       priority=0,
                       dl_type=ether_types.ETH_TYPE_IP,
                       dl_vlan=VLANID_NONE,
                       dl_dst=dl_dst,
                       actions=actions)

    def update_table(self):
        self.tm.dijkstra()
        for i in self.tm.switches:
            for j in self.tm.hosts:
                if i.get_dpid() == j.switch_id:
                    self.add_forwarding_rule(i.get_dp(), j.get_mac(),
                                             j.switch_port)
                else:
                    if (i.get_dpid(), j.switch_id) in self.tm.flow_table:
                        self.add_forwarding_rule(
                            i.get_dp(), j.get_mac(),
                            self.tm.flow_table[(i.get_dpid(), j.switch_id)])

        print("@@@ FLOW TABLE START @@@")
        count = 0
        for key in self.tm.flow_table.keys():
            count += 1
            value = self.tm.flow_table[key]
            # print(key, value)
            print("Device {:2d}->{:2d}: Go Port {:2d}".format(
                key[0], key[1], value),
                  end=";  ")
            if count == 3:
                count = 0
                print()
        print("@@@ FLOW TABLE END @@@")

        print("%%% SHORTEST PATH BEGIN %%%")
        for sw in self.tm.switches:
            sID = sw.get_dpid()
            List = self.tm.shortest_path(sID)
            print("Switch {:d}: ".format(sID))
            for i in List:
                if not i: continue
                print(" * To Switch {:2d}: ".format(i[-1]), end="")
                print(i)
        print("%%% SHORTEST PATH END %%%")
        list1 = self.tm.topology_graph()
        print("&&& TOPOLOGY BEGIN &&&")
        count = 0
        for i in list1:
            count += 1
            print("{:2d} <-> {:2d}".format(i[0], i[1]), end=" ")
            if count == 4:
                count = 0
                print()
        print("\n&&& TOPOLOGY END &&&")
예제 #5
0
class ShortestPathSwitching(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(ShortestPathSwitching, self).__init__(*args, **kwargs)

        self.tm = TopoManager()

    def add_forwarding_rule(self, datapath, dl_dst, port):
        ofctl = OfCtl.factory(datapath, self.logger)
        actions = [datapath.ofproto_parser.OFPActionOutput(port)]
        ofctl.set_flow(cookie=0,
                       priority=0,
                       dl_type=ether_types.ETH_TYPE_IP,
                       dl_vlan=VLANID_NONE,
                       dl_dst=dl_dst,
                       actions=actions)

    def BFS_1(self):
        mac_infos = []
        for i in self.tm.switches:
            tables = i.BFS_2(self.tm.switches)
            self.logger.warn("switches {} length is {}".format(
                i.dpid, len(tables)))
            for j in tables:
                self.add_forwarding_rule(i.switch.dp, j[0].mac, j[1])
                self.logger.warn("switch:{} ,Mac:{} port:{}".format(
                    i.dpid, j[0].mac, j[1]))
                mac_infos.append((i.dpid, j[0].mac, j[1]))
        self.logger.warn("BFS 1 finish")

        begin = []
        end = []
        for i in self.tm.hostes:
            begin.append(i)
            end.append(i)
        for i in begin:
            for j in end:
                if i.mac == j.mac:
                    continue
                else:
                    print("1 {}".format(i.name))
                    begin_switch = i.get_neighbors()[0][0]
                    next_switch = i.get_neighbors()[0][0]
                    print(begin_switch.name)
                    while type(begin_switch) == type(next_switch):
                        begin_switch = next_switch
                        next_switch_port = None
                        for k in mac_infos:
                            if begin_switch.dpid == k[0] and j.mac == k[1]:
                                next_switch_port = k[2]
                                break
                        for k in begin_switch.get_neighbors():
                            if k[1] == next_switch_port:
                                print(k[0].name)
                                next_switch = k[0]
                                break
        print("shortest finish")

    def show_shortest_path(self):
        begin = []
        end = []
        for i in self.tm.hostes:
            begin.append(i)
            end.append(i)
        # for i in begin:
        #    for j in end:

        pass

    @set_ev_cls(event.EventSwitchEnter)
    def handle_switch_add(self, ev):
        """
        switch加入,没有链接其他的
        Event handler indicating a switch has come online.
        """
        switch = ev.switch

        self.logger.warn("Added Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.add_switch(switch)
        self.BFS_1()

    @set_ev_cls(event.EventSwitchLeave)
    def handle_switch_delete(self, ev):
        """
        switch关闭,需要删除其本身和所有邻居
        Event handler indicating a switch has been removed
        """
        switch = ev.switch

        self.logger.warn("Removed Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)
        self.tm.remove_switch(switch.dp.id)
        # TODO:  Update network topology and flow rules
        self.BFS_1()

    @set_ev_cls(event.EventHostAdd)
    def handle_host_add(self, ev):
        """
        host开启,加入列表
        Event handler indiciating a host has joined the network
        This handler is automatically triggered when a host sends an ARP response.
        """
        host = ev.host
        self.logger.warn("Host Added:  %s (IPs:  %s) on switch%s/%s (%s)",
                         host.mac, host.ipv4, host.port.dpid,
                         host.port.port_no, host.port.hw_addr)
        h = self.tm.add_host(host)
        self.logger.warn("host type is {}".format(type(h)))
        self.tm.get_switches_dpid(host.port.dpid).add_neighbor(
            (h, host.port.port_no))
        self.tm.get_switches_dpid(host.port.dpid).add_host(host)
        # TODO:  Update network topology and flow rules
        h.add_neighbor((self.tm.get_switches_dpid(host.port.dpid), None))
        self.BFS_1()

    @set_ev_cls(event.EventLinkAdd)
    def handle_link_add(self, ev):
        """
        switch1-switch2 连接上 
        Event handler indicating a link between two switches has been added
        """
        link = ev.link
        src_port = ev.link.src
        dst_port = ev.link.dst
        self.logger.warn("Added Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)
        src_switch = self.tm.get_switches_dpid(src_port.dpid)
        dst_switch = self.tm.get_switches_dpid(dst_port.dpid)
        src_switch.add_neighbor((dst_switch, src_port.port_no))
        # TODO:  Update network topology and flow rules
        self.BFS_1()

    @set_ev_cls(event.EventLinkDelete)
    def handle_link_delete(self, ev):
        """
        switch1-switch2 连接关闭
        Event handler indicating when a link between two switches has been deleted
        """
        link = ev.link
        src_port = link.src
        dst_port = link.dst

        self.logger.warn("Deleted Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)
        src_switch = self.tm.get_switches_dpid(src_port.dpid)
        dst_switch = self.tm.get_switches_dpid(dst_port.dpid)
        print(src_switch is None, dst_switch is None)
        if src_switch is not None and dst_switch is not None and dst_switch in src_switch.get_neighbors(
        ):
            src_switch.remove_neighbor(dst_switch)
        # TODO:  Update network topology and flow rules
        self.BFS_1()

    @set_ev_cls(event.EventPortModify)
    def handle_port_modify(self, ev):
        """
        switch-host
        Event handler for when any switch port changes state.
        This includes links for hosts as well as links between switches.
        """
        port = ev.port
        self.logger.warn(
            "Port Changed:  switch%s/%s (%s):  %s,device name is %s",
            port.dpid, port.port_no, port.hw_addr,
            "UP" if port.is_live() else "DOWN", port.name)
        if port.is_live():
            pass
        else:
            if (port.port_no) > 1000:
                return
            src_switch = self.tm.get_switches_dpid(port.dpid)
            dst_switch = None
            for i in src_switch.get_neighbors():
                if (i[1] == port.port_no):
                    dst_switch = i[0]
            if dst_switch is None:
                return
            print(src_switch.name, dst_switch.name)
            for i in src_switch.get_neighbors():
                if i[0] == dst_switch:
                    print("src removed")
                    src_switch.remove_neighbor(i)
            for i in dst_switch.get_neighbors():
                if i[0] == src_switch:
                    print("dst  remove")
                    dst_switch.remove_neighbor(i)
            # if dst_switch in src_switch.get_neighbors():
            #    print("src removed")
            #    src_switch.remove_neighbor(dst_switch)
            # if src_switch in dst_switch.get_neighbors():
            #    print("src removed")
            #    dst_switch.remove_neighbor(  src_switch )
        # TODO:  Update network topology and flow rules
            self.BFS_1()
        print("Port change success")

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        """
       EventHandler for PacketIn messages
        """
        msg = ev.msg

        # In OpenFlow, switches are called "datapaths".  Each switch gets its own datapath ID.
        # In the controller, we pass around datapath objects with metadata about each switch.
        dp = msg.datapath

        # Use this object to create packets for the given datapath
        ofctl = OfCtl.factory(dp, self.logger)

        in_port = msg.in_port
        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]

        if eth.ethertype == ether_types.ETH_TYPE_ARP:
            arp_msg = pkt.get_protocols(arp.arp)[0]

            if arp_msg.opcode == arp.ARP_REQUEST:
                self.logger.warning(
                    "Received ARP REQUEST on switch%d/%d:  Who has %s?  Tell %s,ip:%s,opcode:%s",
                    dp.id, in_port, arp_msg.dst_ip, arp_msg.src_mac,
                    arp_msg.src_ip, arp_msg.opcode)
                self.logger.warning(arp_msg)
                """
        Generate an ARP packet and send it
        Arguments:
        arp_opcode    -- Opcode for message
        vlan_id       -- VLAN identifier, or VLANID_NONE
        dst_mac       -- Destination to send the packet (not an ARP field)
        sender_mac    -- Sender hardware address
        sender_ip     -- Sender protocol address
        target_mac    -- Target hardware address
        target_ip     -- Target protocol address
        src_port      -- Source port number for sending message (can be OFPP_CONTROLLER)
        output_port   -- Outgoing port number to send message
                """# src 出发,dst收尾
                reply_mac = None
                for i in self.tm.hostes:
                    print(i.host.ipv4)
                    if i.host.ipv4[0] == arp_msg.dst_ip:
                        reply_mac = i.mac
                        break
                print("reply is {}".format(reply_mac))
                if reply_mac is None:
                    return
                else:
                    ofctl.send_arp(
                        arp_opcode=2,
                        vlan_id=VLANID_NONE,
                        dst_mac=arp_msg.src_mac,
                        sender_mac=reply_mac,
                        sender_ip=arp_msg.dst_ip,
                        target_mac=arp_msg.src_mac,
                        target_ip=arp_msg.src_ip,
                        src_port=ofctl.dp.ofproto.OFPP_CONTROLLER,
                        #src_port=in_port,
                        output_port=in_port)
                print("send finish")
                for i in self.tm.switches:
                    for j in i.neighbors:
                        print(i.name, j[0].name, j[1])
                    # for j in i.host:
                    # print(i.name,j.name,j.host.port.port_no)
                self.BFS_1()
예제 #6
0
class ShortestPathSwitching(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(ShortestPathSwitching, self).__init__(*args, **kwargs)

        self.tm = TopoManager()
        self.graph = Graph(100)  # switch 数量
        self.belong = {
        }  # 每个host属于哪个switch: key, value <- host_mac, (switch_id, switch_port_num)
        self.mac_to_port = {}  # ???
        self.res = {}  # 最短路结果: key, value <- [i][j] = (i-> out1_port)
        self.ip_mac_dict = {}  # 每个ip对应的host是什么:key, value <- ip, host_mac
        self.switch_list = []  # 所有switch的list
        # self.datapath_set = {}

        self.spanning_tree = SpanningTree(1000)
        # 用于处理广播包的伸展树 只处理内部switch节点
        self.switch_contain_host = {
        }  # 每个switch有哪些host: key=switch.dp.id, value=[] (host_mac, switch_port_num)

    def show_topology(self):
        print("=========== XCC Graph ============")
        print("Src SID\tPort\tDst SID")
        # print(self.switch_contain_host)
        for u in self.switch_contain_host:
            s = set()
            for v, port in self.graph.go_from(u):
                if v not in s:
                    s.add(v)
                    print(f"{u}\t{port}\t{v}")
        print("")
        print("")
        print("SID\tPort\tHost")
        for u in self.switch_contain_host:
            for item in self.switch_contain_host[u]:
                print(f"{u}\t{item[1]}\t{item[0]}")
        print("============= Done ===============")

    def show_shortest_path(self):
        print("=========== XCC Path =============")
        try:
            for u in self.switch_list:
                for v in self.switch_list:
                    paths = []
                    now = u.dp.id
                    while now != v.dp.id:
                        port = self.res[now][v.dp.id]
                        p = None
                        for t, tp in self.graph.go_from(now):
                            if tp != port:
                                continue
                            p = t
                            break
                        assert p is not None
                        now = p
                        paths.append((port, p))
                    res = f"s{u.dp.id}"
                    for path in paths:
                        res += f" -p{path[0]}-> s{path[1]}"
                    print(res)
        except KeyError:
            pass
        print("============= Done ===============")

    def show_spanning_tree(self):
        print("=========== XCC Tree =============")
        try:
            for u in self.switch_list:
                last_ids = set()
                for edge_layers in self.spanning_tree.flood(u.dp.id):
                    for edge in edge_layers:
                        try:
                            last_ids.remove(edge[0])
                        except KeyError as e:
                            pass
                        print(
                            f"tree_root=s{u.dp.id}: s{edge[0]} -p{edge[1]}-> s{edge[2]}"
                        )
                # break
        except KeyError:
            pass
        print("============= Done ===============")

    @set_ev_cls(event.EventSwitchEnter)
    def handle_switch_add(self, ev):
        """
        Event handler indicating a switch has come online.
        """
        switch = ev.switch

        self.logger.warn("Added Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        self.tm.add_switch(switch)
        self.switch_list.append(switch)  # 加入controller控制的switch列表

        # switch上线 加入mapping 为之后链路做准备
        self.one_switch_special_case()

    @set_ev_cls(event.EventSwitchLeave)
    def handle_switch_delete(self, ev):  # switch下线
        """
        Event handler indicating a switch has been removed
        """
        switch = ev.switch

        self.logger.warn("Removed Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        try:
            self.switch_list.remove(switch)  # 移除controller控制的switch列表
        except ValueError:
            # 应该已经被移除了
            pass

        self.one_switch_special_case()

    @set_ev_cls(event.EventHostAdd)
    def handle_host_add(self, ev):  # 主机上线
        """
        Event handler indiciating a host has joined the network
        This handler is automatically triggered when a host sends an ARP response.
        """
        host = ev.host

        self.logger.warn("Host Added:  %s (IPs:  %s) on switch%s/%s (%s)",
                         host.mac, host.ipv4, host.port.dpid,
                         host.port.port_no, host.port.hw_addr)

        # 1 记录这个主机对应的switch 增加转发表
        # 相当于终端
        self.belong[host.mac] = (host.port.dpid, host.port.port_no)
        self.add_forwaring_rule(
            ofctl_api.get_datapath(self, dpid=host.port.dpid), host.mac,
            host.port.port_no)

        # 2 更新其他switch上的转发表
        for dpid in self.res:  # 最短路的计算结果?
            dp = ofctl_api.get_datapath(self, dpid=dpid)

            # 如果是自己 就跳过
            if dpid == host.port.dpid:
                continue

            # 从图上更新其他switch
            self.add_forwaring_rule(dp, host.mac,
                                    self.res[dpid][host.port.dpid])

        # JL: 增加ip-mac对应
        self.ip_mac_dict[host.ipv4[0]] = host.mac

        self.tm.add_host(host)

        # 增加switch-host记录
        print("[DEBUG ADD] add switch-host before",
              (host.port.dpid not in self.switch_contain_host))
        if host.port.dpid not in self.switch_contain_host:
            # 如果这个host相连的switch还没有被记录过
            self.switch_contain_host[host.port.dpid] = []

        self.switch_contain_host[host.port.dpid].append(
            (host.mac, host.port.port_no))

        print("[BELONG]", self.belong)

        self.calc_spanning_tree()

        self.one_switch_special_case()

        self.show_topology()
        self.show_shortest_path()
        self.show_spanning_tree()

    @set_ev_cls(event.EventLinkAdd)
    def handle_link_add(self, ev):
        """
        Event handler indicating a link between two switches has been added
        """
        link = ev.link
        src_port = ev.link.src
        dst_port = ev.link.dst
        self.logger.warn("Added Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # 链路上线 更新内部图
        self.graph.add(src_port.dpid, dst_port.dpid, src_port.port_no)
        self.graph.add(dst_port.dpid, src_port.dpid, dst_port.port_no)
        # 更新最短路结果
        self.shortest_path()

        # 链路上线 更新内部伸展树
        # 只用更新单向边
        self.spanning_tree.add(
            Edge(src_port.dpid, src_port.port_no, dst_port.dpid,
                 dst_port.port_no))

        # 更新伸展树结果
        self.spanning_tree.reset_tree()
        self.spanning_tree.work()

        # 全部flow清空
        self.flow_reset()

        # 重建 flow
        self.flow_recreate()

        # print("[DEBUG!!!] self.switch_contain_host", self.switch_contain_host)
        # 上层伸展树
        self.calc_spanning_tree()

        self.show_topology()
        self.show_shortest_path()
        self.show_spanning_tree()

    @set_ev_cls(event.EventLinkDelete)
    def handle_link_delete(self, ev):
        """
        Event handler indicating when a link between two switches has been deleted
        """
        link = ev.link
        src_port = link.src
        dst_port = link.dst

        print("[*] delete happened")
        self.logger.warn("Deleted Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # 更新内部最短路
        self.graph.remove(src_port.dpid, dst_port.dpid)
        self.graph.remove(dst_port.dpid, src_port.dpid)
        self.shortest_path()

        # 链路下线 更新内部伸展树
        # 只用更新单向边
        self.spanning_tree.remove(
            Edge(src_port.dpid, src_port.port_no, dst_port.dpid,
                 dst_port.port_no))

        # 更新伸展树结果
        self.spanning_tree.reset_tree()
        self.spanning_tree.work()

        # 全部flow清空
        self.flow_reset()

        # 重建 flow
        self.flow_recreate()

        # 上层伸展树
        self.calc_spanning_tree()

        self.show_topology()
        self.show_shortest_path()
        self.show_spanning_tree()

    @set_ev_cls(event.EventPortModify)
    def handle_port_modify(self, ev):
        """
        Event handler for when any switch port changes state.
        This includes links for hosts as well as links between switches.
        """
        port = ev.port
        self.logger.warn("Port Changed:  switch%s/%s (%s):  %s", port.dpid,
                         port.port_no, port.hw_addr,
                         "UP" if port.is_live() else "DOWN")

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        # 如果收到 PacketIn 请求
        pass

    def shortest_path(self):
        self.res = {}
        for i in range(self.graph.n):
            queue = [i]
            dis = [1000] * self.graph.n
            inq = [False] * self.graph.n

            dis[i] = 0
            inq[i] = True
            while len(queue) != 0:
                u = queue[0]
                for v, port in self.graph.go_from(u):
                    if dis[v] > dis[u] + 1:
                        dis[v] = dis[u] + 1
                        if not inq[v]:
                            queue.append(v)
                inq[u] = False
                del queue[0]

            for j in range(self.graph.n):
                for v, port in self.graph.go_from(j):
                    if dis[v] + 1 == dis[j]:
                        if j in self.res:
                            self.res[j][i] = port
                        else:
                            self.res[j] = {}
                            self.res[j][i] = port
        print("[!!!!]", self.res)

    def one_switch_special_case(self):
        # 只有一个 switch 的时候 没有生成树
        if (len(self.switch_list) == 1):
            for switch in self.switch_list:
                for host_mac in self.belong:
                    host_port_dpid, host_port_no = self.belong[host_mac]
                    datapath = ofctl_api.get_datapath(self,
                                                      dpid=host_port_dpid)
                    match = datapath.ofproto_parser.OFPMatch(
                        dl_dst=haddr_to_bin(ETHERNET_MULTICAST),  # 这是一个广播包
                        dl_src=haddr_to_bin(host_mac),  # 来源 MAC 地址
                    )
                    actions = [
                        datapath.ofproto_parser.OFPActionOutput(i[1])
                        for i in self.switch_contain_host[host_port_dpid]
                    ]

                    ofp_parser = datapath.ofproto_parser

                    flow_mod = datapath.ofproto_parser.OFPFlowMod(
                        datapath,
                        match=match,
                        cookie=0,
                        priority=50,
                        actions=actions)
                    datapath.send_msg(flow_mod)

    def flow_reset(self):
        # flow 清空
        for switch in self.switch_list:
            datapath = ofctl_api.get_datapath(self, dpid=switch.dp.id)
            empty_match = datapath.ofproto_parser.OFPMatch()
            cmd = datapath.ofproto.OFPFC_DELETE
            actions = []

            ofp_parser = datapath.ofproto_parser

            flow_mod = datapath.ofproto_parser.OFPFlowMod(datapath,
                                                          match=empty_match,
                                                          cookie=0,
                                                          command=cmd,
                                                          priority=65535,
                                                          actions=actions)
            datapath.send_msg(flow_mod)

    def flow_recreate(self):
        # flow 重建
        for host_mac in self.belong:
            host_port_dpid, host_port_no = self.belong[host_mac]
            self.add_forwaring_rule(
                ofctl_api.get_datapath(self, dpid=host_port_dpid), host_mac,
                host_port_no)

            for dpid in self.res:  # 最短路的计算结果?
                dp = ofctl_api.get_datapath(self, dpid=dpid)

                # 如果是自己 就跳过
                if dpid == host_port_dpid:
                    continue

                # 从图上更新其他switch
                self.add_forwaring_rule(dp, host_mac,
                                        self.res[dpid][host_port_dpid])

    def calc_spanning_tree(self):
        # 广播包应该也到当前节点的内部节点!!!!!
        for switch in self.switch_contain_host:
            current_id = switch

            # if current_id in self.switch_contain_host:
            #     # 当前switch是边界switch
            #     # 可以设定内部switch
            # else:
            #     # 当前switch是内部switch
            # if current_id in self.switch_contain_host:

            for (host_mac,
                 switch_port_num) in self.switch_contain_host[current_id]:

                print("[^] processing on ", host_mac, current_id)

                last_ids = set()

                for edge_layers in self.spanning_tree.flood(current_id):
                    print("[>>]", edge_layers)
                    if len(edge_layers) > 0:  # 只设定有子结点的

                        for edge in edge_layers:
                            try:
                                last_ids.remove(edge[0])
                            except KeyError as e:
                                print("[^] already removed ", edge[0])
                            print("[$$$]", "from=", current_id, "to=", edge[2],
                                  "last_switch=", edge[0], "last_port=",
                                  edge[1])
                            last_ids.add(edge[2])

                        # 预处理 按照父节点分类
                        # edge: father, father_out_port, son
                        out_port_dict = {}
                        for edge in edge_layers:
                            if edge[0] not in out_port_dict:
                                out_port_dict[edge[0]] = []
                            out_port_dict[edge[0]].append((edge[1], edge[2]))

                        for father in out_port_dict:

                            datapath = ofctl_api.get_datapath(self,
                                                              dpid=father)
                            ofproto = datapath.ofproto
                            match = datapath.ofproto_parser.OFPMatch(
                                dl_dst=haddr_to_bin(
                                    ETHERNET_MULTICAST),  # 这是一个广播包
                                dl_src=haddr_to_bin(host_mac),  # 来源 MAC 地址
                            )

                            new_out_ports = [
                                i[0] for i in out_port_dict[father]
                            ]

                            if father in self.switch_contain_host:
                                host_out_li = [
                                    i[1]
                                    for i in self.switch_contain_host[father]
                                ]
                                new_out_ports += host_out_li

                            actions = [
                                datapath.ofproto_parser.OFPActionOutput(i)
                                for i in new_out_ports
                            ]

                            mod = datapath.ofproto_parser.OFPFlowMod(
                                datapath=datapath,
                                match=match,
                                cookie=0,
                                command=ofproto.OFPFC_ADD,
                                idle_timeout=0,
                                hard_timeout=0,
                                priority=1,  # 优先级至少比默认最短路优先级高 (或许可以设定一个统一值)
                                flags=ofproto.OFPFF_SEND_FLOW_REM,
                                actions=actions)
                            datapath.send_msg(mod)

                print("[^] terminals: ", last_ids)

                for terminal_id in last_ids:
                    # 终端的话 发给对应的host就好了
                    if terminal_id in self.switch_contain_host:
                        datapath = ofctl_api.get_datapath(self,
                                                          dpid=terminal_id)
                        ofproto = datapath.ofproto
                        match = datapath.ofproto_parser.OFPMatch(
                            dl_dst=haddr_to_bin(ETHERNET_MULTICAST),  # 这是一个广播包
                            dl_src=haddr_to_bin(host_mac),  # 来源 MAC 地址
                        )

                        host_out_li = [
                            i[1] for i in self.switch_contain_host[terminal_id]
                        ]
                        actions = [
                            datapath.ofproto_parser.OFPActionOutput(i)
                            for i in host_out_li
                        ]

                        mod = datapath.ofproto_parser.OFPFlowMod(
                            datapath=datapath,
                            match=match,
                            cookie=0,
                            command=ofproto.OFPFC_ADD,
                            idle_timeout=0,
                            hard_timeout=0,
                            priority=1,  # 优先级至少比默认最短路优先级高 (或许可以设定一个统一值)
                            flags=ofproto.OFPFF_SEND_FLOW_REM,
                            actions=actions)
                        datapath.send_msg(mod)

    def add_forwaring_rule(self, datapath, dl_dst, port):
        ofctl = OfCtl.factory(datapath, self.logger)

        actions = [datapath.ofproto_parser.OFPActionOutput(port)]

        ofctl.set_flow(cookie=0,
                       priority=100,
                       dl_vlan=VLANID_NONE,
                       dl_dst=dl_dst,
                       actions=actions)

    def delete_forwarding_rule(self, datapath, dl_dst):
        ofctl = OfCtl.factory(datapath, self.logger)

        match = datapath.ofproto_parser.OFPMatch(dl_dst=dl_dst)

        ofctl.delete_flow(cookie=0, priority=0, match=match)

    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,
                                idle_timeout=5,
                                hard_timeout=15,
                                match=match,
                                instructions=inst)
        datapath.send_msg(mod)
예제 #7
0
class ShortestPathSwitching(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(ShortestPathSwitching, self).__init__(*args, **kwargs)

        self.tm = TopoManager()
        self.graph = Graph(100)  # switch 数量
        self.belong = {
        }  # 每个host属于哪个switch: key, value <- host_mac, (switch_id, switch_port_num)
        self.mac_to_port = {}  # ???
        self.res = {}  # 最短路结果: key, value <- [i][j] = (i-> out1_port)
        self.ip_mac_dict = {}  # 每个ip对应的host是什么:key, value <- ip, host_mac

    @set_ev_cls(event.EventSwitchEnter)
    def handle_switch_add(self, ev):
        """
        Event handler indicating a switch has come online.
        """
        switch = ev.switch

        self.logger.warn("Added Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        self.tm.add_switch(switch)

    @set_ev_cls(event.EventSwitchLeave)
    def handle_switch_delete(self, ev):  # switch上线
        """
        Event handler indicating a switch has been removed
        """
        switch = ev.switch

        self.logger.warn("Removed Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        # TODO:  Update network topology and flow rules

    @set_ev_cls(event.EventHostAdd)
    def handle_host_add(self, ev):  # 主机上线
        """
        Event handler indiciating a host has joined the network
        This handler is automatically triggered when a host sends an ARP response.
        """
        host = ev.host

        self.logger.warn("Host Added:  %s (IPs:  %s) on switch%s/%s (%s)",
                         host.mac, host.ipv4, host.port.dpid,
                         host.port.port_no, host.port.hw_addr)

        # 1 记录这个主机对应的switch 增加转发表
        # 相当于终端
        self.belong[host.mac] = (host.port.dpid, host.port.port_no)
        self.add_forwaring_rule(
            ofctl_api.get_datapath(self, dpid=host.port.dpid), host.mac,
            host.port.port_no)

        # 2 更新其他switch上的转发表
        for dpid in self.res:  # 最短路的计算结果?
            dp = ofctl_api.get_datapath(self, dpid=dpid)

            # 如果是自己 就跳过
            if dpid == host.port.dpid:
                continue

            # 从图上更新其他switch
            self.add_forwaring_rule(dp, host.mac,
                                    self.res[dpid][host.port.dpid])

        # JL: 增加ip-mac对应
        self.ip_mac_dict[host.ipv4[0]] = host.mac

        self.tm.add_host(host)

        print("[BELONG]", self.belong)

    @set_ev_cls(event.EventLinkAdd)
    def handle_link_add(self, ev):
        """
        Event handler indicating a link between two switches has been added
        """
        link = ev.link
        src_port = ev.link.src
        dst_port = ev.link.dst
        self.logger.warn("Added Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # 链路上线 更新内部图
        self.graph.add(src_port.dpid, dst_port.dpid, src_port.port_no)
        self.graph.add(dst_port.dpid, src_port.dpid, dst_port.port_no)
        # 更新最短路结果
        self.shortest_path()

    @set_ev_cls(event.EventLinkDelete)
    def handle_link_delete(self, ev):
        """
        Event handler indicating when a link between two switches has been deleted
        """
        link = ev.link
        src_port = link.src
        dst_port = link.dst

        self.logger.warn("Deleted Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # TODO:  Update network topology and flow rules

    @set_ev_cls(event.EventPortModify)
    def handle_port_modify(self, ev):
        """
        Event handler for when any switch port changes state.
        This includes links for hosts as well as links between switches.
        """
        port = ev.port
        self.logger.warn("Port Changed:  switch%s/%s (%s):  %s", port.dpid,
                         port.port_no, port.hw_addr,
                         "UP" if port.is_live() else "DOWN")

        # TODO:  Update network topology and flow rules

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        # 如果收到 PacketIn 请求
        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto

        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocol(ethernet.ethernet)
        if eth.ethertype == ether_types.ETH_TYPE_LLDP:
            # ignore lldp packet
            return
        dst = eth.dst
        src = eth.src

        dpid = datapath.id  # message source?

        header_list = dict(
            (p.protocol_name, p) for p in pkt.protocols if type(p) != str)

        if eth.ethertype == ether_types.ETH_TYPE_ARP:
            arp_msg = pkt.get_protocols(arp.arp)[0]
            print("[ARP] arp_msg.opcode", arp_msg.opcode, "src=", src, "dst=",
                  dst)
            if arp_msg.opcode == arp.ARP_REQUEST:

                self.logger.warning(
                    "[ARP WHO HAS] Received ARP REQUEST on switch%d/%d:  Who has %s?  Tell %s",
                    dpid, msg.in_port, arp_msg.dst_ip, arp_msg.src_mac)
                # return

        # self.mac_to_port.setdefault(dpid, {})

        # self.logger.info("[PI] packet in src_dp=%s src_in_port=%s src_mac=%s dst_mac=%s ", dpid, msg.in_port,  src, dst)

        # # learn a mac address to avoid FLOOD next time.
        # self.mac_to_port[dpid][src] = msg.in_port
        # print("[PI]","dpid=",dpid, "knows",self.mac_to_port[dpid])

        # if dst in self.mac_to_port[dpid]:
        #     # dst is known, redirect the message to target port
        #     out_port = self.mac_to_port[dpid][dst]
        # else:
        #     # dst not directly known, need to flood?
        #     print("{exp} OFPP_FLOOD HAPPENED")
        #     out_port = ofproto.OFPP_FLOOD

        if eth.ethertype == ether_types.ETH_TYPE_ARP and dst == ETHERNET_MULTICAST and arp_msg.opcode == 2:
            print("[-] ARP Broadcast, ignoring...")
        else:

            src_belong_switch_dpid = self.belong[arp_msg.src_mac][0]
            print("[*] src_belong_dpid", src_belong_switch_dpid)
            dest_mac = self.ip_mac_dict[arp_msg.dst_ip]
            print("[*] dest_mac", dest_mac)
            dest_belong_switch_dpid = self.belong[dest_mac][0]
            print("[*] dst_belong_dpid", dest_belong_switch_dpid)

            if src_belong_switch_dpid != dest_belong_switch_dpid:
                out_port = self.res[src_belong_switch_dpid][
                    dest_belong_switch_dpid]
            else:
                out_port = self.belong[dest_mac][0]
            print("[->] out port", out_port)

            actions = [datapath.ofproto_parser.OFPActionOutput(msg.in_port)]

            ARP_Reply = packet.Packet()
            ARP_Reply.add_protocol(
                ethernet.ethernet(ethertype=header_list[ETHERNET].ethertype,
                                  dst=arp_msg.src_mac,
                                  src=dest_mac))
            ARP_Reply.add_protocol(
                arp.arp(opcode=arp.ARP_REPLY,
                        src_mac=dest_mac,
                        src_ip=arp_msg.dst_ip,
                        dst_mac=arp_msg.src_mac,
                        dst_ip=arp_msg.src_ip))

            ARP_Reply.serialize()

            out = datapath.ofproto_parser.OFPPacketOut(
                datapath=datapath,
                buffer_id=datapath.ofproto.OFP_NO_BUFFER,
                in_port=datapath.ofproto.OFPP_CONTROLLER,
                actions=actions,
                data=ARP_Reply.data)
            datapath.send_msg(out)  # send openflow flow entry

        # actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]

        # # install a flow to avoid packet_in next time
        # if out_port != ofproto.OFPP_FLOOD:
        #     self.add_flow(datapath, msg.in_port, dst, src, actions)

        # data = None
        # if msg.buffer_id == ofproto.OFP_NO_BUFFER: # whether to include the packed in raw packet
        #     data = msg.data

        # out = datapath.ofproto_parser.OFPPacketOut(
        #     datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port,
        #     actions=actions, data=data)
        # datapath.send_msg(out) # send openflow flow entry

    def shortest_path(self):
        self.res = {}
        for i in range(self.graph.n):
            queue = [i]
            dis = [1000] * self.graph.n
            inq = [False] * self.graph.n

            dis[i] = 0
            inq[i] = True
            while len(queue) != 0:
                u = queue[0]
                for v, port in self.graph.go_from(u):
                    if dis[v] > dis[u] + 1:
                        dis[v] = dis[u] + 1
                        if not inq[v]:
                            queue.append(v)
                inq[u] = False
                del queue[0]

            for j in range(self.graph.n):
                for v, port in self.graph.go_from(j):
                    if dis[v] + 1 == dis[j]:
                        if j in self.res:
                            self.res[j][i] = port
                        else:
                            self.res[j] = {}
                            self.res[j][i] = port

    # def shortest_path(self):
    #     '''
    #     最短路计算
    #     '''
    #     self.res = {}
    #     for i in range(self.graph.n):
    #         queue = [i]
    #         dis = [(1e9, -1)] * self.graph.n
    #         inq = [False] * self.graph.n

    #         dis[i] = (0, -1)
    #         inq[i] = True
    #         while len(queue) != 0:
    #             u = queue[0]
    #             for v, port in self.graph.go_from(u):
    #                 if dis[v][0] > dis[u][0] + 1:
    #                     dis[v] = (dis[u][0] + 1, port)
    #                     if not inq[v]:
    #                         queue.append(v)
    #                         inq[v] = True
    #             inq[u] = False
    #             del queue[0]

    #         for j in range(self.graph.n):
    #             v, port = dis[j]
    #             if v == 1e9:
    #                 continue
    #             if port == -1:
    #                 continue
    #             if i in self.res:
    #                 self.res[i][j] = port
    #             else:
    #                 self.res[i] = {}
    #                 self.res[i][j] = port
    #     print(self.res)

    def add_forwaring_rule(self, datapath, dl_dst, port):
        ofctl = OfCtl.factory(datapath, self.logger)

        actions = [datapath.ofproto_parser.OFPActionOutput(port)]

        ofctl.set_flow(cookie=0,
                       priority=0,
                       dl_type=ether_types.ETH_TYPE_IP,
                       dl_vlan=VLANID_NONE,
                       dl_dst=dl_dst,
                       actions=actions)

    def delete_forwarding_rule(self, datapath, dl_dst):
        ofctl = OfCtl.factory(datapath, self.logger)

        match = datapath.ofproto_parser.OFPMatch(dl_dst=dl_dst)

        ofctl.delete_flow(cookie=0, priority=0, match=match)

    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,
                                idle_timeout=5,
                                hard_timeout=15,
                                match=match,
                                instructions=inst)
        datapath.send_msg(mod)
예제 #8
0
class ShortestPathSwitching(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(ShortestPathSwitching, self).__init__(*args, **kwargs)

        self.tm = TopoManager()

    @set_ev_cls(event.EventSwitchEnter)
    def handle_switch_add(self, ev):
        """
        Event handler indicating a switch has come online.
        """
        switch = ev.switch

        self.logger.warn("Added Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.add_switch(switch)

    @set_ev_cls(event.EventSwitchLeave)
    def handle_switch_delete(self, ev):
        """
        Event handler indicating a switch has been removed
        """
        switch = ev.switch

        self.logger.warn("Removed Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        # TODO:  Update network topology and flow rules

    @set_ev_cls(event.EventHostAdd)
    def handle_host_add(self, ev):
        """
        Event handler indiciating a host has joined the network
        This handler is automatically triggered when a host sends an ARP response.
        """
        host = ev.host
        self.logger.warn("Host Added:  %s (IPs:  %s) on switch%s/%s (%s)",
                         host.mac, host.ipv4, host.port.dpid,
                         host.port.port_no, host.port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.add_host(host)

    @set_ev_cls(event.EventLinkAdd)
    def handle_link_add(self, ev):
        """
        Event handler indicating a link between two switches has been added
        """
        link = ev.link
        src_port = ev.link.src
        dst_port = ev.link.dst
        self.logger.warn("Added Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # TODO:  Update network topology and flow rules

    @set_ev_cls(event.EventLinkDelete)
    def handle_link_delete(self, ev):
        """
        Event handler indicating when a link between two switches has been deleted
        """
        link = ev.link
        src_port = link.src
        dst_port = link.dst

        self.logger.warn("Deleted Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # TODO:  Update network topology and flow rules

    @set_ev_cls(event.EventPortModify)
    def handle_port_modify(self, ev):
        """
        Event handler for when any switch port changes state.
        This includes links for hosts as well as links between switches.
        """
        port = ev.port
        self.logger.warn("Port Changed:  switch%s/%s (%s):  %s", port.dpid,
                         port.port_no, port.hw_addr,
                         "UP" if port.is_live() else "DOWN")

        # TODO:  Update network topology and flow rules

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        """
       EventHandler for PacketIn messages
        """
        msg = ev.msg

        # In OpenFlow, switches are called "datapaths".  Each switch gets its own datapath ID.
        # In the controller, we pass around datapath objects with metadata about each switch.
        dp = msg.datapath

        # Use this object to create packets for the given datapath
        ofctl = OfCtl.factory(dp, self.logger)

        in_port = msg.in_port
        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]

        if eth.ethertype == ether_types.ETH_TYPE_ARP:
            arp_msg = pkt.get_protocols(arp.arp)[0]

            if arp_msg.opcode == arp.ARP_REQUEST:

                self.logger.warning(
                    "Received ARP REQUEST on switch%d/%d:  Who has %s?  Tell %s",
                    dp.id, in_port, arp_msg.dst_ip, arp_msg.src_mac)
예제 #9
0
class ShortestPathSwitching(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(ShortestPathSwitching, self).__init__(*args, **kwargs)

        self.tm = TopoManager()

    @set_ev_cls(event.EventSwitchEnter)
    def handle_switch_add(self, ev):
        """
        Event handler indicating a switch has come online.
        """
        switch = ev.switch

        self.logger.warn("Added Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.add_switch(switch)

    @set_ev_cls(event.EventSwitchLeave)
    def handle_switch_delete(self, ev):
        """
        Event handler indicating a switch has been removed
        """
        switch = ev.switch

        self.logger.warn("Removed Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        # TODO:  Update network topology and flow rules

    @set_ev_cls(event.EventHostAdd)
    def handle_host_add(self, ev):
        """
        Event handler indiciating a host has joined the network
        This handler is automatically triggered when a host sends an ARP response.
        """
        host = ev.host
        self.logger.warn("Host Added:  %s (IPs:  %s) on switch%s/%s (%s)",
                         host.mac, host.ipv4, host.port.dpid,
                         host.port.port_no, host.port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.add_host(host)

    @set_ev_cls(event.EventLinkAdd)
    def handle_link_add(self, ev):
        """
        Event handler indicating a link between two switches has been added
        """
        link = ev.link
        src_port = ev.link.src
        dst_port = ev.link.dst
        self.logger.warn("Added Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # TODO:  Update network topology and flow rules

    @set_ev_cls(event.EventLinkDelete)
    def handle_link_delete(self, ev):
        """
        Event handler indicating when a link between two switches has been deleted
        """
        link = ev.link
        src_port = link.src
        dst_port = link.dst

        self.logger.warn("Deleted Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # TODO:  Update network topology and flow rules

    @set_ev_cls(event.EventPortModify)
    def handle_port_modify(self, ev):
        """
        Event handler for when any switch port changes state.
        This includes links for hosts as well as links between switches.
        """
        port = ev.port
        self.logger.warn("Port Changed:  switch%s/%s (%s):  %s", port.dpid,
                         port.port_no, port.hw_addr,
                         "UP" if port.is_live() else "DOWN")

        # TODO:  Update network topology and flow rules

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        """
        EventHandler for PacketIn messages
        """
        msg = ev.msg

        # In OpenFlow, switches are called "datapaths".  Each switch gets its own datapath ID.
        # In the controller, we pass around datapath objects with metadata about each switch.
        dp = msg.datapath

        # Use this object to create packets for the given datapath
        ofctl = OfCtl.factory(dp, self.logger)

        in_port = msg.in_port
        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]

        if eth.ethertype == ether_types.ETH_TYPE_ARP:
            arp_msg = pkt.get_protocols(arp.arp)[0]

            if arp_msg.opcode == arp.ARP_REQUEST:

                self.logger.warning(
                    "Received ARP REQUEST on switch%d/%d:  Who has %s?  Tell %s",
                    dp.id, in_port, arp_msg.dst_ip, arp_msg.src_mac)

                # TODO:  Generate a *REPLY* for this request based on your switch state

                # Here is an example way to send an ARP packet using the ofctl utilities
                #ofctl.send_arp(vlan_id=VLANID_NONE,
                #               src_port=ofctl.dp.ofproto.OFPP_CONTROLLER,
                #               . . .)

    # from document
    def add_forwaring_rule(self, datapath, dl_dst, port):
        '''
        datapath: switch instance <- ofctl_api.get_datapath(self, dpid=host.port.dpid)
        dl_dst: dest mac addr
        port: out interface (1,2,3,4...)
        '''
        ofctl = OfCtl.factory(datapath, self.logger)
        actions = [datapath.ofproto_parser.OFPActionOutput(port)]
        ofctl.set_flow(cookie=0,
                       priority=0,
                       dl_type=ether_types.ETH_TYPE_IP,
                       dl_vlan=VLANID_NONE,
                       dl_dst=dl_dst,
                       actions=actions)

    def delete_forwarding_rule(self, datapath, dl_dst):
        ofctl = OfCtl.factory(datapath, self.logger)
        match = datapath.ofproto_parser.OFPMatch(dl_dst=dl_dst)
        ofctl.delete_flow(cookie=0, priority=0, match=match)
예제 #10
0
class ShortestPathSwitching(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(ShortestPathSwitching, self).__init__(*args, **kwargs)
        self.tm = TopoManager()

    @set_ev_cls(event.EventSwitchEnter)
    def handle_switch_add(self, ev):
        """
        Event handler indicating a switch has come online.
        """
        switch = ev.switch
        self.logger.warn("Added Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.add_switch(switch)
        self.flowtable_update()

    @set_ev_cls(event.EventSwitchLeave)
    def handle_switch_delete(self, ev):
        """
        Event handler indicating a switch has been removed
        """
        switch = ev.switch

        self.logger.warn("Removed Switch switch%d with ports:", switch.dp.id)
        for port in switch.ports:
            self.logger.warn("\t%d:  %s", port.port_no, port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.delete_switch(ev)
        self.flowtable_update()

    @set_ev_cls(event.EventHostDelete)
    def handle_host_delete(self, ev):
        """
        Event handler indicating when a host has been deleted
        """
        host = ev.host

        self.logger.warn("Host Deleted:  %s (IPs:  %s) on switch%s/%s (%s)",
                         host.mac, host.ipv4,
                         host.port.dpid, host.port.port_no, host.port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.delete_host(ev)
        self.flowtable_update()

    @set_ev_cls(event.EventHostAdd)
    def handle_host_add(self, ev):
        """
        Event handler indiciating a host has joined the network
        This handler is automatically triggered when a host sends an ARP response.
        """
        host = ev.host
        self.logger.warn("Host Added:  %s (IPs:  %s) on switch%s/%s (%s)",
                         host.mac, host.ipv4,
                         host.port.dpid, host.port.port_no, host.port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.add_host(host)
        self.flowtable_update()

        # store the ip to mac information to the topo manager
        # find record first, if found, update it
        # if not found, add an entry
        found = False
        for entry in self.tm.ip_to_mac:
            if entry[1] == host.mac:
                entry[0] = host.ipv4[0]
                found = True
            break
        if not found:
            self.tm.ip_to_mac.append([host.ipv4[0], host.mac])

    @set_ev_cls(event.EventLinkAdd)
    def handle_link_add(self, ev):
        """
        Event handler indicating a link between two switches has been added
        """
        link = ev.link
        print(link)
        src_port = ev.link.src
        dst_port = ev.link.dst
        self.logger.warn("Added Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.add_link(ev)
        self.flowtable_update()

    @set_ev_cls(event.EventLinkDelete)
    def handle_link_delete(self, ev):
        """
        Event handler indicating when a link between two switches has been deleted
        """
        link = ev.link
        src_port = link.src
        dst_port = link.dst

        self.logger.warn("Deleted Link:  switch%s/%s (%s) -> switch%s/%s (%s)",
                         src_port.dpid, src_port.port_no, src_port.hw_addr,
                         dst_port.dpid, dst_port.port_no, dst_port.hw_addr)

        # TODO:  Update network topology and flow rules
        self.tm.delete_link(ev)
        self.flowtable_update()

    @set_ev_cls(event.EventPortModify)
    def handle_port_modify(self, ev):
        """
        Event handler for when any switch port changes state.
        This includes links for hosts as well as links between switches.
        """
        port = ev.port
        self.logger.warn("Port Changed:  switch%s/%s (%s):  %s",
                         port.dpid, port.port_no, port.hw_addr,
                         "UP" if port.is_live() else "DOWN")

        # TODO:  Update network topology and flow rules
        self.flowtable_update()

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        """
       EventHandler for PacketIn messages
        """
        msg = ev.msg

        # In OpenFlow, switches are called "datapaths".  Each switch gets its own datapath ID.
        # In the controller, we pass around datapath objects with metadata about each switch.
        dp = msg.datapath

        # Use this object to create packets for the given datapath
        ofctl = OfCtl.factory(dp, self.logger)

        in_port = msg.in_port
        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]
        if eth.ethertype == ether_types.ETH_TYPE_ARP:
            arp_msg = pkt.get_protocols(arp.arp)[0]

            if arp_msg.opcode == arp.ARP_REQUEST:

                self.logger.warning("Received ARP REQUEST on switch%d/%d:  Who has %s?  Tell %s",
                                    dp.id, in_port, arp_msg.dst_ip, arp_msg.src_mac)

                # TODO:  Generate a *REPLY* for this request based on your switch state

                # Here is an example way to send an ARP packet using the ofctl utilities
                r_mac = ""
                found = False
                for translation in self.tm.ip_to_mac:
                    if translation[0] == arp_msg.dst_ip:
                        r_mac = translation[1]
                        found = True
                        break
                if found:
                    ofctl.send_arp(vlan_id=VLANID_NONE, src_port=ofctl.dp.ofproto.OFPP_CONTROLLER, arp_opcode=2,
                                   dst_mac=arp_msg.src_mac, sender_mac=r_mac, sender_ip=arp_msg.dst_ip, target_ip=arp_msg.src_ip,
                                   target_mac=arp_msg.src_mac, output_port=in_port)

    def add_forwarding_rule(self, datapath, dl_dst, port):
        ofctl = OfCtl.factory(datapath, self.logger)
        actions = [datapath.ofproto_parser.OFPActionOutput(port)]
        ofctl.set_flow(cookie=0, priority=0, dl_type=ether_types.ETH_TYPE_IP, dl_vlan=VLANID_NONE, dl_dst=dl_dst,
                       actions=actions)
        print('forwarding_rule:\n    switch:%s\n    dl_dst: %s\n    port: %s' %
              (datapath.id, dl_dst, port))

    def delete_forwarding_rule(self, datapath, dl_dst):
        ofctl = OfCtl.factory(datapath, self.logger)
        match = datapath.ofproto_parser.OFPMatch(dl_dst=dl_dst)
        ofctl.delete_flow(cookie=0, priority=0, match=match)

    def flowtable_update(self):
        for device in self.tm.all_devices:
            if isinstance(device, TMHost):
                self.dijkstra(device)
        self.topology_show()
    
    def topology_show(self):
        print("------------Topology Table------------")
        print("Topology of Hosts:")
        for i in self.tm.all_devices:
            if isinstance(i, TMHost):
                self.logger.warning("    %s is connected to %s", i.name, i.neighbors[0][0].name)
        print("Topology of Switch:")
        for i in self.tm.all_devices:
            if isinstance(i, TMSwitch):
                self.logger.warning("    %s", i.name)
                for neighbor in i.neighbors:
                    if isinstance(neighbor[0], TMHost):
                        self.logger.warning("        Connected to %s by Port_%s", neighbor[0].name, neighbor[1].port_no)
                    if isinstance(neighbor[0], TMSwitch):
                        self.logger.warning("        Connected to %s by Port_%s", neighbor[0].name, neighbor[1].port_no)
        print("--------------------------------------")   

    def rules_update(self):
        for i in self.tm.all_devices:
            if isinstance(i, TMHost):
                for point, num_port in i.path:
                    point.actions = []
                    self.delete_forwarding_rule(point.get_dp(), "00:00:00:00:00:00")

        for i in self.tm.all_devices:
            if isinstance(i, TMHost):
                for point, num_port in i.path:
                    point.flag = False
                    self.delete_forwarding_rule(point.get_dp(), i.get_mac())
                    self.add_forwarding_rule(point.get_dp(), i.get_mac(), num_port)
                    datapath = point.get_dp()
                    point.actions += [datapath.ofproto_parser.OFPActionOutput(num_port)]
                    #self.add_forwarding_rule(point.get_dp(), "00:00:00:00:00:00", num_port)
        
        for i in self.tm.all_devices:
            if isinstance(i, TMHost):
                for point, num_port in i.path:
                    if not point.flag:
                        point.flag = True
                        ofctl = OfCtl.factory(point.get_dp(), self.logger)
                        ofctl.set_flow(cookie=0, priority=0, dl_type=ether_types.ETH_TYPE_IP, dl_vlan=VLANID_NONE, dl_dst="00:00:00:00:00:00", actions=point.actions)


    def dijkstra(self, start):
        q = queue.PriorityQueue(0)
        for device in self.tm.all_devices:
            if device is not start:
                device.distance = MAX
            else:
                device.distance = 0
            device.checked = False
            device.path = []
            device.shortestpath = []
            q.put((device.distance, device))

        while  q.qsize()>0:
            first = q.get()
            top = first[1]
            if isinstance(top, TMHost):
                if not top.checked:
                    top.checked = True
                    if top.distance == 0:
                        top.neighbors[0][0].distance = 1
                        #top.neighbors[0][0].shortestpath = top.shortestpath+[top.name]
                        q.put((1, top.neighbors[0][0]))
                    else:
                        continue
            elif isinstance(top, TMSwitch):
                for neighbor in top.neighbors:
                    neighbor_type_switch = isinstance(neighbor[0], TMSwitch)
                    if not neighbor[0].checked:
                        adjacent = False
                        for i in neighbor[0].neighbors:
                            if i[0] is top:
                                adjacent = True
                        if neighbor[1].is_live():
                            if (not neighbor_type_switch) or (neighbor_type_switch and adjacent):
                                if neighbor[0].distance < top.distance + 1:
                                    continue
                                neighbor[0].path = top.path + [(top, neighbor[1].port_no)]
                                neighbor[0].shortestpath = top.shortestpath + [top.name]
                                neighbor[0].distance = top.distance + 1
                                q.put((neighbor[0].distance, neighbor[0]))
                top.checked = True

        self.rules_update()
        
        # output the shortest path
        print("------------Shortest Path Table------------")
        print("From "+start.name)
        for device in self.tm.all_devices:
            if isinstance(device, TMHost):
                self.logger.warning("    To %s: shortest distance is %s,\n        path is:%s", device.name, device.distance, device.shortestpath)
        print("-------------------------------------------")