diff --git a/src/fast_library.cpp b/src/fast_library.cpp index bca5807c..3c2d277d 100644 --- a/src/fast_library.cpp +++ b/src/fast_library.cpp @@ -1428,3 +1428,14 @@ std::string convert_any_ip_to_string(subnet_ipv6_cidr_mask_t subnet) { return print_ipv6_cidr_subnet(subnet); } +// Return true if we have this IP in patricia tree +bool ip_belongs_to_patricia_tree_ipv6(patricia_tree_t* patricia_tree, struct in6_addr client_ipv6_address) { + prefix_t prefix_for_check_address; + + prefix_for_check_address.family = AF_INET6; + prefix_for_check_address.bitlen = 128; + prefix_for_check_address.add.sin6 = client_ipv6_address; + + return patricia_search_best2(patricia_tree, &prefix_for_check_address, 1) != NULL; +} + diff --git a/src/fast_library.h b/src/fast_library.h index c65e32ce..06357a84 100644 --- a/src/fast_library.h +++ b/src/fast_library.h @@ -103,6 +103,7 @@ subnet_cidr_mask_t convert_subnet_from_string_to_binary_with_cidr_format(std::st bool manage_interface_promisc_mode(std::string interface_name, bool switch_on); #endif +bool ip_belongs_to_patricia_tree_ipv6(patricia_tree_t* patricia_tree, struct in6_addr client_ipv6_address); std::string serialize_attack_description(attack_details_t& current_attack); attack_type_t detect_attack_type(attack_details_t& current_attack); std::string get_printable_attack_name(attack_type_t attack); diff --git a/src/fastnetmon.conf b/src/fastnetmon.conf index 63d74d68..cbd173eb 100644 --- a/src/fastnetmon.conf +++ b/src/fastnetmon.conf @@ -17,6 +17,9 @@ logging:remote_syslog_port = 514 # Enable/Disable any actions in case of attack enable_ban = on +# Enable ban for IPv6 +enable_ban_ipv6 = on + # disable processing for certain direction of traffic process_incoming_traffic = on process_outgoing_traffic = on diff --git a/src/fastnetmon_logic.cpp b/src/fastnetmon_logic.cpp index 5b6c3fcb..b7f7a11c 100644 --- a/src/fastnetmon_logic.cpp +++ b/src/fastnetmon_logic.cpp @@ -506,6 +506,12 @@ std::string print_ban_thresholds(ban_settings_t current_ban_settings) { output_buffer << "We call ban script: no\n"; } + if (current_ban_settings.enable_ban_ipv6) { + output_buffer << "We call ban script for IPv6: yes\n"; + } else { + output_buffer << "We call ban script for IPv6: no\n"; + } + output_buffer << "Packets per second: "; if (current_ban_settings.enable_ban_for_pps) { output_buffer << current_ban_settings.ban_threshold_pps; @@ -604,6 +610,10 @@ ban_settings_t read_ban_settings(configuration_map_t configuration_map, std::str ban_settings.enable_ban = configuration_map[prefix + "enable_ban"] == "on"; } + if (configuration_map.count(prefix + "enable_ban_ipv6") != 0) { + ban_settings.enable_ban_ipv6 = configuration_map[prefix + "enable_ban_ipv6"] == "on"; + } + if (configuration_map.count(prefix + "ban_for_pps") != 0) { ban_settings.enable_ban_for_pps = configuration_map[prefix + "ban_for_pps"] == "on"; } @@ -2304,10 +2314,212 @@ void traffic_draw_ipv4_program() { timeval_subtract(&drawing_thread_execution_time, &end_calc_time, &start_calc_time); } +std::string get_human_readable_threshold_type(attack_detection_threshold_type_t detecttion_type) { + if (detecttion_type == attack_detection_threshold_type_t::unknown) { + return "unknown"; + } else if (detecttion_type == attack_detection_threshold_type_t::packets_per_second) { + return "packets per second"; + } else if (detecttion_type == attack_detection_threshold_type_t::bytes_per_second) { + return "bytes per second"; + } else if (detecttion_type == attack_detection_threshold_type_t::flows_per_second) { + return "flows per second"; + } else if (detecttion_type == attack_detection_threshold_type_t::tcp_packets_per_second) { + return "tcp packets per second"; + } else if (detecttion_type == attack_detection_threshold_type_t::tcp_syn_packets_per_second) { + return "tcp syn packets per second"; + } else if (detecttion_type == attack_detection_threshold_type_t::tcp_syn_bytes_per_second) { + return "tcp syn bytes per second"; + } else if (detecttion_type == attack_detection_threshold_type_t::udp_packets_per_second) { + return "udp packets per second"; + } else if (detecttion_type == attack_detection_threshold_type_t::icmp_packets_per_second) { + return "icmp packets per second"; + } else if (detecttion_type == attack_detection_threshold_type_t::tcp_bytes_per_second) { + return "tcp bytes per second"; + } else if (detecttion_type == attack_detection_threshold_type_t::udp_bytes_per_second) { + return "udp bytes per second"; + } else if (detecttion_type == attack_detection_threshold_type_t::icmp_bytes_per_second) { + return "icmp bytes per second"; + } + + return "unknown"; +} + + +// This function fills attack information from different information sources +bool fill_attack_information(map_element_t average_speed_element, + attack_details_t& current_attack, + std::string& host_group_name, + std::string& parent_host_group_name, + bool unban_enabled, + int ban_time) { + uint64_t pps = 0; + + uint64_t in_pps = average_speed_element.in_packets; + uint64_t out_pps = average_speed_element.out_packets; + uint64_t in_bps = average_speed_element.in_bytes; + uint64_t out_bps = average_speed_element.out_bytes; + uint64_t in_flows = average_speed_element.in_flows; + uint64_t out_flows = average_speed_element.out_flows; + + direction_t data_direction; + + // TODO: move this logic to different function!!! + + // Detect attack direction with simple heuristic + if (abs(int((int)in_pps - (int)out_pps)) < 1000) { + // If difference between pps speed is so small we should do additional + // investigation using + // bandwidth speed + if (in_bps > out_bps) { + data_direction = INCOMING; + pps = in_pps; + } else { + data_direction = OUTGOING; + pps = out_pps; + } + } else { + if (in_pps > out_pps) { + data_direction = INCOMING; + pps = in_pps; + } else { + data_direction = OUTGOING; + pps = out_pps; + } + } + + current_attack.attack_protocol = detect_attack_protocol(average_speed_element, data_direction); + + current_attack.host_group = host_group_name; + current_attack.parent_host_group = parent_host_group_name; + + std::string data_direction_as_string = get_direction_name(data_direction); + + logger << log4cpp::Priority::INFO << "We run attack block code with following params" + << " in: " << in_pps << " pps " << convert_speed_to_mbps(in_bps) << " mbps" + << " out: " << out_pps << " pps " << convert_speed_to_mbps(out_bps) << " mbps" + << " and we decided it's " << data_direction_as_string << " attack"; + + // Store ban time + time(¤t_attack.ban_timestamp); + // set ban time in seconds + current_attack.ban_time = ban_time; + current_attack.unban_enabled = unban_enabled; + + // Pass main information about attack + current_attack.attack_direction = data_direction; + current_attack.attack_power = pps; + current_attack.max_attack_power = pps; + + current_attack.in_packets = in_pps; + current_attack.out_packets = out_pps; + + current_attack.in_bytes = in_bps; + current_attack.out_bytes = out_bps; + + // pass flow information + current_attack.in_flows = in_flows; + current_attack.out_flows = out_flows; + + current_attack.fragmented_in_packets = average_speed_element.fragmented_in_packets; + current_attack.tcp_in_packets = average_speed_element.tcp_in_packets; + current_attack.tcp_syn_in_packets = average_speed_element.tcp_syn_in_packets; + current_attack.udp_in_packets = average_speed_element.udp_in_packets; + current_attack.icmp_in_packets = average_speed_element.icmp_in_packets; + + current_attack.fragmented_out_packets = average_speed_element.fragmented_out_packets; + current_attack.tcp_out_packets = average_speed_element.tcp_out_packets; + current_attack.tcp_syn_out_packets = average_speed_element.tcp_syn_out_packets; + current_attack.udp_out_packets = average_speed_element.udp_out_packets; + current_attack.icmp_out_packets = average_speed_element.icmp_out_packets; + + current_attack.fragmented_out_bytes = average_speed_element.fragmented_out_bytes; + current_attack.tcp_out_bytes = average_speed_element.tcp_out_bytes; + current_attack.tcp_syn_out_bytes = average_speed_element.tcp_syn_out_bytes; + current_attack.udp_out_bytes = average_speed_element.udp_out_bytes; + current_attack.icmp_out_bytes = average_speed_element.icmp_out_bytes; + + current_attack.fragmented_in_bytes = average_speed_element.fragmented_in_bytes; + current_attack.tcp_in_bytes = average_speed_element.tcp_in_bytes; + current_attack.tcp_syn_in_bytes = average_speed_element.tcp_syn_in_bytes; + current_attack.udp_in_bytes = average_speed_element.udp_in_bytes; + current_attack.icmp_in_bytes = average_speed_element.icmp_in_bytes; + + current_attack.average_in_packets = average_speed_element.in_packets; + current_attack.average_in_bytes = average_speed_element.in_bytes; + current_attack.average_in_flows = average_speed_element.in_flows; + + current_attack.average_out_packets = average_speed_element.out_packets; + current_attack.average_out_bytes = average_speed_element.out_bytes; + current_attack.average_out_flows = average_speed_element.out_flows; + + return true; +} + + // Speed recalculation function for IPv6 hosts calls it for each host during speed recalculation void speed_callback_ipv6(subnet_ipv6_cidr_mask_t* current_subnet, map_element_t* current_average_speed_element) { - // TODO: attack action logic should be implemented here - return; + // We should check thresholds only for per host counters for IPv6 and only when any ban actions for IPv6 traffic were enabled + if (!global_ban_settings.enable_ban_ipv6) { + return; + } + + // We support only global group + std::string host_group_name = "global"; + + attack_detection_threshold_type_t attack_detection_source; + attack_detection_direction_type_t attack_detection_direction; + + bool should_ban = we_should_ban_this_entity(current_average_speed_element, global_ban_settings, + attack_detection_source, attack_detection_direction); + + if (!should_ban) { + return; + } + + // This code works only for /128 subnets + bool in_white_list = ip_belongs_to_patricia_tree_ipv6(whitelist_tree_ipv6, current_subnet->subnet_address); + + if (in_white_list) { + // logger << log4cpp::Priority::INFO << "This IP was whitelisted"; + return; + } + + bool we_already_have_buckets_for_this_ip = packet_buckets_ipv6_storage.we_have_bucket_for_this_ip(*current_subnet); + + if (we_already_have_buckets_for_this_ip) { + return; + } + + bool this_ip_is_already_banned = ban_list_ipv6_ng.is_blackholed(*current_subnet); + + if (this_ip_is_already_banned) { + return; + } + + std::string ddos_detection_threshold_as_string = get_human_readable_threshold_type(attack_detection_source); + + logger << log4cpp::Priority::INFO << "We have detected IPv6 attack for " << print_ipv6_cidr_subnet(*current_subnet) + << " with " << ddos_detection_threshold_as_string << " threshold host group: " << host_group_name; + + std::string parent_group; + + attack_details_t attack_details; + fill_attack_information(*current_average_speed_element, attack_details, host_group_name, parent_group, + unban_enabled, global_ban_time); + + attack_details.ipv6 = true; + // TODO: Also, we should find IPv6 network for attack here + + bool enable_backet_capture = + packet_buckets_ipv6_storage.enable_packet_capture(*current_subnet, attack_details, collection_pattern_t::ONCE); + + if (!enable_backet_capture) { + logger << log4cpp::Priority::ERROR << "Could not enable packet capture for deep analytics for IPv6 " + << print_ipv6_cidr_subnet(*current_subnet); + return; + } + + logger << log4cpp::Priority::INFO << "Enabled packet capture for IPv6 " << print_ipv6_address(current_subnet->subnet_address); } diff --git a/src/fastnetmon_types.h b/src/fastnetmon_types.h index 1bf5bde9..903dbdb4 100644 --- a/src/fastnetmon_types.h +++ b/src/fastnetmon_types.h @@ -169,6 +169,13 @@ class attack_details_t : public map_element_t { customer_network.subnet_address = 0; customer_network.cidr_prefix_length = 0; } + + // Host group for this attack + std::string host_group; + + // Parent hostgroup for host's host group + std::string parent_host_group; + direction_t attack_direction; // first attackpower detected uint64_t attack_power; @@ -189,6 +196,9 @@ class attack_details_t : public map_element_t { bool unban_enabled; int ban_time; // seconds of the ban + // If this attack was detected for IPv6 protocol + bool ipv6 = false; + subnet_cidr_mask_t customer_network; packet_storage_t pcap_attack_dump; @@ -258,7 +268,7 @@ class packed_conntrack_hash_t { class ban_settings_t { public: ban_settings_t() - : enable_ban(false), enable_ban_for_pps(false), enable_ban_for_bandwidth(false), + : enable_ban(false), enable_ban_ipv6(false), enable_ban_for_pps(false), enable_ban_for_bandwidth(false), enable_ban_for_flows_per_second(false), enable_ban_for_tcp_pps(false), enable_ban_for_tcp_bandwidth(false), enable_ban_for_udp_pps(false), enable_ban_for_udp_bandwidth(false), enable_ban_for_icmp_pps(false), @@ -267,6 +277,7 @@ class ban_settings_t { ban_threshold_icmp_pps(0), ban_threshold_mbps(0), ban_threshold_flows(0), ban_threshold_pps(0) { } bool enable_ban; + bool enable_ban_ipv6; bool enable_ban_for_pps; bool enable_ban_for_bandwidth;