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)
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)
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
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
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())