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
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 __init__(self, *args, **kwargs): super(ShortestPathSwitching, self).__init__(*args, **kwargs) self.mac_table = {} self.tm = TopoManager()
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 &&&")
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()
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)
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)
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)
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)
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("-------------------------------------------")