Example #1
0
  def __init__(self, *args, **kwargs):
    super(CoscinApp, self).__init__(*args, **kwargs)

    # The nib is the universal variable containing network configuration and 
    # learned state (so far)
    nib = NetworkInformationBase()
    self.nib = nib

    config_file = os.getenv("COSCIN_CFG_FILE", "coscin_gates_testbed.json")
    nib.load_config(config_file)

    hostname = socket.gethostname()
    on_switch = self.nib.switch_for_controller_host(hostname)
    if on_switch == None:
      self.logger.error("The hostname "+hostname+" is not present in a controller_hosts attribute for the switch in "+config_file)
      sys.exit(1)
    zookeeper_for_switch = self.nib.zookeeper_for_switch(on_switch)
    if zookeeper_for_switch == "":
      self.mc = None
    else:
      self.mc = MultipleControllers(self.logger, hostname, zookeeper_for_switch)
      self.heartbeat_monitor_started = False

    # Register all handlers
    self.l2_learning_switch_handler = L2LearningSwitchHandler(nib, self.logger)
    self.cross_campus_handler = CrossCampusHandler(nib, self.logger)
    self.arp_handler = ArpHandler(nib, self.logger)
    self.path_selection_handler = PathSelectionHandler(nib, self.logger)
Example #2
0
  def __init__(self, config_file='laptop_demo_network.json'):
    frenetic.App.__init__(self) 
    nib = NetworkInformationBase()
    self.nib = nib
    nib.load_config(config_file)

    self.arp_handler = ArpHandler(self, nib)
    self.intranet_handler = IntranetHandler(self, nib)
    self.cross_campus_handler = CrossCampusHandler(self, nib)
Example #3
0
 def create_handler(self, pkt, dp_to_customer, in_port, data, datapath,
                    controller, federazione, public_to_private_a,
                    public_to_private_b):
     print 'publice to private a', public_to_private_a, 'publice to private', public_to_private_b
     cs = controller.get_customers()
     arp_header = pkt.get_protocol(arp.arp)
     ip_header = pkt.get_protocol(ipv4.ipv4)
     udp_header = pkt.get_protocol(udp.udp)
     if arp_header is not None:
         self._handler = ArpHandler(pkt, datapath, in_port, controller)
     #print('------pkt-------',pkt)
     #dns_pkt=DNS(pkt[-1])
     if udp_header is not None:
         print('-----------', cs[0].get_ns_domain_name())
         dns_pkt = DNS(pkt[-1])
         if cs[0].get_ns_domain_name() is None:
             print '--calling alg1---'
             if dns_pkt.qr == 0:
                 self._handler = DNSQueryHandler(pkt, in_port, data,
                                                 datapath, controller,
                                                 federazione,
                                                 public_to_private_a,
                                                 public_to_private_b)
             elif dns_pkt.qr == 1:
                 self._handler = DNSResponseHandler(
                     pkt, in_port, data, datapath, controller, federazione,
                     public_to_private_a, public_to_private_b, pkt[-1])
         else:
             print '--calling alg2---'
             if dns_pkt.ancount == 1:
                 dnsrr = dns_pkt.an
                 controller.set_packet_ip(ip_header.src)
                 self._handler = DNSResponseHandlerAlg2_2(
                     pkt, in_port, data, datapath, controller, federazione,
                     public_to_private_a, public_to_private_b, pkt[-1])
             elif dns_pkt.qr == 0:
                 self._handler = DNSQueryHandlerAlg2(
                     pkt, in_port, data, datapath, controller, federazione,
                     public_to_private_a, public_to_private_b)
             elif dns_pkt.qr == 1 and dns_pkt.qd.qname != cs[
                     0].get_ns_domain_name():
                 #print ('--dns.id------',dns_pkt.id,'----udp_dst_port_--',udp_header.dst_port,'----udp_src_port_--',udp_header.src_port)
                 self._handler = DNSResponseHandlerAlg2(
                     pkt, in_port, data, datapath, controller, federazione,
                     public_to_private_a, public_to_private_b, pkt[-1])
     else:
         ip_header = pkt.get_protocol(ipv4.ipv4)
         if ip_header is not None:
             self._handler = IpHandler(pkt, dp_to_customer, in_port, data,
                                       datapath, controller, federazione,
                                       public_to_private_a,
                                       public_to_private_b)
     return self._handler
