fastnetmon-ng/fastnetmon.cpp

1576 lines
52 KiB
C++
Raw Normal View History

2013-10-18 12:16:55 +02:00
/*
TODO:
2013-11-15 15:35:44 +01:00
1) Add network average load for 30 second/60 and 5 minutes
2014-06-09 11:33:43 +02:00
2) Migrate ban list to blacklist struct
3) Enable work as standard linux user with CAP Admin
4) Migrate belongs_to_network to prefix bitwise tree
5) http://hg.python.org/cpython/file/3fa1414ce505/Lib/heapq.py#l183 - поиск топ 10
6) Try libsparsehash-dev
2013-10-18 12:16:55 +02:00
*/
2013-10-21 11:43:54 +02:00
/* Author: pavel.odintsov@gmail.com */
/* License: GPLv2 */
2013-10-18 12:16:55 +02:00
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
2013-10-21 11:43:54 +02:00
#include <time.h>
2013-10-18 12:16:55 +02:00
#include <sys/socket.h>
#include <sys/resource.h>
2013-10-18 12:16:55 +02:00
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
2013-10-21 11:43:54 +02:00
#include <netinet/if_ether.h>
#include <netinet/in.h>
2013-10-18 12:16:55 +02:00
#include <algorithm>
#include <iostream>
#include <map>
2014-06-09 11:08:19 +02:00
#include <fstream>
2013-11-15 15:35:44 +01:00
2014-06-09 13:53:31 +02:00
// It's buggy, http://www.stableit.ru/2013/11/unorderedmap-c11-debian-wheezy.html
// #include <unordered_map>
2013-11-15 15:35:44 +01:00
2013-10-18 12:16:55 +02:00
#include <vector>
#include <utility>
#include <sstream>
2013-10-21 21:45:33 +02:00
// C++ 11
2013-10-21 17:10:35 +02:00
#include <thread>
2013-10-21 21:45:33 +02:00
#include <chrono>
#include <mutex>
2013-10-21 16:47:31 +02:00
2013-11-15 15:35:44 +01:00
// Boost lib for strings split
2013-10-18 12:16:55 +02:00
#include <boost/algorithm/string.hpp>
2013-10-18 12:59:58 +02:00
#ifdef ULOG2
2013-10-18 12:16:55 +02:00
#include "libipulog.h"
2013-10-18 12:59:58 +02:00
#endif
2013-10-18 12:16:55 +02:00
2013-12-28 20:11:20 +01:00
#ifdef GEOIP
#include "GeoIP.h"
#endif
2013-10-18 13:35:50 +02:00
#ifdef PCAP
#include <pcap.h>
#endif
2013-10-20 00:10:19 +02:00
#ifdef REDIS
#include <hiredis/hiredis.h>
#endif
2013-10-18 12:21:41 +02:00
2014-03-11 17:19:20 +01:00
#ifdef PF_RING
#include "pfring.h"
#endif
2013-10-21 11:43:54 +02:00
using namespace std;
2014-03-11 20:48:15 +01:00
/* 802.1Q VLAN tags are 4 bytes long. */
#define VLAN_HDRLEN 4
/* Complete list of ethertypes: http://en.wikipedia.org/wiki/EtherType */
2014-03-11 20:48:15 +01:00
/* This is the decimal equivalent of the VLAN tag's ether frame type */
#define VLAN_ETHERTYPE 0x8100
/* This is ethertype code for IP protocol */
#define IP_ETHERTYPE 0x0800
#define IP6_ETHERTYPE 0x86dd
#define ARP_ETHERTYPE 0x0806
2014-03-11 20:48:15 +01:00
2013-10-21 11:43:54 +02:00
/*
2013-10-18 12:16:55 +02:00
Pcap docs:
http://www.linuxforu.com/2011/02/capturing-packets-c-program-libpcap/
2014-06-09 13:53:31 +02:00
http://vichargrave.com/develop-a-packet-sniffer-with-libpcap/ pcap parser
2013-10-18 12:16:55 +02:00
*/
string work_on_interfaces = "";
2013-11-15 15:35:44 +01:00
/* Configuration block, we must move it to configuration file */
2013-10-21 12:27:43 +02:00
#ifdef REDIS
int redis_port = 6379;
string redis_host = "127.0.0.1";
// because it's additional and very specific feature we should disable it by default
bool redis_enabled = false;
2013-10-21 12:27:43 +02:00
#endif
2013-10-18 12:16:55 +02:00
2013-12-28 20:11:20 +01:00
#ifdef GEOIP
GeoIP * geo_ip = NULL;
#endif
2013-10-21 16:47:31 +02:00
#ifdef ULOG2
2013-11-15 15:35:44 +01:00
// netlink group number for listening for traffic
2013-10-21 16:47:31 +02:00
int ULOGD_NLGROUP_DEFAULT = 1;
/* Size of the socket receive memory. Should be at least the same size as the 'nlbufsiz' module loadtime parameter of ipt_ULOG.o If you have _big_ in-kernel queues, you may have to increase this number. (
* --qthreshold 100 * 1500 bytes/packet = 150kB */
2013-11-15 15:35:44 +01:00
2013-10-21 16:47:31 +02:00
int ULOGD_RMEM_DEFAULT = 131071;
/* Size of the receive buffer for the netlink socket. Should be at least of RMEM_DEFAULT size. */
int ULOGD_BUFSIZE_DEFAULT = 150000;
#endif
2013-10-21 12:27:43 +02:00
int DEBUG = 0;
2014-06-20 17:18:27 +02:00
// flag about dumping all packets to console
bool DEBUG_DUMP_ALL_PACKETS = false;
2013-11-15 15:35:44 +01:00
// Period for recounting pps/traffic
2013-10-21 12:27:43 +02:00
int check_period = 3;
2013-11-15 15:35:44 +01:00
#ifdef PCAP
// Enlarge receive buffer for PCAP for minimize packet drops
2013-10-21 12:27:43 +02:00
int pcap_buffer_size_mbytes = 10;
2013-11-15 15:35:44 +01:00
#endif
2013-10-21 12:27:43 +02:00
2013-11-15 15:35:44 +01:00
// Key used for sorting clients in output. Allowed sort params: packets/bytes
2013-10-21 15:43:00 +02:00
string sort_parameter = "packets";
2014-06-09 11:08:19 +02:00
// Path to notify script
string notify_script_path = "/usr/local/bin/notify_about_attack.sh";
2013-11-15 15:35:44 +01:00
// Number of lines in programm output
2013-10-21 15:43:00 +02:00
int max_ips_in_list = 7;
2013-10-21 12:27:43 +02:00
2013-11-15 15:35:44 +01:00
// We must ban IP if it exceeed this limit in PPS
2013-10-21 12:27:43 +02:00
int ban_threshold = 20000;
2013-11-15 15:35:44 +01:00
// Number of lines for sending ben attack details to email
2013-10-21 12:27:43 +02:00
int ban_details_records_count = 500;
2014-06-09 14:47:11 +02:00
// log file
ofstream log_file("/var/log/fastnetmon.log");
2013-11-15 15:35:44 +01:00
/* Configuration block ends */
2013-10-21 12:27:43 +02:00
2013-11-15 15:35:44 +01:00
/* Our data structs */
2013-10-18 12:16:55 +02:00
2013-11-15 15:35:44 +01:00
// Enum with availible sort by field
2013-10-21 15:43:00 +02:00
enum sort_type { PACKETS, BYTES };
2013-10-18 12:16:55 +02:00
enum direction {INCOMING, OUTGOING, INTERNAL, OTHER};
2013-11-15 15:35:44 +01:00
// simplified packet struct for lightweight save into memory
2013-10-21 12:27:43 +02:00
struct simple_packet {
uint32_t src_ip;
uint32_t dst_ip;
uint16_t source_port;
uint16_t destination_port;
int protocol;
int length;
};
2013-11-15 02:39:23 +01:00
2013-11-15 15:35:44 +01:00
// Struct for Long Prefix Match Tree
2013-11-15 02:39:23 +01:00
typedef struct leaf {
bool bit;
2013-11-15 04:19:41 +01:00
bool end_of_path;
2013-11-15 02:39:23 +01:00
struct leaf *right, *left;
} tree_leaf;
2013-10-21 12:27:43 +02:00
typedef pair<int, direction> banlist_item;
typedef pair<uint32_t, uint32_t> subnet;
// main data structure for storing traffic data for all our IPs
2013-10-21 15:43:00 +02:00
typedef struct {
int in_bytes;
int out_bytes;
int in_packets;
int out_packets;
} map_element;
2013-11-15 15:35:44 +01:00
typedef map <uint32_t, map_element> map_for_counters;
2013-10-21 15:43:00 +02:00
// data structure for storing data in Vector
typedef pair<uint32_t, map_element> pair_of_map_elements;
2013-10-21 12:27:43 +02:00
2013-11-15 15:35:44 +01:00
/* End of our data structs */
2013-10-18 12:16:55 +02:00
2013-10-21 21:45:33 +02:00
std::mutex counters_mutex;
2013-10-21 11:43:54 +02:00
#ifdef REDIS
2013-10-21 12:27:43 +02:00
redisContext *redis_context = NULL;
2013-10-21 11:43:54 +02:00
#endif
2013-10-18 12:16:55 +02:00
#ifdef ULOG2
2013-11-15 15:35:44 +01:00
// For counting number of communication errors via netlink
2013-10-18 12:16:55 +02:00
int netlink_error_counter = 0;
int netlink_packets_counter = 0;
#endif
2013-10-18 13:35:50 +02:00
#ifdef PCAP
2013-11-15 15:35:44 +01:00
// pcap handler, we want it as global variable beacuse it used in singnal handler
2013-10-21 12:27:43 +02:00
pcap_t* descr = NULL;
2013-10-18 13:35:50 +02:00
#endif
2013-10-18 12:16:55 +02:00
2014-03-11 20:48:15 +01:00
#ifdef PF_RING
pfring* pf_ring_descr = NULL;
#endif
2013-11-15 15:35:44 +01:00
// main map for storing traffic data
2013-10-21 15:43:00 +02:00
map_for_counters DataCounter;
2013-10-21 12:27:43 +02:00
2013-12-28 20:11:20 +01:00
#ifdef GEOIP
map_for_counters GeoIpCounter;
#endif
2013-10-18 12:16:55 +02:00
int total_count_of_incoming_packets = 0;
int total_count_of_outgoing_packets = 0;
int total_count_of_other_packets = 0;
int total_count_of_internal_packets = 0;
int total_count_of_incoming_bytes = 0;
int total_count_of_outgoing_bytes = 0;
int total_count_of_other_bytes = 0;
int total_count_of_internal_bytes = 0;
2013-10-19 17:15:17 +02:00
// В информации о ддосе мы храним силу атаки и ее направление
map<uint32_t, banlist_item> ban_list;
2013-10-18 15:28:00 +02:00
map<uint32_t, vector<simple_packet> > ban_list_details;
2013-10-18 12:16:55 +02:00
time_t start_time;
2013-10-21 12:27:43 +02:00
// стандартно у нас смещение для типа DLT_EN10MB, Ethernet
int DATA_SHIFT_VALUE = 14;
2013-10-18 12:16:55 +02:00
2013-11-14 18:26:30 +01:00
// начальный размер unordered_map для хранения данных
2013-11-15 02:39:23 +01:00
int MAP_INITIAL_SIZE = 2048;
2013-11-14 18:26:30 +01:00
2013-11-15 03:13:41 +01:00
vector<subnet> our_networks;
vector<subnet> whitelist_networks;
//tree_leaf* our_networks;
//tree_leaf* whitelist_networks;
2013-10-18 12:16:55 +02:00
/*
Тут кроется огромный баго-фич:
В случае прослушивания any интерфейсов мы ловим фичу-баг, вместо эзернет хидера у нас тип 113, который LINUX SLL, а следовательно размер хидера не 14, а 16 байт!
Если мы сниффим один интерфейсе - у нас хидер эзернет, 14 байт, а если ANY, то хидер у нас 16 !!!
packetptr += 14; // Ethernet
packetptr += 16; // LINUX SLL, только в случае указания any интерфейса
Подробнее:
https://github.com/the-tcpdump-group/libpcap/issues/324
http://comments.gmane.org/gmane.network.tcpdump.devel/5043
http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html
https://github.com/the-tcpdump-group/libpcap/issues/163
*/
// prototypes
std::string print_channel_speed(string traffic_type, int total_number_of_packets, int total_number_of_bytes, int check_period);
2014-06-21 16:11:53 +02:00
void process_packet(simple_packet& current_packet);
2014-06-09 16:28:56 +02:00
void copy_networks_from_string_form_to_binary(vector<string> networks_list_as_string, vector<subnet>& our_networks);
2013-11-15 02:39:23 +01:00
void insert_prefix_bitwise_tree(tree_leaf* root, string subnet, int cidr_mask);
2013-11-15 03:13:41 +01:00
//bool belongs_to_networks(tree_leaf* root, uint32_t ip);
bool belongs_to_networks(vector<subnet>& networks_list, uint32_t ip);
2014-06-09 11:33:43 +02:00
bool file_exists(string path);
2013-10-21 16:47:31 +02:00
void calculation_programm();
2013-10-18 13:40:24 +02:00
void pcap_main_loop(char* dev);
2014-03-11 18:19:00 +01:00
void pf_ring_main_loop(char* dev);
void parse_packet(u_char *user, struct pcap_pkthdr *packethdr, const u_char *packetptr);
2013-10-18 13:35:50 +02:00
void ulog_main_loop();
2013-10-18 12:16:55 +02:00
void signal_handler(int signal_number);
uint32_t convert_cidr_to_binary_netmask(int cidr);
// Function for sorting Vector of pairs
2013-10-21 15:43:00 +02:00
bool compare_function_by_in_packets (pair_of_map_elements a, pair_of_map_elements b) {
return a.second.in_packets > b.second.in_packets;
}
bool compare_function_by_out_packets (pair_of_map_elements a, pair_of_map_elements b) {
return a.second.out_packets > b.second.out_packets;
}
bool compare_function_by_out_bytes (pair_of_map_elements a, pair_of_map_elements b) {
return a.second.out_bytes > b.second.out_bytes;
}
bool compare_function_by_in_bytes(pair_of_map_elements a, pair_of_map_elements b) {
return a.second.in_bytes > b.second.in_bytes;
2013-10-18 12:16:55 +02:00
}
2013-10-21 11:43:54 +02:00
string get_direction_name(direction direction_value) {
string direction_name;
switch (direction_value) {
case INCOMING: direction_name = "incoming"; break;
case OUTGOING: direction_name = "outgoing"; break;
case INTERNAL: direction_name = "internal"; break;
case OTHER: direction_name = "other"; break;
default: direction_name = "unknown"; break;
}
return direction_name;
}
2013-10-18 12:16:55 +02:00
uint32_t convert_ip_as_string_to_uint(string ip) {
struct in_addr ip_addr;
inet_aton(ip.c_str(), &ip_addr);
// in network byte order
return ip_addr.s_addr;
}
string convert_ip_as_uint_to_string(uint32_t ip_as_string) {
struct in_addr ip_addr;
ip_addr.s_addr = ip_as_string;
return (string)inet_ntoa(ip_addr);
}
2013-10-20 00:10:19 +02:00
// convert integer to string
2013-10-21 11:43:54 +02:00
string convert_int_to_string(int value) {
2013-10-20 00:10:19 +02:00
string pps_as_string;
std::stringstream out;
out << value;
return out.str();
}
2013-10-18 12:59:58 +02:00
2014-06-09 11:08:19 +02:00
// convert string to integer
int convert_string_to_integer(string line) {
return atoi(line.c_str());
}
2014-06-09 13:53:31 +02:00
// exec command in shell
2013-10-18 12:16:55 +02:00
vector<string> exec(string cmd) {
vector<string> output_list;
FILE* pipe = popen(cmd.c_str(), "r");
if (!pipe) return output_list;
char buffer[256];
std::string result = "";
while(!feof(pipe)) {
if(fgets(buffer, 256, pipe) != NULL) {
size_t newbuflen = strlen(buffer);
// remove newline at the end
if (buffer[newbuflen - 1] == '\n') {
buffer[newbuflen - 1] = '\0';
}
output_list.push_back(buffer);
}
}
pclose(pipe);
return output_list;
}
2014-06-09 13:53:31 +02:00
// exec command and pass data to it stdin
2013-10-19 16:40:54 +02:00
bool exec_with_stdin_params(string cmd, string params) {
FILE* pipe = popen(cmd.c_str(), "w");
if (!pipe) return false;
if (fputs(params.c_str(), pipe)) {
fclose(pipe);
return true;
} else {
fclose(pipe);
return false;
}
}
2013-12-28 20:11:20 +01:00
#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
2013-10-20 00:10:19 +02:00
#ifdef REDIS
2013-10-21 11:43:54 +02:00
bool redis_init_connection() {
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
redis_context = redisConnectWithTimeout(redis_host.c_str(), redis_port, timeout);
if (redis_context->err) {
printf("Connection error: %s\n", redis_context->errstr);
return false;
}
// Нужно проверить соединение пингом, так как, по-моему, оно не првоеряет само подключение при коннекте
redisReply* reply = (redisReply*)redisCommand(redis_context, "PING");
if (reply) {
freeReplyObject(reply);
} else {
return false;
}
return true;
}
2013-10-20 00:10:19 +02:00
void update_traffic_in_redis(uint32_t ip, int traffic_bytes, direction my_direction) {
string ip_as_string = convert_ip_as_uint_to_string(ip);
redisReply *reply;
2013-10-21 11:43:54 +02:00
if (!redis_context) {
printf("Please initialize Redis handle");
return;
2013-10-20 00:10:19 +02:00
}
2013-10-21 11:43:54 +02:00
2013-10-20 00:10:19 +02:00
string key_name = ip_as_string + "_" + get_direction_name(my_direction);
2013-10-21 11:43:54 +02:00
reply = (redisReply *)redisCommand(redis_context, "INCRBY %s %s", key_name.c_str(), convert_int_to_string(traffic_bytes).c_str());
// Только в случае, если мы обновили без ошибки
if (!reply) {
printf("Can't increment traffic in redis error_code: %d error_string: %s", redis_context->err, redis_context->errstr);
// Такое может быть в случае перезапуска redis, нам надо попробовать решить это без падения программы
if (redis_context->err == 1 or redis_context->err == 3) {
// Connection refused
redis_init_connection();
}
} else {
freeReplyObject(reply);
}
2013-10-20 00:10:19 +02:00
}
#endif
2013-12-28 20:36:22 +01:00
// TODO: унифицировать с draw_table
void draw_asn_table(map_for_counters& my_map_packets, direction data_direction) {
std::vector<pair_of_map_elements> vector_for_sort;
for( map_for_counters::iterator ii=my_map_packets.begin(); ii!=my_map_packets.end(); ++ii) {
vector_for_sort.push_back( make_pair((*ii).first, (*ii).second) );
}
// sort ONLY BY BYTES!!!
// используем разные сортировочные функции
if (data_direction == INCOMING) {
std::sort( vector_for_sort.begin(), vector_for_sort.end(), compare_function_by_in_bytes);
} else if (data_direction == OUTGOING) {
std::sort( vector_for_sort.begin(), vector_for_sort.end(), compare_function_by_out_bytes);
} else {
// unexpected
}
int element_number = 0;
for( vector<pair_of_map_elements>::iterator ii=vector_for_sort.begin(); ii!=vector_for_sort.end(); ++ii) {
uint32_t client_ip = (*ii).first;
string asn_as_string = convert_int_to_string((*ii).first);
int in_pps = int( (double)(*ii).second.in_packets / (double)check_period );
int out_pps = int( (double)(*ii).second.out_packets / (double)check_period );
2013-12-28 20:36:22 +01:00
int in_bps = int( (double)(*ii).second.in_bytes / (double)check_period );
int out_bps = int( (double)(*ii).second.out_bytes / (double)check_period );
2013-12-28 20:36:22 +01:00
int pps = 0;
int bps = 0;
// делаем "полиморфную" полосу и ппс
if (data_direction == INCOMING) {
pps = in_pps;
bps = in_bps;
} else if (data_direction == OUTGOING) {
pps = out_pps;
bps = out_bps;
}
int mbps = int((double)bps / 1024 / 1024 * 8);
// Выводим первые max_ips_in_list элементов в списке, при нашей сортировке, будут выданы топ 10 самых грузящих клиентов
if (element_number < max_ips_in_list) {
cout << asn_as_string << "\t\t" << pps << " pps " << mbps << " mbps" << endl;
}
element_number++;
}
}
2013-10-21 15:43:00 +02:00
void draw_table(map_for_counters& my_map_packets, direction data_direction, bool do_redis_update, sort_type sort_item) {
2013-10-18 12:16:55 +02:00
std::vector<pair_of_map_elements> vector_for_sort;
/* Вобщем-то весь код ниже зависит лишь от входных векторов и порядка сортировки данных */
2013-10-21 15:43:00 +02:00
for( map_for_counters::iterator ii=my_map_packets.begin(); ii!=my_map_packets.end(); ++ii) {
2013-10-18 12:16:55 +02:00
// кладем все наши элементы в массив для последующей сортировки при отображении
2013-11-15 04:19:41 +01:00
//pair_of_map_elements current_pair = make_pair((*ii).first, (*ii).second);
vector_for_sort.push_back( make_pair((*ii).first, (*ii).second) );
}
2013-10-21 15:43:00 +02:00
if (sort_item == PACKETS) {
// используем разные сортировочные функции
if (data_direction == INCOMING) {
std::sort( vector_for_sort.begin(), vector_for_sort.end(), compare_function_by_in_packets);
} else if (data_direction == OUTGOING) {
std::sort( vector_for_sort.begin(), vector_for_sort.end(), compare_function_by_out_packets);
} else {
// unexpected
}
2013-10-18 12:16:55 +02:00
2013-10-31 20:09:02 +01:00
} else if (sort_item == BYTES) {
2013-10-21 15:43:00 +02:00
if (data_direction == INCOMING) {
std::sort( vector_for_sort.begin(), vector_for_sort.end(), compare_function_by_in_bytes);
} else if (data_direction == OUTGOING) {
std::sort( vector_for_sort.begin(), vector_for_sort.end(), compare_function_by_out_bytes);
}
} else {
assert("Unexpected bahaviour");
}
int element_number = 0;
2013-10-18 12:16:55 +02:00
for( vector<pair_of_map_elements>::iterator ii=vector_for_sort.begin(); ii!=vector_for_sort.end(); ++ii) {
uint32_t client_ip = (*ii).first;
string client_ip_as_string = convert_ip_as_uint_to_string((*ii).first);
2013-10-21 15:43:00 +02:00
int in_pps = (*ii).second.in_packets / check_period;
int out_pps = (*ii).second.out_packets / check_period;
2013-10-18 12:16:55 +02:00
2013-10-21 15:43:00 +02:00
int in_bps = (*ii).second.in_bytes / check_period;
int out_bps = (*ii).second.out_bytes / check_period;
int pps = 0;
int bps = 0;
// делаем "полиморфную" полосу и ппс
if (data_direction == INCOMING) {
pps = in_pps;
bps = in_bps;
} else if (data_direction == OUTGOING) {
pps = out_pps;
bps = out_bps;
}
int mbps = int((double)bps / 1024 / 1024 * 8);
if (pps > ban_threshold) {
if (belongs_to_networks(whitelist_networks, client_ip)) {
// IP в белом списке
} else {
// если клиента еще нету в бан листе
if (ban_list.count(client_ip) == 0) {
string data_direction_as_string = get_direction_name(data_direction);
2013-10-21 22:16:11 +02:00
ban_list[client_ip] = make_pair(pps, data_direction);
2013-10-21 15:43:00 +02:00
ban_list_details[client_ip] = vector<simple_packet>();
2014-06-09 11:33:43 +02:00
string pps_as_string = convert_int_to_string(pps);
if (file_exists(notify_script_path)) {
2014-06-09 14:47:11 +02:00
log_file<<"Attack with direction: "<<data_direction_as_string<<" IP: "<<client_ip_as_string<<" Power: "<<pps_as_string<<endl;
2014-06-09 11:33:43 +02:00
exec(notify_script_path + " " + client_ip_as_string + " " + data_direction_as_string + " " + pps_as_string);
}
2013-10-21 15:43:00 +02:00
}
}
}
// Выводим первые max_ips_in_list элементов в списке, при нашей сортировке, будут выданы топ 10 самых грузящих клиентов
if (element_number < max_ips_in_list) {
2013-10-21 22:16:11 +02:00
string is_banned = ban_list.count(client_ip) > 0 ? " *banned* " : "";
cout << client_ip_as_string << "\t\t" << pps << " pps " << mbps << " mbps" << is_banned << endl;
2013-10-21 15:43:00 +02:00
}
2013-10-31 20:09:02 +01:00
#ifdef REDIS
if (redis_enabled && do_redis_update) {
2013-10-21 15:43:00 +02:00
//cout<<"Start updating traffic in redis"<<endl;
update_traffic_in_redis( (*ii).first, (*ii).second.in_packets, INCOMING);
update_traffic_in_redis( (*ii).first, (*ii).second.out_packets, OUTGOING);
2013-10-20 00:10:19 +02:00
}
2013-10-31 20:09:02 +01:00
#endif
2013-10-21 15:43:00 +02:00
element_number++;
}
2013-10-18 12:16:55 +02:00
}
2014-06-09 13:53:31 +02:00
// check file existence
2013-10-18 12:59:58 +02:00
bool file_exists(string path) {
FILE* check_file = fopen(path.c_str(), "r");
if (check_file) {
fclose(check_file);
return true;
} else {
return false;
}
}
2014-06-09 11:57:28 +02:00
// read whole file to vector
vector<string> read_file_to_vector(string file_name) {
vector<string> data;
string line;
ifstream reading_file (file_name);
if (reading_file.is_open()) {
while ( getline(reading_file, line) ) {
data.push_back(line);
}
}
return data;
}
2014-06-09 11:08:19 +02:00
// Load configuration
bool load_configuration_file() {
ifstream config_file ("/etc/fastnetmon.conf");
string line;
map<string, std::string> configuration_map;
if (config_file.is_open()) {
while ( getline(config_file, line) ) {
vector<string> parsed_config;
split( parsed_config, line, boost::is_any_of(" ="), boost::token_compress_on );
configuration_map[ parsed_config[0] ] = parsed_config[1];
}
if (configuration_map.count("threshold_pps") != 0) {
ban_threshold = convert_string_to_integer( configuration_map[ "threshold_pps" ] );
}
2014-06-09 11:57:28 +02:00
#ifdef REDIS
2014-06-09 11:08:19 +02:00
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_enabled") != 0) {
if (configuration_map[ "redis_enabled" ] == "yes") {
redis_enabled = true;
} else {
redis_enabled = false;
}
}
2014-06-09 11:57:28 +02:00
#endif
2014-06-09 11:08:19 +02:00
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) {
sort_parameter = 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("interfaces") != 0) {
work_on_interfaces = configuration_map[ "interfaces" ];
}
2014-06-09 11:08:19 +02:00
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 ) {
notify_script_path = configuration_map[ "notify_script_path" ];
}
} else {
std::cout<<"Can't open config file"<<std::endl;
}
}
void enable_core_dumps() {
struct rlimit rlim;
if (!getrlimit(RLIMIT_CORE, &rlim)) {
rlim.rlim_cur = rlim.rlim_max;
setrlimit(RLIMIT_CORE, &rlim);
}
}
2013-11-15 15:35:44 +01:00
bool load_our_networks_list() {
2013-10-18 12:16:55 +02:00
// вносим в белый список, IP из этой сети мы не баним
2013-11-15 03:13:41 +01:00
//whitelist_networks = new tree_leaf;
//whitelist_networks->left = whitelist_networks->right = NULL;
2013-11-15 04:19:41 +01:00
// whitelist_networks.end_of_path = false;
2013-11-15 02:39:23 +01:00
2013-11-15 03:13:41 +01:00
//insert_prefix_bitwise_tree(whitelist_networks, "159.253.17.0", 24);
2014-06-09 16:28:56 +02:00
//subnet white_subnet = std::make_pair(convert_ip_as_string_to_uint("159.253.17.0"), convert_cidr_to_binary_netmask(24));
//whitelist_networks.push_back(white_subnet);
2014-06-09 14:47:11 +02:00
if (file_exists("/etc/networks_whitelist")) {
2014-06-09 16:28:56 +02:00
vector<string> network_list_from_config = read_file_to_vector("/etc/networks_whitelist");
2014-06-09 14:47:11 +02:00
2014-06-09 16:28:56 +02:00
copy_networks_from_string_form_to_binary(network_list_from_config, whitelist_networks);
log_file<<"We loaded "<<network_list_from_config.size()<< " networks from whitelist file"<<endl;
2014-06-09 14:47:11 +02:00
}
2013-11-15 15:35:44 +01:00
// Whet we used unordered_map it will encrease it perfomance
//DataCounter.reserve(MAP_INITIAL_SIZE);
2013-11-14 18:26:30 +01:00
2013-10-18 12:16:55 +02:00
vector<string> networks_list_as_string;
// если мы на openvz ноде, то "свои" IP мы можем получить из спец-файла в /proc
string our_networks_netmask;
2013-10-18 12:59:58 +02:00
if (file_exists("/proc/vz/version")) {
2013-10-18 12:16:55 +02:00
cout<<"We found OpenVZ"<<endl;
// тут искусствено добавляем суффикс 32
vector<string> openvz_ips = read_file_to_vector("/proc/vz/veip");
2014-06-09 13:53:31 +02:00
for( vector<string>::iterator ii=openvz_ips.begin(); ii!=openvz_ips.end(); ++ii) {
// skip IPv6 addresses
if (strstr(ii->c_str(), ":") != NULL) {
continue;
}
// skip header
if (strstr(ii->c_str(), "Version") != NULL) {
continue;
}
vector<string> subnet_as_string;
split( subnet_as_string, *ii, boost::is_any_of(" "), boost::token_compress_on );
string openvz_subnet = subnet_as_string[1] + "/32";
networks_list_as_string.push_back(openvz_subnet);
}
2013-10-18 12:16:55 +02:00
}
2013-10-18 12:59:58 +02:00
if (file_exists("/etc/networks_list")) {
2014-06-09 11:57:28 +02:00
vector<string> network_list_from_config = read_file_to_vector("/etc/networks_list");
2013-10-18 12:59:58 +02:00
networks_list_as_string.insert(networks_list_as_string.end(), network_list_from_config.begin(), network_list_from_config.end());
2014-06-09 16:28:56 +02:00
log_file<<"We loaded "<<network_list_from_config.size()<< " networks from networks file"<<endl;
2013-10-18 12:59:58 +02:00
}
2013-10-18 12:16:55 +02:00
// если это ложь, то в моих функциях косяк
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) );
2013-11-15 02:39:23 +01:00
//our_networks.push_back(current_subnet);
2013-11-15 03:13:41 +01:00
//our_networks = new tree_leaf;
//our_networks->left = our_networks->right = NULL;
2013-11-15 02:39:23 +01:00
2014-06-09 16:28:56 +02:00
copy_networks_from_string_form_to_binary(networks_list_as_string, our_networks);
return true;
}
void copy_networks_from_string_form_to_binary(vector<string> networks_list_as_string, vector<subnet>& our_networks ) {
2013-10-18 12:16:55 +02:00
for( vector<string>::iterator ii=networks_list_as_string.begin(); ii!=networks_list_as_string.end(); ++ii) {
vector<string> subnet_as_string;
split( subnet_as_string, *ii, boost::is_any_of("/"), boost::token_compress_on );
2014-06-09 11:08:19 +02:00
int cidr = convert_string_to_integer(subnet_as_string[1]);
2013-10-18 12:16:55 +02:00
uint32_t subnet_as_int = convert_ip_as_string_to_uint(subnet_as_string[0]);
uint32_t netmask_as_int = convert_cidr_to_binary_netmask(cidr);
subnet current_subnet = std::make_pair(subnet_as_int, netmask_as_int);
2013-11-15 03:13:41 +01:00
our_networks.push_back(current_subnet);
//insert_prefix_bitwise_tree(our_networks, subnet_as_string[0], cidr);
2014-06-09 16:28:56 +02:00
}
}
2013-10-18 12:16:55 +02:00
uint32_t convert_cidr_to_binary_netmask(int cidr) {
uint32_t binary_netmask = 0xFFFFFFFF;
binary_netmask = binary_netmask << ( 32 - cidr );
// htonl from host byte order to network
// ntohl from network byte order to host
// поидее, на выходе тут нужен network byte order
return htonl(binary_netmask);
}
2013-11-14 12:29:10 +01:00
bool belongs_to_networks(vector<subnet>& networks_list, uint32_t ip) {
2013-10-18 12:16:55 +02:00
for( vector<subnet>::iterator ii=networks_list.begin(); ii!=networks_list.end(); ++ii) {
if ( (ip & (*ii).second) == ((*ii).first & (*ii).second) ) {
return true;
}
}
return false;
}
string print_simple_packet(struct simple_packet packet) {
std::stringstream buffer;
2013-10-18 15:28:00 +02:00
string proto_name;
switch (packet.protocol) {
case IPPROTO_TCP:
proto_name = "tcp";
break;
case IPPROTO_UDP:
proto_name = "udp";
break;
case IPPROTO_ICMP:
proto_name = "icmp";
break;
default:
proto_name = "unknown";
break;
}
buffer
2013-10-19 15:41:21 +02:00
<<convert_ip_as_uint_to_string(packet.src_ip)<<":"<<packet.source_port
<<" > "
<<convert_ip_as_uint_to_string(packet.dst_ip)<<":"<<packet.destination_port
<<" protocol: "<<proto_name
2013-10-19 16:40:54 +02:00
<<" size: "<<packet.length<<" bytes"<<"\n";
// используется \n вместо endl, ибо иначе начинается хрень всякая при передаче данной строки команде на stdin
return buffer.str();
2013-10-18 15:28:00 +02:00
}
2014-03-11 18:19:00 +01:00
// Обработчик для pf_ring, так как у него иной формат входных параметров
void parse_packet_pf_ring(const struct pfring_pkthdr *h, const u_char *p, const u_char *user_bytes) {
2014-06-21 16:11:53 +02:00
//printf("hash%d\n",h->extended_hdr.pkt_hash);
2014-06-21 17:31:16 +02:00
// Описание всех полей: http://www.ntop.org/pfring_api/structpkt__parsing__info.html
simple_packet packet;
/* We handle only IPv4 */
if (h->extended_hdr.parsed_pkt.ip_version == 4) {
/* PF_RING хранит данные в host byte order, а мы использум только network byte order */
packet.src_ip = htonl(h->extended_hdr.parsed_pkt.ip_src.v4);
packet.dst_ip = htonl( h->extended_hdr.parsed_pkt.ip_dst.v4);
packet.source_port = h->extended_hdr.parsed_pkt.l4_src_port;
packet.destination_port = h->extended_hdr.parsed_pkt.l4_dst_port;
packet.length = h->len;
packet.protocol = h->extended_hdr.parsed_pkt.l3_proto;
2014-06-21 17:33:41 +02:00
process_packet(packet);
2014-06-21 17:31:16 +02:00
//std::cout<<print_simple_packet(packet)<<std::endl;
}
2014-03-11 18:19:00 +01:00
}
2013-10-18 12:16:55 +02:00
// в случае прямого вызова скрипта колбэка - нужно конст, напрямую в хендлере - конст не нужно
void parse_packet(u_char *user, struct pcap_pkthdr *packethdr, const u_char *packetptr) {
struct ip* iphdr;
struct icmphdr* icmphdr;
struct tcphdr* tcphdr;
struct udphdr* udphdr;
unsigned short id, seq;
2014-03-11 20:48:15 +01:00
struct ether_header *eptr; /* net/ethernet.h */
eptr = (struct ether_header* )packetptr;
// проверяем тип эзернет фрейма и его принадлежность к типу "фрейм с VLAN"
if ( ntohs(eptr->ether_type) == VLAN_ETHERTYPE ) {
// это тегированный трафик, поэтому нужно отступить еще 4 байта, чтобы добраться до данных
packetptr += DATA_SHIFT_VALUE + VLAN_HDRLEN;
} else if (ntohs(eptr->ether_type) == IP_ETHERTYPE) {
2014-03-11 20:48:15 +01:00
// Skip the datalink layer header and get the IP header fields.
packetptr += DATA_SHIFT_VALUE;
} else if (ntohs(eptr->ether_type) == IP6_ETHERTYPE or ntohs(eptr->ether_type) == ARP_ETHERTYPE) {
// we know about it but does't not care now
} else {
// printf("Packet with non standard ethertype found: 0x%x\n", ntohs(eptr->ether_type));
2014-03-11 20:48:15 +01:00
}
2013-10-18 12:16:55 +02:00
iphdr = (struct ip*)packetptr;
// исходящий/входящий айпи это in_addr, http://man7.org/linux/man-pages/man7/ip.7.html
uint32_t src_ip = iphdr->ip_src.s_addr;
uint32_t dst_ip = iphdr->ip_dst.s_addr;
// The ntohs() function converts the unsigned short integer netshort from network byte order to host byte order
int packet_length = ntohs(iphdr->ip_len);
2013-10-19 15:41:21 +02:00
simple_packet current_packet;
2013-10-18 15:28:00 +02:00
// Advance to the transport layer header then parse and display
// the fields based on the type of hearder: tcp, udp or icmp
packetptr += 4*iphdr->ip_hl;
switch (iphdr->ip_p) {
case IPPROTO_TCP:
tcphdr = (struct tcphdr*)packetptr;
2013-10-19 15:41:21 +02:00
current_packet.source_port = ntohs(tcphdr->source);
current_packet.destination_port = ntohs(tcphdr->dest);
2013-10-18 15:28:00 +02:00
break;
2013-10-19 15:41:21 +02:00
case IPPROTO_UDP:
2013-10-18 15:28:00 +02:00
udphdr = (struct udphdr*)packetptr;
2013-10-19 15:41:21 +02:00
current_packet.source_port = ntohs(udphdr->source);
current_packet.destination_port = ntohs(udphdr->dest);
break;
case IPPROTO_ICMP:
// there are no port for ICMP
current_packet.source_port = 0;
current_packet.destination_port = 0;
break;
2013-10-18 15:28:00 +02:00
}
2013-10-19 15:41:21 +02:00
current_packet.protocol = iphdr->ip_p;
2013-10-18 15:28:00 +02:00
current_packet.src_ip = src_ip;
current_packet.dst_ip = dst_ip;
current_packet.length = packet_length;
2014-06-20 17:18:27 +02:00
2014-06-21 16:11:53 +02:00
/* Передаем пакет в обработку */
process_packet(current_packet);
#ifdef THREADLESS
calculation_programm();
#endif
}
/* Производим обработку уже переданного нам пакета в простом формате */
void process_packet(simple_packet& current_packet) {
2014-06-20 17:18:27 +02:00
// Packets dump is very useful for bug hunting
if (DEBUG_DUMP_ALL_PACKETS) {
cout<<"Dump: "<<print_simple_packet(current_packet);
}
2013-10-18 15:28:00 +02:00
2014-06-21 16:11:53 +02:00
// определение направления пакета тоже нужно вынести в функцию!!!
2013-10-18 12:16:55 +02:00
direction packet_direction;
2013-11-15 02:39:23 +01:00
// try to cache succesful lookups
2014-06-21 16:11:53 +02:00
bool our_ip_is_destination = DataCounter.count(current_packet.dst_ip) > 0;
bool our_ip_is_source = DataCounter.count(current_packet.src_ip) > 0;
2013-11-15 02:39:23 +01:00
2013-11-15 04:19:41 +01:00
if (! our_ip_is_destination) {
2014-06-21 16:11:53 +02:00
our_ip_is_destination = belongs_to_networks(our_networks, current_packet.dst_ip);
2013-11-15 04:19:41 +01:00
}
2013-10-18 12:16:55 +02:00
2013-11-15 04:19:41 +01:00
if (!our_ip_is_source) {
2014-06-21 16:11:53 +02:00
our_ip_is_source = belongs_to_networks(our_networks, current_packet.src_ip);
2013-11-15 04:19:41 +01:00
}
2013-11-15 02:39:23 +01:00
if (our_ip_is_source && our_ip_is_destination) {
packet_direction = INTERNAL;
2013-10-21 21:45:33 +02:00
counters_mutex.lock();
2013-10-18 12:16:55 +02:00
total_count_of_internal_packets ++;
2014-06-21 16:11:53 +02:00
total_count_of_internal_bytes += current_packet.length;
2013-10-21 21:45:33 +02:00
counters_mutex.unlock();
2013-10-18 12:16:55 +02:00
2013-11-15 02:39:23 +01:00
} else if (our_ip_is_source) {
2013-10-18 12:16:55 +02:00
packet_direction = OUTGOING;
2013-10-21 21:45:33 +02:00
counters_mutex.lock();
2013-10-18 12:16:55 +02:00
total_count_of_outgoing_packets ++;
2014-06-21 16:11:53 +02:00
total_count_of_outgoing_bytes += current_packet.length;
2013-10-21 21:45:33 +02:00
counters_mutex.unlock();
2013-10-18 12:16:55 +02:00
2013-10-21 15:43:00 +02:00
// собираем данные для деталей при бане клиента
2014-06-21 16:11:53 +02:00
if (ban_list_details.count(current_packet.src_ip) > 0 && ban_list_details[current_packet.src_ip].size() < ban_details_records_count) {
ban_list_details[current_packet.src_ip].push_back(current_packet);
2013-10-18 15:28:00 +02:00
}
2013-10-21 21:45:33 +02:00
counters_mutex.lock();
2014-06-21 16:11:53 +02:00
DataCounter[ current_packet.src_ip ].out_packets++;
DataCounter[ current_packet.src_ip ].out_bytes += current_packet.length;
2013-10-21 21:45:33 +02:00
counters_mutex.unlock();
2013-12-28 20:11:20 +01:00
2013-11-15 02:39:23 +01:00
} else if (our_ip_is_destination) {
2013-10-18 12:16:55 +02:00
packet_direction = INCOMING;
2013-10-21 21:45:33 +02:00
counters_mutex.lock();
2013-10-18 12:16:55 +02:00
total_count_of_incoming_packets++;
2014-06-21 16:11:53 +02:00
total_count_of_incoming_bytes += current_packet.length;
2013-10-21 21:45:33 +02:00
counters_mutex.unlock();
2013-10-18 12:16:55 +02:00
2013-10-21 15:43:00 +02:00
// собираемы данные для деталей при бане клиента
2014-06-21 16:11:53 +02:00
if (ban_list_details.count(current_packet.dst_ip) > 0 && ban_list_details[current_packet.dst_ip].size() < ban_details_records_count) {
ban_list_details[current_packet.dst_ip].push_back(current_packet);
2013-10-18 15:28:00 +02:00
}
2013-10-21 21:45:33 +02:00
counters_mutex.lock();
2014-06-21 16:11:53 +02:00
DataCounter[ current_packet.dst_ip ].in_packets ++;
DataCounter[ current_packet.dst_ip ].in_bytes += current_packet.length;
2013-10-21 21:45:33 +02:00
counters_mutex.unlock();
2013-10-18 12:16:55 +02:00
} else {
packet_direction = OTHER;
2013-10-21 21:45:33 +02:00
counters_mutex.lock();
2013-10-18 12:16:55 +02:00
total_count_of_other_packets ++;
2014-06-21 16:11:53 +02:00
total_count_of_other_bytes += current_packet.length;
2013-10-21 21:45:33 +02:00
counters_mutex.unlock();
2013-10-18 12:16:55 +02:00
}
2013-12-28 20:11:20 +01:00
#ifdef GEOIP
// Execute GeoIP lookup
if (packet_direction == INCOMING or packet_direction == OUTGOING) {
2014-06-21 16:11:53 +02:00
uint32_t remote_ip = packet_direction == INCOMING ? current_packet.src_ip : dst_ip;
2013-12-28 20:11:20 +01:00
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
vector<string> asn_as_string;
split( asn_as_string, asn_raw, boost::is_any_of(" "), boost::token_compress_on );
2013-12-29 20:01:11 +01:00
// free up original string
free(asn_raw);
2013-12-28 20:11:20 +01:00
// extract raw number
2014-06-09 11:08:19 +02:00
asn_number = convert_string_to_integer(asn_as_string[0].substr(2));
2013-12-28 20:11:20 +01:00
}
// кладем данные по трафику ASN в хэш
counters_mutex.lock();
2014-03-12 09:50:29 +01:00
#ifdef GEOIP
2013-12-28 20:11:20 +01:00
if (packet_direction == INCOMING) {
// Incoming
GeoIpCounter[ asn_number ].out_packets++;
2014-06-21 16:11:53 +02:00
GeoIpCounter[ asn_number ].out_bytes += current_packet.length;
2013-12-28 20:11:20 +01:00
} else {
// Outgoing
GeoIpCounter[ asn_number ].in_packets++;
2014-06-21 16:11:53 +02:00
GeoIpCounter[ asn_number ].in_bytes += current_packet.length;
2013-12-28 20:11:20 +01:00
}
2014-03-12 09:50:29 +01:00
#endif
2013-12-28 20:11:20 +01:00
counters_mutex.unlock();
}
#endif
2013-10-21 16:47:31 +02:00
}
2013-10-21 21:45:33 +02:00
// void* void* data
void calculation_thread() {
while (1) {
//sleep(check_period);
std::this_thread::sleep_for(std::chrono::seconds( check_period ));
calculation_programm();
}
}
2014-06-09 14:47:11 +02:00
2013-10-21 16:47:31 +02:00
void calculation_programm() {
2013-10-18 12:16:55 +02:00
time_t current_time;
time(&current_time);
2013-10-21 21:45:33 +02:00
#ifdef THREADLESS
2013-10-18 12:16:55 +02:00
if ( difftime(current_time, start_time) >= check_period ) {
2013-10-21 21:45:33 +02:00
#endif
2013-10-18 12:16:55 +02:00
// clean up screen
system("clear");
2013-10-21 15:43:00 +02:00
sort_type sorter;
if (sort_parameter == "packets") {
sorter = PACKETS;
} else if (sort_parameter == "bytes") {
sorter = BYTES;
2014-06-22 12:59:11 +02:00
} else {
cout<<"Unexpected sorter type: "<<sort_parameter<<endl;
sorter = PACKETS;
2013-10-21 15:43:00 +02:00
}
2014-06-09 11:08:19 +02:00
cout<<"FastNetMon v1.0 "<<"IPs ordered by: "<<sort_parameter<<" "<<"threshold is: "<<ban_threshold<<endl<<endl;
2013-10-18 12:16:55 +02:00
cout<<print_channel_speed("Incoming Traffic", total_count_of_incoming_packets, total_count_of_incoming_bytes, check_period)<<endl;
2013-10-21 15:43:00 +02:00
draw_table(DataCounter, INCOMING, true, sorter);
2013-10-18 12:16:55 +02:00
cout<<endl;
cout<<print_channel_speed("Outgoing traffic", total_count_of_outgoing_packets, total_count_of_outgoing_bytes, check_period)<<endl;
2013-10-21 15:43:00 +02:00
draw_table(DataCounter, OUTGOING, false, sorter);
2013-10-18 12:16:55 +02:00
cout<<endl;
cout<<print_channel_speed("Internal traffic", total_count_of_internal_packets, total_count_of_internal_bytes, check_period)<<endl;
2013-10-18 12:16:55 +02:00
cout<<endl;
cout<<print_channel_speed("Other traffic", total_count_of_other_packets, total_count_of_other_bytes, check_period)<<endl;
2013-10-18 12:16:55 +02:00
cout<<endl;
2013-12-28 20:36:22 +01:00
// TODO: ВРЕМЕННО ДЕАКТИВИРОВАНО
2014-03-12 09:50:29 +01:00
#ifdef GEOIP
2013-12-28 20:36:22 +01:00
if (false) {
cout<<"Incoming channel: ASN traffic\n";
draw_asn_table(GeoIpCounter, OUTGOING);
cout<<endl;
cout<<"Outgoing channel: ASN traffic\n";
draw_asn_table(GeoIpCounter, INCOMING);
cout<<endl;
2014-03-12 09:50:29 +01:00
}
#endif
2013-12-28 20:36:22 +01:00
2013-10-18 12:16:55 +02:00
#ifdef PCAP
struct pcap_stat current_pcap_stats;
if (pcap_stats(descr, &current_pcap_stats) == 0) {
cout<<"PCAP statistics"<<endl<<"Received packets: "<<current_pcap_stats.ps_recv<<endl
<<"Dropped packets: "<<current_pcap_stats.ps_drop
<<" ("<<int((double)current_pcap_stats.ps_drop/current_pcap_stats.ps_recv*100)<<"%)"<<endl
<<"Dropped by driver or interface: "<<current_pcap_stats.ps_ifdrop<<endl;
}
#endif
#ifdef ULOG2
cout<<"ULOG buffer errors: " << netlink_error_counter<<" ("<<int((double)netlink_error_counter/netlink_packets_counter)<<"%)"<<endl;
cout<<"ULOG packets received: "<< netlink_packets_counter<<endl;
#endif
2014-03-11 21:54:18 +01:00
#ifdef PF_RING
pfring_stat pfring_status_data;
if(pfring_stats(pf_ring_descr, &pfring_status_data) >= 0) {
printf(
2014-03-12 10:39:27 +01:00
"Packets received:\t%lu\n"
"Packets dropped:\t%lu\n"
"Packets dropped:\t%.1f %%\n",
2014-03-11 21:54:18 +01:00
(long unsigned int) pfring_status_data.recv,
2014-03-12 09:06:21 +01:00
(long unsigned int) pfring_status_data.drop,
(double) pfring_status_data.drop/pfring_status_data.recv*100
2014-03-11 21:54:18 +01:00
);
} else {
cout<<"Can't get PF_RING stats"<<endl;
}
#endif
2013-10-18 12:16:55 +02:00
if (ban_list.size() > 0) {
cout<<endl<<"Ban list:"<<endl;
2013-10-19 17:15:17 +02:00
for( map<uint32_t,banlist_item>::iterator ii=ban_list.begin(); ii!=ban_list.end(); ++ii) {
2013-10-19 16:40:54 +02:00
string client_ip_as_string = convert_ip_as_uint_to_string((*ii).first);
2013-10-21 15:43:00 +02:00
string pps_as_string = convert_int_to_string(((*ii).second).first);
2013-10-19 16:40:54 +02:00
2013-10-19 17:15:17 +02:00
string attack_direction = get_direction_name(((*ii).second).second);
cout<<client_ip_as_string<<"/"<<pps_as_string<<" pps "<<attack_direction<<endl;
2013-10-18 15:28:00 +02:00
2013-10-18 15:32:28 +02:00
// странная проверка, но при мощной атаке набить ban_details_records_count пакетов - очень легко
2013-10-19 15:41:21 +02:00
if (ban_list_details.count( (*ii).first ) > 0 && ban_list_details[ (*ii).first ].size() == ban_details_records_count) {
2013-10-19 16:40:54 +02:00
string attack_details;
2013-10-18 15:28:00 +02:00
for( vector<simple_packet>::iterator iii=ban_list_details[ (*ii).first ].begin(); iii!=ban_list_details[ (*ii).first ].end(); ++iii) {
2013-10-19 16:40:54 +02:00
attack_details += print_simple_packet( *iii );
2013-10-18 15:28:00 +02:00
}
2013-10-19 16:40:54 +02:00
2013-10-21 22:16:11 +02:00
// отсылаем детали атаки (отпечаток пакетов) по почте
2014-06-09 11:33:43 +02:00
if (file_exists(notify_script_path)) {
exec_with_stdin_params(notify_script_path + " " + client_ip_as_string + " " + attack_direction + " " + pps_as_string, attack_details );
2014-06-09 14:47:11 +02:00
log_file<<"Attack with direction: "<<attack_direction<<" IP: "<<client_ip_as_string<<" Power: "<<pps_as_string<<endl;
log_file<<attack_details<<endl;
2014-06-09 11:33:43 +02:00
}
2013-10-19 16:40:54 +02:00
// удаляем ключ из деталей атаки, чтобы он не выводился снова и в него не собирался трафик
ban_list_details.erase((*ii).first);
2013-10-18 15:28:00 +02:00
}
2013-10-18 12:16:55 +02:00
}
}
// переустанавливаем время запуска
time(&start_time);
2013-10-21 15:43:00 +02:00
// зануляем счетчик пакетов
2013-10-21 21:45:33 +02:00
counters_mutex.lock();
2013-10-21 15:43:00 +02:00
DataCounter.clear();
2013-12-28 20:36:22 +01:00
#ifdef GEOIP
GeoIpCounter.clear();
#endif
2013-10-18 12:16:55 +02:00
total_count_of_incoming_bytes = 0;
total_count_of_outgoing_bytes = 0;
total_count_of_other_packets = 0;
total_count_of_other_bytes = 0;
total_count_of_internal_packets = 0;
total_count_of_internal_bytes = 0;
total_count_of_incoming_packets = 0;
total_count_of_outgoing_packets = 0;
2013-10-21 21:45:33 +02:00
counters_mutex.unlock();
#ifdef THREADLESS
2013-10-18 12:16:55 +02:00
}
2013-10-21 21:45:33 +02:00
#endif
2013-10-18 12:16:55 +02:00
}
// pretty print channel speed in pps and MBit
std::string print_channel_speed(string traffic_type, int total_number_of_packets,
int total_number_of_bytes, int check_period) {
int number_of_tabs = 1;
// We need this for correct alignment of blocks
if (traffic_type == "Other traffic") {
number_of_tabs = 2;
}
std::stringstream stream;
stream<<traffic_type;
for (int i = 0; i < number_of_tabs; i ++ ) {
stream<<"\t";
}
int speed_in_pps = int( (double)total_number_of_packets/(double)check_period );
double speed_in_bps = (double)total_number_of_bytes/(double)check_period;
int speed_in_mbps = int(speed_in_bps/1024/1024*8);
stream<<speed_in_pps<<" pps "<< speed_in_mbps<<" mbps";
return stream.str();
}
2013-10-18 12:16:55 +02:00
int main(int argc,char **argv) {
2014-03-11 18:19:00 +01:00
// listened device
2013-10-18 12:16:55 +02:00
char *dev;
// enable core dumps
enable_core_dumps();
2014-06-20 17:18:27 +02:00
if (getenv("DUMP_ALL_PACKETS") != NULL) {
DEBUG_DUMP_ALL_PACKETS = true;
}
2013-10-18 13:35:50 +02:00
#ifdef PCAP
2013-10-18 12:16:55 +02:00
char errbuf[PCAP_ERRBUF_SIZE];
const u_char *packet;
struct pcap_pkthdr hdr;
2013-10-18 13:35:50 +02:00
#endif
2014-06-09 14:47:11 +02:00
if (!log_file.is_open()) {
printf("Cant open log file, plese check filesystem!");
exit(1);
}
log_file<<"Read configuration file"<<endl;
2014-06-09 13:15:22 +02:00
load_configuration_file();
2013-10-18 12:16:55 +02:00
time(&start_time);
printf("I need few seconds for collecting data, please wait. Thank you!\n");
2014-03-11 18:19:00 +01:00
#ifdef PF_RING
if (work_on_interfaces == "" && argc != 2) {
fprintf(stdout, "Usage: %s \"eth0\" or \"eth0,eth1\" or specify interfaces param in config file\n", argv[0]);
2014-03-11 18:19:00 +01:00
exit(1);
}
if (work_on_interfaces != "") {
dev = const_cast<char*>(work_on_interfaces.c_str());
} else {
dev = argv[1];
}
2014-03-11 18:19:00 +01:00
fprintf(stdout, "We selected %s\n", dev);
#endif
2013-10-18 12:44:57 +02:00
#ifdef PCAP
2013-10-18 12:16:55 +02:00
if (argc != 2) {
fprintf(stdout, "Usage: %s \"eth0\" or \"any\"\n", argv[0]);
2013-10-18 12:44:57 +02:00
2013-10-18 12:16:55 +02:00
cout<< "We must automatically select interface"<<endl;
/* Now get a device */
dev = pcap_lookupdev(errbuf);
if(dev == NULL) {
fprintf(stderr, "%s\n", errbuf);
exit (1);
}
printf("Automatically selected %s device\n", dev);
} else {
dev = argv[1];
}
2013-10-18 12:44:57 +02:00
#endif
2013-10-18 12:16:55 +02:00
2013-10-21 11:43:54 +02:00
// иницилизируем соединение с Redis
#ifdef REDIS
if (redis_enabled) {
if (!redis_init_connection()) {
printf("Can't establish connection to the redis\n");
exit(1);
}
2013-10-21 11:43:54 +02:00
}
#endif
2013-12-28 20:11:20 +01:00
// иницилизируем GeoIP
#ifdef GEOIP
if(!geoip_init()) {
printf("Can't load geoip tables");
exit(1);
}
#endif
2013-10-18 13:35:50 +02:00
// загружаем наши сети и whitelist
2013-10-18 12:16:55 +02:00
load_our_networks_list();
2013-10-18 12:44:57 +02:00
2013-10-18 13:35:50 +02:00
// устанавливаем обработчик CTRL+C
signal(SIGINT, signal_handler);
2013-10-21 21:45:33 +02:00
#ifndef THREADLESS
2013-10-21 16:47:31 +02:00
// запускаем поток-обсчета данных
2013-10-21 21:45:33 +02:00
thread calc_thread(calculation_thread);
#endif
2013-10-21 16:47:31 +02:00
2013-10-18 13:35:50 +02:00
#ifdef PCAP
pcap_main_loop(dev);
#endif
2014-03-11 18:19:00 +01:00
#ifdef PF_RING
pf_ring_main_loop(dev);
#endif
2013-10-18 13:35:50 +02:00
#ifdef ULOG2
2013-10-21 21:45:33 +02:00
thread ulog_thread(ulog_main_loop);
ulog_thread.join();
2013-10-31 20:09:02 +01:00
#endif
2013-12-29 20:01:11 +01:00
calc_thread.join();
2013-12-28 20:11:20 +01:00
#ifdef GEOIP
// Free up geoip handle
GeoIP_delete(geo_ip);
#endif
2013-12-29 20:01:11 +01:00
2013-10-18 13:35:50 +02:00
return 0;
}
2014-03-11 18:19:00 +01:00
#ifdef PF_RING
void pf_ring_main_loop(char* dev) {
// We could pool device in multiple threads
int num_threads = 1;
int promisc = 1;
2014-06-21 16:11:53 +02:00
/* This flag manages packet parser for extended_hdr */
2014-06-21 17:31:16 +02:00
u_int8_t use_extended_pkt_header = 1;
2014-06-21 16:11:53 +02:00
u_int8_t touch_payload = 0, enable_hw_timestamp = 0, dont_strip_timestamps = 0;
2014-03-11 18:19:00 +01:00
u_int32_t flags = 0;
if(num_threads > 1) flags |= PF_RING_REENTRANT;
if(use_extended_pkt_header) flags |= PF_RING_LONG_HEADER;
if(promisc) flags |= PF_RING_PROMISC;
if(enable_hw_timestamp) flags |= PF_RING_HW_TIMESTAMP;
if(!dont_strip_timestamps) flags |= PF_RING_STRIP_HW_TIMESTAMP;
flags |= PF_RING_DNA_SYMMETRIC_RSS; /* Note that symmetric RSS is ignored by non-DNA drivers */
// use default value from pfcount.c
int snaplen = 128;
2014-03-11 20:48:15 +01:00
pf_ring_descr = pfring_open(dev, snaplen, flags);
2014-03-11 18:19:00 +01:00
2014-03-11 20:48:15 +01:00
if(pf_ring_descr == NULL) {
2014-03-11 18:19:00 +01:00
fprintf(stderr, "pfring_open error [%s] (pf_ring not loaded or perhaps you use quick mode and have already a socket bound to %s ?)\n", strerror(errno), dev);
exit(1);
} else {
fprintf(stdout, "Successully binded to: %s\n", dev);
2014-03-12 10:39:27 +01:00
/*
2014-03-11 22:24:51 +01:00
u_char mac_address[6] = { 0 };
if(pfring_get_bound_device_address(pf_ring_descr, mac_address) != 0) {
fprintf(stderr, "Unable to read the device address\n");
} else {
int ifindex = -1;
pfring_get_bound_device_ifindex(pf_ring_descr, &ifindex);
printf("Capturing from %s [%s][ifIndex: %d]\n", dev, mac_address, ifindex);
2014-03-12 10:39:27 +01:00
}
*/
2014-03-11 22:24:51 +01:00
fprintf(stdout, "Device RX channels number: %d\n", pfring_get_num_rx_channels(pf_ring_descr));
2014-03-11 18:19:00 +01:00
u_int32_t version;
2014-03-11 22:24:51 +01:00
// задаемт имя приложения для его указания в переменной PCAP_PF_RING_APPNAME в статистике в /proc
2014-03-11 20:48:15 +01:00
pfring_set_application_name(pf_ring_descr, (char*)"fastnetmon");
pfring_version(pf_ring_descr, &version);
2014-03-11 18:19:00 +01:00
fprintf(stdout, "Using PF_RING v.%d.%d.%d\n",
(version & 0xFFFF0000) >> 16,
(version & 0x0000FF00) >> 8,
version & 0x000000FF);
}
2014-03-11 22:24:51 +01:00
int rc;
if((rc = pfring_set_socket_mode(pf_ring_descr, recv_only_mode)) != 0)
fprintf(stderr, "pfring_set_socket_mode returned [rc=%d]\n", rc);
char path[256] = { 0 };
if(pfring_get_appl_stats_file_name(pf_ring_descr, path, sizeof(path)) != NULL)
fprintf(stderr, "Dumping statistics on %s\n", path);
// enable ring
if (pfring_enable_ring(pf_ring_descr) != 0) {
printf("Unable to enable ring :-(\n");
pfring_close(pf_ring_descr);
exit(-1);
}
2014-03-11 18:19:00 +01:00
// WTF?
u_int8_t wait_for_packet = 1;
2014-03-11 22:24:51 +01:00
2014-03-11 20:48:15 +01:00
pfring_loop(pf_ring_descr, parse_packet_pf_ring, (u_char*)NULL, wait_for_packet);
2014-03-11 18:19:00 +01:00
}
#endif
2013-10-18 12:44:57 +02:00
#ifdef PCAP
2013-10-18 13:35:50 +02:00
void pcap_main_loop(char* dev) {
char errbuf[PCAP_ERRBUF_SIZE];
2013-10-18 12:16:55 +02:00
/* open device for reading in promiscuous mode */
int promisc = 1;
int pcap_read_timeout = -1;
bpf_u_int32 maskp; /* subnet mask */
bpf_u_int32 netp; /* ip */
cout<<"Start listening on "<<dev<<endl;
/* Get the network address and mask */
pcap_lookupnet(dev, &netp, &maskp, errbuf);
descr = pcap_create(dev, errbuf);
if (descr == NULL) {
printf("pcap_create was failed with error: %s", errbuf);
exit(0);
}
int set_buffer_size_res = pcap_set_buffer_size(descr, pcap_buffer_size_mbytes * 1024 * 1024);
if (set_buffer_size_res != 0 ) { // выставляем буфер в 1 мегабайт
if (set_buffer_size_res == PCAP_ERROR_ACTIVATED) {
printf("Can't set buffer size because pcap already activated\n");
exit(1);
} else {
printf("Can't set buffer size due to error %d\n", set_buffer_size_res);
exit(1);
}
}
/*
Вот через этот спец механизм можно собирать лишь хидеры!
If you don't need the entire contents of the packet - for example, if you are only interested in the TCP headers of packets - you can set the "snapshot length" for the capture to an appropriate value.
*/
/*
if (pcap_set_snaplen(descr, 32 ) != 0 ) {
printf("Can't set snap len\n");
exit(1);
}
*/
if (pcap_set_promisc(descr, promisc) != 0) {
printf("Can't activate promisc mode for interface: %s\n", dev);
exit(1);
}
if (pcap_activate(descr) != 0) {
printf("Call pcap_activate was failed: %s\n", pcap_geterr(descr));
exit(1);
}
// man pcap-linktype
int link_layer_header_type = pcap_datalink(descr);
if (link_layer_header_type == DLT_EN10MB) {
DATA_SHIFT_VALUE = 14;
} else if (link_layer_header_type == DLT_LINUX_SLL) {
DATA_SHIFT_VALUE = 16;
} else {
printf("We did not support link type %d\n", link_layer_header_type);
exit(0);
}
// пока деактивируем pcap, начинаем интегрировать ULOG
pcap_loop(descr, -1, (pcap_handler)parse_packet, NULL);
2013-10-18 12:44:57 +02:00
/*
Альтернативный парсер, пока не совсем корректно работает, так как возвращает NULL
const u_char* packetptr;
struct pcap_pkthdr packethdr;
while ( (packetptr = pcap_next(descr, &packethdr) ) != NULL) {
parse_packet(NULL, &packethdr, packetptr);
}
*/
2013-10-18 13:35:50 +02:00
}
2013-10-18 12:16:55 +02:00
#endif
2013-10-18 12:44:57 +02:00
#ifdef ULOG2
2013-10-18 13:35:50 +02:00
void ulog_main_loop() {
2013-10-18 12:16:55 +02:00
// В загрузке модуля есть параметры: modprobe ipt_ULOG nlbufsiz=131072
// Увеличиваем размер буфера в ядре, так как стандартно он всего-то 3712
2013-10-18 13:35:50 +02:00
// Текущий размер буфера смотреть: /sys/module/ipt_ULOG/parameters/nlbufsiz
// В рантайме его указать нельзя, только при загрузке модуля ipt_ULOG
2013-10-18 12:16:55 +02:00
struct ipulog_handle *libulog_h;
unsigned char *libulog_buf;
libulog_buf = (unsigned char*)malloc(ULOGD_BUFSIZE_DEFAULT);
if (!libulog_buf) {
printf("Can't allocate buffer");
exit(1);
}
libulog_h = ipulog_create_handle(ipulog_group2gmask(ULOGD_NLGROUP_DEFAULT), ULOGD_RMEM_DEFAULT);
if (!libulog_h) {
printf("Can't create ipulog handle");
exit(0);
}
int len;
while ( len = ipulog_read(libulog_h, libulog_buf, ULOGD_BUFSIZE_DEFAULT) ) {
if (len <= 0) {
if (errno == EAGAIN) {
break;
}
if (errno == 105) {
// Наш уютный бажик: errno = '105' ('No buffer space available'
netlink_error_counter++;
continue;
}
// поймали ошибку - зафиксируем ее при расчетах
printf("ipulog_read = '%d'! "
"ipulog_errno = '%d' ('%s'), "
"errno = '%d' ('%s')\n",
len, ipulog_errno,
ipulog_strerror(ipulog_errno),
errno, strerror(errno));
continue;
}
// успешний прием пакета
netlink_packets_counter++;
ulog_packet_msg_t *upkt;
while ((upkt = ipulog_get_packet(libulog_h, libulog_buf, len))) {
2013-10-18 13:35:50 +02:00
// вот такой хитрый хак, так как данные начинаются без ethernet хидера и нам не нужно выполнять никакого смещения
2013-10-18 12:16:55 +02:00
DATA_SHIFT_VALUE = 0;
parse_packet(NULL, NULL, upkt->payload);
}
}
free(libulog_buf);
}
2013-10-18 13:35:50 +02:00
#endif
2013-10-18 12:16:55 +02:00
2013-11-15 15:35:44 +01:00
// For correct programm shutdown by CTRL+C
2013-10-18 12:16:55 +02:00
void signal_handler(int signal_number) {
#ifdef PCAP
2013-11-15 15:35:44 +01:00
// Stop PCAP loop
2013-10-18 12:16:55 +02:00
pcap_breakloop(descr);
#endif
2014-03-11 20:48:15 +01:00
#ifdef PF_RING
pfring_breakloop(pf_ring_descr);
#endif
2013-10-21 11:43:54 +02:00
#ifdef REDIS
if (redis_enabled) {
redisFree(redis_context);
}
2013-10-21 11:43:54 +02:00
#endif
2013-10-18 12:16:55 +02:00
exit(1);
}
2013-11-15 02:39:23 +01:00
void dump_ip_lookup_tree(tree_leaf* root) {
2013-10-18 12:16:55 +02:00
2013-11-15 02:39:23 +01:00
}