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 remove_connection(self, conn):

    dpid_removed = conn.dpid
    log.info("ConnectionDown message for Switch with DPID {}".format(dpid_removed))
 
    self.dpid_dict.pop( dpid_removed , None)
    self.hosts = {k: v for k, v in self.hosts.iteritems() if v[0] != dpid_removed}
    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 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_flow_stats(event):
    """ Listener que muestra datos estadisticos de un switch. Captura eventos FlowStatsReceived """
    switch_id = event.connection.dpid
    all_stats = {
        "tcp": {
            "packet_count": 0,
            "byte_count": 0
        },
        "udp": {
            "packet_count": 0,
            "byte_count": 0
        },
        "icmp": {
            "packet_count": 0,
            "byte_count": 0
        }
    }
    for f in event.stats:
        if is_udp(f.match):
            all_stats["udp"]["packet_count"] += f.packet_count
            all_stats["udp"]["byte_count"] += f.byte_count

        if is_tcp(f.match):
            all_stats["tcp"]["packet_count"] += f.packet_count
            all_stats["tcp"]["byte_count"] += f.byte_count

        if is_icmp(f.match):
            all_stats["icmp"]["packet_count"] += f.packet_count
            all_stats["icmp"]["byte_count"] += f.byte_count
    log.info("SWITCH_%s stats: %s", switch_id, all_stats)
 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
Exemple #7
0
def launch ():
  import pox.log.color
  pox.log.color.launch()
  import pox.log
  pox.log.launch(format="[@@@bold@@@level%(name)-22s@@@reset] " +
                        "@@@bold%(message)s@@@normal")

  core.openflow.addListenerByName("PacketIn", _handle_PacketIn)

  log.info("Pong component running.")
 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 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 add_connection(self, conn):
    
    dpid_joined = conn.dpid
	
    log.info("ConnectionUp message for Switch with DPID {}".format(dpid_joined))
    
    conn.addListeners(self)

    self.dpid_dict.update({dpid_joined : {}})
    self.dpid_dict[dpid_joined].update({"connection" : conn})
    self.dpid_dict[dpid_joined].update({"mac_to_port" : {}})
    self.dpid_dict[dpid_joined].update({"links" : []})
    self.dpid_dict[dpid_joined].update({"hosts" : []})
 def __init__(self, connection, dpid):
     # Guarda la conexion con el switch
     self.connection = connection
     # guardo el id del switch y un sinonimo del mismo
     self.switch_id = dpid
     self.dpid = dpid
     log.info("SWITCH %s CONECTADO" % self.switch_id)
     # TABLA Mac -> puerto_salida_switch
     self.mac_to_port = {}
     # Agrego listeners de conexion (como PacketIn)
     self.connection.addListeners(self)
     switches[dpid] = self
     # Cada switch tiene su propia BLACKLIST de firewalled-ips
     self.firewall_ips = set()
    def __init__(self):
        log.info(
            "Initializing acn controller. Registering listeners for discovery module!"
        )

        # Add listeners for LinkEvent published by the
        # openflow_discovery module.
        core.openflow_discovery.addListeners(self)

        # Dictionary that keeps connection/links/hosts/mac_to_port
        # and possibly name of the switch. This dictionary holds
        # whatever state we need to store per switch
        # switches are recognized with their datapath-ids
        # dpid { hosts : {},
        #        links : {},
        #        connection : {},
        #        mac_to_port: {}
        #      }
        self.dpid_dict = {}

        # Hosts observed in the networks (IPs), for each
        # host only keep the dpid which connects on and the
        # port of the dpit that connects on.
        self.hosts = {}

        # contains the implemented policies for the controller
        # Policies are of the form:
        # (src_ip, dest_ip, dpid)
        # where: src_ip: The source host IP
        #        dst_ip: The destination host IP
        #        dpid  : The dpid of the switch the traffic has to pass through
        #
        # The dictionary maps a hash (src_ip, dst_ip) to the dpid of the switch
        self.policies = {}

        # Network graph
        self.graph = nx.Graph()

        # Parses policies from files and returns a dictionary as described above
        # Sanity is assumed regarding the contents of the file (no checks).
        with open('policies.txt', 'r') as f:
            for line in f:
                if line[0] == '#' or line.strip() == '':
                    continue
                src_ip, dst_ip, dpid = line.split()
                log.info("Parsed policy {} -> {} via {}".format(
                    src_ip, dst_ip, dpid))
                self.policies[(src_ip, dst_ip)] = int(dpid)
 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)