Example #4
0
class CoscinApp(app_manager.RyuApp):
  OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

  # This is the maximum number of seconds between probes from the switch.  On HP switches, it's listed
  # as the Backoff interval
  MAXIMUM_HEARTBEAT_INTERVAL = 20

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

    # The nib is the universal variable containing network configuration and 
    # learned state (so far)
    nib = NetworkInformationBase()
    self.nib = nib

    config_file = os.getenv("COSCIN_CFG_FILE", "coscin_gates_testbed.json")
    nib.load_config(config_file)

    hostname = socket.gethostname()
    on_switch = self.nib.switch_for_controller_host(hostname)
    if on_switch == None:
      self.logger.error("The hostname "+hostname+" is not present in a controller_hosts attribute for the switch in "+config_file)
      sys.exit(1)
    zookeeper_for_switch = self.nib.zookeeper_for_switch(on_switch)
    if zookeeper_for_switch == "":
      self.mc = None
    else:
      self.mc = MultipleControllers(self.logger, hostname, zookeeper_for_switch)
      self.heartbeat_monitor_started = False

    # Register all handlers
    self.l2_learning_switch_handler = L2LearningSwitchHandler(nib, self.logger)
    self.cross_campus_handler = CrossCampusHandler(nib, self.logger)
    self.arp_handler = ArpHandler(nib, self.logger)
    self.path_selection_handler = PathSelectionHandler(nib, self.logger)

  # The heartbeat timer is started on switch startup.  It ensures that a heartbeat occurs every
  # 10 seconds or so by default.  
  def heartbeat_monitor(self, _):
    while True:
      # If we don't currently hold the lock as a master, just sleep until we do.  
      if self.mc.holds_lock():
        secs_ago = time.time() - self.last_heartbeat
        # If it has stopped, then we relinquish the lock in Zookeeper so the backup controller can be promoted
        # At this point, the controller is in limbo as far as Zookeeper is concerned.  When the Switch Up event
        # happens, it will become a backup controller.  Note: we don't actually send a role_request to the switch
        # to demote it because ... well, we can't contact the switch!  The promotion to master of the other 
        # controller will ensure the demotion of this one takes place. 
        if secs_ago > (2.0 * self.MAXIMUM_HEARTBEAT_INTERVAL):
          self.logger.error("Lost connection with the switch.  Demoting to backup controller.")
          self.mc.release_lock()
          self.nib.clear()
      time.sleep(self.MAXIMUM_HEARTBEAT_INTERVAL)

  @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
  def handle_switch_up(self, ev):
    dp = ev.msg.datapath
    ofp_parser = dp.ofproto_parser
    ofp = dp.ofproto
    self.logger.info("Switch "+str(dp.id)+" says hello.")
    switch = self.nib.save_switch(dp)
    self.logger.info("Connected to Switch: "+self.nib.switch_description(dp))

    if self.mc != None:
      # This will block until the controller actually becomes a primary
      self.mc.handle_datapath(ev)

      # Start background thread to monitor switch-to-controller heartbeat
      self.last_heartbeat = time.time()
      if not self.heartbeat_monitor_started:
        thread.start_new_thread( self.heartbeat_monitor, (self, ) )
      self.heartbeat_monitor_started = True

    OpenflowUtils.delete_all_rules(dp)
    OpenflowUtils.send_table_miss_config(dp)

    self.l2_learning_switch_handler.install_fixed_rules(dp)
    self.cross_campus_handler.install_fixed_rules(dp)
    self.arp_handler.install_fixed_rules(dp)
    self.path_selection_handler.install_fixed_rules(dp)

  @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
  def handle_packet_in(self, ev):
    # The HP considers a successful Packet In as a probe, so we reset the heartbeat here as well
    self.last_heartbeat = time.time()

    msg = ev.msg
    self.logger.debug("Packet In")
    self.l2_learning_switch_handler.packet_in(msg)
    self.cross_campus_handler.packet_in(msg)
    self.arp_handler.packet_in(msg)
    self.path_selection_handler.packet_in(msg)

  @set_ev_cls(ofp_event.EventOFPErrorMsg, [CONFIG_DISPATCHER, MAIN_DISPATCHER])
  def error_msg_handler(self, ev):
    msg = ev.msg
    self.logger.error('OFPErrorMsg received: type=0x%02x code=0x%02x '
      'message=%s',
       msg.type, msg.code, utils.hex_array(msg.data)
     )

  @set_ev_cls(ofp_event.EventOFPEchoRequest, [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER])
  def echo_request_handler(self, ev):
    self.last_heartbeat = time.time()

  @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
  def port_status_handler(self, ev):
    msg = ev.msg

    # Currently only the l2 switch is interested in these events.
    self.l2_learning_switch_handler.port_status(msg)
    def create_handler(self, pkt, dp_to_customer, in_port, data, datapath,
                       controller, federazione, public_to_private_a,
                       public_to_private_b):
        print 'publice to private a', public_to_private_a, 'publice to private', public_to_private_b
        cs = controller.get_customers()
        arp_header = pkt.get_protocol(arp.arp)
        #print('--arp---',arp_header)
        ip_header = pkt.get_protocol(ipv4.ipv4)
        udp_header = pkt.get_protocol(udp.udp)

        if arp_header is not None:
            self._handler = ArpHandler(pkt, datapath, in_port, controller)
        elif udp_header is not None:
            #print ('------------------I am inside UDP part',udp_header.src_port)
            pport = udp_header.src_port
            #print('------pkt-------',pkt)
            dns_pkt = DNS(pkt[-1])
            #print('------DNS-pkt-Q------',dns_pkt)
            print('------DNS-pkt-Q-type-----', dns_pkt.qd.qtype)
            csR = controller.get_customers_remote()
            cr_list = []
            cr_ns_list = []
            for cr in range(len(csR)):
                if csR[cr].get_ip() is not None:
                    cr_list.append(csR[cr].get_ip())
                    #name=csR[cr].get_name_server()
                    #name=name.split('.')
                    #new_sub=name[1]+'.'+name[2]+'.'+name[3]+'.'
                    #print'-----name[1:]--',new_sub
                #cr_ns_list.append(new_sub)
            if len(cr_list) >= 1:
                query = dns_pkt.qd.qname
                print '--calling alg1---, ', dns_pkt.qd.qname
                if dns_pkt.qr == 0:
                    self._handler = DNSQueryHandler(pkt, in_port, data,
                                                    datapath, controller,
                                                    federazione,
                                                    public_to_private_a,
                                                    public_to_private_b)
                elif dns_pkt.qr == 1:
                    self._handler = DNSResponseHandler(
                        pkt, in_port, data, datapath, controller, federazione,
                        public_to_private_a, public_to_private_b, pkt[-1])
            else:
                print '--calling alg2---'
                if dns_pkt.ancount == 1:
                    print '--I have final answer--'
                    controller.set_packet_ip(ip_header.src)
                    self._handler = DNSResponseHandlerAlg2_2(
                        pkt, in_port, data, datapath, controller, federazione,
                        public_to_private_a, public_to_private_b, pkt[-1])
                elif dns_pkt.qr == 0 and dns_pkt.qd.qtype == 1:
                    self._handler = DNSQueryHandlerAlg2(
                        pkt, in_port, data, datapath, controller, federazione,
                        public_to_private_a, public_to_private_b)
                elif dns_pkt.qr == 1:
                    #print ('--dns.id------',dns_pkt.id,'----udp_dst_port_--',udp_header.dst_port,'----udp_src_port_--',udp_header.src_port)
                    self._handler = DNSResponseHandlerAlg2(
                        pkt, in_port, data, datapath, controller, federazione,
                        public_to_private_a, public_to_private_b, pkt[-1])

        elif ip_header is not None:
            print('------------------I am inside IP part-----------')

            self._handler = IpHandler(pkt, dp_to_customer, in_port, data,
                                      datapath, controller, federazione,
                                      public_to_private_a, public_to_private_b)
            #self._handler = IpHandler(pkt, dp_to_customer, in_port, data, datapath, controller, federazione,public_to_private_a,public_to_private_b)

        return self._handler
