Fix bugz in Redis

This commit is contained in:
Pavel Odintsov 2013-10-21 13:43:54 +04:00
parent e70087e386
commit 4d061c1b22
3 changed files with 87 additions and 57 deletions

View File

@ -11,7 +11,7 @@ Install
# CentOS
yum install -y git libpcap-devel gcc-c++ boost-devel boost
# for traffic counting
# If you need traffic counting
apt-get install -y libhiredis-dev
git clone https://github.com/FastVPSEestiOu/fastnetmon.git

View File

@ -13,11 +13,11 @@ fi
REDIS_SUPPORT="yes"
if [ "yes" == $REDIS_SUPPORT ]; then
LIBS="$LIST -lhiredis"
LIBS="$LIBS -lhiredis"
fi
# TODO вынести в опции подключаемые либы
g++ libipulog.c -c -o libipulog.o -Wno-write-strings
g++ -DREDIS -D$ENGINE fastnetmon.cpp libipulog.o $LIBS -o fastnetmon
g++ -DREDIS -D$ENGINE fastnetmon.cpp libipulog.o $LIBS -o fastnetmon -g

View File

@ -3,29 +3,29 @@
1) Добавить среднюю нагрузку за 30 секунд/минуту/5 минут, хз как ее сделать -- не уверен, что это нужно
2) Подумать на тему выноса всех параметров в конфиг
3) Подумать как бы сделать лимитер еще по суммарному трафику
4) Если собрано с редисом, то падает по сегменатции при его отключении
5) Вынести уведомления о ддосах/обсчет данных трафика в отдельный тред
6) Не забыть сделать синхронизацию при очистке аккумуляторов
4) Вынести уведомления о ддосах/обсчет данных трафика в отдельный тред
5) Не забыть сделать синхронизацию при очистке аккумуляторов
*/
/* Author: pavel.odintsov@gmail.com */
/* License: GPLv2 */
#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 <time.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 <netinet/if_ether.h>
#include <netinet/in.h>
#include <algorithm>
#include <iostream>
@ -33,7 +33,6 @@
#include <vector>
#include <utility>
#include <sstream>
#include <time.h>
// for boost split
#include <boost/algorithm/string.hpp>
@ -49,8 +48,10 @@
#ifdef REDIS
#include <hiredis/hiredis.h>
#endif
/*
using namespace std;
/*
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
@ -61,10 +62,6 @@ Custom pcap:
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;
@ -75,26 +72,18 @@ map_for_counters TrafficCounterOutgoing;
enum direction {INCOMING, OUTGOING, INTERNAL, OTHER};
#ifdef REDIS
int redis_port = 6379;
string redis_host = "127.0.0.1";
static redisContext *redis_context = NULL;
#endif
#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;
@ -181,6 +170,20 @@ bool compare_function (pair_of_map_elements a, pair_of_map_elements b) {
return a.second > b.second;
}
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;
}
uint32_t convert_ip_as_string_to_uint(string ip) {
struct in_addr ip_addr;
inet_aton(ip.c_str(), &ip_addr);
@ -196,7 +199,7 @@ string convert_ip_as_uint_to_string(uint32_t ip_as_string) {
}
// convert integer to string
string convert_in_to_string(int value) {
string convert_int_to_string(int value) {
string pps_as_string;
std::stringstream out;
out << value;
@ -243,41 +246,59 @@ bool exec_with_stdin_params(string cmd, string params) {
}
#ifdef REDIS
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;
}
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;
// делаем переменную static, чтобы не реиницилизровать соединение каждый раз заново
static redisContext *c = NULL;
if (!c) {
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
c = redisConnectWithTimeout((char*)"127.0.0.1", 6379, timeout);
if (c->err) {
printf("Connection error: %s\n", c->errstr);
return;
}
if (!redis_context) {
printf("Please initialize Redis handle");
return;
}
string key_name = ip_as_string + "_" + get_direction_name(my_direction);
string redis_command = "INCRBY " + key_name + " " + convert_in_to_string(traffic_bytes);
reply = (redisReply *)redisCommand(c, redis_command.c_str());
freeReplyObject(reply);
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);
}
}
#endif
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;
pair_of_map_elements current_pair = make_pair((*ii).first, (*ii).second);
vector_for_sort.push_back(current_pair);
}
@ -305,6 +326,8 @@ void draw_table(map_for_counters& my_map_packets, map_for_counters& my_map_traff
// add IP to BAN list
if (ban_list.count(client_ip) == 0) {
string data_direction_as_string = get_direction_name(data_direction);
ban_list[client_ip].first = pps;
ban_list[client_ip].second = data_direction;
@ -337,11 +360,6 @@ void draw_table(map_for_counters& my_map_packets, map_for_counters& my_map_traff
}
}
#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) {
@ -668,6 +686,14 @@ int main(int argc,char **argv) {
}
#endif
// иницилизируем соединение с Redis
#ifdef REDIS
if (!redis_init_connection()) {
printf("Can't establish connection to the redis\n");
exit(1);
}
#endif
// загружаем наши сети и whitelist
load_our_networks_list();
@ -859,6 +885,10 @@ void signal_handler(int signal_number) {
pcap_breakloop(descr);
#endif
#ifdef REDIS
redisFree(redis_context);
#endif
exit(1);
}