class HttpSessionTests(unittest.TestCase): """ Unit tests for utility functions in convert.py """ def setUp(self): """ Start HTTP server :return: """ self.http_server = SDNControllerServer(TestSDNController()) self.http_server.start() # TODO - sleeping to wait for the server to start. Look at the # http_server class to see if the start() call can bock time.sleep(1) def tearDown(self): self.http_server.stop() def test_session(self): attack = { 'src_mac': '00:00:00:00:00', 'src_ip': '10.0.0.1', 'dst_ip': '10.1.0.1', 'dst_port': '1234', 'packet_size': '12', 'attack_type': 'test', } ret_val = requests.post(url='http://127.0.0.1:9998/attack', params=attack) self.assertEquals(201, ret_val.status_code)
def __init__(self, topo, controllers, http_server_port): self.topo = topo self.packet_telemetry = PacketTelemetry() self.controllers = controllers self.http_server = SDNControllerServer(self, http_server_port) self.running = False
def setUp(self): """ Start HTTP server :return: """ self.http_server = SDNControllerServer(TestSDNController()) self.http_server.start() # TODO - sleeping to wait for the server to start. Look at the # http_server class to see if the start() call can bock time.sleep(1)
def __init__(self, topo, controllers, http_server_port, controller_user, ansible_inventory, ae_ip_str=None, drop_rpt_freq=10, is_delta=False): self.topo = topo self.controllers = controllers self.http_server = SDNControllerServer(self, http_server_port) self.drop_rpt_thread = threading.Thread(target=self.create_drop_report) self.ansible_inventory = ansible_inventory self.controller_user = controller_user self.running = False self.is_delta = is_delta if ae_ip_str: self.ae_ip = ipaddress.ip_address(ae_ip_str) else: self.ae_ip = None self.drop_rpt_freq = drop_rpt_freq self.drop_rpt_count = dict()
class DdosSdnController: """ SDN controller for quelling DDoS attacks """ def __init__(self, topo, controllers, http_server_port, controller_user, ansible_inventory, ae_ip_str=None, drop_rpt_freq=10, is_delta=False): self.topo = topo self.controllers = controllers self.http_server = SDNControllerServer(self, http_server_port) self.drop_rpt_thread = threading.Thread(target=self.create_drop_report) self.ansible_inventory = ansible_inventory self.controller_user = controller_user self.running = False self.is_delta = is_delta if ae_ip_str: self.ae_ip = ipaddress.ip_address(ae_ip_str) else: self.ae_ip = None self.drop_rpt_freq = drop_rpt_freq self.drop_rpt_count = dict() def start(self): logger.info('Starting Controllers - [%s]', self.controllers) for controller in self.controllers.values(): controller.start(self.ansible_inventory, self.controller_user) logger.info('Starting HTTP server on port - [%s]', self.http_server.port) self.http_server.start() self.drop_rpt_thread.start() self.__main_loop() def stop(self): logger.info('Stopping SDN controller') self.running = False self.http_server.stop() self.drop_rpt_thread.join() def create_drop_report(self): while True: self.send_drop_report() sleep(self.drop_rpt_freq) def send_drop_report(self): logger.info('Attempting to send drop report') if self.ae_ip: self.__create_drop_report() else: logger.debug('No configured AE IP to send drop report') def __create_drop_report(self): for controller in self.controllers.values(): if controller == self.get_agg_controller(): logger.info("Creating drop report for controller: %s", controller) drp_entries = controller.count_dropped_packets() for match_keys, drop_count in drp_entries: if match_keys != 0: logger.debug('Match keys - [%s]', match_keys) attack_key = tps_utils.create_attack_hash(**match_keys) logger.debug( 'Attack key value - [%s] and drop report delta ' 'mode - [%s]', attack_key, self.is_delta) if attack_key: if self.is_delta: old_count = self.drop_rpt_count.get(attack_key) logger.debug('Previous drop count - [%s]', old_count) if not old_count: old_count = 0 rpt_count = drop_count - old_count self.drop_rpt_count[attack_key] = drop_count else: rpt_count = drop_count self.__send_drop_pkt(attack_key, rpt_count) else: logger.debug('No drops to report') def __send_drop_pkt(self, attack_key, rpt_count): host_name = socket.gethostname() sdn_ip = socket.gethostbyname(host_name) ip_len = (IPV4_HDR_LEN + UDP_INT_HDR_LEN + DRPT_LEN + UDP_HDR_LEN + DRPT_PAYLOAD_LEN) udp_int_len = ip_len - IPV4_HDR_LEN udp_len = UDP_HDR_LEN + DRPT_PAYLOAD_LEN drop_pkt = Ether(type=consts.IPV4_TYPE) drop_pkt = drop_pkt / IP( dst=str(self.ae_ip), src=sdn_ip, len=ip_len, proto=consts.UDP_PROTO) drop_pkt = drop_pkt / UdpInt(sport=consts.UDP_INT_SRC_PORT, dport=consts.UDP_TRPT_DST_PORT, len=udp_int_len) drop_pkt = drop_pkt / DropReport( ver=consts.DRPT_VER, node_id=self.topo['switches']['aggregate']['int_id'], in_type=consts.DRPT_IN_TYPE, rpt_len=consts.DRPT_REP_LEN, md_len=consts.DRPT_MD_LEN, rep_md_bits=consts.DRPT_MD_BITS, domain_id=consts.TRPT_DOMAIN_ID, var_opt_bsmd=consts.DRPT_BS_MD, timestamp=int(time.time()), drop_count=rpt_count, drop_tbl_keys=attack_key) drop_pkt = drop_pkt / UDP(dport=consts.UDP_DRPT_DST_PORT, sport=consts.UDP_DRPT_SRC_PORT, len=udp_len) drop_pkt = drop_pkt / 'tps drop report' try: sendp(drop_pkt, verbose=2) logger.info( "Sent Drop Report with attack key - [%s], and count - [%s]", attack_key, rpt_count) except Exception as e: logger.info("Unable to send drop report - [%s]", e) def add_data_forward(self, df_req): """ Adds a data forward table entry into the expected switch """ self.__data_forward(df_req) def del_data_forward(self, df_req): """ Adds a data forward table entry into the expected switch """ self.__data_forward(df_req, True) def __data_forward(self, df_req, del_flag=False): """ Removes a device to mitigate an attack """ logger.debug('df_req - [%s]', df_req) for key, controller in self.controllers.items(): for switch in controller.switches: logger.debug('switch - [%s]', switch.device_id) if switch.mac == df_req['switch_mac']: if del_flag: switch.del_data_forward(df_req['dst_mac']) else: try: switch.add_data_forward(df_req['dst_mac'], df_req['output_port']) except Exception as e: if 'ALREADY_EXISTS' in str(e): pass else: raise e return logger.warning('Could not find switch with switch_mac - [%s]', df_req['switch_mac']) def add_data_inspection(self, di_req): """ Adds a data inspection table entry into the expected switch """ self.__data_inspection(di_req) def del_data_inspection(self, di_req): """ Adds a data inspection table entry into the expected switch """ self.__data_inspection(di_req, True) def __data_inspection(self, di_req, del_flag=False): """ Removes a device to mitigate an attack """ logger.debug('di_req - [%s]', di_req) for key, controller in self.controllers.items(): for switch in controller.switches: logger.debug('switch - [%s]', switch.device_id) if switch.mac == di_req['switch_mac']: if del_flag: switch.del_data_inspection( di_req['device_id'], di_req['device_mac']) else: switch.add_data_inspection( di_req['device_id'], di_req['device_mac']) return logger.warning('Could not find switch with device_id - [%s]', di_req['device_id']) def remove_attacker(self, attack): """ Removes a device to mitigate an attack :param attack: dict of attack """ host, gw_controller = self.__get_attack_host(attack) logger.info('Adding attack to gateways with host - [%s]', host) try: gw_controller.remove_attacker(attack, host) except Exception as e: logger.error( 'Error removing attacker to host - [%s] with error - [%s])', host, e) def add_attacker(self, attack): """ Adds a device to mitigate an attack :param attack: dict of attack """ host, gw_controller = self.__get_attack_host(attack) logger.info('Adding attack to gateways with host - [%s]', host) try: gw_controller.add_attacker(attack, host) except Exception as e: logger.error( 'Error adding attacker to host - [%s] with error - [%s])', host, e) raise e def remove_agg_attacker(self, attack): """ Removes a device to mitigate an attack :param attack: dict of attack """ agg_controller = self.get_agg_controller() logger.info('Removing attack from aggregate') if agg_controller: try: agg_controller.remove_attacker(attack, None) except Exception as e: logger.error('Error removing attacker with error - [%s])', e) else: logger.warning('Aggregate controller cannot stop the attack') def add_agg_attacker(self, attack): """ Adds a device to mitigate an attack :param attack: dict of attack """ agg_controller = self.get_agg_controller() logger.info('Adding attack to aggregate') if agg_controller: try: agg_controller.add_attacker(attack, None) except Exception as e: logger.error('Error adding attacker with error - [%s])', e) else: logger.warning('Aggregate controller cannot add attack') def activate_telem_rpt(self, request): """ Adds a device to mitigate an attack :param request: dict of the request """ core_controller = self.get_core_controller() logger.info('Activating telemetry report for core') if core_controller: try: core_controller.setup_telem_rpt(**request) except Exception as e: logger.error( 'Error setting up telemetry report with error - [%s])', e) else: logger.warning('Aggregate controller cannot add attack') def update_dflt_port(self, request): """ Updates the default port :param request: dict of the request """ logger.debug('request - [%s]', request) for key, controller in self.controllers.items(): for switch in controller.switches: logger.debug('switch - [%s]', switch.device_id) if switch.mac == request['switch_mac']: switch.update_default_port(request['port']) def update_mcast_grp(self, request): """ Updates the mcast ports :param request: dict of the request """ logger.debug('Update mcast request - [%s]', request) for key, controller in self.controllers.items(): for switch in controller.switches: logger.debug('switch mac - [%s], request mac - [%s]', switch.mac, request['switch_mac']) if switch.mac == request['switch_mac']: logger.debug('Update mcast on - [%s]', switch.mac) ports = ast.literal_eval(request['ports']) logger.debug('Ports to update - [%s]', ports) switch.update_arp_multicast(ports=ports) def get_mcast_grp_ports(self, request): """ Retrieve the mcast ports :param request: dict of the request """ logger.debug('Retrieve mcast ports - [%s]', request) for key, controller in self.controllers.items(): for switch in controller.switches: logger.debug('switch - [%s]', switch.device_id) if switch.mac == request['switch_mac']: return switch.get_arp_multicast_ports() def set_trpt_sampling_value(self, request): """ Adds a device to mitigate an attack :param request: dict of the request """ core_controller = self.get_core_controller() logger.info('Setting telemetry report sampling value to core') sample_size = request.get('sample') if core_controller and sample_size: try: logger.info('Adding sample_size - [%s]', sample_size) core_controller.set_trpt_sampling_value(int(sample_size)) except Exception as e: logger.error( 'Error setting TRPT sample value with error - [%s])', e) else: logger.warning('Aggregate controller cannot add attack') def deactivate_telem_rpt(self, request): """ Adds a device to mitigate an attack :param request: dict of the request """ core_controller = self.get_core_controller() logger.info('Deactivating telemetry report config to core') if core_controller: try: core_controller.setup_telem_rpt(**request) except Exception as e: logger.error('Error adding attacker with error - [%s])', e) else: logger.warning('Aggregate controller cannot add attack') def get_agg_controller(self): agg_controller = self.controllers.get(AGG_CTRL_KEY) return agg_controller def get_core_controller(self): core_controller = self.controllers.get(CORE_CTRL_KEY) return core_controller def __get_attack_host(self, attack): """ Returns the host value or None :param attack: :return: """ gateway_controller = self.controllers.get(GATEWAY_CTRL_KEY) if gateway_controller: logger.info('Attack received - %s', attack) conditions = {'mac': attack['src_mac']} logger.debug('Created conditions - [%s]', conditions) values = self.topo.get('hosts').values() logger.debug('Creating host with values - [%s]', values) host = list(filter( lambda item: all( (item[k] == v for (k, v) in conditions.items())), values)) logger.debug( 'Check the hosts and register the attack with host object ' '- [%s]', host) logger.debug('host.__class__ - [%s]', host.__class__) if len(host) > 0: logger.debug('host len is - [%s]', len(host)) return host[0], gateway_controller else: logger.error('No Device Matches MAC [%s]', attack.get('src_mac')) else: logger.warning('No Gateway Controller call') def __main_loop(self): """ Starts polling thread/Error adding attacker to host """ logger.info('Starting thread') self.running = True try: while self.running: logger.info('SDN Controller heartbeat') sleep(10) except KeyboardInterrupt: logger.warning(' Shutting down.')
class DdosSdnController: """ SDN controller for quelling DDoS attacks """ def __init__(self, topo, controllers, http_server_port): self.topo = topo self.packet_telemetry = PacketTelemetry() self.controllers = controllers self.http_server = SDNControllerServer(self, http_server_port) self.running = False def start(self, add_di): logger.info('Starting Controllers - [%s]', self.controllers) for controller in self.controllers.values(): controller.start() logger.debug('Making switch rules with data inspection - [%s]', add_di) self.__make_switch_rules(add_di) self.__build_skeleton_packet_telemetry() logger.info('Starting HTTP server on port - [%s]', self.http_server.port) self.http_server.start() self.__main_loop() def stop(self): logger.info('Stopping SDN controller') self.running = False self.http_server.stop() def __make_switch_rules(self, add_di): """ Creates the rules for each controller :return: """ logger.info('Creating switch rules on #%s different controllers', len(self.controllers)) for controller in self.controllers.values(): controller.make_switch_rules(add_di) logger.debug('Switch rule creation completed') def __build_skeleton_packet_telemetry(self): logger.info('Building skeleton packet telemetry') for host, host_info in self.topo['hosts'].items(): self.packet_telemetry.add_host( host_info['id'], host_info['mac'], host_info['name'], host_info['type']) for switch, switch_info in self.topo['switches'].items(): self.packet_telemetry.add_switch( switch_info['id'], switch_info['mac'], switch_info['name'], switch_info['type']) for switch, switch_info in self.topo['switches'].items(): conditions = {'north_node': switch} south_facing_links = filter( lambda item: all( (item[k] == v for (k, v) in conditions.items())), self.topo.get('links') ) for link in south_facing_links: device_name = link.get('south_node') if self.topo.get('hosts').get(device_name) is None: if self.topo.get('switches').get(device_name) is None: if self.topo.get('external').get(device_name) is None: logger.warning( 'Unknown device type in link %s', device_name) else: logger.info( 'Ignoring Externals for packet telemetry') else: device = self.topo.get('switches').get(device_name) self.packet_telemetry.add_child( switch_info['id'], device['id']) logger.debug( 'Added Switch %s southbound of %s', device.get('name'), switch_info.get('name')) else: device = self.topo.get('hosts').get(device_name) self.packet_telemetry.add_child( switch_info['id'], device['id']) logger.debug( 'Added Device %s southbound of %s' % (device.get('name'), switch_info.get('name'))) def add_attacker(self, attack): """ Adds a device to perform an attack :param attack: dict of attack """ gateway_controller = self.controllers.get(GATEWAY_CTRL_KEY) if gateway_controller: logger.info('Attack received - %s', attack) conditions = {'mac': attack['src_mac']} values = self.topo.get('hosts').values() host = filter( lambda item: all( (item[k] == v for (k, v) in conditions.items())), values) if len(host) != 0: host = host[0] logger.info('Adding attack to gateways') try: gateway_controller.add_attacker(attack, host) self.packet_telemetry.register_attack(host['id']) except Exception as e: logger.error( 'Error adding attacker to host - [%s] with error - ' '[%s])', host['name'], e) else: logger.error('No Device Matches MAC [%s]', attack.get('src_mac')) else: logger.warn('No Gateway Controller call') def __main_loop(self): """ Starts polling thread/Error adding attacker to host """ logger.info('Starting thread') self.running = True try: while self.running: logger.info('SDN Controller heartbeat') sleep(10) except KeyboardInterrupt: logger.warning(' Shutting down.')