コード例 #1
0
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)
コード例 #2
0
    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
コード例 #3
0
    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)
コード例 #4
0
    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()
コード例 #5
0
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.')
コード例 #6
0
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.')