823 lines
29 KiB
C++
823 lines
29 KiB
C++
/*
|
||
TODO:
|
||
1) Добавить среднюю нагрузку за 30 секунд/минуту/5 минут, хз как ее сделать -- не уверен, что это нужно
|
||
2) Подумать на тему выноса всех параметров в конфиг
|
||
*/
|
||
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <errno.h>
|
||
#include <sys/socket.h>
|
||
#include <netinet/in.h>
|
||
#include <arpa/inet.h>
|
||
#include <netinet/if_ether.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#include <signal.h>
|
||
#include <sys/socket.h>
|
||
#include <netinet/in.h>
|
||
#include <arpa/inet.h>
|
||
#include <netinet/ip.h>
|
||
#include <netinet/tcp.h>
|
||
#include <netinet/udp.h>
|
||
#include <netinet/ip_icmp.h>
|
||
|
||
#include <algorithm>
|
||
#include <iostream>
|
||
#include <map>
|
||
#include <vector>
|
||
#include <utility>
|
||
#include <sstream>
|
||
#include <time.h>
|
||
|
||
// for boost split
|
||
#include <boost/algorithm/string.hpp>
|
||
|
||
#ifdef ULOG2
|
||
#include "libipulog.h"
|
||
#endif
|
||
|
||
#ifdef PCAP
|
||
#include <pcap.h>
|
||
#endif
|
||
|
||
/*
|
||
|
||
Custom pcap:
|
||
Install PCAP from sources: http://www.stableit.ru/2013/10/lib-pcap-debian-squeeze.html
|
||
g++ sniffer.cpp -lpcap -I/opt/libpcap140/include -L/opt/libpcap140/lib
|
||
g++ sniffer.cpp -Linclude
|
||
LD_LIBRARY_PATH=/opt/libpcap140/lib ./a.out
|
||
Pcap docs:
|
||
http://www.linuxforu.com/2011/02/capturing-packets-c-program-libpcap/
|
||
http://vichargrave.com/develop-a-packet-sniffer-with-libpcap/ парсер отсюда
|
||
*/
|
||
|
||
using namespace std;
|
||
// not a good idea
|
||
//using namespace boost;
|
||
|
||
// main data structure for storing traffic data for all our IPs
|
||
typedef map <uint32_t, int> map_for_counters;
|
||
map_for_counters PacketsCounterIncoming;
|
||
map_for_counters PacketsCounterOutgoing;
|
||
|
||
map_for_counters TrafficCounterIncoming;
|
||
map_for_counters TrafficCounterOutgoing;
|
||
|
||
enum direction {INCOMING, OUTGOING, INTERNAL, OTHER};
|
||
|
||
#ifdef ULOG2
|
||
// для подсчета числа ошибок буфера при работе по netlink
|
||
int netlink_error_counter = 0;
|
||
int netlink_packets_counter = 0;
|
||
#endif
|
||
|
||
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;
|
||
}
|
||
|
||
#ifdef PCAP
|
||
// делаем глобальной, так как нам нужно иметь к ней доступ из обработчика сигнала
|
||
pcap_t* descr;
|
||
#endif
|
||
|
||
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;
|
||
|
||
// структура для "легкого" хранения статистики соединений в памяти
|
||
struct simple_packet {
|
||
uint32_t src_ip;
|
||
uint32_t dst_ip;
|
||
uint16_t source_port;
|
||
uint16_t destination_port;
|
||
int protocol;
|
||
int length;
|
||
};
|
||
|
||
typedef pair<int, direction> banlist_item;
|
||
|
||
// В информации о ддосе мы храним силу атаки и ее направление
|
||
map<uint32_t, banlist_item> ban_list;
|
||
map<uint32_t, vector<simple_packet> > ban_list_details;
|
||
// сколько строк мы высылаем в деталях атаки на почту
|
||
int ban_details_records_count = 500;
|
||
|
||
time_t start_time;
|
||
int DEBUG = 0;
|
||
|
||
// Период, через который мы пересчитываем pps/трафик
|
||
int check_period = 3;
|
||
|
||
// Увеличиваем буфер, чтобы минимизировать потери пакетов
|
||
int pcap_buffer_size_mbytes = 10;
|
||
|
||
// Нас не интересуют запросы IP, у которых менее XXX pps в секунду
|
||
int threshold = 2000;
|
||
|
||
// Баним IP, если он превысил данный порог
|
||
int ban_threshold = 10000;
|
||
|
||
// data structure for storing data in Vector
|
||
typedef pair<uint32_t, int> pair_of_map_elements;
|
||
|
||
/*
|
||
Тут кроется огромный баго-фич:
|
||
В случае прослушивания 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
|
||
|
||
*/
|
||
|
||
// стандартно у нас смещение для типа DLT_EN10MB, Ethernet
|
||
int DATA_SHIFT_VALUE = 14;
|
||
|
||
typedef pair<uint32_t, uint32_t> subnet;
|
||
vector<subnet> our_networks;
|
||
vector<subnet> whitelist_networks;
|
||
|
||
// prototypes
|
||
void pcap_main_loop(char* dev);
|
||
void ulog_main_loop();
|
||
void signal_handler(int signal_number);
|
||
uint32_t convert_cidr_to_binary_netmask(int cidr);
|
||
bool belongs_to_networks(vector<subnet> networks_list, uint32_t ip);
|
||
|
||
// Function for sorting Vector of pairs
|
||
bool compare_function (pair_of_map_elements a, pair_of_map_elements b) {
|
||
return a.second > b.second;
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
|
||
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;
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
void draw_table(map_for_counters& my_map_packets, map_for_counters& my_map_traffic, direction data_direction) {
|
||
string data_direction_as_string = get_direction_name(data_direction);
|
||
|
||
std::vector<pair_of_map_elements> vector_for_sort;
|
||
|
||
/* Вобщем-то весь код ниже зависит лишь от входных векторов и порядка сортировки данных */
|
||
for( map<uint32_t,int>::iterator ii=my_map_packets.begin(); ii!=my_map_packets.end(); ++ii) {
|
||
// кладем все наши элементы в массив для последующей сортировки при отображении
|
||
pair_of_map_elements current_pair;
|
||
current_pair.first = (*ii).first;
|
||
current_pair.second = (*ii).second;
|
||
|
||
vector_for_sort.push_back(current_pair);
|
||
}
|
||
|
||
std::sort( vector_for_sort.begin(), vector_for_sort.end(), compare_function);
|
||
|
||
for( vector<pair_of_map_elements>::iterator ii=vector_for_sort.begin(); ii!=vector_for_sort.end(); ++ii) {
|
||
int pps = (*ii).second / check_period;
|
||
uint32_t client_ip = (*ii).first;
|
||
string client_ip_as_string = convert_ip_as_uint_to_string((*ii).first);
|
||
|
||
if (pps >= threshold) {
|
||
string pps_as_string;
|
||
std::stringstream out;
|
||
out << pps;
|
||
pps_as_string = out.str();
|
||
|
||
// еси клиента еще нету в бан листе
|
||
if (pps > ban_threshold) {
|
||
if (belongs_to_networks(whitelist_networks, client_ip)) {
|
||
// IP в белом списке
|
||
} else {
|
||
|
||
cout<<"!!!ALARM!!! WE MUST BAN THIS IP!!! ";
|
||
// add IP to BAN list
|
||
|
||
if (ban_list.count(client_ip) == 0) {
|
||
ban_list[client_ip].first = pps;
|
||
ban_list[client_ip].second = data_direction;
|
||
|
||
ban_list_details[client_ip] = vector<simple_packet>();
|
||
cout << "*BAN EXECUTED* ";
|
||
|
||
exec("./notify_about_attack.sh " + client_ip_as_string + " " + data_direction_as_string + " " + pps_as_string);
|
||
} else {
|
||
// Есдли вдруг атака стала мощнее, то обновим ее предельную мощность в памяти (на почте так и остается старая цифра)
|
||
// в итоге я решил, что это плохая идея, так как гугл тогда перестает схлопывать темы двух писем идущих подряд =)
|
||
//if (ban_list[client_ip].first < pps) {
|
||
// ban_list[client_ip].first = pps;
|
||
//}
|
||
|
||
cout << "*BAN EXECUTED* ";
|
||
// already in ban list
|
||
}
|
||
}
|
||
}
|
||
|
||
// determine attack speed
|
||
int bps = my_map_traffic[ (*ii).first ] / check_period;
|
||
// convert to mbps
|
||
int mbps = bps / 1024 / 1024 * 8;
|
||
cout << client_ip_as_string << "\t\t" << pps << " pps " << mbps << " mbps" << endl;
|
||
}
|
||
}
|
||
}
|
||
|
||
#include <boost/assign/std/vector.hpp>
|
||
|
||
// bring 'operator+=()' into scope
|
||
using namespace boost::assign;
|
||
|
||
bool file_exists(string path) {
|
||
FILE* check_file = fopen(path.c_str(), "r");
|
||
if (check_file) {
|
||
fclose(check_file);
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool load_our_networks_list() {
|
||
// вносим в белый список, IP из этой сети мы не баним
|
||
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);
|
||
|
||
vector<string> networks_list_as_string;
|
||
// если мы на openvz ноде, то "свои" IP мы можем получить из спец-файла в /proc
|
||
string our_networks_netmask;
|
||
|
||
if (file_exists("/proc/vz/version")) {
|
||
cout<<"We found OpenVZ"<<endl;
|
||
// тут искусствено добавляем суффикс 32
|
||
networks_list_as_string = exec("cat /proc/vz/veip | awk '{print $1\"/32\"}' |grep -vi version |grep -v ':'");
|
||
}
|
||
|
||
if (file_exists("/etc/networks_list")) {
|
||
vector<string> network_list_from_config = exec("cat /etc/networks_list");
|
||
networks_list_as_string.insert(networks_list_as_string.end(), network_list_from_config.begin(), network_list_from_config.end());
|
||
}
|
||
|
||
// если это ложь, то в моих функциях косяк
|
||
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) );
|
||
|
||
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 );
|
||
int cidr = atoi(subnet_as_string[1].c_str());
|
||
|
||
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);
|
||
|
||
our_networks.push_back(current_subnet);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
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);
|
||
}
|
||
|
||
bool belongs_to_networks(vector<subnet> networks_list, uint32_t ip) {
|
||
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;
|
||
|
||
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
|
||
<<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
|
||
<<" size: "<<packet.length<<" bytes"<<"\n";
|
||
// используется \n вместо endl, ибо иначе начинается хрень всякая при передаче данной строки команде на stdin
|
||
|
||
return buffer.str();
|
||
}
|
||
|
||
// в случае прямого вызова скрипта колбэка - нужно конст, напрямую в хендлере - конст не нужно
|
||
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;
|
||
char iphdrInfo[256], srcip_char[256], dstip_char[256];
|
||
unsigned short id, seq;
|
||
|
||
// Skip the datalink layer header and get the IP header fields.
|
||
packetptr += DATA_SHIFT_VALUE;
|
||
iphdr = (struct ip*)packetptr;
|
||
|
||
// исходящий/входящий айпи это in_addr, http://man7.org/linux/man-pages/man7/ip.7.html
|
||
strcpy(srcip_char, inet_ntoa(iphdr->ip_src));
|
||
strcpy(dstip_char, inet_ntoa(iphdr->ip_dst));
|
||
|
||
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);
|
||
|
||
simple_packet current_packet;
|
||
|
||
// 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;
|
||
current_packet.source_port = ntohs(tcphdr->source);
|
||
current_packet.destination_port = ntohs(tcphdr->dest);
|
||
break;
|
||
case IPPROTO_UDP:
|
||
udphdr = (struct udphdr*)packetptr;
|
||
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;
|
||
}
|
||
|
||
current_packet.protocol = iphdr->ip_p;
|
||
current_packet.src_ip = src_ip;
|
||
current_packet.dst_ip = dst_ip;
|
||
current_packet.length = packet_length;
|
||
|
||
//count<<print_simple_packet(current_packet);
|
||
|
||
direction packet_direction;
|
||
|
||
if (belongs_to_networks(our_networks, src_ip) && belongs_to_networks(our_networks, dst_ip)) {
|
||
packet_direction = INTERNAL;
|
||
|
||
total_count_of_internal_packets ++;
|
||
total_count_of_internal_bytes += packet_length;
|
||
|
||
} else if (belongs_to_networks(our_networks, src_ip)) {
|
||
packet_direction = OUTGOING;
|
||
|
||
total_count_of_outgoing_packets ++;
|
||
total_count_of_outgoing_bytes += packet_length;
|
||
|
||
if (ban_list_details.count(src_ip) > 0 && ban_list_details[src_ip].size() < ban_details_records_count) {
|
||
ban_list_details[src_ip].push_back(current_packet);
|
||
}
|
||
|
||
PacketsCounterOutgoing[ src_ip ]++;
|
||
TrafficCounterOutgoing[ src_ip ] += packet_length;
|
||
} else if (belongs_to_networks(our_networks, dst_ip)) {
|
||
packet_direction = INCOMING;
|
||
|
||
total_count_of_incoming_packets++;
|
||
total_count_of_incoming_bytes += packet_length;
|
||
|
||
if (ban_list_details.count(dst_ip) > 0 && ban_list_details[dst_ip].size() < ban_details_records_count) {
|
||
ban_list_details[dst_ip].push_back(current_packet);
|
||
}
|
||
|
||
PacketsCounterIncoming[ dst_ip ]++;
|
||
TrafficCounterIncoming[ dst_ip ] += packet_length;
|
||
} else {
|
||
packet_direction = OTHER;
|
||
total_count_of_other_packets ++;
|
||
total_count_of_other_bytes += packet_length;
|
||
}
|
||
|
||
time_t current_time;
|
||
time(¤t_time);
|
||
|
||
if ( difftime(current_time, start_time) >= check_period ) {
|
||
// clean up screen
|
||
system("clear");
|
||
|
||
cout<<"Below you can see all clients with more than "<<threshold<<" pps"<<endl<<endl;
|
||
|
||
cout<<"Incoming Traffic"<<"\t"<<total_count_of_incoming_packets/check_period<<" pps "<<total_count_of_incoming_bytes/check_period/1024/1024*8<<" mbps"<<endl;
|
||
draw_table(PacketsCounterIncoming, TrafficCounterIncoming, INCOMING);
|
||
|
||
cout<<endl;
|
||
|
||
cout<<"Outgoing traffic"<<"\t"<<total_count_of_outgoing_packets/check_period<<" pps "<<total_count_of_outgoing_bytes/check_period/1024/1024*8<<" mbps"<<endl;
|
||
draw_table(PacketsCounterOutgoing, TrafficCounterOutgoing, OUTGOING);
|
||
|
||
cout<<endl;
|
||
|
||
cout<<"Internal traffic"<<"\t"<<total_count_of_internal_packets/check_period<<" pps"<<endl;
|
||
|
||
cout<<endl;
|
||
|
||
cout<<"Other traffic"<<"\t\t"<<total_count_of_other_packets/check_period<<" pps"<<endl;
|
||
|
||
cout<<endl;
|
||
|
||
#ifdef PCAP
|
||
struct pcap_stat current_pcap_stats;
|
||
if (pcap_stats(descr, ¤t_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
|
||
|
||
if (ban_list.size() > 0) {
|
||
cout<<endl<<"Ban list:"<<endl;
|
||
|
||
for( map<uint32_t,banlist_item>::iterator ii=ban_list.begin(); ii!=ban_list.end(); ++ii) {
|
||
string client_ip_as_string = convert_ip_as_uint_to_string((*ii).first);
|
||
string pps_as_string;
|
||
std::stringstream out;
|
||
out << ((*ii).second).first;
|
||
pps_as_string = out.str();
|
||
|
||
string attack_direction = get_direction_name(((*ii).second).second);
|
||
|
||
cout<<client_ip_as_string<<"/"<<pps_as_string<<" pps "<<attack_direction<<endl;
|
||
|
||
// странная проверка, но при мощной атаке набить ban_details_records_count пакетов - очень легко
|
||
if (ban_list_details.count( (*ii).first ) > 0 && ban_list_details[ (*ii).first ].size() == ban_details_records_count) {
|
||
string attack_details;
|
||
for( vector<simple_packet>::iterator iii=ban_list_details[ (*ii).first ].begin(); iii!=ban_list_details[ (*ii).first ].end(); ++iii) {
|
||
attack_details += print_simple_packet( *iii );
|
||
}
|
||
|
||
// отсылаем детали атаки по почте, к сожалению, без направления атаки только
|
||
exec_with_stdin_params("./notify_about_attack.sh " + client_ip_as_string + " " + attack_direction + " " + pps_as_string, attack_details );
|
||
// удаляем ключ из деталей атаки, чтобы он не выводился снова и в него не собирался трафик
|
||
ban_list_details.erase((*ii).first);
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
// переустанавливаем время запуска
|
||
time(&start_time);
|
||
// зануляем счетчики пакетов
|
||
PacketsCounterIncoming.clear();
|
||
PacketsCounterOutgoing.clear();
|
||
TrafficCounterIncoming.clear();
|
||
TrafficCounterOutgoing.clear();
|
||
|
||
/* вот здесь можно сбросить данные в Redis */
|
||
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;
|
||
}
|
||
}
|
||
|
||
|
||
int main(int argc,char **argv) {
|
||
char *dev;
|
||
|
||
#ifdef PCAP
|
||
char errbuf[PCAP_ERRBUF_SIZE];
|
||
const u_char *packet;
|
||
struct pcap_pkthdr hdr;
|
||
struct ether_header *eptr; /* net/ethernet.h */
|
||
#endif
|
||
|
||
time(&start_time);
|
||
printf("I need few seconds for collecting data, please wait. Thank you!\n");
|
||
|
||
#ifdef PCAP
|
||
if (argc != 2) {
|
||
fprintf(stdout, "Usage: %s \"eth0\" or \"any\"\n", argv[0]);
|
||
|
||
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];
|
||
}
|
||
#endif
|
||
|
||
// загружаем наши сети и whitelist
|
||
load_our_networks_list();
|
||
|
||
// устанавливаем обработчик CTRL+C
|
||
signal(SIGINT, signal_handler);
|
||
|
||
#ifdef PCAP
|
||
pcap_main_loop(dev);
|
||
#endif
|
||
|
||
#ifdef ULOG2
|
||
ulog_main_loop();
|
||
#endif
|
||
|
||
return 0;
|
||
}
|
||
|
||
#ifdef PCAP
|
||
void pcap_main_loop(char* dev) {
|
||
char errbuf[PCAP_ERRBUF_SIZE];
|
||
/* 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);
|
||
}
|
||
|
||
// В общем-то можно фильтровать то, что нам падает от PCAP, но в моем случае это совершенно не требуется
|
||
// тут было argv[1], но я убрал фильтрацию
|
||
/* Now we'll compile the filter expression*/
|
||
// struct bpf_program fp; /* hold compiled program */
|
||
//if(pcap_compile(descr, &fp, "", 0, netp) == -1) {
|
||
// fprintf(stderr, "Error calling pcap_compile\n");
|
||
// exit(1);
|
||
|
||
//}
|
||
|
||
/* set the filter */
|
||
//if(pcap_setfilter(descr, &fp) == -1) {
|
||
// fprintf(stderr, "Error setting filter\n");
|
||
// 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);
|
||
|
||
/*
|
||
Альтернативный парсер, пока не совсем корректно работает, так как возвращает NULL
|
||
const u_char* packetptr;
|
||
struct pcap_pkthdr packethdr;
|
||
while ( (packetptr = pcap_next(descr, &packethdr) ) != NULL) {
|
||
parse_packet(NULL, &packethdr, packetptr);
|
||
}
|
||
*/
|
||
}
|
||
#endif
|
||
|
||
#ifdef ULOG2
|
||
void ulog_main_loop() {
|
||
/* 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 */
|
||
int ULOGD_RMEM_DEFAULT = 131071;
|
||
|
||
// В загрузке модуля есть параметры: modprobe ipt_ULOG nlbufsiz=131072
|
||
// Увеличиваем размер буфера в ядре, так как стандартно он всего-то 3712
|
||
// Текущий размер буфера смотреть: /sys/module/ipt_ULOG/parameters/nlbufsiz
|
||
// В рантайме его указать нельзя, только при загрузке модуля ipt_ULOG
|
||
|
||
/* Size of the receive buffer for the netlink socket. Should be at least of RMEM_DEFAULT size. */
|
||
int ULOGD_BUFSIZE_DEFAULT = 150000;
|
||
int ULOGD_NLGROUP_DEFAULT = 1;
|
||
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))) {
|
||
// вот такой хитрый хак, так как данные начинаются без ethernet хидера и нам не нужно выполнять никакого смещения
|
||
DATA_SHIFT_VALUE = 0;
|
||
parse_packet(NULL, NULL, upkt->payload);
|
||
}
|
||
}
|
||
|
||
free(libulog_buf);
|
||
}
|
||
#endif
|
||
|
||
|
||
// для корректной остановки программы по CTRL+C
|
||
void signal_handler(int signal_number) {
|
||
|
||
#ifdef PCAP
|
||
// останавливаем PCAP цикл
|
||
pcap_breakloop(descr);
|
||
#endif
|
||
|
||
exit(1);
|
||
}
|
||
|
||
|