Ipv6 callback (#888)

* Added proper storage for captured IPv6 packets

* Added final logic to trigger IPv6 attacks
This commit is contained in:
Pavel Odintsov 2020-11-24 23:48:26 +00:00 committed by GitHub
parent ff69f6f42a
commit 3e9060e6b8
Signed by: GitHub
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 241 additions and 3 deletions

@ -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;
}

@ -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);

@ -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

@ -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(&current_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);
}

@ -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;