def act_like_firewall_drop(self, packet, packet_in): """ Implement switch-like behavior. 1. Implementing a "simple" learning switch. """ log.debug("Installing dropping flow...") # Maybe the log statement should have source/destination/port? msg = of.ofp_flow_mod() ## Set fields to match received packet # Exact match msg.match = of.ofp_match.from_packet(packet_in) msg.idle_timeout = 30 # msg.hard_timeout = 60 # for debugging purpose msg.buffer_id = packet_in.buffer_id msg.data = packet_in # No action for dropped packet # msg.actions.append(of.ofp_action_output(port=of.OFPP_NONE)) # Send msg to swtich self.connection.send(msg) log.debug("------------------------------")
def act_like_firewall_drop(self, packet, packet_in): """ Implement switch-like behavior. 1. Implementing a "simple" learning switch. """ log.debug("Installing dropping flow...") # Maybe the log statement should have source/destination/port? msg = of.ofp_flow_mod() ## Set fields to match received packet # Exact match msg.match = of.ofp_match.from_packet(packet_in) msg.idle_timeout = 30 # msg.hard_timeout = 60 # for debugging purpose msg.buffer_id = packet_in.buffer_id msg.data = packet_in # No action for dropped packet # msg.actions.append(of.ofp_action_output(port=of.OFPP_NONE)) # Send msg to swtich self.connection.send(msg) # else: # log.debug(" ==> Unknowd {0} -- Flood all".format(packet.dst)) # # Flood the packet out everything but the input port # # This part looks familiar, right? # self.resend_packet(packet_in, of.OFPP_ALL) log.debug("------------------------------")
def _handle_LinkEvent(self, event): """ Listener de NUEVO ENLACE """ global _flood_delay _flood_delay = _flood_delay + FLOOD_DELAY_INCREMENT log.debug('NUEVO ENLACE DETECTADO _flood_delay: %d SEGUNDOS' % _flood_delay) self._adjust_adjacency()
def act_like_firewall_drop (self, packet, packet_in): """ Implement switch-like behavior. 1. Implementing a "simple" learning switch. """ log.debug("Installing dropping flow...") # Maybe the log statement should have source/destination/port? msg = of.ofp_flow_mod() ## Set fields to match received packet # Exact match msg.match = of.ofp_match.from_packet(packet_in) msg.idle_timeout = 30 # msg.hard_timeout = 60 # for debugging purpose msg.buffer_id = packet_in.buffer_id msg.data = packet_in # No action for dropped packet # msg.actions.append(of.ofp_action_output(port=of.OFPP_NONE)) # Send msg to swtich self.connection.send(msg) # else: # log.debug(" ==> Unknowd {0} -- Flood all".format(packet.dst)) # # Flood the packet out everything but the input port # # This part looks familiar, right? # self.resend_packet(packet_in, of.OFPP_ALL) log.debug("------------------------------")
def act_like_firewall_drop (self, packet, packet_in): """ Implement switch-like behavior. 1. Implementing a "simple" learning switch. """ log.debug("Installing dropping flow...") # Maybe the log statement should have source/destination/port? msg = of.ofp_flow_mod() ## Set fields to match received packet # Exact match msg.match = of.ofp_match.from_packet(packet_in) msg.idle_timeout = 30 # msg.hard_timeout = 60 # for debugging purpose msg.buffer_id = packet_in.buffer_id msg.data = packet_in # No action for dropped packet # msg.actions.append(of.ofp_action_output(port=of.OFPP_NONE)) # Send msg to swtich self.connection.send(msg) log.debug("------------------------------")
def _handle_ConnectionUp(self, event): """ Listener de NUEVO SWITCH conectado """ global _flood_delay _flood_delay = _flood_delay + FLOOD_DELAY_INCREMENT log.debug('NUEVO SWITCH DETECTADO _flood_delay: %d SEGUNDOS' % _flood_delay) # Creo un nuevo switch Switch(event.connection, event.dpid)
def send_stats_requests(): """ Send stats request to the connecting switch """ for connection in core.openflow._connections.values(): connection.send(of.ofp_stats_request(body=of.ofp_flow_stats_request())) connection.send(of.ofp_stats_request(body=of.ofp_port_stats_request())) log.debug("Sent %i flow/port stats request(s)", len(core.openflow._connections))
def _adjust_adjacency(self): """ Ajusta la ADYACENCIA entre componentes conectados """ log.debug('AJUSTANDO ADYACENCIA') adj.clear() switch_ids.clear() # por cada enlace nuevo, se ajustan adj y switch_ids for l in core.openflow_discovery.adjacency: adj[l.dpid1][l.dpid2] = l switch_ids.add(l.dpid1) switch_ids.add(l.dpid2)
def _handle_PacketIn (self, event): packet = event.parsed packet_in = event.ofp dpid = event.dpid log.debug("PacketIn message from Switch with dpid = {}".format(dpid)) self.track_host(packet, packet_in, dpid) self.simple_hub(dpid , packet, packet_in)
def _handle_PacketIn(self, event): packet = event.parsed packet_in = event.ofp dpid = event.dpid log.debug("PacketIn message from Switch with dpid = {}".format(dpid)) self.track_host(packet, packet_in, dpid) self.simple_hub(dpid, packet, packet_in)
def insertFlow(self, packet, connection, buffer_id): log.debug('Installing flow on switch %d' % self.dpid) msg = of.ofp_flow_mod() msg.match.dl_type = pkt.ethernet.IP_TYPE msg.match.nw_src = IPAddr('10.0.0.1') msg.buffer_id = buffer_id msg.actions.append(of.ofp_action_nw_addr.set_dst(IPAddr('10.0.0.102'))) msg.actions.append(of.ofp_action_dl_addr.set_dst(EthAddr('00:00:01:02:03:04'))) msg.actions.append(of.ofp_action_output(port = 2)) connection.send(msg.pack())
def track_host(self, packet, packet_in, dpid): if packet.type == packet.ARP_TYPE: src_ip = packet.payload.protosrc if src_ip not in self.hosts: log.debug("DISCOVERED host with ip {} at switch with dpid {}".format(src_ip, dpid)) self.hosts[src_ip] = dpid, packet_in.in_port self.dpid_dict[dpid]["hosts"].append(src_ip.toStr()) return
def _handle_PacketIn (self, event): packet = event.parsed packet_in = event.ofp dpid = event.dpid log.debug("PacketIn message from Switch with dpid = {}".format(dpid)) self.track_host(packet, packet_in, dpid) # self.simple_hub(dpid , packet, packet_in) # self.learning_controller(dpid, packet, packet_in) self.learning_microflow_controller(dpid, packet, packet_in)
def startup(): #core.openflow.addListeners(self, priority=0) core.openflow.addListeners(self) core.openflow_discovery.addListeners(self) # Listen for flow stats core.openflow.addListenerByName("FlowStatsReceived", handle_flow_stats) # EL LISTENER DE ABAJO ES PARA UNA SOLUCION ALTERNATIVA DEL FIREWALL CON SOLICITUD DE ESTADISTICAS PERIODICAS. #core.openflow.addListenerByName("FlowStatsReceived", handle_udp_flow_stats) core.openflow.addListenerByName("FlowRemoved", handle_flow_removed) core.host_tracker.addListenerByName("HostEvent", handle_host_tracker_HostEvent) log.debug("ZgnFattreeController ESTA LISTO")
def is_ip_blocked(self, packet): """ Handle IP packet 2. Modify the above simple learning switch to include the logic blocking IP traffic between host 2 and host 3. """ ip = packet.find("ipv4") # log.debug(" => IP:{0}".format(ip)) if ip is not None and Tutorial.ip_block.get(ip.srcip) == ip.dstip: log.debug(" ==> IP is blocked from {0} to {1}".format(ip.srcip, ip.dstip)) return True return False
def insertFlow(self, packet, connection, buffer_id): log.debug('Installing flow on switch %d' % self.dpid) msg = of.ofp_flow_mod() msg.match.dl_type = pkt.ethernet.IP_TYPE msg.match.nw_src = IPAddr('10.0.0.1') msg.buffer_id = buffer_id msg.actions.append(of.ofp_action_nw_addr.set_dst(IPAddr('10.0.0.102'))) msg.actions.append( of.ofp_action_dl_addr.set_dst(EthAddr('00:00:01:02:03:04'))) msg.actions.append(of.ofp_action_output(port=2)) connection.send(msg.pack())
def _handle_PacketIn(self, event): packet = event.parsed packet_in = event.ofp dpid = event.dpid log.debug("PacketIn message from Switch with dpid = {}".format(dpid)) self.track_host(packet, packet_in, dpid) # self.simple_hub(dpid , packet, packet_in) # self.learning_controller(dpid, packet, packet_in) self.learning_microflow_controller(dpid, packet, packet_in)
def _handle_LinkEvent (self, event): link = event.link dpid_source = link.dpid1 dpid_dest = link.dpid2 port_source = link.port1 port_dest = link.port2 if event.added: log.debug("Link added for switches S-{}:port-{} --> S-{}:port-{}".format(dpid_source, port_source, dpid_dest, port_dest)) self.dpid_dict[dpid_source]["links"].append(link) else: log.debug("Link removed for switches S-{}:port-{} --> S-{}:port-{}".format(dpid_source, port_source, dpid_dest, port_dest))
def is_ip_blocked(self, packet): """ Handle IP packet 2. Modify the above simple learning switch to include the logic blocking IP traffic between host 2 and host 3. """ ip = packet.find("ipv4") # log.debug(" => IP:{0}".format(ip)) if ip is not None and Tutorial.ip_block.get(ip.srcip) == ip.dstip: log.debug(" ==> IP is blocked from {0} to {1}".format( ip.srcip, ip.dstip)) return True return False
def install_rule( self, connection, priority, src_port, src_mac, dst_mac, out_port, timeout, dl_type): log.debug("Switch-{}: Installing rule src:{} , src_port {}, dst {} -> dst_port {}". format( connection.dpid, src_mac, src_port, dst_mac, out_port)) # TODO: Maybe do a more fine-grained match ? msg = of.ofp_flow_mod() msg.priority = priority msg.match = of.ofp_match( in_port = src_port, dl_src = src_mac, dl_dst = dst_mac, dl_type = dl_type) msg.hard_timeout = timeout msg.actions.append( of.ofp_action_output( port = out_port)) connection.send(msg)
def reverseProxy(self, packet, buffer_id): log.debug('Installing flow for reverse proxy on switch %d' % self.dpid) # From interior to exterior. n = packet.find("ipv4") msg = of.ofp_flow_mod() msg.match.dl_type = pkt.ethernet.IP_TYPE; msg.match.nw_dst = n.dstip msg.buffer_id = buffer_id msg.actions.append(of.ofp_action_nw_addr.set_src(self.vhost.ip)) msg.actions.append(of.ofp_action_dl_addr.set_src(self.vhost.mac)) msg.actions.append(of.ofp_action_output(port = self.publicPort)) self.connection.send(msg)
def macLearning(self, packet): ip = None mac = packet.src if packet.find("ipv4"): n = packet.find("ipv4") ip = n.srcip elif packet.find("arp"): a = packet.find("arp") if a.prototype == a.PROTO_TYPE_IP: ip = a.protosrc if ip is not None and self.hosts.get(ip) is None: self.hosts[ip] = Host(ip, mac) log.debug('Learned: %s <=> %s' % (ip, mac))
def reverseProxy(self, packet, buffer_id): log.debug('Installing flow for reverse proxy on switch %d' % self.dpid) # From interior to exterior. n = packet.find("ipv4") msg = of.ofp_flow_mod() msg.match.dl_type = pkt.ethernet.IP_TYPE msg.match.nw_dst = n.dstip msg.buffer_id = buffer_id msg.actions.append(of.ofp_action_nw_addr.set_src(self.vhost.ip)) msg.actions.append(of.ofp_action_dl_addr.set_src(self.vhost.mac)) msg.actions.append(of.ofp_action_output(port=self.publicPort)) self.connection.send(msg)
def forwardProxy(self, packet, buffer_id): log.debug('Installing flow for forward proxy on switch %d' % self.dpid) # From exterior to interior. n = packet.find("ipv4") msg = of.ofp_flow_mod() msg.match.dl_type = pkt.ethernet.IP_TYPE; msg.match.nw_src = n.srcip msg.buffer_id = buffer_id msg.actions.append(of.ofp_action_nw_addr.set_dst(IPAddr('10.0.0.102'))) msg.actions.append(of.ofp_action_dl_addr.set_dst(self.hosts[IPAddr('10.0.0.102')].mac)) msg.actions.append(of.ofp_action_output(port = self.privatePort)) self.connection.send(msg)
def flood(): """ Hace un flood de los paquetes UNICAMENTE por los puertos habilitados por SPT """ msg = of.ofp_packet_out() # se crea el mensaje de flood time_diff = time.time() - self.connection.connect_time flood_ok = time_diff >= _flood_delay if flood_ok: # Realizar flood solo despues de que venza el tiempo de prevencion de flood log.debug("SWITCH_%i: FLOOD %s -> %s", self.switch_id, src_mac, dst_mac) # Hacemos flood por todos los puertos excepto los bloqueados por el SPT msg.actions.append(of.ofp_action_output(port=of.OFPP_FLOOD)) else: log.debug("ESPERANDO FLOOD DE SWITCH %s , RESTAN %d SEGUNDOS" % (self.switch_id, int(_flood_delay - time_diff))) msg.data = packet_in msg.in_port = in_port self.connection.send(msg)
def forwardProxy(self, packet, buffer_id): log.debug('Installing flow for forward proxy on switch %d' % self.dpid) # From exterior to interior. n = packet.find("ipv4") msg = of.ofp_flow_mod() msg.match.dl_type = pkt.ethernet.IP_TYPE msg.match.nw_src = n.srcip msg.buffer_id = buffer_id msg.actions.append(of.ofp_action_nw_addr.set_dst(IPAddr('10.0.0.102'))) msg.actions.append( of.ofp_action_dl_addr.set_dst( self.hosts[IPAddr('10.0.0.102')].mac)) msg.actions.append(of.ofp_action_output(port=self.privatePort)) self.connection.send(msg)
def _handle_ConnectionUp(self, event): if event.dpid == self.DPID_L2_SWITCH_CLIENTS or \ event.dpid == self.DPID_L2_SWITCH_SERVERS: # We make the switch which is connected by the clients (servers) # a learning switch so that the clients (servers) can communicate # with each other. # The switch is named 's1' ('s3') in Mininet. LearningSwitch(event.connection, False) log.debug('Learning switch %d' % event.dpid) elif event.dpid == self.DPID_BALANCER: # We make the switch which is connected by the servers a load-balancer. # The switch is named 's2' in Mininet. if self.balancer is None: self.balancer = LoadBalancer(1, 2) self.balancer.connect(event.connection) log.debug('Balancer switch %d' % event.dpid) else: log.error('Unrecognized switch (dpid = %d)' % event.dpid)
def learning_microflow_controller(self, dpid, packet, packet_in): connection = self.dpid_dict[dpid]["connection"] self.dpid_dict[dpid]["mac_to_port"][packet.src] = packet_in.in_port mac_to_port = self.dpid_dict[dpid]["mac_to_port"] # if dst is multicast flood it and return from the method if packet.dst.is_multicast: log.debug("Switch-{}: Type: {} . host {} --> FLOOD --> host {}". format( str(dpid), pkt.ETHERNET.ethernet.getNameForType(packet.type),str(packet.src), str(packet.dst) )) self.flood( connection, packet, packet_in) self.install_rule( connection, 1, packet_in.in_port, packet.src, EthAddr("ff:ff:ff:ff:ff:ff"), of.OFPP_FLOOD, 200, packet.type) elif packet.dst in mac_to_port: out_port = mac_to_port[packet.dst] log.debug("Switch-{}: Type: {} . host {} --> port {} --> host {}". format( dpid, pkt.ETHERNET.ethernet.getNameForType(packet.type), str(packet.src), str(out_port), str(packet.dst))) self.resend_packet( connection, packet_in, out_port) # additionally, install a rule per flow (src, src-port, dst, dst-port) self.install_rule( connection, 1, packet_in.in_port, packet.src, packet.dst, out_port, GLOBAL_TIMEOUT, packet.type) else: log.debug("Switch-{}: Type: {} . host {} --> FLOOD --> host {}". format( dpid, pkt.ETHERNET.ethernet.getNameForType(packet.type),str(packet.src), str(packet.dst) )) # Destination mac of packet not in our dictionary # flood the packet. self.flood( connection, packet, packet_in) return
def learning_controller(self, dpid, packet, packet_in): connection = self.dpid_dict[dpid]["connection"] self.dpid_dict[dpid]["mac_to_port"][packet.src] = packet_in.in_port mac_to_port = self.dpid_dict[dpid]["mac_to_port"] # if dst is multicast flood it and return from the method if packet.dst.is_multicast: log.debug("Switch-{}: Type: {} . host {} --> FLOOD --> host {}". format( str(dpid), pkt.ETHERNET.ethernet.getNameForType(packet.type),str(packet.src), str(packet.dst) )) self.flood(connection, packet, packet_in) elif packet.dst in mac_to_port: out_port = mac_to_port[packet.dst] log.debug("Switch-{}: Type: {} . host {} --> port {} --> host {}". format( str(dpid), pkt.ETHERNET.ethernet.getNameForType(packet.type), str(packet.src), str(out_port), str(packet.dst))) # send packet to specific port self.resend_packet(connection, packet_in, out_port) else: log.debug("Switch-{}: Type: {} . host {} --> FLOOD --> host {}". format( str(dpid), pkt.ETHERNET.ethernet.getNameForType(packet.type),str(packet.src), str(packet.dst) )) self.flood_packet(connection, packet, packet_in) return
def handle_flow_removed(event): """ Listener que maneja eliminaciones de flujos en switches. Escucha eventos tipo FlowRemoved """ switch_id = event.connection.dpid match = event.ofp.match packet_count = event.ofp.packet_count if is_udp(match): dst_ip = match.get_nw_dst( ) # Tupla IP , bits_mascara. Ejemplo: (IPAddr('10.0.0.2'), 32) log.info( 'SWITCH_%s: FLUJO REMOVIDO DE TIPO UDP CON DESTINO IP %s . packet_count: %s', switch_id, str(dst_ip[0]), packet_count) if packet_count > UDP_FIREWALL_THRESHOLD: # Si la cantidad de paquetes UDP supera el THRESHOLD establecido -> instalo un blackhole firewall blackhole_udp_packets(switch_id, FIREWALL_DURATION, dst_ip) else: # Si la cantidad de paquetes UDP para un destino baja luego de un tiempo, entonces se lo quita del blacklist de destinos bloqueados str_dst_ip = str(dst_ip[0]) switch = switches[switch_id] switch.remove_firewall_ip(str_dst_ip) elif is_icmp(match): log.debug('SWITCH_%s: FLUJO REMOVIDO DE TIPO ICMP . packet_count: %s', switch_id, packet_count) elif is_tcp(match): dst_ip = match.get_nw_dst( ) # Tupla IP , bits_mascara. Ejemplo: (IPAddr('10.0.0.2'), 32) log.debug( 'SWITCH_%s: FLUJO REMOVIDO DE TIPO TCP CON DESTINO IP %s . packet_count: %s', switch_id, dst_ip, packet_count) else: log.debug('SWITCH_%s: FLUJO REMOVIDO packet_count: %s', switch_id, packet_count)
def _handle_LinkEvent(self, event): link = event.link dpid_source = link.dpid1 dpid_dest = link.dpid2 port_source = link.port1 port_dest = link.port2 if event.added: log.info( "Link added for switches S-{}:port-{} --> S-{}:port-{}".format( dpid_source, port_source, dpid_dest, port_dest)) self.dpid_dict[dpid_source]["links"].append(link) # Update our graph with new nodes/edges self.graph.add_edge(dpid_source, dpid_dest, ports={ dpid_source: port_source, dpid_dest: port_dest }) log.debug("Graph Edges") log.debug(self.graph.edges()) else: log.debug( "Link removed for switches S-{}:port-{} --> S-{}:port-{}". format(dpid_source, port_source, dpid_dest, port_dest))
def setup_logging(test_mode=False, log_file=None, log_folder=None, **kwargs): """ Launch and set parameters for logging. :param test_mode: use test mode logging (default: False) :type test_mode: bool :param log_file: log file path :type log_file: str :return: None """ global LOG_FOLDER if log_folder is not None: LOG_FOLDER = log_folder if not os.path.exists(LOG_FOLDER): os.makedirs(LOG_FOLDER) if log_file is None: log_file = os.path.join(LOG_FOLDER, "escape.log") # Enable logging in specific logging level level.launch(**kwargs) # Launch colorful logging color.launch() if test_mode: # Define logger for test mode pox.log.launch(format=TEST_LOGGER_FORMAT) log.debug("Setup Logger - formatter: %s, level: %s" % (pox.log.launch.__module__, logging.getLevelName(log.getEffectiveLevel()))) # Set default log_file to log in file in test mode else: # Define default logger pox.log.launch(format=DEFAULT_LOGGER_FORMAT) log.debug("Setup logger - formatter: %s, level: %s" % (setup_logging.__module__, logging.getLevelName(log.getEffectiveLevel()))) if log_file: # Define additional logger for logging to file pox.log.launch(format=FILE_LOGGER_FORMAT, file=log_file + ',w') log.debug("Setup Logger - formatter: %s, level: %s, file: %s" % (pox.log.launch.__module__, logging.getLevelName(log.getEffectiveLevel()), log_file))
def handle_unknown(): """ Maneja un paquete de tipo desconocido """ log.debug('PAQUETE DESCONOCIDO DETECTADO DE TIPO %s::%s', eth_getNameForType, pkt_type_name) # TODO : VER QUE ES MEJOR , SI MANEJAR LOS PAQUETES DESCONOCIDOS O SI DROPEARLOS drop(30)
def act_like_switch (self, packet, packet_in, dpid): """ Implement switch-like behavior. 1. Implementing a "simple" learning switch. """ p = packet if p.find('ipv4'): log.debug(" => SWITCH[{2}]: Rcv {0} , packet_in: {1}".format(packet, packet_in, dpid)) log.debug(" ==> Pkt Data:{0}".format(packet.dump())) while p: if isinstance(p, basestring): log.debug( "[%s bytes]={%s}" % (len(p),p) ) break log.debug("[%s]=%s" % (p.__class__.__name__, p)) p = p.next # Learn the port for the source MAC # log.debug(" ==> MAC: {0}->{1}".format(packet.src, packet.dst)) self.mac_to_port[str(packet.src)] = packet_in.in_port is_halted = False # log.debug(" ==> MAC2PORT: {0}".format(self.mac_to_port)) port_out = self.mac_to_port.get(str(packet.dst)) if port_out: is_authenticated = True if packet.find("ipv4"): # Authenticate a packet is_authenticated = self.authenticate(packet) log.debug("==> ipv4: packet auth={0}".format(is_authenticated)) if is_authenticated: log.debug("Installing flow...") msg = of.ofp_flow_mod() # Exact match msg.match = of.ofp_match.from_packet(packet_in) msg.idle_timeout = 60 msg.hard_timeout = 30 # for debugging purpose msg.buffer_id = packet_in.buffer_id msg.data = packet_in msg.actions.append(of.ofp_action_output(port=port_out)) # log.debug(" ==> Msg: {0}".format(msg)) # Send msg to swtich self.connection.send(msg) else: log.debug("==> Packet is not authenticated! Drop the packet") is_halted = True else: log.debug(" ==> Unknowd {0} -- Flood all".format(packet.dst)) # Flood the packet out everything but the input port self.resend_packet(packet_in, of.OFPP_ALL) log.debug("------------------------------") return is_halted
def act_like_switch(self, packet, packet_in, dpid): """ Implement switch-like behavior. 1. Implementing a "simple" learning switch. """ p = packet if p.find('ipv4'): log.debug(" => SWITCH[{2}]: Rcv {0} , packet_in: {1}".format( packet, packet_in, dpid)) log.debug(" ==> Pkt Data:{0}".format(packet.dump())) while p: if isinstance(p, basestring): log.debug("[%s bytes]={%s}" % (len(p), p)) break log.debug("[%s]=%s" % (p.__class__.__name__, p)) p = p.next # Learn the port for the source MAC # log.debug(" ==> MAC: {0}->{1}".format(packet.src, packet.dst)) self.mac_to_port[str(packet.src)] = packet_in.in_port is_halted = False # log.debug(" ==> MAC2PORT: {0}".format(self.mac_to_port)) port_out = self.mac_to_port.get(str(packet.dst)) if port_out: is_authenticated = True if packet.find("ipv4"): # Authenticate a packet is_authenticated = self.authenticate(packet) log.debug("==> ipv4: packet auth={0}".format(is_authenticated)) if is_authenticated: log.debug("Installing flow...") msg = of.ofp_flow_mod() # Exact match msg.match = of.ofp_match.from_packet(packet_in) msg.idle_timeout = 60 msg.hard_timeout = 30 # for debugging purpose msg.buffer_id = packet_in.buffer_id msg.data = packet_in msg.actions.append(of.ofp_action_output(port=port_out)) # log.debug(" ==> Msg: {0}".format(msg)) # Send msg to swtich self.connection.send(msg) else: log.debug("==> Packet is not authenticated! Drop the packet") is_halted = True else: log.debug(" ==> Unknowd {0} -- Flood all".format(packet.dst)) # Flood the packet out everything but the input port self.resend_packet(packet_in, of.OFPP_ALL) log.debug("------------------------------") return is_halted
def policy_controller(self, dpid, packet, packet_in): connection = self.dpid_dict[dpid]["connection"] mac_to_port = self.dpid_dict[dpid]["mac_to_port"] # if protocol is IP then implement policies # for all other traffic etc. ARP implement l2 switch if packet.type == packet.IP_TYPE: ip_packet = packet.payload src_ip = ip_packet.srcip dst_ip = ip_packet.dstip hosts = self.dpid_dict[dpid]["hosts"] links = self.dpid_dict[dpid]["links"] log.debug("Received IP packet from {} to {}, implementing policy!".format(src_ip, dst_ip)) # implementing H1 H4 s3, H2 H4 s3 if (H1 == src_ip.toStr() or H2 == src_ip.toStr()) and H4 == dst_ip: log.debug("Implement policy from {} to {}. Should pass from upper switch".format(src_ip, dst_ip)) # Can only infer switch identity only for hosts connected to us :( # if we are S1 install rule to send to S3 (switch with no hosts) if H1 in hosts or H2 in hosts: log.debug("In S1!") # should pass from S3, find link which dpid has no hosts :) for link in links: next_dpid = link.dpid2 own_port = link.port1 # check if link dictionary is empty for this dpid, if yes it is the right switch (S3) if not self.dpid_dict[next_dpid]["hosts"]: break log.debug("Installing flow at S1 for S3!") self.install_ip_policy( connection, dpid, 1, packet_in.in_port, src_ip, dst_ip, own_port, GLOBAL_TIMEOUT) self.resend_packet( connection, packet_in, own_port) # if we are S2 install rule to send directly to H4 elif H3 in hosts or H4 in hosts: log.debug("In S2!") # retrieve host data from hosts dictionary dpid, own_port = self.hosts[dst_ip] log.debug("Installing rule and forwarding data to host!") self.install_ip_policy( connection, dpid, 1, packet_in.in_port, src_ip, dst_ip, own_port, GLOBAL_TIMEOUT) self.resend_packet( connection, packet_in, own_port) # if we are S3 install rule to send to S2 (switch with hosts H3,H4) else: log.debug("In S3!") for link in links: next_dpid = link.dpid2 own_port = link.port1 # check if link dictionary is empty for this dpid, if yes it is the right switch (S3) if H3 in self.dpid_dict[next_dpid]["hosts"] or H4 in self.dpid_dict[next_dpid]["hosts"]: break log.debug("Installing flow for host!") self.install_ip_policy( connection, dpid, 1, packet_in.in_port, src_ip, dst_ip, own_port, GLOBAL_TIMEOUT) self.resend_packet( connection, packet_in, own_port) else: if H1 in hosts or H2 in hosts: log.debug("In S1") elif H3 in hosts or H4 in hosts: log.debug("In S2") else: log.debug("In S3") # if dst_ip is connected to switch forward to host and install rule if dst_ip.toStr() in hosts: dpid, own_port = self.hosts[dst_ip] log.debug("Installing rule and forwarding data to host!") self.install_ip_policy( connection, dpid, 1, packet_in.in_port, src_ip, dst_ip, own_port, GLOBAL_TIMEOUT) self.resend_packet( connection, packet_in, own_port) else: # find dpid which host is connected to, from links to us for link in links: next_dpid = link.dpid2 own_port = link.port1 # check in dpid connected to us if dst_ip is conencted to it, if yes install rule and forward if dst_ip.toStr() in self.dpid_dict[next_dpid]["hosts"]: break log.debug("Installing flow for host!") self.install_ip_policy( connection, dpid, 1, packet_in.in_port, src_ip, dst_ip, own_port, GLOBAL_TIMEOUT) self.resend_packet( connection, packet_in, own_port) else: self.learning_microflow_controller(dpid, packet, packet_in)
def act_like_switch (self, packet, packet_in, dpid): """ Implement switch-like behavior. """ # log.debug(" => SWITCH[{2}]: Rcv {0} , packet_in: {1}".format(packet, packet_in, dplog.debug(" => SWITCH[{2}]: Rcv {0} , packet_in: {1}".format(packet, packet_in, dpid)) log.debug(" => SWITCH[{2}]: Rcv {0}".format(packet, packet_in, dpid)) # p = packet # if p.find('ipv4'): # while p: # if isinstance(p, basestring): # log.debug( "[%s bytes]={%s}" % (len(p),p) ) # break # log.debug("[%s]=%s" % (p.__class__.__name__, p)) # p = p.next # packet_in.data += "Lock is here" # # log.debug(" ==> Pkt Data:{0}".format(packet.dump())) # log.debug(" ++> Raw: {0}".format(packet.raw)) # log.debug(" ==> PacketIn Date:{0}".format(packet_in.data[53:])) # Learn the port for the source MAC # log.debug(" ==> MAC: {0}->{1}".format(packet.src, packet.dst)) self.mac_to_port[str(packet.src)] = packet_in.in_port # log.debug(" ==> MAC2PORT: {0}".format(self.mac_to_port)) port_out = self.mac_to_port.get(str(packet.dst)) if port_out: # Send packet out the associated port # self.resend_packet(packet_in, self.mac_to_port.get(str(packet.dst))) msg = None # If the packcet is ipv4, modify a raw packet to append encrypted data. # Otherwise, install a rule. # It is assumed that raw packet given from the switch is only first 128 # bytes as a default POX setting. if packet.find("ipv4"): msg = of.ofp_packet_out(in_port=of.OFPP_NONE) mod_raw = packet.raw mod_raw += "auth=true" msg.data = mod_raw msg.actions.append(of.ofp_action_output(port=port_out)) log.debug("==> IPv4: Added encrypted data to the packet.") else: msg = of.ofp_flow_mod() ## Set fields to match received packet # Exact match msg.match = of.ofp_match.from_packet(packet_in) msg.idle_timeout = 60 msg.hard_timeout = 30 # for debugging purpose msg.buffer_id = packet_in.buffer_id msg.data = packet_in msg.actions.append(of.ofp_action_output(port=port_out)) log.debug("==> Not IPv4: Send OF Mod") # Send msg to swtich self.connection.send(msg) else: log.debug(" ==> Unknowd {0} -- Flood all".format(packet.dst)) # Flood the packet out everything but the input port self.resend_packet(packet_in, of.OFPP_ALL) log.debug("------------------------------")
def start_switch (event): log.debug("Controlling %s" % (event.connection,)) Tutorial(event.connection)
def _handle_PacketIn(self, event): packet_in = event.ofp # objeto EVENTO de tipo PACKET_IN. packet = event.parsed src_mac = packet.src # MAC origen del paquete dst_mac = packet.dst # MAC destino del paquete in_port = packet_in.in_port # puerto de switch por donde ingreso el paquete # guardo la asociacion mac_origen -> puerto_entrada log.debug('SWITCH_%s: Asociando MAC %s a puerto de entrada %s', self.switch_id, src_mac, in_port) self.mac_to_port[src_mac] = in_port eth_getNameForType = pkt.ETHERNET.ethernet.getNameForType(packet.type) # Parseo tempranamente los tipos de datos conocidos pkt_is_ipv6 = eth_getNameForType == 'IPV6' icmp_pkt = packet.find('icmp') tcp_pkt = packet.find('tcp') udp_pkt = packet.find('udp') pkt_is_arp = packet.type == packet.ARP_TYPE ip_pkt = packet.find('ipv4') # Obtengo el nombre 'imprimible' del paquete pkt_type_name = eth_getNameForType if icmp_pkt: pkt_type_name = 'ICMP' if tcp_pkt: pkt_type_name = 'TCP' if udp_pkt: pkt_type_name = 'UDP' if pkt_is_arp: pkt_type_name = 'ARP' def build_flow_key(): """ Crea la clave de un flujo a partir de campos disponibles del paquete procesado """ flow_key = pkt_type_name + '#' if ip_pkt: flow_key += str(ip_pkt.srcip) + 'to' + str(ip_pkt.dstip) if tcp_pkt: flow_key += ':' + str(tcp_pkt.dstport) + "-" + str(tcp_pkt.ACK) # TODO ... PARA UDP SE DEBEN CONSTRUIR FLUJOS DISTINTOS PARA LA IDA Y LA VUELTA... CONSIDERAR USAR LA MAC if udp_pkt: flow_key += ':' + str(udp_pkt.dstport) if icmp_pkt: flow_key += '-' + str(icmp_pkt.type) log.info("SWITCH_%s: flow_key = %s", self.switch_id, flow_key) return flow_key def install_flow(out_port, duration=FLOW_INSTALL_DURATION): """ Instala un flujo en el switch del tipo MAC_ORIGEN@PUERTO_ENTRADA -> MAC_DESTINO@PUERTO_SALIDA """ if not pkt_is_arp: log.info( "SWITCH_%s: Instale un flujo de %s@PUERTO_%i hacia %s@PUERTO_%i de tipo %s" % (self.switch_id, src_mac, in_port, dst_mac, out_port, pkt_type_name)) msg = of.ofp_flow_mod() msg.match = of.ofp_match.from_packet(packet, in_port) msg.idle_timeout = duration msg.hard_timeout = duration msg.actions.append(of.ofp_action_output(port=out_port)) msg.data = packet_in # OFPFF_SEND_FLOW_REM indica al switch que debe notificar al controlador cuando un flujo haya sido dado de baja. Ver funcion handle_flow_removed # OFPFF_CHECK_OVERLAP pide al switch que verifique overlap de reglas de flujo msg.flags = of.OFPFF_SEND_FLOW_REM + of.OFPFF_CHECK_OVERLAP self.connection.send(msg) def drop(duration=None): """ Dropea el paquete y opcionalmente instala un flujo en el switch para que siga dropeando paquetes de este tipo. NOTA: PODRIA USARSE PARA EL FIREWALL """ if duration is not None: if not isinstance(duration, tuple): duration = (duration, duration) msg = of.ofp_flow_mod() msg.match = of.ofp_match.from_packet(packet) msg.idle_timeout = duration[0] msg.hard_timeout = duration[1] msg.buffer_id = packet_in.buffer_id msg.actions.append(of.ofp_action_output( port=of.OFPP_NONE)) # Enviar el paquete a la NADA self.connection.send(msg) elif packet_in.buffer_id is not None: msg = of.ofp_packet_out() msg.buffer_id = packet_in.buffer_id msg.in_port = in_port msg.actions.append(of.ofp_action_output( port=of.OFPP_NONE)) # Enviar el paquete a la NADA self.connection.send(msg) def flood(): """ Hace un flood de los paquetes UNICAMENTE por los puertos habilitados por SPT """ msg = of.ofp_packet_out() # se crea el mensaje de flood time_diff = time.time() - self.connection.connect_time flood_ok = time_diff >= _flood_delay if flood_ok: # Realizar flood solo despues de que venza el tiempo de prevencion de flood log.debug("SWITCH_%i: FLOOD %s -> %s", self.switch_id, src_mac, dst_mac) # Hacemos flood por todos los puertos excepto los bloqueados por el SPT msg.actions.append(of.ofp_action_output(port=of.OFPP_FLOOD)) else: log.debug("ESPERANDO FLOOD DE SWITCH %s , RESTAN %d SEGUNDOS" % (self.switch_id, int(_flood_delay - time_diff))) msg.data = packet_in msg.in_port = in_port self.connection.send(msg) def handle_all(): """ Maneja los paquetes de forma generica """ # TODO : MODIFICAR ESTE COMPORTAMIENTO PARA SOPORTAR ECMP # NOTA : dado que instalar un flujo demora tiempo, handle_all se esta llamando multiples veces... # si el puerto de salida se encuentra en la tabla de MACs entonces instalo un flujo en el switch if dst_mac in self.mac_to_port: out_port = self.mac_to_port[dst_mac] install_flow(out_port) else: flood() def handle_dhcp(): """ Maneja paquetes DHCP ... Pensar si acaso deberian dropearse... """ dstip = ip_pkt.dstip log.debug('MANEJANDO PAQUETE DHCP HACIA IP %s' % str(dstip)) handle_all() def install_path_flow(out_port, duration=FLOW_INSTALL_DURATION): """ Instala un flujo selectivo a partir de un path """ log.info( "SWITCH_%s: Instalando flujo a partir de path %s@PUERTO_%i -> %s@PUERTO_%i de tipo %s" % (self.switch_id, src_mac, in_port, dst_mac, out_port, pkt_type_name)) msg = of.ofp_flow_mod() if udp_pkt: udp_dst_port = udp_pkt.dstport msg.match = of.ofp_match(dl_type=IP_dl_type, nw_proto=UDP_nw_proto, tp_dst=udp_dst_port) elif tcp_pkt: tcp_dst_port = tcp_pkt.dstport msg.match = of.ofp_match(dl_type=IP_dl_type, nw_proto=TCP_nw_proto, tp_dst=tcp_dst_port) else: msg.match = of.ofp_match(dl_type=IP_dl_type, nw_proto=ICMP_nw_proto) str_dst_ip = ip_pkt.dstip msg.match.set_nw_dst(str_dst_ip, 32) msg.idle_timeout = duration msg.hard_timeout = duration msg.actions.append(of.ofp_action_output(port=out_port)) msg.data = packet_in # OFPFF_SEND_FLOW_REM indica al switch que debe notificar al controlador cuando un flujo haya sido dado de baja. Ver funcion handle_flow_removed # OFPFF_CHECK_OVERLAP pide al switch que verifique overlap de reglas de flujo msg.flags = of.OFPFF_SEND_FLOW_REM + of.OFPFF_CHECK_OVERLAP self.connection.send(msg) def handle_ip_complex(): """ Maneja paquetes tipo ip """ dst_mac_str = str(dst_mac) # obtengo el string de mac destino log.info("SWITCH_%s: Mac destino es %s", self.switch_id, dst_mac_str) # si el host destino es desconocido, entonces me falta conocer a mas hosts y manejo el paquete como un switch bobo if dst_mac_str not in hosts: return handle_all() host_switch_port = get_host_switch_port(dst_mac_str, self.switch_id) # si la mac destino es de un host y este switch esta directamente conectado al mismo, entonces instalo un flujo inmediatamente if host_switch_port is not None: log.info( 'SWITCH_%s: La Mac destino %s corresponde a un host conectado a MI puerto %d!', self.switch_id, dst_mac_str, host_switch_port) return install_flow(host_switch_port) # TODOOOOOOOOOO : VERIFICAR SI ACASO SE DEBE USAR install_flow EN VEZ DE install_path_flow # verifico si ya existe un path asignado a este flujo flow_key = build_flow_key() if flow_key in current_paths: path = current_paths[flow_key] log.info('SWITCH_%s: el path %s esta asignado al flow_key %s', self.switch_id, str(path), flow_key) # instalo un flujo para forwardear el paquete switch_switch_link = get_switch_switch_link( self.switch_id, path) if switch_switch_link is not None: out_port = switch_switch_link.port1 log.info( "SWITCH_%s: El paquete debe salir por mi puerto %d", self.switch_id, out_port) return install_flow(out_port) #return install_path_flow(out_port) else: log.warn( 'SWITCH_%s: encontre un path... pero yo no tengo enlace ALGO ESTA MAL', self.switch_id) return handle_all() # si llegue a este punto es porque no hay un path asignado al camino indicado... probablemente este switch es de borde # debo solicitar un camino libre y asignarlo host = get_host_by_mac(dst_mac) if host is not None: end_switch_id = host[ 'switch_id'] # obtengo el id del switch al cual esta conectado el host destino # busco o bien un camino libre o cualquier camino en caso de no existir ninguno libre log.info("SWITCH_%s: Busco un path hacia switch %s", self.switch_id, end_switch_id) path = find_non_taken_path(self.switch_id, end_switch_id) if path is None: path = find_any_path(self.switch_id, end_switch_id) path_str = str(path) log.info( "SWITCH_%s: Voy a usar el path %s y se lo asigno al flujo %s", self.switch_id, path_str, flow_key) # guardo la asociacion entre la clave del flujo y el path encontrado current_paths[flow_key] = path # marco al path encontrado como TOMADO taken_paths.add(path_str) # incremento la cantidad de veces que el camino esta siendo usado current_paths_load[path_str] += 1 # instalo un flujo para forwardear el paquete switch_switch_link = get_switch_switch_link( self.switch_id, path) if switch_switch_link is not None: out_port = switch_switch_link.port1 install_flow(out_port) #return install_path_flow(out_port) def remove_taken_path(): log.info("SWITCH_%s: ELIMINANDO PATH %s DE FLUJO %s", self.switch_id, path_str, flow_key) if path_str in taken_paths: taken_paths.remove(path_str) if flow_key in current_paths: current_paths.pop(flow_key) current_paths_load[path_str] -= 1 # despues de un tiempo elimino el path de flujo instalado Timer(FLOW_INSTALL_DURATION, remove_taken_path) return True else: log.warn( 'SWITCH_%s: encontre un path... pero yo no tengo enlace ALGO ESTA MAL', self.switch_id) return handle_all() # condicion fallback ... manejo el paquete como puedo handle_all() def handle_ip(): if HANDLE_IP_COMPLEX: return handle_ip_complex() else: return handle_all() def handle_udp(): """ Maneja paquetes UDP. Si detecta que un destino esta bloqueado por un firewall, descarta el paquete """ dstip = ip_pkt.dstip str_dst_ip = str(dstip) if str_dst_ip in self.firewall_ips: log.info( 'SWITCH_%s PAQUETES CON DESTINO %s SIGUEN BLOQUEADOS... REALIZANDO DROP', self.switch_id, str_dst_ip) drop() return dstport = udp_pkt.dstport if dstport == DHCP_PORT: return handle_dhcp() handle_ip() def handle_unknown(): """ Maneja un paquete de tipo desconocido """ log.debug('PAQUETE DESCONOCIDO DETECTADO DE TIPO %s::%s', eth_getNameForType, pkt_type_name) # TODO : VER QUE ES MEJOR , SI MANEJAR LOS PAQUETES DESCONOCIDOS O SI DROPEARLOS drop(30) #handle_all() # LOS PAQUETES DESCONOCIDOS SON DROPEADOS. POR AHORA IGNORAMOS LOS PAQUETES IPV6 # DADO QUE ESTAMOS USANDO host_tracker, DEBEMOS MANEJAR LOS PAQUETES ARP (NO DROPEAR) unknown_pkt = pkt_is_ipv6 or (icmp_pkt is None and tcp_pkt is None and udp_pkt is None and not pkt_is_arp) if unknown_pkt: return handle_unknown() # los paquetes ARP los despacho inmediatamente sin crear ni reservar flujos if pkt_is_arp: return handle_all() log.debug( 'SWITCH_%s@PORT_%d LLEGO PAQUETE TIPO %s::%s MAC_ORIGEN: %s MAC_DESTINO: %s' % (self.switch_id, in_port, eth_getNameForType, pkt_type_name, src_mac, dst_mac)) # por alguna razon bizarra... esta linea rompe # si la mac es ff:ff:ff:ff:ff:ff entonces hago un flood #if dst_mac.is_multicast: flood() # si el mac origen es igual al mac destino entonces dropeo if src_mac == dst_mac: log.info("SWITCH_%s: MAC ORIGEN ES IGUAL A MAC DESTINO!", self.switch_id) drop() if udp_pkt: return handle_udp() if ip_pkt: return handle_ip() handle_all()
def handle_dhcp(): """ Maneja paquetes DHCP ... Pensar si acaso deberian dropearse... """ dstip = ip_pkt.dstip log.debug('MANEJANDO PAQUETE DHCP HACIA IP %s' % str(dstip)) handle_all()
def policy_controller(self, dpid, packet, packet_in): connection = self.dpid_dict[dpid]["connection"] mac_to_port = self.dpid_dict[dpid]["mac_to_port"] # if protocol is IP then implement policies # for all other traffic etc. ARP implement l2 switch if packet.type == packet.IP_TYPE: ip_packet = packet.payload src_ip = ip_packet.srcip dst_ip = ip_packet.dstip hosts = self.dpid_dict[dpid]["hosts"] links = self.dpid_dict[dpid]["links"] # This will be the path of switches that will be chosen chosen_path = [] src_switch, src_port = self.hosts[src_ip] dst_switch, dst_port = self.hosts[dst_ip] # Check to see if we have to apply policy if (src_ip.toStr(), dst_ip.toStr()) in self.policies: policy_dpid = self.policies[src_ip.toStr(), dst_ip.toStr()] log.debug( "Have to implement policy from {} to {} through switch {}". format(src_ip.toStr(), dst_ip.toStr(), policy_dpid)) # Get all simple paths between the switches connected to the hosts simple_paths = nx.all_simple_paths(self.graph, src_switch, dst_switch) for simple_path in simple_paths: if policy_dpid in simple_path: chosen_path = simple_path break if chosen_path == []: log.info( "Could not satisfy policy. Choosing shortest path!") chosen_path = nx.shortest_path(self.graph, src_switch, dst_switch) else: log.debug("Have to use shortest path!") chosen_path = nx.shortest_path(self.graph, src_switch, dst_switch) log.debug("Will use path:") log.debug(chosen_path) chosen_edges = [(chosen_path[i], chosen_path[i + 1]) for i in xrange(0, len(chosen_path) - 1)] edge_path = [] switch_order = chosen_path for edge in chosen_edges: port1 = self.graph[edge[0]][edge[1]]['ports'][edge[0]] port2 = self.graph[edge[0]][edge[1]]['ports'][edge[1]] edge_path.append((port1, port2)) edge_path.insert(0, (1, src_port)) edge_path.append((dst_port, 1)) # Install IP rules for all the switches in the path for i in xrange(0, len(edge_path) - 1): switch_dpid = switch_order[i] switch_connection = self.dpid_dict[switch_dpid]["connection"] src_port, dst_port = (edge_path[i][1], edge_path[i + 1][0]) log.debug( "Installing policy on switch {}. {} -> {} with ports {} -> {}" .format(switch_dpid, src_ip, dst_ip, src_port, dst_port)) self.install_ip_policy(switch_connection, switch_dpid, 1000, src_port, src_ip, dst_ip, dst_port, 1000) self.resend_packet(connection, packet_in, edge_path[1][0]) else: # Packet is not IP self.learning_microflow_controller(dpid, packet, packet_in)