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)
class L2SwitchApp(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 LEARN_NEW_MACS_RULE = 1000 LEARNED_MAC_TIMEOUT = 3600 # = 1 Hour def __init__(self, *args, **kwargs): super(L2SwitchApp, self).__init__(*args, **kwargs) # The nib is the universal variable containing network configuration and # learned state (so far) nib = NetworkInformationBase() self.nib = nib nib.load_config(os.getenv("COSCIN_CFG_FILE", "coscin_gates_testbed.json")) hostname = socket.gethostname() on_switch = self.nib.switch_for_controller_host(hostname) zookeeper_for_switch = self.nib.zookeeper_for_switch(on_switch) self.mc = MultipleControllers(self.logger, hostname, zookeeper_for_switch) self.heartbeat_monitor_started = False # 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 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 parser = dp.ofproto_parser ofproto = dp.ofproto switch = self.nib.save_switch(dp) self.logger.info("Connected to Switch: "+self.nib.switch_description(dp)) # 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) # Table 0, the Source Mac table, has table miss=Goto Controller so it can learn # newly-connected macs match_all = parser.OFPMatch() actions = [ parser.OFPActionOutput(ofproto.OFPP_CONTROLLER) ] OpenflowUtils.add_flow(dp, priority=0, match=match_all, actions=actions, table_id=0, cookie=self.LEARN_NEW_MACS_RULE) # Table 1, the Dest Mac table, has table-miss = Flood. If the destination exists, # it'll send a reply packet that'll get learned in table 0. We can afford to do a # sloppy non-spanning-tree flood because there's only one switch in our topology actions = [ parser.OFPActionOutput(ofproto.OFPP_FLOOD) ] OpenflowUtils.add_flow(dp, priority=0, match=match_all, actions=actions, table_id=1) @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 dp = msg.datapath switch = self.nib.switch_for_dp(dp) ofproto = dp.ofproto parser = dp.ofproto_parser in_port = msg.match['in_port'] # Interesting packet data pkt = packet.Packet(msg.data) eth = pkt.get_protocols(ethernet.ethernet)[0] dst = eth.dst src = eth.src if not self.nib.learned(src): match_mac_src = parser.OFPMatch( vlan_vid = self.nib.vlan_for_switch(switch), eth_src = src ) match_mac_dst = parser.OFPMatch( vlan_vid = self.nib.vlan_for_switch(switch), eth_dst = src ) OpenflowUtils.add_goto_table(dp, priority=65535, match=match_mac_src, goto_table_id=1, table_id=0) # And a send-to-port instruction for a destination match in table 1 actions = [ parser.OFPActionOutput(in_port) ] OpenflowUtils.add_flow(dp, priority=0, match=match_mac_dst, actions=actions, table_id=1) # Learn it for posterity self.nib.learn(switch, self.nib.ENDHOST_PORT, in_port, src, "0.0.0.0") # Now we have to deal with the Mac destination. If we know it already, send the packet out that port. # Otherwise flood it. output_p = self.nib.port_for_mac(dst) if output_p == None: output_p = ofproto.OFPP_FLOOD out = parser.OFPPacketOut(datapath=dp, buffer_id=msg.buffer_id, in_port=in_port, actions=[ parser.OFPActionOutput(output_p) ], data=msg.data) dp.send_msg(out) @set_ev_cls(ofp_event.EventOFPErrorMsg, [CONFIG_DISPATCHER, MAIN_DISPATCHER]) def error_msg_handler(self, ev): msg = ev.msg self.logger.info('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()