Exemple #14
0
def show_stats(event):
    """
  Create stats on h1 incoming/outoging traffic
  3. Extend this program to count all traffic going to or leaving host 1
  """
    normal_flows = 0
    normal_packets = 0
    normal_bytes = 0

    for flow in event.stats:
        # Check if ip addresses match the blocks
        normal_flows += 1
        normal_packets += flow.packet_count
        normal_bytes += flow.byte_count

    log.info(
        "  ==> {4} ALL TRAFFIC [dpid={0}]: {1} bytes / {2} packets / {3} flows"
        .format(dpidToStr(event.connection.dpid), normal_bytes, normal_packets,
                normal_flows, str(datetime.now())))
def handle_host_tracker_HostEvent(event):
    """ Listener de eventos tipo HOST NUEVO CONECTADO """

    host_mac = str(event.entry.macaddr)
    switch_id = event.entry.dpid
    switch_port = event.entry.port

    # Supuesto codigo para obtener la ip de un host inmediatamente... no funciona
    #ipaddr_keys = event.entry.ipAddrs.keys()
    #ip = None
    #if len(ipaddr_keys) > 0 :
    #  ip = ipaddr_keys[0].toStr()

    if host_mac not in hosts:
        hosts[host_mac] = {"switch_id": switch_id, "switch_port": switch_port}
        if switch_id in switches:
            log.info('NUEVO HOST %s CON SWITCH_%s@PORT_%s', host_mac,
                     switch_id, switch_port)
        else:
            log.warn("Missing switch")
def show_stats(event):

  """
  Create stats on h1 incoming/outoging traffic
  3. Extend this program to count all traffic going to or leaving host 1
  """
  normal_flows    = 0
  normal_packets  = 0
  normal_bytes    = 0

  for flow in event.stats:
    # Check if ip addresses match the blocks
    normal_flows   += 1
    normal_packets += flow.packet_count
    normal_bytes   += flow.byte_count

  log.info("  ==> {4} ALL TRAFFIC [dpid={0}]: {1} bytes / {2} packets / {3} flows".format(
    dpidToStr(event.connection.dpid),
    normal_bytes, normal_packets, normal_flows,
    str(datetime.now())
    ))
def blackhole_udp_packets(switch_id, duration, udp_dst_ip, udp_dst_port=None):
    """ Instala un flujo de dopeo de paquetes UDP para un destino determinado """
    # NOTA: ESTA FUNCION INSTALA UN FIREWALL TIPO BLACKHOLE EN UN SOLO SWITCH... SE DEBE CONSIDERAR SI ACASO EL FIREWALL DEBE INSTALARSE EN TODOS LOS SWITCHES AL MISMO TIEMPO...
    str_dst_ip = str(udp_dst_ip[0])
    switch = switches[switch_id]
    switch.add_firewall_ip(str_dst_ip)
    log.info('SWITCH_%s: INSTALANDO FIREWALL DE PAQUETES CON DESTINO %s ',
             switch_id, str_dst_ip)
    msg = of.ofp_flow_mod()
    if USE_UDP_PORT_FOR_FIREWALL:
        msg.match = of.ofp_match(dl_type=IP_dl_type,
                                 nw_proto=UDP_nw_proto,
                                 tp_dst=udp_dst_port)
    else:
        msg.match = of.ofp_match(dl_type=IP_dl_type, nw_proto=UDP_nw_proto)
    msg.match.set_nw_dst(str_dst_ip, udp_dst_ip[1])
    msg.idle_timeout = duration
    msg.hard_timeout = duration
    msg.actions.append(
        of.ofp_action_output(port=of.OFPP_NONE))  # Enviar el paquete a la NADA
    msg.flags = of.OFPFF_SEND_FLOW_REM  # Generar evento FlowRemoved luego de que el flujo sea removido
    switches[switch_id].connection.send(msg)
  def __init__(self):
    log.info("Initializing acn controller. Registering listeners for discovery module!")

    # add listeners for LinkEvent published by the
    # openflow_discovery module.
    core.openflow_discovery.addListeners(self)

    # dictionary that keeps connection/links/hosts/mac_to_port
    # and possibly name of the switch. This dictionary holds
    # whatever state we need to store per switch
    # switches are recognized with their datapath-ids
    # dpid { hosts : {}, 
    #        links : {},
    #        connection : {},
    #        mac_to_port: {}
    #      }
    self.dpid_dict = {}

    # hosts observed in the networks (IPs), for each
    # host only keep the dpid which connects on and the 
    # port of the dpit that connects on.
    self.hosts = {}