Example #6
0
class CoscinSwitchApp(frenetic.App):
  client_id = "coscin_switch"
  frenetic_http_host = "localhost"
  frenetic_http_port = "9000"

  # The switch can be in one of three states
  STATE_INITIAL_CONFIG = 0
  STATE_ROUTER_LEARNING = 1
  STATE_NORMAL_OPERATION = 2
  state = STATE_INITIAL_CONFIG

  # Used as destination Mac for ARP replies that we ignore
  BOGUS_MAC = "00:de:ad:00:be:ef"

  def __init__(self, config_file='laptop_demo_network.json'):
    frenetic.App.__init__(self) 
    nib = NetworkInformationBase()
    self.nib = nib
    nib.load_config(config_file)

    self.arp_handler = ArpHandler(self, nib)
    self.intranet_handler = IntranetHandler(self, nib)
    self.cross_campus_handler = CrossCampusHandler(self, nib)
    #self.broadcast_handler = BroadcastHandler(self, nib)

  ##################################
  # Common operations

  # Send payload to all ports  
  def flood(self, switch, except_port, payload):
    flood_to_ports = [ p for p in self.nib.switches[switch] if p != except_port ]
    output_actions = [ Output(Physical(p)) for p in flood_to_ports ]
    # Only bother to send the packet out if there are ports to send it out on.
    if output_actions:
      self.pkt_out(self.nib.switch_to_dpid(switch), payload, output_actions)

  def arp_payload(self, e, pkt):
    p = packet.Packet()
    p.add_protocol(e)
    p.add_protocol(pkt)
    p.serialize()
    return NotBuffered(binascii.a2b_base64(binascii.b2a_base64(p.data)))

  def send_arp_request(self, switch, src_ip, target_ip):
    # It's unclear what the source should be, since the switch has no mac or IP address.
    # It just hears all replies and picks out the interesting stuff.
    src_mac = self.BOGUS_MAC
    dst_mac = "ff:ff:ff:ff:ff:ff"
    e = ethernet.ethernet(dst=dst_mac, src=src_mac, ethertype=ether.ETH_TYPE_ARP)
    pkt = arp.arp_ip(arp.ARP_REQUEST, src_mac, src_ip, "00:00:00:00:00:00", target_ip)
    payload = self.arp_payload(e, pkt)
    logging.info("Sending Arp Request to "+target_ip)
    # None means the request will go out every available port
    self.flood(switch, None, payload)

  def send_arp_request_router_interface(self, switch, target_ip_net):
    # Note this may not or may not be a real host, but the reply will always come back to the switch anyway.
    # TODO: host 250 is appropriate for /24 subnets, but not anything smaller
    src_ip = NetUtils.ip_for_network(target_ip_net, 250)
    dst_ip = NetUtils.ip_for_network(target_ip_net, 1)  # The IP of the router interface will always be a .1
    self.send_arp_request(switch, src_ip, dst_ip)

  def update_and_clear_dirty(self):
    self.update(self.normal_operation_policy())
    self.nib.clear_dirty()

  ########################
  # Frenetic Dispatchers

  def connected(self):
    def handle_current_switches(switches):
      self.nib.save_switches_and_ports(switches)

      logging.info("Connected to Frenetic - Switches: "+self.nib.switch_description())
      logging.info("Installing router learning rules")
      self.update(self.router_learning_policy())

      logging.info("Pausing 2 seconds to allow rules to be installed")
      IOLoop.instance().add_timeout(datetime.timedelta(seconds=2), self.learn_routers)

    # If Frenetic went down, it'll call connected() when it comes back up.  In that case, just 
    # resend the current policies
    if self.state == self.STATE_NORMAL_OPERATION:
      self.update_and_clear_dirty()
    else:
      # Turn on remove_tail_drops to get around issue 463
      #self.config( CompilerOptions("empty", "IP4Dst < EthType < Location < IP4Src < Switch", True, False, True) )
      self.current_switches(callback=handle_current_switches) 

  def packet_in(self, dpid, port, payload):
    if self.state == self.STATE_INITIAL_CONFIG:
      logging.info("Packets received before initialization, dropping" )
    elif self.state == self.STATE_ROUTER_LEARNING:
      self.packet_in_router_learning(dpid, port, payload)
    else:
      self.packet_in_normal_operation(dpid, port, payload)

  # TODO: Bizarrely, port_up events from an HP switch are reported as port_down and vice-versa.
  # I don't know if the problem is in the switch, Frenetic, or the language bindings.  
  def port_down(self, dpid, port):
    switch = self.nib.dpid_to_switch(dpid)
    logging.info("Port up: "+switch+"/"+str(port))
    # If port comes up, remove any learned macs on it (probably won't be any).  This is necessary
    # in case port_down was not fired.   
    if self.nib.unlearn_mac_on_port(switch, port):
      self.update_and_clear_dirty()

  def port_up(self, dpid, port):
    switch = self.nib.dpid_to_switch(dpid)
    logging.info("Port down: "+switch+"/"+str(port))
    # If port goes down, remove any learned macs on it
    if self.nib.unlearn_mac_on_port(switch, port):
      self.update_and_clear_dirty()

  def switch_up(self, dpid, ports):
    switch = self.nib.dpid_to_switch(dpid)
    # If we've seen this switch before, just return.  Otherwise add the ports to unlearned. 
    if self.nib.contains_switch(switch):
      return
    self.nib.add_switches_and_ports(switch, ports)
    self.update_and_clear_dirty()
    logging.info("Updated Switches: "+self.nib.switch_description())

  # Don't remove switch info when it supposedly goes down.  That way if it comes back up 
  # without properly signalling switch_up, we're not over a barrel.  
  def switch_down(self, dpid):
    switch = self.nib.dpid_to_switch(dpid)
    logging.info("Switch down: "+switch)

  ########################
  # Router Learning Mode
  # We assume all router interfaces are up for all alternate paths and that they're fixed for the duration
  # of the controller.  In this mode, we send ARP requests to all paths so we can learn (1) their Mac address 
  # (2) their switch port.  

  # RouterLearningHandler.packet_in
  def packet_in_router_learning(self, dpid, port, payload):
    p = NetUtils.packet(payload, 'arp')
    p_eth = NetUtils.packet(payload, 'ethernet')
    switch = self.nib.dpid_to_switch(dpid)
    logging.info("Received ("+str(p_eth.ethertype)+"): "+p_eth.src+"/"+p.src_ip+" -> ("+switch+", "+str(port)+") -> "+p.dst_ip)

    # Supposedly, the src mac in the ARP reply and the ethernet frame itself should be the
    # the same, but that's not always the case.  The Ethernet frame is definitive. 
    self.nib.learn(switch, self.nib.ROUTER_PORT, port, p_eth.src, p.src_ip)
    self.waiting_for_router_arps -= 1
    if self.waiting_for_router_arps == 0:
      self.normal_mode()

  # RouterLearningHandler.policy
  def router_learning_policy(self):
    # In the intial config, grab all ARP replies for ourselves
    return Filter( Policies.is_arp() ) >> Policies.send_to_controller()

  # RouterLearningHandler.start
  def learn_routers(self):
    logging.info("Switching to Router Learning mode")
    self.state = self.STATE_ROUTER_LEARNING
    self.waiting_for_router_arps = 0
    for side in [ "ithaca", "nyc" ]:
      if self.nib.switch_present(side):
        self.send_arp_request_router_interface(side, self.nib.actual_net_for(side))
        self.waiting_for_router_arps += 1

  ########################
  # Normal Mode
  # This basically handles all traffic afterwards.  It learns host ports coming up.  

  def normal_mode(self):
    logging.info("Switching to Normal mode.  Host ports to learn: "+self.nib.unlearned_ports_description())
    self.update(self.normal_operation_policy())
    self.state = self.STATE_NORMAL_OPERATION

  def normal_operation_policy(self):
    return Union([
      self.arp_handler.policy(),
      #self.broadcast_handler.policy(),
      self.intranet_handler.policy(),
      self.cross_campus_handler.policy()
    ])

  def packet_in_normal_operation(self, dpid, port, payload):
    switch = self.nib.dpid_to_switch(dpid)
    # TODO: deal with non-IP packets, although that's fairly unlikely
    p_eth = NetUtils.packet(payload, 'ethernet')
    if p_eth.ethertype == 0x0806:
      p_arp = NetUtils.packet(payload, 'arp')
      src_ip = p_arp.src_ip
      dst_ip = p_arp.dst_ip
    elif p_eth.ethertype == 0x0800:
      p_ip = NetUtils.packet(payload, 'ipv4')
      src_ip = p_ip.src
      dst_ip = p_ip.dst
    else:
      src_ip = '0.0.0.0'
      dst_ip = '0.0.0.0'
      logging.info("Received packet of type "+str(p_eth.ethertype))

    # TODO: Handle DHCP requests someday, ... maybe
    if src_ip == '0.0.0.0':
      return

    if self.nib.learn(switch, self.nib.ENDHOST_PORT, port, p_eth.src, src_ip):
      self.nib.set_dirty()

    logging.info("Received ("+str(p_eth.ethertype)+"): "+p_eth.src+"/"+src_ip+" -> ("+switch+", "+str(port)+") -> "+dst_ip)

    self.arp_handler.packet_in(dpid, port, payload)
    #self.broadcast_handler.packet_in(dpid, port, payload)
    self.intranet_handler.packet_in(dpid, port, payload)
    self.cross_campus_handler.packet_in(dpid, port, payload)

    if self.nib.is_dirty():
      logging.info("Installing new policy")
      # This doesn't actually wait two seconds, but it seems to serialize the updates so they occur in the right
      # order, as opposed to just calling update_and_clear_dirty on its own.
      IOLoop.instance().add_timeout(datetime.timedelta(seconds=2), self.update_and_clear_dirty)

  def set_preferred_path(self, new_preferred_path):
    # Send pings
    preferred_path = self.nib.get_preferred_path() 
    logging.info("Adjusting preferred path from "+str(preferred_path)+" to "+str(new_preferred_path))
    # Setting a preferred path is pretty hard on the switch - it sends a whole new flow table
    # so don't do it unless absolutely necessary
    if preferred_path == new_preferred_path:
      logging.info("No change.")
    else:
      self.nib.set_preferred_path(new_preferred_path)
      self.update(self.normal_operation_policy())