def simulate(self): simulation = self.initialize_simulation() # Some implementations send delete flow mods when disconnecting switches; ignore these flow_mods if self.ignore_trailing_flow_mod_deletes: # switch -> whether we have observed a flow_mod other than a trailing delete yet dpid2seen_non_delete = { switch.dpid : False for switch in simulation.topology.switches } # Reproduce the routing table state. all_flow_mods = [] for next_event in self.event_list: if is_flow_mod(next_event): msg.special_event("Injecting %r" % next_event) all_flow_mods.append(next_event) else: msg.openflow_event("Injecting %r" % next_event) next_event.manually_inject(simulation) # Now filter out all flow_mods that don't correspond to an entry in the # final routing table. Do that by walking backwards through the flow_mods, # and checking if they match the flow table. If so, add it to list, and # remove all flow entries that currently match it to filter overlapping # flow_mods from earlier in the event_list. relevant_flow_mods = [] for last_event in reversed(all_flow_mods): switch = simulation.topology.get_switch(last_event.dpid) # Ignore all trailing flow mod deletes if self.ignore_trailing_flow_mod_deletes: flow_mod_command = last_event.get_packet().command if (not dpid2seen_non_delete[switch.dpid] and (flow_mod_command == OFPFC_DELETE or flow_mod_command == OFPFC_DELETE_STRICT)): continue else: dpid2seen_non_delete[switch.dpid] = True if switch.table.matching_entries(last_event.get_packet().match) != []: relevant_flow_mods.append(last_event) switch.table.remove_matching_entries(last_event.get_packet().match) relevant_flow_mods.reverse() # Print filtered flow mods print "\n" msg.event("Filtered flow mods:") for next_event in relevant_flow_mods: print "%r" % next_event print "\n" # Add back removed entries. for flow_mod_event in relevant_flow_mods: flow_mod_event.manually_inject(simulation) # Now print flow tables of each switch. msg.event("Flow tables:") for switch in simulation.topology.switches: print "Switch %s" % switch.dpid switch.show_flow_table() return simulation
def loop(self): if self.steps: end_time = self.logical_time + self.steps else: end_time = sys.maxint exit_code = 0 self.interrupted = False old_interrupt = None def interrupt(sgn, frame): msg.interactive("Interrupting fuzzer, dropping to console (press ^C again to terminate)") signal.signal(signal.SIGINT, self.old_interrupt) self.old_interrupt = None self.interrupted = True raise KeyboardInterrupt() self.old_interrupt = signal.signal(signal.SIGINT, interrupt) try: # Always connect to controllers explicitly self.simulation.connect_to_controllers() self._log_input_event(ConnectToControllers()) if self.delay_startup: # Wait until the first OpenFlow message is received log.info("Waiting until first OpenfFlow message received..") while self.simulation.god_scheduler.pending_receives() == []: self.simulation.io_master.select(self.delay) while self.logical_time < end_time: self.logical_time += 1 try: self.trigger_events() msg.event("Round %d completed." % self.logical_time) halt = self.maybe_check_invariant() if halt: exit_code = 5 break self.maybe_inject_trace_event() time.sleep(self.delay) except KeyboardInterrupt as e: if self.interrupted: interactive = Interactive(self.simulation_cfg, self._input_logger) interactive.simulate(self.simulation, bound_objects=( ('fuzzer', self), )) self.old_interrupt = signal.signal(signal.SIGINT, interrupt) else: raise e log.info("Terminating fuzzing after %d rounds" % self.logical_time) if self.print_buffers: self._print_buffers() finally: if self.old_interrupt: signal.signal(signal.SIGINT, self.old_interrupt) if self._input_logger is not None: self._input_logger.close(self, self.simulation_cfg) return exit_code
def permit_dp_event(self, dp_event): """ Given a SwitchDpPacketOut event, permit it to be forwarded """ # TODO(cs): self.forward_packet should not be externally visible! msg.event("Forwarding dataplane event") # Invoke superclass DpPacketOut handler self.handle_DpPacketOut(dp_event) self._remove_dp_event(dp_event)
def permit_dp_event(self, dp_event): ''' Given a SwitchDpPacketOut event, permit it to be forwarded ''' # TODO(cs): self.forward_packet should not be externally visible! msg.event("Forwarding dataplane event") # Invoke superclass DpPacketOut handler self.handle_DpPacketOut(dp_event) self._remove_dp_event(dp_event)
def start(self): '''Start a new controller process based on the config's cmdline attribute. Registers the Popen member variable for deletion upon a SIG* received in the simulator process.''' msg.event("Starting controller %s" % (str(self.cid))) env = None if self.config.sync: # if a sync connection has been configured in the controller conf # launch the controller with environment variable 'sts_sync' set # to the appropriate listening port. This is quite a hack. env = os.environ.copy() port_match = re.search(r':(\d+)$', self.config.sync) if port_match is None: raise ValueError("sync: cannot find port in %s" % self.config.sync) port = port_match.group(1) env['sts_sync'] = "ptcp:0.0.0.0:%d" % (int(port),) if self.config.name == "pox": src_dir = os.path.join(os.path.dirname(__file__), "..") pox_ext_dir = os.path.join(self.config.cwd, "ext") if os.path.exists(pox_ext_dir): for f in ("sts/util/io_master.py", "sts/syncproto/base.py", "sts/syncproto/pox_syncer.py", "sts/__init__.py", "sts/util/socket_mux/__init__.py", "sts/util/socket_mux/pox_monkeypatcher.py", "sts/util/socket_mux/base.py", "sts/util/socket_mux/server_socket_multiplexer.py"): src_path = os.path.join(src_dir, f) if not os.path.exists(src_path): raise ValueError("Integrity violation: sts sync source path %s (abs: %s) does not exist" % (src_path, os.path.abspath(src_path))) dst_path = os.path.join(pox_ext_dir, f) dst_dir = os.path.dirname(dst_path) init_py = os.path.join(dst_dir, "__init__.py") if not os.path.exists(dst_dir): os.makedirs(dst_dir) if not os.path.exists(init_py): open(init_py, "a").close() if os.path.islink(dst_path): # remove symlink and recreate os.remove(dst_path) if not os.path.exists(dst_path): rel_link = os.path.abspath(src_path) self.log.debug("creating symlink %s -> %s", rel_link, dst_path) os.symlink(rel_link, dst_path) else: self.log.warn("Could not find pox ext dir in %s. Cannot check/link in sync module" % pox_ext_dir) self.log.info("Launching controller %s: %s" % (self.label, " ".join(self.config.expanded_cmdline))) self.process = popen_filtered("[%s]"%self.label, self.config.expanded_cmdline, self.config.cwd, env=env) self._register_proc(self.process) if self.config.sync: self.sync_connection = self.sync_connection_manager.connect(self, self.config.sync) self.alive = True
def sever_link(self, link): msg.event("Cutting link %s" % str(link)) if link not in self.network_links: raise ValueError("unknown link %s" % str(link)) if link in self.cut_links: raise RuntimeError("link %s already cut!" % str(link)) self.cut_links.add(link) link.start_software_switch.take_port_down(link.start_port)
def drop_dp_event(self, dp_event): ''' Given a SwitchDpPacketOut event, remove it from our buffer, and do not forward. Return the dropped event. ''' msg.event("Dropping dataplane event") self._remove_dp_event(dp_event) return dp_event
def drop_dp_event(self, dp_event): """ Given a SwitchDpPacketOut event, remove it from our buffer, and do not forward. Return the dropped event. """ msg.event("Dropping dataplane event") self._remove_dp_event(dp_event) return dp_event
def drop_dp_event(self, dp_event): ''' Given a SwitchDpPacketOut event, remove it from our buffer, and do not forward. Return the dropped event. ''' msg.event("Dropping dataplane event") self.buffered_dp_out_events.remove(dp_event) self.dropped_dp_events.append(dp_event) return dp_event
def kill(self): '''Kill the process the controller is running in.''' msg.event("Killing controller %s" % (str(self.uuid))) if self.sync_connection: self.sync_connection.close() kill_procs([self.process]) self._unregister_proc(self.process) self.alive = False self.process = None
def fuzz_traffic(self): if not self.simulation.dataplane_trace: # randomly generate messages from switches for host in self.simulation.topology.hosts: if self.random.random() < self.params.traffic_generation_rate: if len(host.interfaces) > 0: msg.event("injecting a random packet") traffic_type = "icmp_ping" # Generates a packet, and feeds it to the software_switch dp_event = self.traffic_generator.generate(traffic_type, host) self._log_input_event(TrafficInjection(), dp_event=dp_event)
def recover_switch(self, software_switch, down_controller_ids=None): msg.event("Rebooting software_switch %s" % str(software_switch)) if down_controller_ids is None: down_controller_ids = set() if software_switch not in self.failed_switches: log.warn("Switch %s not currently down. (Currently down: %s)" % (str(software_switch), str(self.failed_switches))) connected_to_at_least_one = software_switch\ .recover(down_controller_ids=down_controller_ids) if connected_to_at_least_one: self.failed_switches.remove(software_switch) return connected_to_at_least_one
def recover_switch(self, software_switch, down_controller_ids=None): msg.event("Rebooting software_switch %s" % str(software_switch)) if down_controller_ids is None: down_controller_ids = set() if software_switch not in self.failed_switches: log.warn( "Switch %s not currently down. (Currently down: %s)" % (str(software_switch), str(self.failed_switches)) ) connected_to_at_least_one = software_switch.recover(down_controller_ids=down_controller_ids) if connected_to_at_least_one: self.failed_switches.remove(software_switch) return connected_to_at_least_one
def fuzz_traffic(self): if not self.simulation.dataplane_trace: # randomly generate messages from switches for host in self.simulation.topology.hosts: if self.random.random() < self.params.traffic_generation_rate: if len(host.interfaces) > 0: msg.event("injecting a random packet") traffic_type = "icmp_ping" # Generates a packet, and feeds it to the software_switch dp_event = self.traffic_generator.generate( traffic_type, host) self._log_input_event(TrafficInjection(), dp_event=dp_event)
def kill(self): ''' Kill the process the controller is running in ''' if self.state != ControllerState.ALIVE: self.log.warn("Killing controller %s when it is not alive!" % self.label) return msg.event("Killing controller %s" % self.cid) kill_procs([self.process]) if self.config.kill_cmd != "": self.log.info("Killing controller %s: %s" % (self.label, " ".join(self.config.expanded_kill_cmd))) popen_filtered("[%s]" % self.label, self.config.expanded_kill_cmd, self.config.cwd) self._unregister_proc(self.process) self.process = None self.state = ControllerState.DEAD
def kill(self): """ Kill the process the controller is running in """ if self.state != ControllerState.ALIVE: self.log.warn("Killing controller %s when it is not alive!" % self.label) return msg.event("Killing controller %s (pid %d)" % (self.cid, self.pid)) kill_procs([self.process]) if self.config.kill_cmd not in ["", None]: self.log.info( "Killing controller %s: %s" % (self.label, " ".join(self.config.expanded_kill_cmd))) popen_filtered("[%s]" % self.label, self.config.expanded_kill_cmd, self.config.cwd) self._unregister_proc(self.process) self.process = None self.state = ControllerState.DEAD
def loop(self): if self.steps: end_time = self.logical_time + self.steps else: end_time = sys.maxint try: while self.logical_time < end_time: self.logical_time += 1 self.trigger_events() msg.event("Round %d completed." % self.logical_time) halt = self.maybe_check_invariant() if halt: break self.maybe_inject_trace_event() time.sleep(self.delay) finally: if self._input_logger is not None: self._input_logger.close(self.simulation_cfg)
def check_migrations(self): for access_link in list(self.simulation.topology.access_links): if self.random.random() < self.params.host_migration_rate: old_ingress_dpid = access_link.switch.dpid old_ingress_port_no = access_link.switch_port.port_no live_edge_switches = list(self.simulation.topology.live_edge_switches) if len(live_edge_switches) > 0: new_switch = random.choice(live_edge_switches) new_switch_dpid = new_switch.dpid new_port_no = max(new_switch.ports.keys()) + 1 msg.event("Migrating host %s" % str(access_link.host)) self.simulation.topology.migrate_host( old_ingress_dpid, old_ingress_port_no, new_switch_dpid, new_port_no ) self._log_input_event( HostMigration( old_ingress_dpid, old_ingress_port_no, new_switch_dpid, new_port_no, access_link.host.name ) ) self._send_initialization_packet(access_link.host, self_pkt=True)
def start(self, multiplex_sockets=False): """ Start a new POX controller process based on the config's start_cmd attribute. Registers the Popen member variable for deletion upon a SIG* received in the simulator process """ self.log.info(self.welcome_msg) if self.state != ControllerState.DEAD: self.log.warn( "Starting controller %s when controller is not dead!" % self.label) return msg.event("Starting POX controller %s" % (str(self.cid))) env = None if self.config.sync: # If a sync connection has been configured in the controller conf # launch the controller with environment variable 'sts_sync' set # to the appropriate listening port. This is quite a hack. env = os.environ.copy() port_match = re.search(r':(\d+)$', self.config.sync) if port_match is None: raise ValueError("sync: cannot find port in %s" % self.config.sync) port = port_match.group(1) env['sts_sync'] = "ptcp:0.0.0.0:%d" % (int(port), ) if self.config.sync or multiplex_sockets: src_dir = os.path.join(os.path.dirname(__file__), "../../") pox_ext_dir = os.path.join(self.config.cwd, "ext") if os.path.exists(pox_ext_dir): for f in ("sts/util/io_master.py", "sts/syncproto/base.py", "sts/syncproto/pox_syncer.py", "sts/__init__.py", "sts/util/socket_mux/__init__.py", "sts/util/socket_mux/pox_monkeypatcher.py", "sts/util/socket_mux/base.py", "sts/util/socket_mux/server_socket_multiplexer.py"): src_path = os.path.join(src_dir, f) if not os.path.exists(src_path): raise ValueError( "Integrity violation: sts sync source path %s (abs: %s) " "does not exist" % (src_path, os.path.abspath(src_path))) dst_path = os.path.join(pox_ext_dir, f) dst_dir = os.path.dirname(dst_path) init_py = os.path.join(dst_dir, "__init__.py") if not os.path.exists(dst_dir): os.makedirs(dst_dir) if not os.path.exists(init_py): open(init_py, "a").close() if os.path.islink(dst_path): # Remove symlink and recreate os.remove(dst_path) if not os.path.exists(dst_path): rel_link = os.path.abspath(src_path) self.log.debug("Creating symlink %s -> %s", rel_link, dst_path) os.symlink(rel_link, dst_path) else: self.log.warn("Could not find pox ext dir in %s. " + "Cannot check/link in sync module" % pox_ext_dir) if self.config.start_cmd in ["", None]: raise RuntimeError("No command found to start controller %s!" % self.label) start_cmd = getattr(self.config, "expanded_start_cmd", self.config.start_cmd) self.log.info("Launching controller %s: %s" % (self.label, " ".join(start_cmd))) launch_in_network_namespace = getattr(self.config, "launch_in_network_namespace", False) if launch_in_network_namespace: (self.process, self.guest_eth_addr, self.host_device) = \ launch_namespace( " ".join(start_cmd), self.config.address, self.cid, host_ip_addr_str=IPAddressSpace.find_unclaimed_address( ip_prefix=self.config.address), cwd=self.config.cwd, env=env) else: self.process = popen_filtered("[%s]" % self.label, start_cmd, self.config.cwd, env) self._register_proc(self.process) if self.config.sync: self.sync_connection = self.sync_connection_manager.connect( self, self.config.sync) self._check_snapshot_connect() self.state = ControllerState.ALIVE
def repair_link(self, link): msg.event("Restoring link %s" % str(link)) if link not in self.network_links: raise ValueError("Unknown link %s" % str(link)) link.start_software_switch.bring_port_up(link.start_port) self.cut_links.remove(link)
def crash_switch(self, software_switch): msg.event("Crashing software_switch %s" % str(software_switch)) software_switch.fail() self.failed_switches.add(software_switch)
def reboot_controller(controller): msg.event("Restarting controller %s" % str(controller)) controller.start()
def delay_dp_event(self, dp_event): msg.event("Delaying dataplane event") # (Monkey patch on a delay counter) if not hasattr(dp_event, "delayed_rounds"): dp_event.delayed_rounds = 0 dp_event.delayed_rounds += 1
def start(self, multiplex_sockets=False): """ Start a new POX controller process based on the config's start_cmd attribute. Registers the Popen member variable for deletion upon a SIG* received in the simulator process """ self.log.info(self.welcome_msg) if self.state != ControllerState.DEAD: self.log.warn( "Starting controller %s when controller is not dead!" % self.label) return msg.event("Starting POX controller %s" % (str(self.cid))) env = None if self.config.sync: # If a sync connection has been configured in the controller conf # launch the controller with environment variable 'sts_sync' set # to the appropriate listening port. This is quite a hack. env = os.environ.copy() port_match = re.search(r':(\d+)$', self.config.sync) if port_match is None: raise ValueError("sync: cannot find port in %s" % self.config.sync) port = port_match.group(1) env['sts_sync'] = "ptcp:0.0.0.0:%d" % (int(port),) if self.config.sync or multiplex_sockets: src_dir = os.path.join(os.path.dirname(__file__), "../../") pox_ext_dir = os.path.join(self.config.cwd, "ext") if os.path.exists(pox_ext_dir): for f in ("sts/util/io_master.py", "sts/syncproto/base.py", "sts/syncproto/pox_syncer.py", "sts/__init__.py", "sts/util/socket_mux/__init__.py", "sts/util/socket_mux/pox_monkeypatcher.py", "sts/util/socket_mux/base.py", "sts/util/socket_mux/server_socket_multiplexer.py"): src_path = os.path.join(src_dir, f) if not os.path.exists(src_path): raise ValueError( "Integrity violation: sts sync source path %s (abs: %s) " "does not exist" % (src_path, os.path.abspath(src_path))) dst_path = os.path.join(pox_ext_dir, f) dst_dir = os.path.dirname(dst_path) init_py = os.path.join(dst_dir, "__init__.py") if not os.path.exists(dst_dir): os.makedirs(dst_dir) if not os.path.exists(init_py): open(init_py, "a").close() if os.path.islink(dst_path): # Remove symlink and recreate os.remove(dst_path) if not os.path.exists(dst_path): rel_link = os.path.abspath(src_path) self.log.debug("Creating symlink %s -> %s", rel_link, dst_path) os.symlink(rel_link, dst_path) else: self.log.warn("Could not find pox ext dir in %s. " + "Cannot check/link in sync module" % pox_ext_dir) if self.config.start_cmd in ["", None]: raise RuntimeError( "No command found to start controller %s!" % self.label) start_cmd = getattr(self.config, "expanded_start_cmd", self.config.start_cmd) self.log.info( "Launching controller %s: %s" % (self.label, " ".join(start_cmd))) launch_in_network_namespace = getattr(self.config, "launch_in_network_namespace", False) if launch_in_network_namespace: (self.process, self.guest_eth_addr, self.host_device) = \ launch_namespace( " ".join(start_cmd), self.config.address, self.cid, host_ip_addr_str=IPAddressSpace.find_unclaimed_address( ip_prefix=self.config.address), cwd=self.config.cwd, env=env) else: self.process = popen_filtered("[%s]" % self.label, start_cmd, self.config.cwd, env) self._register_proc(self.process) if self.config.sync: self.sync_connection = self.sync_connection_manager.connect( self, self.config.sync) self._check_snapshot_connect() self.state = ControllerState.ALIVE
def unblock_connection(self, connection): msg.event("Unblocking connection %s" % connection) return connection.io_worker.unblock()
def kill_controller(controller): msg.event("Killing controller %s" % str(controller)) controller.kill()
def simulate(self): simulation = self.initialize_simulation() # Some implementations send delete flow mods when disconnecting switches; ignore these flow_mods if self.ignore_trailing_flow_mod_deletes: # switch -> whether we have observed a flow_mod other than a trailing delete yet dpid2seen_non_delete = { switch.dpid: False for switch in simulation.topology.switches } # Reproduce the routing table state. all_flow_mods = [] for next_event in self.event_list: if is_flow_mod(next_event): msg.special_event("Injecting %r" % next_event) all_flow_mods.append(next_event) else: msg.openflow_event("Injecting %r" % next_event) next_event.manually_inject(simulation) # Now filter out all flow_mods that don't correspond to an entry in the # final routing table. Do that by walking backwards through the flow_mods, # and checking if they match the flow table. If so, add it to list, and # remove all flow entries that currently match it to filter overlapping # flow_mods from earlier in the event_list. relevant_flow_mods = [] for last_event in reversed(all_flow_mods): switch = simulation.topology.get_switch(last_event.dpid) # Ignore all trailing flow mod deletes if self.ignore_trailing_flow_mod_deletes: flow_mod_command = last_event.get_packet().command if (not dpid2seen_non_delete[switch.dpid] and (flow_mod_command == OFPFC_DELETE or flow_mod_command == OFPFC_DELETE_STRICT)): continue else: dpid2seen_non_delete[switch.dpid] = True if switch.table.matching_entries( last_event.get_packet().match) != []: relevant_flow_mods.append(last_event) switch.table.remove_matching_entries( last_event.get_packet().match) relevant_flow_mods.reverse() # Print filtered flow mods print "\n" msg.event("Filtered flow mods:") for next_event in relevant_flow_mods: print "%r" % next_event print "\n" # Add back removed entries. for flow_mod_event in relevant_flow_mods: flow_mod_event.manually_inject(simulation) # Now print flow tables of each switch. msg.event("Flow tables:") for switch in simulation.topology.switches: print "Switch %s" % switch.dpid switch.show_flow_table() return simulation
def loop(self): if self.steps: end_time = self.logical_time + self.steps else: end_time = sys.maxint self.interrupted = False old_interrupt = None def interrupt(sgn, frame): msg.interactive("Interrupting fuzzer, dropping to console (press ^C again to terminate)") signal.signal(signal.SIGINT, self.old_interrupt) self.old_interrupt = None self.interrupted = True raise KeyboardInterrupt() self.old_interrupt = signal.signal(signal.SIGINT, interrupt) try: # Always connect to controllers explicitly self.simulation.connect_to_controllers() self._log_input_event(ConnectToControllers()) if self.delay_startup: # Wait until the first OpenFlow message is received log.info("Waiting until first OpenfFlow message received..") while self.simulation.god_scheduler.pending_receives() == []: self.simulation.io_master.select(self.delay) sent_self_packets = False while self.logical_time < end_time: self.logical_time += 1 try: if not self._initializing(): self.trigger_events() halt = self.maybe_check_invariant() if halt: self.simulation.set_exit_code(5) break self.maybe_inject_trace_event() else: # Initializing self.check_pending_messages(pass_through=True) if not sent_self_packets and (self.logical_time % self._all_to_all_interval) == 0: # Only need to send self packets once self._send_initialization_packets(self_pkts=True) sent_self_packets = True elif self.logical_time > self.initialization_rounds: # All-to-all mode if (self.logical_time % self._all_to_all_interval) == 0: self._send_initialization_packets(self_pkts=False) self._all_to_all_iterations += 1 if self._all_to_all_iterations > len(self.simulation.topology.hosts): log.info("Done initializing") self._pending_all_to_all = False self.check_dataplane(pass_through=True) msg.event("Round %d completed." % self.logical_time) time.sleep(self.delay) except KeyboardInterrupt as e: if self.interrupted: interactive = Interactive(self.simulation_cfg, self._input_logger) interactive.simulate(self.simulation, bound_objects=(("fuzzer", self),)) self.old_interrupt = signal.signal(signal.SIGINT, interrupt) else: raise e log.info("Terminating fuzzing after %d rounds" % self.logical_time) if self.print_buffers: self._print_buffers() finally: if self.old_interrupt: signal.signal(signal.SIGINT, self.old_interrupt) if self._input_logger is not None: self._input_logger.close(self, self.simulation_cfg) return self.simulation