def launch(flow_duration=10, udp_fwall_pkts=100, fwall_duration=10):
    pox.log.color.launch()
    pox.log.launch(format="[@@@bold@@@level%(name)-22s@@@reset] " +
                   "@@@bold%(message)s@@@normal")

    # Los parametros de configuracion pueden pasarse como --nombre_parametro=VALOR inmediatamente luego del nombre de ESTE MODULO.
    global FLOW_INSTALL_DURATION
    FLOW_INSTALL_DURATION = int(flow_duration)
    log.info("DURACION DE FLUJOS: %s SEGUNDOS", FLOW_INSTALL_DURATION)

    global UDP_FIREWALL_THRESHOLD
    UDP_FIREWALL_THRESHOLD = int(udp_fwall_pkts)
    log.info("CANTIDAD DE PAQUETES UDP LIMITE P/FIREWALL: %s PAQUETES",
             UDP_FIREWALL_THRESHOLD)

    # Duracion base del firewall. Se renueva cada FIREWALL_DURATION segundos (si es necesario).
    global FIREWALL_DURATION
    FIREWALL_DURATION = int(fwall_duration)
    log.info("DURACION DEL FIREWALL: %s SEGUNDOS", FIREWALL_DURATION)

    pox.openflow.discovery.launch()

    # no_flood: If True, we set ports down when a switch connects
    # hold_down: If True, don't allow turning off flood bits until a complete discovery cycle should have completed (mostly makes sense with _noflood_by_default).
    pox.openflow.spanning_tree.launch(no_flood=True, hold_down=True)

    # --arpAware=15 --arpSilent=45 --arpReply=1 --entryMove=4
    host_tracker.launch(arpAware=15, arpSilent=45, entryMove=4)

    core.registerNew(ZgnFattreeController)

    # Estas lineas de abajo exponen las variables adj y switch_ids al modulo interactivo de pox 'PY'
    core.Interactive.variables['adj'] = adj
    core.Interactive.variables['switch_ids'] = switch_ids
    core.Interactive.variables['switches'] = switches
    core.Interactive.variables['stats'] = request_flow_stats
    core.Interactive.variables['all_stats'] = request_all_flow_stats
    core.Interactive.variables['hosts'] = hosts
    core.Interactive.variables['mac_entries'] = get_host_tracker_entries
    core.Interactive.variables['host_ip'] = get_host_ip
    core.Interactive.variables['host_mac'] = get_host_mac
    core.Interactive.variables['find_switch_path'] = find_switch_path
    core.Interactive.variables[
        'get_switch_switch_link'] = get_switch_switch_link
    core.Interactive.variables['set_ip_complex'] = set_ip_complex
    core.Interactive.variables['taken_paths'] = taken_paths
    core.Interactive.variables['current_paths'] = current_paths
    core.Interactive.variables['current_paths_load'] = current_paths_load
    core.Interactive.variables[
        'set_udp_firewall_thresh'] = set_udp_firewall_thresh
Exemple #20
0
def setup_logging(test_mode=False, log_file=None, **kw):
    """
  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
  :param kw: additional parameters for POX's logger
  :type kw: dict
  :return: None
  """
    # Enable logging in specific logging level
    level.launch(**kw)
    # Launch colorful logging
    color.launch()
    if test_mode:
        # Define logger for test mode
        pox.log.launch(format=TEST_LOGGER_FORMAT)
        log.info("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.info("Setup logger - formatter: %s, level: %s" %
                 (setup_logging.__module__,
                  logging.getLevelName(log.getEffectiveLevel())))
    log_file = log_file if log_file is not None else LOG_FILE
    if log_file:
        # Define additional logger for logging to file
        pox.log.launch(format=FILE_LOGGER_FORMAT, file=log_file + ',w')
        log.info("Setup Logger - formatter: %s, level: %s, file: %s" %
                 (pox.log.launch.__module__,
                  logging.getLevelName(log.getEffectiveLevel()), log_file))
        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 remove_firewall_ip(self, ip_str):
     """ Elimina un ip string del set de ips bloqueads x firewall """
     if ip_str in self.firewall_ips:
         log.info("SWITCH_%s: QUITANDO %s DE LISTA NEGRA DE IPs bloqueadas",
                  self.switch_id, ip_str)
         self.firewall_ips.remove(ip_str)
    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)
    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()