def __init__(self, *args, **kwargs): super(SimpleMonitor, self).__init__(*args, **kwargs) self.is_active = True #Topology Discovery self.link_discovery = True self.port_state = {} # datapath_id => ports self.ports = PortDataState() # Port class -> PortData class self.links = LinkState() # Link class -> timestamp self.link_length = 0 self.switch_ports = {} self.dpid_to_ip = map_switch_dpid() #NPS SDN Specific Dictionary################################################## self.dpid_to_node = {0x00012c59e5107640:1, 0x0001c4346b94a200:2,\ 0x0001c4346b99dc00:4, 0x0001c4346b946200:5,\ 0x0001c4346b971ec0:6, 0x0001f0921c219d40:13} ############################################################################## self.active_ips = {} self.arp_table = {} #SDN Application data structures self.blacklist = [] self.throttle_list = [] #SDN Application Flags #This call assigns self.analyze, self.topology, #and self.fingerprint and sets up those apps self._n_fingerprint = True self._n_topology = True self._n_analyze = True self._load_settings() #Listen for a signal that tells the controller to update #its settings. This is really tempramental. #signal.signal(signal.SIGUSR1, self._load_settings) self.threads.append(hub.spawn(self._monitor)) self.threads.append(hub.spawn(self._listener)) self.throttle_list = []
class SimpleMonitor(Routing.SimpleSwitch): LLDP_SEND_GUARD = .05 LLDP_SEND_PERIOD_PER_PORT = .9 TIMEOUT_CHECK_PERIOD = 5. LINK_TIMEOUT = TIMEOUT_CHECK_PERIOD * 2 LINK_LLDP_DROP = 5 LLDP_PACKET_LEN = len(LLDPPacket.lldp_packet(0, 0, DONTCARE_STR, 0)) DEFAULT_TTL = 120 def __init__(self, *args, **kwargs): super(SimpleMonitor, self).__init__(*args, **kwargs) self.is_active = True #Topology Discovery self.link_discovery = True self.port_state = {} # datapath_id => ports self.ports = PortDataState() # Port class -> PortData class self.links = LinkState() # Link class -> timestamp self.link_length = 0 self.switch_ports = {} self.dpid_to_ip = map_switch_dpid() #NPS SDN Specific Dictionary################################################## self.dpid_to_node = {0x00012c59e5107640:1, 0x0001c4346b94a200:2,\ 0x0001c4346b99dc00:4, 0x0001c4346b946200:5,\ 0x0001c4346b971ec0:6, 0x0001f0921c219d40:13} ############################################################################## self.active_ips = {} self.arp_table = {} #SDN Application data structures self.blacklist = [] self.throttle_list = [] #SDN Application Flags #This call assigns self.analyze, self.topology, #and self.fingerprint and sets up those apps self._n_fingerprint = True self._n_topology = True self._n_analyze = True self._load_settings() #Listen for a signal that tells the controller to update #its settings. This is really tempramental. #signal.signal(signal.SIGUSR1, self._load_settings) self.threads.append(hub.spawn(self._monitor)) self.threads.append(hub.spawn(self._listener)) self.throttle_list = [] def _load_settings(self, signal=None, frame=None): print("Loading the settings file") with open("control_node_settings", "r") as f: for line in f.readlines(): exec(line) # If the fingerprint app is checked to run and has not been run yet, # then do all the following. if self.fingerprint and self._n_fingerprint: self._n_fingerprint = False self.fingerprints = {} self.fingerprint_list = createFingerPrintList('fingerprint.xml') self.cluster = Cluster() self.session = self.cluster.connect('fingerprints') db = self.session.execute_async("select * from fpspat") for row in db.result(): mac_addr = row.mac self.fingerprints[mac_addr] = {'ip':row.ip, 'os':row.os,\ 'switch':row.switch, 'port':row.port,\ 'hostname':row.hostname, 'history':row.history} print("Downloaded Fingerprint Database \n") if self.analyze and self._n_analyze: self.analyzer = Analyzer() self._n_analyze = False with open('V','w') as f: f.flush() with open('D','w') as f: f.flush() with open('L','w') as f: f.flush() if self.topology and self._n_topology: self._n_topology = False self.lldp_event = hub.Event() self.link_event = hub.Event() self.threads.append(hub.spawn(self.lldp_loop)) self.threads.append(hub.spawn(self.link_loop)) def _listener(self): """ This function will continuously open a file named 'commands' and execute each line in that file and then clear the file. If a command fails, then the file is not cleared, so be wary of that. """ while True: with open("commands", "r") as f: for line in f.readlines(): exec(line) with open("commands", "w") as f: f.flush() hub.sleep(2) def _monitor(self): """ Runs the monitor app which collects data from the network. Will only run if "analyze" is clicked on the GUI's splash screen. """ if self.topology: while self.dpids == {}: print("Waiting for live datapaths") hub.sleep(2) #self.map_hosts() while True: if self.analyze: for dp in self.dpids: if dp in self.dpid_to_node: self._request_port_stats(self.dpids[dp]) if self.topology: self.draw_graph(1, draw=True) hub.sleep(2) ################################## # ARP and ICMP Packet Handlers # ################################## def _handle_arp_rq(self, dst_ip): pkt = packet.Packet() pkt.add_protocol(ethernet.ethernet(ethertype=0x806,\ dst='ff:ff:ff:ff:ff:ff',\ src=self.hw_addr)) pkt.add_protocol(arp.arp(opcode=arp.ARP_REQUEST,\ src_mac=self.hw_addr,\ src_ip=self.ip_addr,\ dst_mac='00:00:00:00:00:00',\ dst_ip=dst_ip)) self._flood_packet(pkt) def _handle_icmp_reply(self, pkt_icmp, pkt_ipv4, datapath): if pkt_icmp.type != icmp.ICMP_ECHO_REPLY: return if pkt_ipv4.dst != self.ip_addr: print(pkt_ipv4.dst, self.ip_addr) return print("------------------------") print("PING RECEIVED THANK GOD") print(pkt_ipv4.src) print(pkt_ipv4.dst) print(datapath.id) print("------------------------") #Finish ARP redesignation def _handle_arp_reply(self, pkt_arp, port, dpid): if pkt_arp.opcode == arp.ARP_REPLY and pkt_arp.dst_mac == self.hw_addr: if pkt_arp.src_ip not in self.active_ips: print("ARP from " + pkt_arp.src_ip + "\n") self.active_ips[pkt_arp.src_ip] = [dpid, port] self.arp_table[pkt_arp.src_ip] = pkt_arp.src_mac if self.topology: self.draw_graph(1, draw=True) if pkt_arp.opcode == arp.ARP_REQUEST and pkt_arp.src_ip != self.ip_addr: #print("ARP Reqest from: " + pkt_arp.src_mac + " requesting: " + pkt_arp.dst_ip) if pkt_arp.dst_ip not in self.arp_table: return #construct and send ARP reply reply_pkt = packet.Packet() reply_pkt.add_protocol(ethernet.ethernet(ethertype=0x806,\ dst=pkt_arp.src_mac,\ src=self.arp_table[pkt_arp.dst_ip])) reply_pkt.add_protocol(arp.arp(opcode=arp.ARP_REPLY,\ src_mac=self.arp_table[pkt_arp.dst_ip],\ src_ip=pkt_arp.dst_ip,\ dst_mac=pkt_arp.src_mac,\ dst_ip=pkt_arp.src_ip)) print("Responded to ARP Request: " ) print("Gave [" + pkt_arp.src_mac + "," + pkt_arp.src_ip + "]" +\ "[" + pkt_arp.dst_ip + "," + self.arp_table[pkt_arp.dst_ip] + "]") self._send_packet(reply_pkt, self.dpids[int(dpid, 16)]) ############################# # How to send out packets # ############################# def _flood_packet(self, pkt): for dpid in self.dpids: datapath = self.dpids[dpid] ofproto = datapath.ofproto actions = [datapath.ofproto_parser.OFPActionOutput(ofproto.OFPP_FLOOD)] pkt.serialize() out = datapath.ofproto_parser.OFPPacketOut(datapath=datapath, buffer_id=0xffffffff,\ in_port=ofproto.OFPP_CONTROLLER, actions=actions,\ data=pkt.data) datapath.send_msg(out) def _send_packet(self, pkt, datapath=None): #Pick a random datapath to send from if not datapath: datapath = self.dpids[self.dpids.keys()[randint(0,len(self.dpids)-1)]] ofproto = datapath.ofproto pkt.serialize() ether_pkt = pkt.get_protocol(ethernet.ethernet) if ether_pkt.dst in self.mac_to_port[datapath.id]: print("Sending packet out: " + `self.mac_to_port[datapath.id][ether_pkt.dst]`) actions = [datapath.ofproto_parser.OFPActionOutput(self.mac_to_port[datapath.id]\ [ether_pkt.dst])] out = datapath.ofproto_parser.OFPPacketOut(datapath=datapath, buffer_id=0xffffffff,\ in_port=ofproto.OFPP_CONTROLLER, actions=actions,\ data=pkt.data) datapath.send_msg(out) ########################## # How to draw a graph # ######################### def draw_graph(self, timeout, draw=False): """ This function does the actual drawing of the network topology. """ G = nx.Graph() plt.clf() labels = {} nodes = [] host_nodes = [] for id in self.dpids: G.add_node(hex(id)) labels[hex(id)] = hex(id) nodes.append(hex(id)) for link in self.links: G.add_edge(hex(link.src.dpid), hex(link.dst.dpid)) for ip in self.active_ips: if ip not in host_nodes: G.add_node(ip) G.add_edge(self.active_ips[ip][0], ip) host_nodes.append(ip) self.graph = G.copy() pos = nx.spring_layout(G) G = nx.Graph() if draw: nx.draw(G) nx.draw_networkx_nodes(G, pos, nodelist=nodes, node_color='FireBrick',\ node_size=500, alpha=0.8) nx.draw_networkx_nodes(G, pos, nodelist=host_nodes, node_color='DarkGoldenRod',\ node_size=200, alpha=0.8) for link in self.links: G.add_edge(hex(link.src.dpid), hex(link.dst.dpid)) for ip in self.active_ips: G.add_edge(self.active_ips[ip][0],ip) nx.draw_networkx_edges(G, pos) plt.pause(timeout) ########################## # How to create a flow # ########################## def _create_icmp_flow(self,datapath): ofproto = datapath.ofproto ofproto_parser = datapath.ofproto_parser nw_dst = struct.unpack('!I', ipv4_to_bin(self.ip_addr))[0] match = datapath.ofproto_parser.OFPMatch(dl_type=0x800, nw_dst=nw_dst) actions = [datapath.ofproto_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER)] mod = datapath.ofproto_parser.OFPFlowMod( datapath=datapath, match=match, cookie=0, command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, actions=actions, priority=0xFFFF) datapath.send_msg(mod) def _create_lldp_flow(self, datapath): ofproto = datapath.ofproto ofproto_parser = datapath.ofproto_parser if ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: #Add LLDP Rule match = datapath.ofproto_parser.OFPMatch(dl_type=0x88cc) actions = [datapath.ofproto_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER)] mod = datapath.ofproto_parser.OFPFlowMod( datapath=datapath, match=match, cookie=0, command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, actions=actions, priority=0xFFFF) datapath.send_msg(mod) def _create_arp_flow(self, datapath): ofproto = datapath.ofproto ofproto_parser = datapath.ofproto_parser match = datapath.ofproto_parser.OFPMatch(dl_type=0x0806) actions = [datapath.ofproto_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER)] mod = datapath.ofproto_parser.OFPFlowMod( datapath=datapath, match=match, cookie=0, command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, actions=actions, priority=0xFFFF) datapath.send_msg(mod) ################################### # How to use Ryu Event Handlers # ################################### @set_ev_cls(ofp_event.EventOFPStateChange, [MAIN_DISPATCHER, DEAD_DISPATCHER]) def _state_change_handler(self, ev): datapath = ev.datapath if ev.state == MAIN_DISPATCHER: dp_multiple_conns = False if datapath.id in self.dpids: dp_multiple_conns = True self.logger.debug('register datapath: %016x', datapath.id) print("New DPID: " + hex(datapath.id)) self._register(datapath) switch = self._get_switch(datapath.id) if not dp_multiple_conns: self.send_event_to_observers(event.EventSwitchEnter(switch)) self._create_lldp_flow(datapath) self._create_arp_flow(datapath) self._create_icmp_flow(datapath) if not dp_multiple_conns: for port in switch.ports: if not port.is_reserved(): self._port_added(port) elif ev.state == DEAD_DISPATCHER: switch = self._get_switch(datapath.id) if datapath.id in self.dpids: self.logger.debug('unregister datapath: %016x', datapath.id) self._unregister(datapath) self.send_event_to_observers(event.EventSwitchLeave(switch)) for port in switch.ports: if not port.is_reserved(): self.ports.del_port(port) self._link_down(port) if self.topology: self.lldp_event.set() @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) def port_status_handler(self, ev): msg = ev.msg reason = msg.reason dp = msg.datapath ofpport = msg.desc if reason == dp.ofproto.OFPPR_ADD: self.port_state[dp.id].add(ofpport.port_no, ofpport) self.send_event_to_observers(\ event.EventPortAdd(Port(dp.id, dp.ofproto, ofpport))) if not self.link_discovery: return port = self._get_port(dp.id, ofpport.port_no) if port and not port.is_reserved(): self._port_added(port) self.lldp_event.set() elif reason == dp.ofproto.OFPPR_DELETE: self.port_state[dp.id].remove(ofpport.port_no) self.send_event_to_observers(\ event.EventPortDelete(Port(dp.id, dp.ofproto, ofpport))) if not self.link_discovery: return port = self._get_port(dp.id, ofpport.port_no) if port and not port.is_reserved(): self.ports.del_port(port) self._link_down(port) self.lldp_event.set() else: assert reason == dp.ofproto.OFPPR_MODIFY self.port_state[dp.id].modify(ofpport.port_no, ofpport) self.send_event_to_observers(\ event.EventPortModify(Port(dp.id, dp.ofproto, ofpport))) if not self.link_discovery: return port = self._get_port(dp.id, ofpport.port_no) if port and not port.is_reserved(): if self.ports.set_down(port): self._link_down(port) self.lldp_event.set() @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) def _port_stats_reply_handler(self, ev): timestamp = datetime.now() self.analyzer.analyze(ev, timestamp.second + (timestamp.microsecond * 1e-6)) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def app_packet_in_handler(self, ev): msg = ev.msg pkt = packet.Packet(msg.data) pkt_dhcp = pkt.get_protocol(dhcp.dhcp) pkt_arp = pkt.get_protocol(arp.arp) pkt_icmp = pkt.get_protocol(icmp.icmp) pkt_ipv4 = pkt.get_protocol(ipv4.ipv4) if pkt_icmp: self._handle_icmp_reply(pkt_icmp, pkt_ipv4, msg.datapath) if pkt_arp: self._handle_arp_reply(pkt_arp, ev.msg.in_port, hex(msg.datapath.id)) if self.fingerprint: if pkt_dhcp: print("----------------------------------") print(pkt_dhcp.yiaddr) print(pkt_dhcp.op) print("----------------------------------") self._dhcp_handler(msg) if self.topology: self._lldp_handler(msg) ####################### # DHCP Fingerprints # ####################### def _dhcp_handler(self, msg): """ Takes a DHCP packet and parses it for MAC, IP, Switch, Port, Options Using the options, this function checks those options against a fingerprint database in order to guess the operating system used by the host. Afterwards, it stores that information in a Cassandra database. """ dpid = hex(msg.datapath.id) pkt = msg.data try: source_mac, parsedPacket = dhcp_parse(pkt) except TypeError: return hitlist = compare(self.fingerprint_list,parsedPacket) hostname = get_dhcp_option_value(parsedPacket[12], 12) option53 = get_dhcp_option_value(parsedPacket[12], 53) option60 = get_dhcp_option_value(parsedPacket[12], 60) if option53: dhcptype = dhcp_types[option53] if dhcptype != "Discover" and dhcptype != "Request": print("Not a request of discover packets, so ignoring") return if parsedPacket[2]: source_ip = hex_to_ip(hex(parsedPacket[2])) elif parsedPacket[16]: source_ip = hex_to_ip(hex(parsedPacket[16])) elif get_dhcp_option_value(parsedPacket[12], 50): source_ip = map(ord, get_dhcp_option_value(parsedPacket[12], 50)) source_ip = '.'.join(str(x) for x in source_ip) else: source_ip = None mac = hex_to_mac(source_mac) ### NPS CCW Specific Network ### location = DPIDToLocation(dpid)# port = str(msg.in_port) # ################################ if mac not in self.fingerprints: print("New fingerprint") print "Source MAC: {}".format(mac) print "IP Address: {}".format(source_ip) print "Location : {}".format(location) print "Host name : {}".format(hostname) print "DHCP Type : {}".format(dhcptype) mac_history = ["[%s, %s, %s, %s, %s]" % (source_ip, hitlist[0][0],\ location, port, hostname)] self.fingerprints[mac] = {'ip':source_ip, 'os':hitlist[0][0],\ 'switch':location, 'port':port,\ 'hostname':hostname, 'history':mac_history} command = "insert into fpspat (MAC, IP, OS, Switch, Port, Hostname,\ Time, History) values ('{0}', '{1}', '{2}', '{3}', '{4}',\ '{5}', '{6}', {7})".format(mac, source_ip, hitlist[0][0],\ location, port, hostname, str(datetime.now()),\ mac_history) self.session.execute(command) fp = self.fingerprints[mac] changes = [] time = str(datetime.now()) for prop,val in [('ip', source_ip), ('os', hitlist[0][0]),('hostname', hostname)]: if fp[prop] != val: print("The " + prop + " changed") changes.append("[" + fp[prop] + " changed to " + val + " at time " + time + "]") fp[prop] = val command = "update fpspat set " + prop + " = " + `val` + " where MAC = " + `mac` print(command) self.session.execute(command) if changes: print("Database updated to reflect changes") fp['history'] += changes print(fp['history']) command = "update fpspat set History = '{0}' where MAC= '{1}'".format(str(fp['history']),\ str(mac)) self.session.execute(command) ####################################### # LLDP Portion - Topology Detection # ####################################### def close(self): self.is_active = False if self.link_discovery: self.lldp_event.set() self.link_event.set() hub.joinall(self.threads) def _register(self, dp): """ Takes the datapath and registers it as a switch. This is how the controller represents the switches. """ assert dp.id is not None self.dpids[dp.id] = dp if dp.id not in self.port_state: self.port_state[dp.id] = PortState() for port in dp.ports.values(): self.port_state[dp.id].add(port.port_no, port) def _unregister(self, dp): """ This function is called when a switch dies. It helps with the book-keeping and clean up. """ if dp.id in self.dpids: del self.dpids[dp.id] del self.port_state[dp.id] def _get_switch(self, dpid): """ Returns the switch representation of the datapath id. """ if dpid in self.dpids: switch = Switch(self.dpids[dpid]) for ofpport in self.port_state[dpid].values(): switch.add_port(ofpport) return switch def _get_port(self, dpid, port_no): """ Returns the controller's representation of a port. """ switch = self._get_switch(dpid) if switch: for p in switch.ports: if p.port_no == port_no: return p def _port_added(self, port): """ Adds the port to a list of ports used to connect two switches. """ lldp_data = LLDPPacket.lldp_packet( port.dpid, port.port_no, port.hw_addr, self.DEFAULT_TTL) self.ports.add_port(port, lldp_data) # LOG.debug('_port_added dpid=%s, port_no=%s, live=%s', # port.dpid, port.port_no, port.is_live()) def _link_down(self, port): """ Creates an event that will tell the controller that the link state has changed so that the controller can reflect that in its representation. """ try: dst, rev_link_dst = self.links.port_deleted(port) except KeyError: # LOG.debug('key error. src=%s, dst=%s', # port, self.links.get_peer(port)) return link = Link(port, dst) self.send_event_to_observers(event.EventLinkDelete(link)) if rev_link_dst: rev_link = Link(dst, rev_link_dst) self.send_event_to_observers(event.EventLinkDelete(rev_link)) self.ports.move_front(dst) def _lldp_handler(self, msg): """ Will react to LLDP packets by parsing then and creating events so that the controller knows to update its topology representation. """ try: # Attempt to parse the packet as an LLDP packet # Will fail if it is not an LLDP packet src_dpid, src_port_no = LLDPPacket.lldp_parse(msg.data) except LLDPPacket.LLDPUnknownFormat as e: # This handler can receive all the packtes which can be # not-LLDP packet. Ignore it silently #print("Not LLDP Packet") return dst_dpid = msg.datapath.id if msg.datapath.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: dst_port_no = msg.in_port elif msg.datapath.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: dst_port_no = msg.match['in_port'] else: LOG.error('cannot accept LLDP. unsupported version. %x', msg.datapath.ofproto.OFP_VERSION) src = self._get_port(src_dpid, src_port_no) if not src or src.dpid == dst_dpid: return try: self.ports.lldp_received(src) except KeyError: # There are races between EventOFPPacketIn and # EventDPPortAdd. So packet-in event can happend before # port add event. In that case key error can happend. # LOG.debug('lldp_received: KeyError %s', e) pass dst = self._get_port(dst_dpid, dst_port_no) if not dst: return old_peer = self.links.get_peer(src) if old_peer and old_peer != dst: old_link = Link(src, old_peer) self.send_event_to_observers(event.EventLinkDelete(old_link)) link = Link(src, dst) if link not in self.links: self.send_event_to_observers(event.EventLinkAdd(link)) if src_dpid not in self.switch_ports: self.switch_ports[src_dpid] = [] if src_port_no not in self.switch_ports[src_dpid]: self.switch_ports[src_dpid].append(src_port_no) if dst_dpid not in self.switch_ports: self.switch_ports[dst_dpid] = [] if dst_port_no not in self.switch_ports[dst_dpid]: self.switch_ports[dst_dpid].append(dst_port_no) if not self.links.update_link(src, dst): self.ports.move_front(dst) self.lldp_event.set() def send_lldp_packet(self, port): """ Handles the crafting and sending of LLDP packets For each port on each switch, create a packet unique to that (switch,port) combination. """ try: port_data = self.ports.lldp_sent(port) except KeyError as e: # ports can be modified during our sleep in self.lldp_loop() # LOG.debug('send_lldp: KeyError %s', e) return if port_data.is_down: return dp = self.dpids.get(port.dpid, None) if dp is None: # datapath was already deleted return # LOG.debug('lldp sent dpid=%s, port_no=%d', dp.id, port.port_no) # TODO:XXX if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: actions = [dp.ofproto_parser.OFPActionOutput(port.port_no)] dp.send_packet_out(actions=actions, data=port_data.lldp_data) elif dp.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: actions = [dp.ofproto_parser.OFPActionOutput(port.port_no)] out = dp.ofproto_parser.OFPPacketOut( datapath=dp, in_port=dp.ofproto.OFPP_CONTROLLER, buffer_id=dp.ofproto.OFP_NO_BUFFER, actions=actions, data=port_data.lldp_data) dp.send_msg(out) else: LOG.error('cannot send lldp packet. unsupported version. %x', dp.ofproto.OFP_VERSION) def lldp_loop(self): """ This function is how the topology is kept up to date. It will send out LLDP packets at every interval to update the topology. """ while self.is_active: self.lldp_event.clear() now = time.time() timeout = None ports_now = [] ports = [] for (key, data) in self.ports.items(): if data.timestamp is None: ports_now.append(key) continue expire = data.timestamp + self.LLDP_SEND_PERIOD_PER_PORT if expire <= now: ports.append(key) continue timeout = expire - now break for port in ports_now: self.send_lldp_packet(port) for port in ports: self.send_lldp_packet(port) hub.sleep(self.LLDP_SEND_GUARD) # don't burst if timeout is not None and ports: timeout = 0 # We have already slept # LOG.debug('lldp sleep %s', timeout) self.draw_graph(1, draw=True) self.lldp_event.wait(timeout=timeout) def link_loop(self): """ This function helps update the actual connection between switches. I don't know the difference between this function and lldp_loop, but you need both of them, otherwise, the topology won't update. """ while self.is_active: self.link_event.clear() now = time.time() deleted = [] for (link, timestamp) in self.links.items(): # LOG.debug('%s timestamp %d (now %d)', link, timestamp, now) if timestamp + self.LINK_TIMEOUT < now: src = link.src if src in self.ports: port_data = self.ports.get_port(src) # LOG.debug('port_data %s', port_data) if port_data.lldp_dropped() > self.LINK_LLDP_DROP: deleted.append(link) for link in deleted: self.links.link_down(link) # LOG.debug('delete %s', link) self.send_event_to_observers(event.EventLinkDelete(link)) dst = link.dst rev_link = Link(dst, link.src) if rev_link not in deleted: # It is very likely that the reverse link is also # disconnected. Check it early. expire = now - self.LINK_TIMEOUT self.links.rev_link_set_timestamp(rev_link, expire) if dst in self.ports: self.ports.move_front(dst) self.lldp_event.set() if link.dst.port_no in self.switch_ports[link.dst.dpid]: self.switch_ports[link.dst.dpid].remove(link.dst.port_no) if link.src.port_no in self.switch_ports[link.src.dpid]: self.switch_ports[link.src.dpid].remove(link.src.port_no) self.link_event.wait(timeout=self.TIMEOUT_CHECK_PERIOD) ################################## # Application Implementations # ################################## def _request_port_stats(self, datapath): """ Sends a message to each switch requesting port statistics. This is used for network analytics. """ ofproto = datapath.ofproto parser = datapath.ofproto_parser req = parser.OFPPortStatsRequest(datapath, 0, ofproto.OFPP_NONE) datapath.send_msg(req) #Adds or removes an IP flow from each switch def _modify_blacklist(self, ipaddr, mode, dl_type=0x800): res = struct.unpack('!I', ipv4_to_bin(ipaddr))[0] if mode == "add": if ipaddr in self.blacklist: return self.blacklist.append(ipaddr) elif mode == "remove": if ipaddr not in self.blacklist: return self.blacklist.remove(ipaddr) for dp in self.dpids: datapath = self.dpids[dp] ofproto = datapath.ofproto match = datapath.ofproto_parser.OFPMatch(dl_type=dl_type, nw_src=res) if mode == "add": print("Adding " + ipaddr + " to blacklist") command = ofproto.OFPFC_ADD elif mode == "remove": print("Removing " + ipaddr + " to blacklsit") command = ofproto.OFPFC_DELETE mod = datapath.ofproto_parser.OFPFlowMod(datapath=datapath, match=match,\ cookie=0,command=command, idle_timeout=0, hard_timeout=0,\ priority=ofproto.OFP_DEFAULT_PRIORITY+5,\ flags=ofproto.OFPFF_SEND_FLOW_REM, actions=None) datapath.send_msg(mod) #Changes transmission rate on a switch's port -- meant for HP switches #@todo: make more general? Throttle switches that see an IP address def _modify_throttle(self, switch_ip, port, mode, username="******", password="******"): if mode == "add": if ipaddr in self.throttle_list: return self.throttle_list.append((switch, port)) command = 'interface ethernet' + `port` + ' rate-limit all out kbps 10000' elif mode == "rmv": if ipaddr not in self.throttle_list: return self.throttle_list.remove((switch, port)) command = 'no interface ethernet' + `port` + ' rate-limit all out kbps 10000' print("Starting throttle command") s = pexpect.spawn("ssh %s@%s" %(username, switch_ip)) s.expect('.*assword: ') # Send the password s.sendline(password) s.expect('Press any key to continue') # Send the return key s.send('\r') s.sendline('config \n') s.sendline(command) s.sendline('logo') s.sendline('y') print('Throttle Successful') def clear_all_flows(self): for switch in [1,2,3,4,5,6,8,9,10,11,12,13,14]: print("Deleting flows on {}".format(switch)) system("dpctl del-flows tcp:10.10.0.{}:6655".format(switch)) for dp in self.dpids: datapath = self.dpids[dp] print("Adding flows onto {}".format(dp)) self._create_lldp_flow(datapath) self._create_arp_flow(datapath) self._create_icmp_flow(datapath) def switch_on_all_ports(self,username="******", password="******"): for dp in self.dpids: dp = hex(dp) print("logging into " + self.dpid_to_ip[dp]) s = spawn("ssh %s@%s" %(username, self.dpid_to_ip[dp])) s.expect(".*assword") s.sendline(password) s.expect("Press any key to continue") s.sendline("\r") s.sendline("config") for n in range(1,25): #print("Enabling port " + `n` + " on " + self.dpid_to_ip[dp]) s.sendline("interface ethernet " + `n` + " enable") s.sendline("save") s.sendline("logo") s.sendline("y") print("CREATED FULLY CONNECTED GRAPH") def create_spanning_tree(self, username="******", password="******"): T = nx.minimum_spanning_tree(self.graph) used_links = [] disabled_ports = {} for link in self.links: used = False src, dst = hex(link.src.dpid), hex(link.dst.dpid) for edge in T.edges(): if (src,dst) == edge or (dst,src) == edge: used = True if not used: if link.src.dpid not in disabled_ports: disabled_ports[link.src.dpid] = [] disabled_ports[link.src.dpid].append(link.src.port_no) for dp in disabled_ports: ip = self.dpid_to_ip[hex(dp)] print("logging into " + ip) s = spawn("ssh %s@%s" %(username, ip)) s.expect(".*assword") s.sendline(password) s.expect("Press any key to continue") s.sendline("\r") s.sendline("config") for n in disabled_ports[dp]: #print("Enabling port " + `n` + " on " + self.dpid_to_ip[dp]) s.sendline("interface ethernet " + `n` + " disable") s.sendline("save") s.sendline("logo") s.sendline("y") print("CREATED SPANNING TREE") def send_ping(self, ip_dst): pkt = packet.Packet() if ip_dst in self.arp_table: mac_dst = self.arp_table[ip_dst] else: return pkt.add_protocol(ethernet.ethernet(ethertype=0x800,dst=mac_dst,\ src=self.hw_addr)) pkt.add_protocol(ipv4.ipv4(dst= ip_dst, src=self.ip_addr,proto=1)) pkt.add_protocol(icmp.icmp(type_= 8, code=0, csum=0))#Not sure about echo print("Ping packet sent") self._flood_packet(pkt) def map_hosts(self,time=2): with open("ipList.txt", "r") as f: lines = f.readlines() shuffle(lines) for line in lines: line = line.strip() print("Sending ARP to " + line) self._handle_arp_rq(line) hub.sleep(1)