/* Author: pavel.odintsov@gmail.com */ /* License: GPLv2 */ #include #include #include #include #include #include #include #include #include #include #include #include // struct arphdr #include #include #include #include #include #include #include #include #include "bgp_protocol.hpp" #include "fast_library.h" #include "fastnetmon_packet_parser.h" #include "fastnetmon_types.h" #include "libpatricia/patricia.h" #include "packet_storage.h" // Here we store variables which differs for different paltforms #include "fast_platform.h" #include "fastnetmon_logic.hpp" #ifdef ENABLE_DPI #include "fast_dpi.h" #endif #ifdef FASTNETMON_API #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif // __GNUC__ #include "abstract_subnet_counters.hpp" #include "fastnetmon.grpc.pb.h" #include #ifdef __GNUC__ #pragma GCC diagnostic pop #endif // __GNUC__ #endif // Plugins #include "netflow_plugin/netflow_collector.h" #include "pcap_plugin/pcap_collector.h" #include "sflow_plugin/sflow_collector.h" #ifdef NETMAP_PLUGIN #include "netmap_plugin/netmap_collector.h" #endif #ifdef PF_RING #include "pfring_plugin/pfring_collector.h" #endif #ifdef FASTNETMON_ENABLE_AFPACKET #include "afpacket_plugin/afpacket_collector.h" #endif #ifdef ENABLE_GOBGP #include "actions/gobgp_action.h" #endif // Yes, maybe it's not an good idea but with this we can guarantee working code in example plugin #include "example_plugin/example_collector.h" #include #include #include #include #include #include #include #include #include #include #include #include "all_logcpp_libraries.h" // Boost libs #include #include #ifdef GEOIP #include "GeoIP.h" #endif #ifdef REDIS #include #endif #include "packet_bucket.h" #include "ban_list.hpp" #include "metrics/graphite.hpp" #include "metrics/influxdb.hpp" #ifdef FASTNETMON_API using fastmitigation::BanListReply; using fastmitigation::BanListRequest; using fastmitigation::Fastnetmon; using grpc::Server; using grpc::ServerBuilder; using grpc::ServerContext; using grpc::Status; std::unique_ptr api_server; bool enable_api = false; #endif time_t last_call_of_traffic_recalculation; std::string cli_stats_file_path = "/tmp/fastnetmon.dat"; std::string cli_stats_ipv6_file_path = "/tmp/fastnetmon_ipv6.dat"; unsigned int stats_thread_sleep_time = 3600; unsigned int stats_thread_initial_call_delay = 30; // Each this seconds we will check about available data in bucket unsigned int check_for_availible_for_processing_packets_buckets = 1; // Current time with pretty low precision, we use separate thread to update it time_t current_inaccurate_time = 0; // This is thread safe storage for captured from the wire packets for IPv6 traffic packet_buckets_storage_t packet_buckets_ipv6_storage; unsigned int recalculate_speed_timeout = 1; // We will remove all packet buckets which runs longer than this time. This value used only for one shot buckets. // Infinite bucket's will not removed unsigned int maximum_time_since_bucket_start_to_remove = 120; FastnetmonPlatformConfigurtion fastnetmon_platform_configuration; // Send or not any details about attack for ban script call over stdin bool notify_script_pass_details = true; bool notify_script_enabled = true; // We could collect attack dumps in pcap format bool collect_attack_pcap_dumps = false; // We could process this dumps with DPI bool process_pcap_attack_dumps_with_dpi = false; bool unban_only_if_attack_finished = true; logging_configuration_t logging_configuration; // Global map with parsed config file configuration_map_t configuration_map; // Every X seconds we will run ban list cleaner thread // If customer uses ban_time smaller than this value we will use ban_time/2 as unban_iteration_sleep_time int unban_iteration_sleep_time = 60; bool unban_enabled = true; #ifdef ENABLE_DPI struct ndpi_detection_module_struct* my_ndpi_struct = NULL; u_int32_t ndpi_size_flow_struct = 0; u_int32_t ndpi_size_id_struct = 0; #endif #ifdef ENABLE_GOBGP bool gobgp_enabled = false; #endif #ifdef MONGO std::string mongodb_host = "localhost"; unsigned int mongodb_port = 27017; bool mongodb_enabled = false; std::string mongodb_database_name = "fastnetmon"; #endif /* Configuration block, we must move it to configuration file */ #ifdef REDIS unsigned int redis_port = 6379; std::string redis_host = "127.0.0.1"; // redis key prefix std::string redis_prefix = ""; // because it's additional and very specific feature we should disable it by default bool redis_enabled = false; #endif bool monitor_local_ip_addresses = true; // Enable monitoring for OpenVZ VPS IP addresses by reading their list from kernel bool monitor_openvz_vps_ip_addresses = false; // Trigger for enable or disable traffic counting for whole subnets bool enable_subnet_counters = false; // We will announce whole subnet instead single IP with BGP if this flag enabled bool exabgp_announce_whole_subnet = false; // We will announce only /32 host bool exabgp_announce_host = false; // With this flag we will announce more specfic then whole block Flow Spec announces bool exabgp_flow_spec_announces = false; ban_settings_t global_ban_settings; void init_global_ban_settings() { // ban Configuration params global_ban_settings.enable_ban_for_pps = false; global_ban_settings.enable_ban_for_bandwidth = false; global_ban_settings.enable_ban_for_flows_per_second = false; // We must ban IP if it exceeed this limit in PPS global_ban_settings.ban_threshold_pps = 20000; // We must ban IP of it exceed this limit for number of flows in any direction global_ban_settings.ban_threshold_flows = 3500; // We must ban client if it exceed 1GBps global_ban_settings.ban_threshold_mbps = 1000; // Disable per protocol thresholds too global_ban_settings.enable_ban_for_tcp_pps = false; global_ban_settings.enable_ban_for_tcp_bandwidth = false; global_ban_settings.enable_ban_for_udp_pps = false; global_ban_settings.enable_ban_for_udp_bandwidth = false; global_ban_settings.enable_ban_for_icmp_pps = false; global_ban_settings.enable_ban_for_icmp_bandwidth = false; // Ban enable/disable flag global_ban_settings.enable_ban = true; } bool enable_connection_tracking = true; bool enable_afpacket_collection = false; bool enable_data_collection_from_mirror = true; bool enable_netmap_collection = false; bool enable_sflow_collection = false; bool enable_netflow_collection = false; bool enable_pcap_collection = false; // Time consumed by reaclculation for all IPs struct timeval speed_calculation_time; // Time consumed by drawing stats for all IPs struct timeval drawing_thread_execution_time; // Global thread group for packet capture threads boost::thread_group packet_capture_plugin_thread_group; // Global thread group for service processes (speed recalculation, // screen updater and ban list cleaner) boost::thread_group service_thread_group; // Total number of hosts in our networks // We need this as global variable because it's very important value for configuring data structures unsigned int total_number_of_hosts_in_our_networks = 0; #ifdef GEOIP GeoIP* geo_ip = NULL; #endif // IPv4 lookup trees patricia_tree_t *lookup_tree_ipv4, *whitelist_tree_ipv4; // IPv6 lookup trees patricia_tree_t *lookup_tree_ipv6, *whitelist_tree_ipv6; bool DEBUG = 0; // flag about dumping all packets to log bool DEBUG_DUMP_ALL_PACKETS = false; // dump "other" packets bool DEBUG_DUMP_OTHER_PACKETS = false; // Period for update screen for console version of tool unsigned int check_period = 3; // Standard ban time in seconds for all attacks but you can tune this value int global_ban_time = 1800; // We calc average pps/bps for this time double average_calculation_amount = 15; // We calc average pps/bps for subnets with this time, we use longer value for calculation average network traffic double average_calculation_amount_for_subnets = 30; // Show average or absolute value of speed bool print_average_traffic_counts = true; // Key used for sorting clients in output. Allowed sort params: packets/bytes/flows std::string sort_parameter = "packets"; // Number of lines in program output unsigned int max_ips_in_list = 7; // Number of lines for sending ben attack details to email unsigned int ban_details_records_count = 50; // We haven't option for configure it with configuration file unsigned int number_of_packets_for_pcap_attack_dump = 500; // log file log4cpp::Category& logger = log4cpp::Category::getRoot(); // We store all active BGP Flow Spec announces here active_flow_spec_announces_t active_flow_spec_announces; /* Configuration block ends */ // We count total number of incoming/outgoing/internal and other traffic type packets/bytes // And initilize by 0 all fields total_counter_element_t total_counters[4]; total_counter_element_t total_speed_counters[4]; total_counter_element_t total_speed_average_counters[4]; // IPv6 versions of total counters total_counter_element_t total_counters_ipv6[4]; total_counter_element_t total_speed_counters_ipv6[4]; total_counter_element_t total_speed_average_counters_ipv6[4]; // Total amount of non parsed packets uint64_t total_unparsed_packets = 0; uint64_t total_unparsed_packets_speed = 0; // Total amount of IPv4 packets uint64_t total_ipv4_packets = 0; // Total amount of IPv6 packets uint64_t total_ipv6_packets = 0; // Number of non IPv4/IPv6 packets received by us uint64_t non_ip_packets = 0; // Total number of times when we executed process_packet() uint64_t total_simple_packets_processed = 0; // IPv6 traffic which belongs to our own networks uint64_t our_ipv6_packets = 0; uint64_t incoming_total_flows_speed = 0; uint64_t outgoing_total_flows_speed = 0; map_of_vector_counters_t SubnetVectorMap; // Network counters for IPv6 abstract_subnet_counters_t ipv6_subnet_counters; // Host counters for IPv6 abstract_subnet_counters_t ipv6_host_counters; // Here we store taffic per subnet map_for_subnet_counters_t PerSubnetCountersMap; // Here we store traffic speed per subnet map_for_subnet_counters_t PerSubnetSpeedMap; // Here we store average speed per subnet map_for_subnet_counters_t PerSubnetAverageSpeedMap; // Flow tracking structures map_of_vector_counters_for_flow_t SubnetVectorMapFlow; /* End of our data structs */ boost::mutex ban_list_details_mutex; boost::mutex ban_list_mutex; std::mutex flow_counter; // map for flows std::map FlowCounter; // Struct for string speed per IP map_of_vector_counters_t SubnetVectorMapSpeed; // Struct for storing average speed per IP for specified interval map_of_vector_counters_t SubnetVectorMapSpeedAverage; #ifdef GEOIP map_for_counters GeoIpCounter; #endif // IPv6 hosts blackhole_ban_list_t ban_list_ipv6_ng; // In ddos info we store attack power and direction std::map ban_list; std::map > ban_list_details; host_group_map_t host_groups; // Here we store assignment from subnet to certain host group for fast lookup subnet_to_host_group_map_t subnet_to_host_groups; host_group_ban_settings_map_t host_group_ban_settings_map; std::vector our_networks; std::vector whitelist_networks; // ExaBGP support flag bool exabgp_enabled = false; std::string exabgp_community = ""; // We could use separate communities for subnet and host announces std::string exabgp_community_subnet = ""; std::string exabgp_community_host = ""; std::string exabgp_command_pipe = "/var/run/exabgp.cmd"; std::string exabgp_next_hop = ""; // Graphite monitoring bool graphite_enabled = false; std::string graphite_host = "127.0.0.1"; unsigned short int graphite_port = 2003; unsigned int graphite_push_period = 1; // Time consumed by pushing data to Graphite struct timeval graphite_thread_execution_time; // Default graphite namespace std::string graphite_prefix = "fastnetmon"; // Total number of InfluxDB writes uint64_t influxdb_writes_total = 0; // Total number of failed InfluxDB writes uint64_t influxdb_writes_failed = 0; // InfluxDB bool influxdb_enabled = false; std::string influxdb_database = "fastnetmon"; std::string influxdb_host = "127.0.0.1"; unsigned short int influxdb_port = 8086; bool influxdb_auth = false; std::string influxdb_user = ""; std::string influxdb_password = ""; unsigned int influxdb_push_period = 1; bool process_incoming_traffic = true; bool process_outgoing_traffic = true; // Prototypes #ifdef ENABLE_DPI void init_current_instance_of_ndpi(); #endif logging_configuration_t read_logging_settings(configuration_map_t configuration_map); std::string get_amplification_attack_type(amplification_attack_type_t attack_type); std::string generate_flow_spec_for_amplification_attack(amplification_attack_type_t amplification_attack_type, std::string destination_ip); ban_settings_t read_ban_settings(configuration_map_t configuration_map, std::string host_group_name = ""); bool load_configuration_file(); void free_up_all_resources(); void interruption_signal_handler(int signal_number); #ifdef FASTNETMON_API void silent_logging_function(gpr_log_func_args* args) { // We do not want any logging here } // We could not define this variable in top of the file because we should define class before FastnetmonApiServiceImpl api_service; std::unique_ptr StartupApiServer() { std::string server_address("127.0.0.1:50052"); ServerBuilder builder; // Listen on the given address without any authentication mechanism. builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); // Register "service" as the instance through which we'll communicate with // clients. In this case it corresponds to an *synchronous* service. builder.RegisterService(&api_service); // Finally assemble the server. std::unique_ptr current_api_server(builder.BuildAndStart()); logger << log4cpp::Priority::INFO << "API server listening on " << server_address; return current_api_server; } void RunApiServer() { logger << log4cpp::Priority::INFO << "Launch API server"; api_server = StartupApiServer(); // Wait for the server to shutdown. Note that some other thread must be // responsible for shutting down the server for this call to ever return. api_server->Wait(); logger << log4cpp::Priority::INFO << "API server got shutdown signal"; } #endif void sigpipe_handler_for_popen(int signo) { logger << log4cpp::Priority::ERROR << "Sorry but we experienced error with popen. " << "Please check your scripts. They should receive data on stdin! Optionally you could disable " "passing any details with configuration param: notify_script_pass_details = no"; // Well, we do not need exit here because we have another options to notifying about atatck // exit(1); } #ifdef GEOIP bool geoip_init() { // load GeoIP ASN database to memory geo_ip = GeoIP_open("/root/fastnetmon/GeoIPASNum.dat", GEOIP_MEMORY_CACHE); if (geo_ip == NULL) { return false; } else { return true; } } #endif // TODO: move to lirbary // read whole file to vector std::vector read_file_to_vector(std::string file_name) { std::vector data; std::string line; std::ifstream reading_file; reading_file.open(file_name.c_str(), std::ifstream::in); if (reading_file.is_open()) { while (getline(reading_file, line)) { boost::algorithm::trim(line); data.push_back(line); } } else { logger << log4cpp::Priority::ERROR << "Can't open file: " << file_name; } return data; } void parse_hostgroups(std::string name, std::string value) { // We are creating new host group of subnets if (name != "hostgroup") { return; } std::vector splitted_new_host_group; // We have new host groups in form: // hostgroup = new_host_group_name:11.22.33.44/32,.... split(splitted_new_host_group, value, boost::is_any_of(":"), boost::token_compress_on); if (splitted_new_host_group.size() != 2) { logger << log4cpp::Priority::ERROR << "We can't parse new host group"; return; } boost::algorithm::trim(splitted_new_host_group[0]); boost::algorithm::trim(splitted_new_host_group[1]); std::string host_group_name = splitted_new_host_group[0]; if (host_groups.count(host_group_name) > 0) { logger << log4cpp::Priority::WARN << "We already have this host group (" << host_group_name << "). Please check!"; return; } // Split networks std::vector hostgroup_subnets = split_strings_to_vector_by_comma(splitted_new_host_group[1]); for (std::vector::iterator itr = hostgroup_subnets.begin(); itr != hostgroup_subnets.end(); ++itr) { subnet_cidr_mask_t subnet = convert_subnet_from_string_to_binary_with_cidr_format(*itr); host_groups[host_group_name].push_back(subnet); logger << log4cpp::Priority::WARN << "We add subnet " << convert_subnet_to_string(subnet) << " to host group " << host_group_name; // And add to subnet to host group lookup hash if (subnet_to_host_groups.count(subnet) > 0) { // Huston, we have problem! Subnet to host group mapping should map single subnet to single group! logger << log4cpp::Priority::WARN << "Seems you have specified single subnet " << *itr << " to multiple host groups, please fix it, it's prohibited"; } else { subnet_to_host_groups[subnet] = host_group_name; } } logger << log4cpp::Priority::INFO << "We have created host group " << host_group_name << " with " << host_groups[host_group_name].size() << " subnets"; } // Load configuration bool load_configuration_file() { std::ifstream config_file(fastnetmon_platform_configuration.global_config_path.c_str()); std::string line; if (!config_file.is_open()) { logger << log4cpp::Priority::ERROR << "Can't open config file"; return false; } while (getline(config_file, line)) { std::vector parsed_config; boost::algorithm::trim(line); if (line.find("#") == 0 or line.empty()) { // Ignore comments line continue; } boost::split(parsed_config, line, boost::is_any_of("="), boost::token_compress_on); if (parsed_config.size() == 2) { boost::algorithm::trim(parsed_config[0]); boost::algorithm::trim(parsed_config[1]); configuration_map[parsed_config[0]] = parsed_config[1]; // Well, we parse host groups here parse_hostgroups(parsed_config[0], parsed_config[1]); } else { logger << log4cpp::Priority::ERROR << "Can't parse config line: '" << line << "'"; } } if (configuration_map.count("enable_connection_tracking")) { if (configuration_map["enable_connection_tracking"] == "on") { enable_connection_tracking = true; } else { enable_connection_tracking = false; } } if (configuration_map.count("ban_time") != 0) { global_ban_time = convert_string_to_integer(configuration_map["ban_time"]); // Completely disable unban option if (global_ban_time == 0) { unban_enabled = false; } } if (configuration_map.count("pid_path") != 0) { fastnetmon_platform_configuration.pid_path = configuration_map["pid_path"]; } if (configuration_map.count("cli_stats_file_path") != 0) { cli_stats_file_path = configuration_map["cli_stats_file_path"]; } if (configuration_map.count("cli_stats_ipv6_file_path") != 0) { cli_stats_ipv6_file_path = configuration_map["cli_stats_ipv6_file_path"]; } if (configuration_map.count("unban_only_if_attack_finished") != 0) { if (configuration_map["unban_only_if_attack_finished"] == "on") { unban_only_if_attack_finished = true; } else { unban_only_if_attack_finished = false; } } if (configuration_map.count("graphite_prefix") != 0) { graphite_prefix = configuration_map["graphite_prefix"]; } if (configuration_map.count("average_calculation_time") != 0) { average_calculation_amount = convert_string_to_integer(configuration_map["average_calculation_time"]); } if (configuration_map.count("average_calculation_time_for_subnets") != 0) { average_calculation_amount_for_subnets = convert_string_to_integer(configuration_map["average_calculation_time_for_subnets"]); } if (configuration_map.count("speed_calculation_delay") != 0) { recalculate_speed_timeout = convert_string_to_integer(configuration_map["speed_calculation_delay"]); } if (configuration_map.count("monitor_local_ip_addresses") != 0) { monitor_local_ip_addresses = configuration_map["monitor_local_ip_addresses"] == "on" ? true : false; } if (configuration_map.count("monitor_openvz_vps_ip_addresses") != 0) { monitor_openvz_vps_ip_addresses = configuration_map["monitor_openvz_vps_ip_addresses"] == "on" ? true : false; } #ifdef FASTNETMON_API if (configuration_map.count("enable_api") != 0) { enable_api = configuration_map["enable_api"] == "on"; } #endif #ifdef ENABLE_GOBGP // GoBGP configuration if (configuration_map.count("gobgp") != 0) { gobgp_enabled = configuration_map["gobgp"] == "on"; } #endif // ExaBGP configuration if (configuration_map.count("exabgp") != 0) { if (configuration_map["exabgp"] == "on") { exabgp_enabled = true; } else { exabgp_enabled = false; } } if (exabgp_enabled) { // TODO: add community format validation if (configuration_map.count("exabgp_community")) { exabgp_community = configuration_map["exabgp_community"]; } if (configuration_map.count("exabgp_community_subnet")) { exabgp_community_subnet = configuration_map["exabgp_community_subnet"]; } else { exabgp_community_subnet = exabgp_community; } if (configuration_map.count("exabgp_community_host")) { exabgp_community_host = configuration_map["exabgp_community_host"]; } else { exabgp_community_host = exabgp_community; } if (exabgp_enabled && exabgp_announce_whole_subnet && exabgp_community_subnet.empty()) { logger << log4cpp::Priority::ERROR << "You enabled exabgp for subnet but not specified community, we disable exabgp support"; exabgp_enabled = false; } if (exabgp_enabled && exabgp_announce_host && exabgp_community_host.empty()) { logger << log4cpp::Priority::ERROR << "You enabled exabgp for host but not specified community, we disable exabgp support"; exabgp_enabled = false; } } if (exabgp_enabled) { exabgp_command_pipe = configuration_map["exabgp_command_pipe"]; if (exabgp_command_pipe.empty()) { logger << log4cpp::Priority::ERROR << "You enabled exabgp but not specified " "exabgp_command_pipe, so we disable exabgp " "support"; exabgp_enabled = false; } } if (exabgp_enabled) { exabgp_next_hop = configuration_map["exabgp_next_hop"]; if (exabgp_next_hop.empty()) { logger << log4cpp::Priority::ERROR << "You enabled exabgp but not specified exabgp_next_hop, so we disable exabgp support"; exabgp_enabled = false; } if (configuration_map.count("exabgp_flow_spec_announces") != 0) { exabgp_flow_spec_announces = configuration_map["exabgp_flow_spec_announces"] == "on"; } if (exabgp_enabled) { logger << log4cpp::Priority::INFO << "ExaBGP support initialized correctly"; } } if (configuration_map.count("sflow") != 0) { if (configuration_map["sflow"] == "on") { enable_sflow_collection = true; } else { enable_sflow_collection = false; } } if (configuration_map.count("netflow") != 0) { if (configuration_map["netflow"] == "on") { enable_netflow_collection = true; } else { enable_netflow_collection = false; } } if (configuration_map.count("exabgp_announce_whole_subnet") != 0) { exabgp_announce_whole_subnet = configuration_map["exabgp_announce_whole_subnet"] == "on" ? true : false; } if (configuration_map.count("exabgp_announce_host") != 0) { exabgp_announce_host = configuration_map["exabgp_announce_host"] == "on" ? true : false; } if (configuration_map.count("enable_subnet_counters") != 0) { enable_subnet_counters = configuration_map["enable_subnet_counters"] == "on" ? true : false; } // Graphite if (configuration_map.count("graphite") != 0) { graphite_enabled = configuration_map["graphite"] == "on" ? true : false; } if (configuration_map.count("graphite_host") != 0) { graphite_host = configuration_map["graphite_host"]; } if (configuration_map.count("graphite_port") != 0) { graphite_port = convert_string_to_integer(configuration_map["graphite_port"]); } // InfluxDB if (configuration_map.count("influxdb") != 0) { influxdb_enabled = configuration_map["influxdb"] == "on" ? true : false; } if (configuration_map.count("influxdb_port") != 0) { influxdb_port = convert_string_to_integer(configuration_map["influxdb_port"]); } if (configuration_map.count("influxdb_host") != 0) { influxdb_host = configuration_map["influxdb_host"]; } if (configuration_map.count("influxdb_database") != 0) { influxdb_database = configuration_map["influxdb_database"]; } if (configuration_map.count("influxdb_auth") != 0) { influxdb_auth = configuration_map["influxdb_auth"] == "on" ? true : false; } if (configuration_map.count("influxdb_user") != 0) { influxdb_user = configuration_map["influxdb_user"]; } if (configuration_map.count("influxdb_password") != 0) { influxdb_password = configuration_map["influxdb_password"]; } if (configuration_map.count("process_incoming_traffic") != 0) { process_incoming_traffic = configuration_map["process_incoming_traffic"] == "on" ? true : false; } if (configuration_map.count("process_outgoing_traffic") != 0) { process_outgoing_traffic = configuration_map["process_outgoing_traffic"] == "on" ? true : false; } if (configuration_map.count("mirror") != 0) { if (configuration_map["mirror"] == "on") { enable_data_collection_from_mirror = true; } else { enable_data_collection_from_mirror = false; } } if (configuration_map.count("mirror_netmap") != 0) { if (configuration_map["mirror_netmap"] == "on") { enable_netmap_collection = true; } else { enable_netmap_collection = false; } } if (configuration_map.count("mirror_afpacket") != 0) { enable_afpacket_collection = configuration_map["mirror_afpacket"] == "on"; } if (enable_netmap_collection && enable_data_collection_from_mirror) { logger << log4cpp::Priority::ERROR << "You have enabled pfring and netmap data collection " "from mirror which strictly prohibited, please " "select one"; exit(1); } if (configuration_map.count("pcap") != 0) { if (configuration_map["pcap"] == "on") { enable_pcap_collection = true; } else { enable_pcap_collection = false; } } // Read global ban configuration global_ban_settings = read_ban_settings(configuration_map, ""); logging_configuration = read_logging_settings(configuration_map); // logger << log4cpp::Priority::INFO << "We read global ban settings: " << print_ban_thresholds(global_ban_settings); // Read host group ban settings for (host_group_map_t::iterator hostgroup_itr = host_groups.begin(); hostgroup_itr != host_groups.end(); ++hostgroup_itr) { std::string host_group_name = hostgroup_itr->first; logger << log4cpp::Priority::INFO << "We will read ban settings for " << host_group_name; host_group_ban_settings_map[host_group_name] = read_ban_settings(configuration_map, host_group_name); // logger << log4cpp::Priority::INFO << "We read " << host_group_name << " ban settings " // << print_ban_thresholds(host_group_ban_settings_map[ host_group_name ]); } if (configuration_map.count("white_list_path") != 0) { fastnetmon_platform_configuration.white_list_path = configuration_map["white_list_path"]; } if (configuration_map.count("networks_list_path") != 0) { fastnetmon_platform_configuration.networks_list_path = configuration_map["networks_list_path"]; } #ifdef REDIS if (configuration_map.count("redis_port") != 0) { redis_port = convert_string_to_integer(configuration_map["redis_port"]); } if (configuration_map.count("redis_host") != 0) { redis_host = configuration_map["redis_host"]; } if (configuration_map.count("redis_prefix") != 0) { redis_prefix = configuration_map["redis_prefix"]; } if (configuration_map.count("redis_enabled") != 0) { // We use yes and on because it's stupid typo :( if (configuration_map["redis_enabled"] == "on" or configuration_map["redis_enabled"] == "yes") { redis_enabled = true; } else { redis_enabled = false; } } #endif #ifdef MONGO if (configuration_map.count("mongodb_enabled") != 0) { if (configuration_map["mongodb_enabled"] == "on") { mongodb_enabled = true; } } if (configuration_map.count("mongodb_host") != 0) { mongodb_host = configuration_map["mongodb_host"]; } if (configuration_map.count("mongodb_port") != 0) { mongodb_port = convert_string_to_integer(configuration_map["mongodb_port"]); } if (configuration_map.count("mongodb_database_name") != 0) { mongodb_database_name = configuration_map["mongodb_database_name"]; } #endif if (configuration_map.count("ban_details_records_count") != 0) { ban_details_records_count = convert_string_to_integer(configuration_map["ban_details_records_count"]); } if (configuration_map.count("check_period") != 0) { check_period = convert_string_to_integer(configuration_map["check_period"]); } if (configuration_map.count("sort_parameter") != 0) { sort_parameter = configuration_map["sort_parameter"]; } if (configuration_map.count("max_ips_in_list") != 0) { max_ips_in_list = convert_string_to_integer(configuration_map["max_ips_in_list"]); } if (configuration_map.count("notify_script_path") != 0) { fastnetmon_platform_configuration.notify_script_path = configuration_map["notify_script_path"]; } if (configuration_map.count("notify_script_pass_details") != 0) { notify_script_pass_details = configuration_map["notify_script_pass_details"] == "on" ? true : false; } if (file_exists(fastnetmon_platform_configuration.notify_script_path)) { notify_script_enabled = true; } else { logger << log4cpp::Priority::ERROR << "We can't find notify script " << fastnetmon_platform_configuration.notify_script_path; notify_script_enabled = false; } if (configuration_map.count("collect_attack_pcap_dumps") != 0) { collect_attack_pcap_dumps = configuration_map["collect_attack_pcap_dumps"] == "on" ? true : false; } if (configuration_map.count("process_pcap_attack_dumps_with_dpi") != 0) { if (collect_attack_pcap_dumps) { process_pcap_attack_dumps_with_dpi = configuration_map["process_pcap_attack_dumps_with_dpi"] == "on" ? true : false; } } return true; } /* Enable core dumps for simplify debug tasks */ void enable_core_dumps() { struct rlimit rlim; int result = getrlimit(RLIMIT_CORE, &rlim); if (result) { logger << log4cpp::Priority::ERROR << "Can't get current rlimit for RLIMIT_CORE"; return; } else { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } } void subnet_vectors_allocator(prefix_t* prefix, void* data) { // Network byte order uint32_t subnet_as_integer = prefix->add.sin.s_addr; u_short bitlen = prefix->bitlen; double base = 2; int network_size_in_ips = pow(base, 32 - bitlen); // logger<< log4cpp::Priority::INFO<<"Subnet: "<add.sin.s_addr<<" network size: // "<first; std::fill(itr->second.begin(), itr->second.end(), zero_map_element); } } bool load_our_networks_list() { if (file_exists(fastnetmon_platform_configuration.white_list_path)) { unsigned int network_entries = 0; std::vector network_list_from_config = read_file_to_vector(fastnetmon_platform_configuration.white_list_path); for (std::vector::iterator ii = network_list_from_config.begin(); ii != network_list_from_config.end(); ++ii) { std::string text_subnet = *ii; if (text_subnet.empty()) { continue; } if (is_v4_host(text_subnet)) { logger << log4cpp::Priority::INFO << "Assuming /32 netmask for " << text_subnet; text_subnet += "/32"; } else if (!is_cidr_subnet(text_subnet)) { logger << log4cpp::Priority::ERROR << "Can't parse line from whitelist: " << text_subnet; continue; } network_entries++; make_and_lookup(whitelist_tree_ipv4, const_cast(text_subnet.c_str())); } logger << log4cpp::Priority::INFO << "We loaded " << network_entries << " networks from whitelist file"; } std::vector networks_list_ipv4_as_string; std::vector networks_list_ipv6_as_string; // We can build list of our subnets automatically here if (monitor_openvz_vps_ip_addresses && file_exists("/proc/vz/version")) { logger << log4cpp::Priority::INFO << "We found OpenVZ"; // Add /32 CIDR mask for every IP here std::vector openvz_ips = read_file_to_vector("/proc/vz/veip"); for (std::vector::iterator ii = openvz_ips.begin(); ii != openvz_ips.end(); ++ii) { // skip header if (strstr(ii->c_str(), "Version") != NULL) { continue; } /* Example data for this lines: 2a03:f480:1:17:0:0:0:19 0 185.4.72.40 0 */ if (strstr(ii->c_str(), ":") == NULL) { // IPv4 std::vector subnet_as_string; split(subnet_as_string, *ii, boost::is_any_of(" "), boost::token_compress_on); std::string openvz_subnet = subnet_as_string[1] + "/32"; networks_list_ipv4_as_string.push_back(openvz_subnet); } else { // IPv6 std::vector subnet_as_string; split(subnet_as_string, *ii, boost::is_any_of(" "), boost::token_compress_on); std::string openvz_subnet = subnet_as_string[1] + "/128"; networks_list_ipv6_as_string.push_back(openvz_subnet); } } logger << log4cpp::Priority::INFO << "We loaded " << networks_list_ipv4_as_string.size() << " IPv4 networks from /proc/vz/veip"; logger << log4cpp::Priority::INFO << "We loaded " << networks_list_ipv6_as_string.size() << " IPv6 networks from /proc/vz/veip"; } if (monitor_local_ip_addresses && file_exists("/sbin/ip")) { logger << log4cpp::Priority::INFO << "We are working on Linux and could use ip tool for detecting local IP's"; ip_addresses_list_t ip_list = get_local_ip_v4_addresses_list(); logger << log4cpp::Priority::INFO << "We found " << ip_list.size() << " local IP addresses and will monitor they"; for (ip_addresses_list_t::iterator iter = ip_list.begin(); iter != ip_list.end(); ++iter) { // TODO: add IPv6 here networks_list_ipv4_as_string.push_back(*iter + "/32"); } } if (file_exists(fastnetmon_platform_configuration.networks_list_path)) { std::vector network_list_from_config = read_file_to_vector(fastnetmon_platform_configuration.networks_list_path); for (std::vector::iterator line_itr = network_list_from_config.begin(); line_itr != network_list_from_config.end(); ++line_itr) { if (line_itr->length() == 0) { // Skip blank lines in subnet list file silently continue; } if (strstr(line_itr->c_str(), ":") == NULL) { networks_list_ipv4_as_string.push_back(*line_itr); } else { networks_list_ipv6_as_string.push_back(*line_itr); } } logger << log4cpp::Priority::INFO << "We loaded " << network_list_from_config.size() << " networks from networks file"; } // Some consistency checks assert(convert_ip_as_string_to_uint("255.255.255.0") == convert_cidr_to_binary_netmask(24)); assert(convert_ip_as_string_to_uint("255.255.255.255") == convert_cidr_to_binary_netmask(32)); logger << log4cpp::Priority::INFO << "Totally we have " << networks_list_ipv4_as_string.size() << " IPv4 subnets"; logger << log4cpp::Priority::INFO << "Totally we have " << networks_list_ipv6_as_string.size() << " IPv6 subnets"; for (std::vector::iterator ii = networks_list_ipv4_as_string.begin(); ii != networks_list_ipv4_as_string.end(); ++ii) { if (!is_cidr_subnet(*ii)) { logger << log4cpp::Priority::ERROR << "Can't parse line from subnet list: '" << *ii << "'"; continue; } std::string network_address_in_cidr_form = *ii; unsigned int cidr_mask = get_cidr_mask_from_network_as_string(network_address_in_cidr_form); std::string network_address = get_net_address_from_network_as_string(network_address_in_cidr_form); double base = 2; total_number_of_hosts_in_our_networks += pow(base, 32 - cidr_mask); // Make sure it's "subnet address" and not an host address uint32_t subnet_address_as_uint = convert_ip_as_string_to_uint(network_address); uint32_t subnet_address_netmask_binary = convert_cidr_to_binary_netmask(cidr_mask); uint32_t generated_subnet_address = subnet_address_as_uint & subnet_address_netmask_binary; if (subnet_address_as_uint != generated_subnet_address) { std::string new_network_address_as_string = convert_ip_as_uint_to_string(generated_subnet_address) + "/" + convert_int_to_string(cidr_mask); logger << log4cpp::Priority::WARN << "We will use " << new_network_address_as_string << " instead of " << network_address_in_cidr_form << " because it's host address"; network_address_in_cidr_form = new_network_address_as_string; } make_and_lookup(lookup_tree_ipv4, const_cast(network_address_in_cidr_form.c_str())); } for (std::vector::iterator ii = networks_list_ipv6_as_string.begin(); ii != networks_list_ipv6_as_string.end(); ++ii) { // TODO: add IPv6 subnet format validation make_and_lookup_ipv6(lookup_tree_ipv6, (char*)ii->c_str()); } logger << log4cpp::Priority::INFO << "Total number of monitored hosts (total size of all networks): " << total_number_of_hosts_in_our_networks; // 3 - speed counter, average speed counter and data counter uint64_t memory_requirements = 3 * sizeof(map_element_t) * total_number_of_hosts_in_our_networks / 1024 / 1024; logger << log4cpp::Priority::INFO << "We need " << memory_requirements << " MB of memory for storing counters for your networks"; /* Preallocate data structures */ patricia_process(lookup_tree_ipv4, (void_fn_t)subnet_vectors_allocator); logger << log4cpp::Priority::INFO << "We start total zerofication of counters"; zeroify_all_counters(); logger << log4cpp::Priority::INFO << "We finished zerofication"; logger << log4cpp::Priority::INFO << "We loaded " << networks_list_ipv4_as_string.size() << " IPv4 subnets to our in-memory list of networks"; return true; } #ifdef GEOIP unsigned int get_asn_for_ip(uint32_t ip) { char* asn_raw = GeoIP_org_by_name(geo_ip, convert_ip_as_uint_to_string(remote_ip).c_str()); uint32_t asn_number = 0; if (asn_raw == NULL) { asn_number = 0; } else { // split string: AS1299 TeliaSonera International Carrier std::vector asn_as_string; split(asn_as_string, asn_raw, boost::is_any_of(" "), boost::token_compress_on); // free up original string free(asn_raw); // extract raw number asn_number = convert_string_to_integer(asn_as_string[0].substr(2)); } return asn_number; } #endif // It's vizualization thread :) void screen_draw_ipv4_thread() { // we need wait one second for calculating speed by recalculate_speed //#include // prctl(PR_SET_NAME , "fastnetmon calc thread", 0, 0, 0); // Sleep for a half second for shift against calculatiuon thread boost::this_thread::sleep(boost::posix_time::milliseconds(500)); while (true) { // Available only from boost 1.54: boost::this_thread::sleep_for( // boost::chrono::seconds(check_period) ); boost::this_thread::sleep(boost::posix_time::seconds(check_period)); traffic_draw_ipv4_program(); } } // It's vizualization thread :) void screen_draw_ipv6_thread() { // we need wait one second for calculating speed by recalculate_speed //#include // prctl(PR_SET_NAME , "fastnetmon calc thread", 0, 0, 0); // Sleep for a half second for shift against calculatiuon thread boost::this_thread::sleep(boost::posix_time::milliseconds(500)); while (true) { // Available only from boost 1.54: boost::this_thread::sleep_for( // boost::chrono::seconds(check_period) ); boost::this_thread::sleep(boost::posix_time::seconds(check_period)); traffic_draw_ipv6_program(); } } void recalculate_speed_thread_handler() { while (true) { // recalculate data every one second // Available only from boost 1.54: boost::this_thread::sleep_for( boost::chrono::seconds(1) // ); boost::this_thread::sleep(boost::posix_time::seconds(recalculate_speed_timeout)); recalculate_speed(); } } bool file_is_appendable(std::string path) { std::ofstream check_appendable_file; check_appendable_file.open(path.c_str(), std::ios::app); if (check_appendable_file.is_open()) { // all fine, just close file check_appendable_file.close(); return true; } else { return false; } } void init_logging() { // So log4cpp will never notify you if it could not write to log file due to permissions issues // We will check it manually if (!file_is_appendable(fastnetmon_platform_configuration.log_file_path)) { std::cerr << "Can't open log file " << fastnetmon_platform_configuration.log_file_path << " for writing! Please check file and folder permissions" << std::endl; exit(EXIT_FAILURE); } log4cpp::PatternLayout* layout = new log4cpp::PatternLayout(); layout->setConversionPattern("%d [%p] %m%n"); log4cpp::Appender* appender = new log4cpp::FileAppender("default", fastnetmon_platform_configuration.log_file_path); appender->setLayout(layout); logger.setPriority(log4cpp::Priority::INFO); logger.addAppender(appender); logger << log4cpp::Priority::INFO << "Logger initialized!"; } void reconfigure_logging() { log4cpp::PatternLayout* layout = new log4cpp::PatternLayout(); layout->setConversionPattern("[%p] %m%n"); if (logging_configuration.local_syslog_logging) { log4cpp::Appender* local_syslog_appender = new log4cpp::SyslogAppender("fastnetmon", "fastnetmon", LOG_USER); local_syslog_appender->setLayout(layout); logger.addAppender(local_syslog_appender); logger << log4cpp::Priority::INFO << "We start local syslog logging corectly"; } if (logging_configuration.remote_syslog_logging) { log4cpp::Appender* remote_syslog_appender = new log4cpp::RemoteSyslogAppender("fastnetmon", "fastnetmon", logging_configuration.remote_syslog_server, LOG_USER, logging_configuration.remote_syslog_port); remote_syslog_appender->setLayout(layout); logger.addAppender(remote_syslog_appender); logger << log4cpp::Priority::INFO << "We start remote syslog logging corectly"; } } // Call fork function int do_fork() { int status = 0; switch (fork()) { case 0: // It's child break; case -1: /* fork failed */ status = -1; break; default: // We should close master process with _exit(0) // We should not call exit() because it will destroy all global variables for program _exit(0); } return status; } void redirect_fds() { // Close stdin, stdout and stderr close(0); close(1); close(2); if (open("/dev/null", O_RDWR) != 0) { // We can't notify anybody now exit(1); } // Create copy of zero decriptor for 1 and 2 fd's // We do not need return codes here but we need do it for suppressing complaints from compiler int first_dup_result = dup(0); int second_dup_result = dup(0); } int main(int argc, char** argv) { bool daemonize = false; bool only_configuration_check = false; namespace po = boost::program_options; try { // clang-format off po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") ("version", "show version") ("daemonize", "detach from the terminal") ("configuration_check", "check configuration and exit") ("configuration_file", po::value(),"set path to custom configuration file") ("log_file", po::value(), "set path to custom log file"); // clang-format on po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); if (vm.count("help")) { std::cout << desc << std::endl; exit(EXIT_SUCCESS); } if (vm.count("version")) { std::cout << "Version: " << fastnetmon_platform_configuration.fastnetmon_version << std::endl; exit(EXIT_SUCCESS); } if (vm.count("daemonize")) { daemonize = true; } if (vm.count("configuration_check")) { only_configuration_check = true; } if (vm.count("configuration_file")) { fastnetmon_platform_configuration.global_config_path = vm["configuration_file"].as(); std::cout << "We will use custom path to configuration file: " << fastnetmon_platform_configuration.global_config_path << std::endl; } if (vm.count("log_file")) { fastnetmon_platform_configuration.log_file_path = vm["log_file"].as(); std::cout << "We will use custom path to log file: " << fastnetmon_platform_configuration.log_file_path << std::endl; } } catch (po::error& e) { std::cerr << "ERROR: " << e.what() << std::endl << std::endl; exit(EXIT_FAILURE); } // We use ideas from here https://github.com/bmc/daemonize/blob/master/daemon.c if (daemonize) { int status = 0; printf("We will run in daemonized mode\n"); if ((status = do_fork()) < 0) { // fork failed status = -1; } else if (setsid() < 0) { // Create new session status = -1; } else if ((status = do_fork()) < 0) { status = -1; } else { // Clear inherited umask umask(0); // Chdir to root int chdir_result = chdir("/"); // close all descriptors because we are daemon! redirect_fds(); } } // enable core dumps enable_core_dumps(); init_logging(); #ifdef FASTNETMON_API gpr_set_log_function(silent_logging_function); #endif // Set default ban configuration init_global_ban_settings(); // We should read configurartion file _after_ logging initialization bool load_config_result = load_configuration_file(); if (!load_config_result) { std::cerr << "Can't open config file " << fastnetmon_platform_configuration.global_config_path << " please create it!" << std::endl; exit(1); } if (only_configuration_check) { logger << log4cpp::Priority::INFO << "Configuration file is correct. Shutdown toolkit"; exit(0); } if (file_exists(fastnetmon_platform_configuration.pid_path)) { pid_t pid_from_file = 0; if (read_pid_from_file(pid_from_file, fastnetmon_platform_configuration.pid_path)) { // We could read pid if (pid_from_file > 0) { // We use signal zero for check process existence int kill_result = kill(pid_from_file, 0); if (kill_result == 0) { logger << log4cpp::Priority::ERROR << "FastNetMon is already running with pid: " << pid_from_file; exit(1); } else { // Yes, we have pid with pid but it's zero } } else { // pid from file is broken, we assume tool is not running } } else { // We can't open file, let's assume it's broken and tool is not running } } else { // no pid file } // If we not failed in check steps we could run toolkit bool print_pid_to_file_result = print_pid_to_file(getpid(), fastnetmon_platform_configuration.pid_path); if (!print_pid_to_file_result) { logger << log4cpp::Priority::ERROR << "Could not create pid file, please check permissions: " << fastnetmon_platform_configuration.pid_path; exit(EXIT_FAILURE); } #ifdef ENABLE_DPI init_current_instance_of_ndpi(); #endif lookup_tree_ipv4 = New_Patricia(32); whitelist_tree_ipv4 = New_Patricia(32); lookup_tree_ipv6 = New_Patricia(128); whitelist_tree_ipv6 = New_Patricia(128); // nullify total counters for (int index = 0; index < 4; index++) { total_counters[index].bytes = 0; total_counters[index].packets = 0; total_speed_counters[index].bytes = 0; total_speed_counters[index].packets = 0; total_speed_average_counters[index].bytes = 0; total_speed_average_counters[index].packets = 0; } /* Create folder for attack details */ if (!folder_exists(fastnetmon_platform_configuration.attack_details_folder)) { int mkdir_result = mkdir(fastnetmon_platform_configuration.attack_details_folder.c_str(), S_IRWXU); if (mkdir_result != 0) { logger << log4cpp::Priority::ERROR << "Can't create folder for attack details: " << fastnetmon_platform_configuration.attack_details_folder; exit(1); } } if (getenv("DUMP_ALL_PACKETS") != NULL) { DEBUG_DUMP_ALL_PACKETS = true; } if (getenv("DUMP_OTHER_PACKETS") != NULL) { DEBUG_DUMP_OTHER_PACKETS = true; } if (sizeof(packed_conntrack_hash_t) != sizeof(uint64_t) or sizeof(packed_conntrack_hash_t) != 8) { logger << log4cpp::Priority::INFO << "Assertion about size of packed_conntrack_hash, it's " << sizeof(packed_conntrack_hash_t) << " instead 8"; exit(1); } logger << log4cpp::Priority::INFO << "Read configuration file"; // Reconfigure logging. We will enable specific logging methods here reconfigure_logging(); load_our_networks_list(); // Set capacity for nested buffers packet_buckets_ipv6_storage.set_buffers_capacity(ban_details_records_count); // Setup CTRL+C handler if (signal(SIGINT, interruption_signal_handler) == SIG_ERR) { logger << log4cpp::Priority::ERROR << "Can't setup SIGINT handler"; exit(1); } /* Without this SIGPIPE error could shutdown toolkit on call of exec_with_stdin_params */ if (signal(SIGPIPE, sigpipe_handler_for_popen) == SIG_ERR) { logger << log4cpp::Priority::ERROR << "Can't setup SIGPIPE handler"; exit(1); } #ifdef GEOIP // Init GeoIP if (!geoip_init()) { logger << log4cpp::Priority::ERROR << "Can't load geoip tables"; exit(1); } #endif // Init previous run date time(&last_call_of_traffic_recalculation); // We call init for each action #ifdef ENABLE_GOBGP if (gobgp_enabled) { gobgp_action_init(); } #endif #ifdef FASTNETMON_API if (enable_api) { service_thread_group.add_thread(new boost::thread(RunApiServer)); } #endif // Set inaccurate time value which will be used in process_packet() from capture backends time(¤t_inaccurate_time); // Start system speed recalculation thread service_thread_group.add_thread(new boost::thread(system_counters_speed_thread_handler)); // Run screen draw thread for IPv4 service_thread_group.add_thread(new boost::thread(screen_draw_ipv4_thread)); // Run screen draw thread for IPv6 service_thread_group.add_thread(new boost::thread(screen_draw_ipv6_thread)); // Graphite export thread if (graphite_enabled) { service_thread_group.add_thread(new boost::thread(graphite_push_thread)); } // InfluxDB export thread if (influxdb_enabled) { service_thread_group.add_thread(new boost::thread(influxdb_push_thread)); } // start thread for recalculating speed in realtime service_thread_group.add_thread(new boost::thread(recalculate_speed_thread_handler)); // Run banlist cleaner thread if (unban_enabled) { service_thread_group.add_thread(new boost::thread(cleanup_ban_list)); } // This thread will check about filled buckets with packets and process they auto check_traffic_buckets_thread = new boost::thread(check_traffic_buckets); set_boost_process_name(check_traffic_buckets_thread, "check_buckets"); service_thread_group.add_thread(check_traffic_buckets_thread); #ifdef PF_RING if (enable_data_collection_from_mirror) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_pfring_collection, process_packet)); } #endif #ifdef NETMAP_PLUGIN // netmap processing if (enable_netmap_collection) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_netmap_collection, process_packet)); } #endif #ifdef FASTNETMON_ENABLE_AFPACKET if (enable_afpacket_collection) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_afpacket_collection, process_packet)); } #endif if (enable_sflow_collection) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_sflow_collection, process_packet)); } if (enable_netflow_collection) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_netflow_collection, process_packet)); } if (enable_pcap_collection) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_pcap_collection, process_packet)); } // Wait for all threads in capture thread group packet_capture_plugin_thread_group.join_all(); // Wait for all service threads service_thread_group.join_all(); free_up_all_resources(); return 0; } void free_up_all_resources() { #ifdef GEOIP // Free up geoip handle GeoIP_delete(geo_ip); #endif Destroy_Patricia(lookup_tree_ipv4, (void_fn_t)0); Destroy_Patricia(whitelist_tree_ipv4, (void_fn_t)0); Destroy_Patricia(lookup_tree_ipv6, (void_fn_t)0); Destroy_Patricia(whitelist_tree_ipv6, (void_fn_t)0); } // For correct program shutdown by CTRL+C void interruption_signal_handler(int signal_number) { logger << log4cpp::Priority::INFO << "SIGNAL captured, prepare toolkit shutdown"; #ifdef FASTNETMON_API logger << log4cpp::Priority::INFO << "Send shutdown command to API server"; api_server->Shutdown(); #endif logger << log4cpp::Priority::INFO << "Interrupt service threads"; service_thread_group.interrupt_all(); logger << log4cpp::Priority::INFO << "Wait while they finished"; service_thread_group.join_all(); logger << log4cpp::Priority::INFO << "Interrupt packet capture treads"; packet_capture_plugin_thread_group.interrupt_all(); logger << log4cpp::Priority::INFO << "Wait while they finished"; packet_capture_plugin_thread_group.join_all(); logger << log4cpp::Priority::INFO << "Shutdown main process"; // TODO: we should REMOVE this exit command and wait for correct toolkit shutdown exit(1); }