Программа блокировки DDoS-атак через sudo iptables.
Как использовать программу logipinspect?
$ cat log.txt | ./logipinspect _block.log '31/Mar/2009:21:50' 5
Input: argc = 4
Input: argv[0], s_programname = ./logipinspect
Input: len(argv[2]), len(s_datehm) = 17
6000
File '_block.log' opened for reading.
t_ip = 001297450195
t_count = 000000000012
t_time = 001241517703
85.140.239.190
31/Mar/2009:22:05:04 +0400
95.42.221.200
31/Mar/2009:22:05:04 +0400
92.36.89.222
31/Mar/2009:22:05:05 +0400
95.42.221.200
31/Mar/2009:22:05:05 +0400
95.42.221.200
31/Mar/2009:22:05:05 +0400
95.42.221.200
31/Mar/2009:22:05:05 +0400
95.42.221.200
31/Mar/2009:22:05:06 +0400
95.42.221.200
31/Mar/2009:22:05:06 +0400
95.42.221.200
31/Mar/2009:22:05:08 +0400
85.140.239.190
31/Mar/2009:22:05:08 +0400
95.42.221.200
31/Mar/2009:22:05:08 +0400
Unblock: 1297450195 sudo iptables -D INPUT -j DROP -s 77.85.132.211
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for pacify:
pacify is not in the sudoers file. This incident will be reported.
current_b_index = 000000000001
current_b_index_tmp = 000000000000
t_ip = 001297450195
t_count = 000000000012
t_time = 001241517703
log.txt:
85.140.239.190 - - [31/Mar/2009:22:05:04 +0400] "GET /conf/index.php?id=12009 HTTP/1.1" 200 1324
95.42.221.200 - - [31/Mar/2009:22:05:04 +0400] "GET / HTTP/1.1" 200 22607
92.36.89.222 - - [31/Mar/2009:22:05:05 +0400] "GET /i/silver-edge.gif HTTP/1.1" 200 223
95.42.221.200 - - [31/Mar/2009:22:05:05 +0400] "GET / HTTP/1.1" 200 22607
95.42.221.200 - - [31/Mar/2009:22:05:05 +0400] "GET / HTTP/1.1" 200 22607
95.42.221.200 - - [31/Mar/2009:22:05:05 +0400] "GET / HTTP/1.1" 200 22607
95.42.221.200 - - [31/Mar/2009:22:05:06 +0400] "GET / HTTP/1.1" 200 22607
95.42.221.200 - - [31/Mar/2009:22:05:06 +0400] "GET / HTTP/1.1" 200 22607
95.42.221.200 - - [31/Mar/2009:22:05:08 +0400] "GET / HTTP/1.1" 200 22607
85.140.239.190 - - [31/Mar/2009:22:05:08 +0400] "GET /favicon.ico HTTP/1.1" 404 290
95.42.221.200 - - [31/Mar/2009:22:05:08 +0400] "GET / HTTP/1.1" 200 22607
logipinspect.c:
/*
Программа сбора статистики запросов к Web-серверу Apache,
и блокировки определенных адресов по заданным критериям.
Примечание: Для быстрой проверки и выделения адресов IPv4, использован
конечный автомат, написанный "вручную", без средств автоматизации
разработки и без каких-либо средств генерации конечных автоматов.
Автор программы: Александр Лубягин (г.Киров, Кировская область)
E-mail: lubyagin@yandex.ru
Jabber: lubyagin@jabber.ru
*/
#include <wchar.h>
#include <wctype.h>
#include <ctype.h> /* isalpha() */
#include <stdio.h> /* puts() */
#include <string.h> /* strcat(), strlen() */
#include <stdlib.h> /* system(), atoi() */
#include <time.h> /* time(),clock() */
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
/* По-умолчанию, таймаут равен одному часу, 60 минут по 60 секунд */
#define TIMEOUT (60*60)
/* Ссылки на подстроки - аргументы из командной строки */
/* Имя данной (вызываемой) программы */
char *p_programname;
/* Имя файла с данными по заблокированным адресам: адрес IPv4, время блокировки в секундах (Unix) */
char *p_filename_blocked;
/* Интервал времени для проверки числа запросов, продолжительностью 1 минута */
char *p_datehm;
unsigned char f_datehm = TRUE; /* Подстрока даты,времени совпадает с подстрокой из исходного списка */
/* Предельное число запросов по HTTP, приходящих с одного адреса IPv4 */
#define LIMIT 5
char *p_limit;
unsigned int n_limit = LIMIT;
typedef enum _automata_state {
S_WAIT_IPv4_ADDR, /* состояние ожидания очередного элемента типа "адрес IPv4" */
S_WAIT_IPv4_DIGIT, /* промещуточное состояние, после чтения цифры */
S_WAIT_DATEHM, /* состояние ожидания блока типа "дата" */
S_WAIT_DATEHM_CLOSE, /* состояние ожидания завершающего элемента для блока типа "дата" */
S_WAIT_EOL /* состояние ожидания конца строки */
} automata_state;
/* максимально допустимое число адресов IPv4 за одну минуту */
//#define N_MAX_IP_A 1000
#define N_MAX_IP_A 100
/* максимально допустимое число адресов IPv4 за один час */
#define N_MAX_IP_B (N_MAX_IP_A*60)
unsigned int *a_ip;
unsigned int *a_count;
unsigned char *a_f;
time_t *a_time;
unsigned int *b_ip;
unsigned int *b_count; /* дополнительное поле базы, может понадобиться для статистики */
time_t *b_time;
unsigned int current_a_index = 0; /* порядковый номер первой из свободных ячеек массива a */
unsigned int current_b_index = 0; /* то же, для массива b */
/* Подготовить массив a */
void clear_a() {
unsigned int i;
for (i = 0; i < N_MAX_IP_A; i++) {
a_ip[i] = 0;
a_count[i] = 0;
a_f[i] = TRUE; /* По-умолчанию считаем, что время подходит */
a_time[i] = 0;
}
}
/* Подготовить массив b */
void clear_b() {
unsigned int i;
for (i = 0; i < N_MAX_IP_B; i++) {
b_ip[i] = 0;
b_count[i] = 0;
b_time[i] = 0;
}
}
/* Искать ячейку в массиве a_ip по заданному 32-битным числом адресу ip */
signed char find_a_i(unsigned int ip) {
unsigned int i;
signed int found = -1;
for (i = 0; i < N_MAX_IP_A; i++) {
if (a_ip[i] == ip) found = i;
}
return found;
}
/* Искать ячейку в массиве b_ip по заданному 32-битным числом адресу ip */
signed char find_b_i(unsigned int ip) {
unsigned int i;
signed int found = -1;
for (i = 0; i < N_MAX_IP_B; i++) {
if (b_ip[i] == ip) found = i;
}
return found;
}
/* Преобразовать структуру адреса IPv4 в 32-битное число (unsigned int) */
unsigned int a_ip_d_to_ip(unsigned char *a_ip_d) {
unsigned char *a = a_ip_d;
return (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3];
}
/* Поместить запись по заданному адресу IPv4 в массив a */
unsigned int put_rec_to_a(
unsigned int t_ip,
unsigned int t_count,
time_t t_time) {
signed int found = find_a_i(t_ip);
if(found == -1) {
if(current_a_index < N_MAX_IP_A) {
a_ip[current_a_index] = t_ip;
a_count[current_a_index] = t_count;
/* a_f[] не изменяем: время из исходной записи при добавлении адреса IPv4 еще не известно */
a_time[current_a_index] = t_time;
current_a_index++;
}
else {
fprintf(stderr,"Temporary table of IPv4-addresses data is full.\n");
}
}
else {
/* a_ip[found] не изменяем, он - тот же самый, ip */
a_count[found]++;
/* a_f[] не изменяем: время из исходной записи при добавлении адреса IPv4 еще не известно */
a_time[found] = t_time; /* заполняем новое время, начиная с текущего момента */
}
return found;
}
/* Поместить запись по заданному адресу IPv4 в массив b */
unsigned int put_rec_to_b(
unsigned int t_ip,
unsigned int t_count,
time_t t_time) {
signed int found = find_b_i(t_ip);
if(found == -1) {
b_ip[current_b_index] = t_ip;
b_count[current_b_index] = t_count;
b_time[current_b_index] = t_time;
current_b_index++;
}
else {
/* b_ip[found] не изменяем, он - тот же самый, ip */
b_count[found]++;
b_time[found] = t_time; /* заполняем новое время, начиная с текущего момента */
}
return found;
}
/* Загрузить базу данных статистики блокировок в массив b */
void load_database_b(char *filename) {
FILE *f;
unsigned int t_ip;
unsigned int t_count; /* дополнительное поле базы, может понадобиться для статистики */
time_t t_time;
f = fopen(filename,"rt"); // database
if(f == NULL) {
/* Завершить чтение, по причине того, что файл не читается */
fprintf(stderr,"Can't open file '%s' for reading.\n",filename);
return; /* Закрывать файл не нужно (дескриптор файла не используется) */
}
fprintf(stderr,"File '%s' opened for reading.\n",filename);
do {
/* Прочитать очередную запись (состоящую из нескольких полей) для определенного адреса IPv4 */
if((feof(f))||(ferror(f))) break;
int t3,t2,t1,t0;
unsigned int t_count_tmp;
/* 384-битная запись (48 байт) */
fscanf(f,"%02X%02X%02X%02X%08X%032Lu\n",&t3,&t2,&t1,&t0,
&t_count_tmp,
(long long unsigned int *)(&t_time) /* 64-битное значение */
);
((unsigned char *)(&t_ip))[3] = t3;
((unsigned char *)(&t_ip))[2] = t2;
((unsigned char *)(&t_ip))[1] = t1;
((unsigned char *)(&t_ip))[0] = t0;
t_count = t_count_tmp;
printf("t_ip = %012u\n",t_ip);
printf("t_count = %012u\n",t_count);
printf("t_time = %012Lu\n",(long long unsigned int)t_time);
/* Если запись прочиталась успешно (без ошибок), добавляем ее в массив b */
put_rec_to_b(
t_ip,
t_count,
t_time);
} while(TRUE);
fclose(f);
}
/* Выгрузить базу данных статистики блокировок из массива b */
void save_database_b(char *filename) {
FILE *f;
unsigned int t_ip;
unsigned int t_count;
time_t t_time;
int current_b_index_tmp;
f = fopen(filename,"wt"); // database
if(f == NULL) {
/* Завершить чтение, по причине того, что файл не читается */
fprintf(stderr,"Can't open file '%s' for writing.\n",filename);
return; /* Закрывать файл не нужно (дескриптор файла не используется) */
}
printf("current_b_index = %012u\n",current_b_index);
for(current_b_index_tmp = current_b_index-1; current_b_index_tmp >= 0; current_b_index_tmp--) {
/* Скопировать очередную запись (состоящую из нескольких полей) в файл статистики по блокировкам */
printf("current_b_index_tmp = %012u\n",current_b_index_tmp);
t_ip = b_ip[current_b_index_tmp];
t_count = b_count[current_b_index_tmp];
t_time = b_time[current_b_index_tmp];
printf("t_ip = %012u\n",t_ip);
printf("t_count = %012u\n",t_count);
printf("t_time = %012Lu\n",(long long unsigned int)t_time);
/* Записать 2*(8)+32 = 48 байт для каждой записи (всего, */
/* 48 символов в кодировке ASCII) */
/* 384-битная запись (48 байт) */
fprintf(f,"%02X%02X%02X%02X%08X%032Lu\n",
(unsigned char)((t_ip>>24)&0x000000FF),
(unsigned char)((t_ip>>16)&0x000000FF),
(unsigned char)((t_ip>>8)&0x000000FF),
(unsigned char)((t_ip)&0x000000FF),
((unsigned int)(t_count)),
((long long unsigned int)(t_time)) // 64-битное значение
);
if(ferror(f)) break;
}
fclose(f);
}
/* Выполнить отложенную блокировку определенных адресов, после сбора статистики по исходному списку адресов */
void block_by_limit_a(unsigned int limit) {
const int N_MAX_LEN_COMMAND = 200;
char s[N_MAX_LEN_COMMAND];
char s_ip[24]; // NULL terminated
unsigned int i;
unsigned int t_ip;
unsigned int t_count;
time_t t_time;
for (i = 0; i < current_a_index; i++) {
/* Сравнить показания статистики, и использовать признак совпадения подстрок даты,времени */
if((a_count[i] >= limit) && (a_f[i] == TRUE)) {
t_ip = a_ip[i];
t_count = a_count[i];
/* Выполнить блокировку адреса IPv4 */
strcpy(s,"sudo iptables -A INPUT -j DROP -s ");
sprintf(s_ip,"%d.%d.%d.%d",(t_ip>>24)&0x000000FF,(t_ip>>16)&0x000000FF,(t_ip>>8)&0x000000FF,(t_ip)&0x000000FF);
strcat(s,s_ip);
printf("Block: %d %s\n",t_ip,s);
system(s);
/* Для данного адреса IPv4, заполнить соответствующие ячейки массива b */
time(&t_time);
printf("Time: %d\n",(unsigned int)t_time);
signed int found = find_b_i(t_ip);
if(found == -1) {
b_ip[current_b_index] = t_ip;
b_count[current_b_index] = t_count;
b_time[current_b_index] = t_time;
current_b_index++;
}
else {
/* b_ip[found] не изменяем, он - тот же самый, ip */
/* Неизвестно - что делать с накоплением числа адресов, посчитать их сложно */
b_count[found] = t_count;
b_time[found] = t_time; /* заполняем новое время, начиная с текущего момента */
}
}
}
}
/* Выполнить разблокировку определенных адресов, по таймауту */
void unblock_by_time_b() {
const int N_MAX_LEN_COMMAND = 200;
char s[N_MAX_LEN_COMMAND];
char s_ip[24]; // NULL terminated
unsigned int i;
unsigned int t_ip;
unsigned int t_count;
time_t t_time;
time_t t_time_now;
time(&t_time_now);
for (i = 0; i < current_b_index; i++) {
t_ip = b_ip[i];
t_count = b_count[i];
t_time = b_time[i];
if(t_time_now - t_time >= TIMEOUT) {
/* Выполнить разблокировку адреса IPv4 */
strcpy(s,"sudo iptables -D INPUT -j DROP -s ");
sprintf(s_ip,"%d.%d.%d.%d",(t_ip>>24)&0x000000FF,(t_ip>>16)&0x000000FF,(t_ip>>8)&0x000000FF,(t_ip)&0x000000FF);
strcat(s,s_ip);
printf("Unblock: %d %s\n",t_ip,s);
system(s);
}
else {
printf("None\n");
}
}
}
int main(int argc, char **argv) {
automata_state state = S_WAIT_IPv4_ADDR; /* номер текущего состояния */
wint_t wc; /* код текущего символа */
unsigned char a_ip_d[4] = {0,0,0,0}; /* Временная структура для хранения данных адреса IPv4 {0,1,2,3} */
unsigned char m = 0; /* Число из текущей группы разрядов (цифр) */
unsigned char d; /* Текущая цифра, код которой находится в переменной wc */
signed char n = 0; /* Текущий номер группы (разрядов), от 0 до 3 */
unsigned char k = 0; /* Порядковый номер цифры в числе из данной группы, {0,1,2} */
unsigned int j_datehm = 0; /* Номер текущей позиции в подстроке даты,времени */
unsigned int len_datehm = 0; /* Длина подстроки даты,времени не больше максимальной длины командной строки */
unsigned int last_a_index = 0; /* Порядковый номер позиции последнего добавленного элемента в массив a */
/* Пример командной строки: ./inspect blocked '31/Mar/2009:21:50' 5 */
if(argc < 4) {
/* Если число аргументов недостаточно для работы программы */
printf("./inspect blocked '31/Mar/2009:21:50' 5\n");
return -1;
}
/* Чтение аргументов из командной строки */
p_programname = argv[0];
p_filename_blocked = argv[1];
p_datehm = argv[2];
len_datehm = strlen(p_datehm);
p_limit = argv[3];
n_limit = atoi(p_limit);
/* Печать некоторых аргументов из командной строки */
printf("Input: argc = %d\n",argc);
printf("Input: argv[0], s_programname = %s\n",p_programname);
printf("Input: len(argv[2]), len(s_datehm) = %d\n",len_datehm);
/* Подготовка массивов данных */
a_ip = malloc(N_MAX_IP_A*sizeof(unsigned int));
a_count = malloc(N_MAX_IP_A*sizeof(unsigned int));
a_f = malloc(N_MAX_IP_A*sizeof(unsigned char));
a_time = malloc(N_MAX_IP_A*sizeof(time_t));
clear_a(); /* Обнуляем массивы данных, на случай, если они выделяются в стеке */
/* то же, для массива b */
printf("%d\n",N_MAX_IP_B);
b_ip = malloc(N_MAX_IP_B*sizeof(unsigned int));
b_count = malloc(N_MAX_IP_B*sizeof(unsigned int));
b_time = malloc(N_MAX_IP_B*sizeof(time_t));
clear_b(); /* Обнуляем массивы данных, на случай, если они выделяются в стеке */
/* Загрузить базу данных статистики блокировок в массив b */
load_database_b(p_filename_blocked);
/* Считать входной поток символов, в кодировке UTF-8 */
/* Сбросить значения счетчиков текущего блока типа "адрес IPv4" */
void reset() {
k = 0;
m = 0;
}
/* Сбросить значения счетчика и признака совпадения подстрок для блока типа "дата,время" */
void reset_j() {
j_datehm = 0;
f_datehm = TRUE;
}
/* Добавить следующий разряд (десятичный) к числу m */
void increment_m(wint_t wc) {
d = wc-'0';
m *= 10;
m += d;
}
/* Добавить адрес из структуры a_ip_d в массив a, совместно со статистикой по данному адресу IPv4 */
unsigned int append_a_ip_d_to_a() {
unsigned int last_a_index;
unsigned int ip = a_ip_d_to_ip(a_ip_d);
last_a_index = put_rec_to_a(ip,1,0);
return last_a_index;
}
/* Напечатать содержимое структуры a_ip_d */
void print_a_ip_d() {
for(unsigned char i = 0; i < 4; i++) {
printf("%d",a_ip_d[i]);
if(i < 3) printf(".");
else printf("\n");
}
}
/* Обработать стандартный поток ввода, stdin */
while(TRUE) {
/* Считать очередной символ из стандартного потока ввода */
wc = getwchar();
if(wc == WEOF) break; /* поток закончился */
switch(state) {
case S_WAIT_IPv4_ADDR: {
/* очередная цифра */
if(isdigit(wc)) {
state = S_WAIT_IPv4_DIGIT;
k++;
increment_m(wc);
}
else {}; /* none */
break;
}
case S_WAIT_IPv4_DIGIT: {
/* очередная цифра */
if(isdigit(wc)) {
/* состояние прежнее, state */
if(k <= 2) increment_m(wc);
else {}; /* none */
k++;
}
else
if(wc == '.') {
if(n >= 3) state = S_WAIT_DATEHM;
else {
a_ip_d[n] = m;
reset();
state = S_WAIT_IPv4_DIGIT;
}
n++;
}
else
if(isspace(wc)) {
state = S_WAIT_DATEHM;
a_ip_d[n] = m;
last_a_index = append_a_ip_d_to_a();
print_a_ip_d();
}
break;
} // case
case S_WAIT_DATEHM: {
if(wc == '[') {
state = S_WAIT_DATEHM_CLOSE;
/* Устанавливаем значение счетчика и флага для сравнения подстрок */
/* После загрузки базы данных, данной установки делать не нужно, так как не перешли к блоку даты,времени */
reset_j();
}
else if(wc == '\n') {
state = S_WAIT_IPv4_ADDR;
n = 0;
reset();
}
break;
} // case
/* Обработка подстроки даты,времени */
case S_WAIT_DATEHM_CLOSE: {
if(wc == ']') {
/* Если подстрока даты,времени совпадает с исходной, установить признак совпадения в массиве a */
a_f[last_a_index] = f_datehm; /* unsigned char */
reset_j(); /* Очистить значения счетчика и признака совпадения подстрок перед переходом в другое состояние */
state = S_WAIT_EOL;
printf("\n");
}
else if(wc == '\n') {
/* Если подстрока даты,времени совпадает с исходной, установить признак совпадения в массиве a */
a_f[last_a_index] = f_datehm; /* unsigned char */
reset_j(); /* Очистить значения счетчика и признака совпадения подстрок перед переходом в другое состояние */
state = S_WAIT_IPv4_ADDR;
n = 0;
reset();
}
else {
/* Если подстроки не совпадают хотя бы в одном элементе, очищаем признак совпадения f_datehm */
if((wc != p_datehm[j_datehm]) && (j_datehm < 17)) {
f_datehm = FALSE;
}
printf("%c",wc);
}
j_datehm++; /* Увеличить счетчик после каждого элемента в состоянии S_WAIT_DATEHM_CLOSE */
break;
} // case
case S_WAIT_EOL: {
if(wc == '\n') {
state = S_WAIT_IPv4_ADDR;
n = 0;
reset();
}
break;
} // case
} // switch
} // while
/* Выполнить отложенную блокировку определенных адресов IPv4 по заданным критериям */
block_by_limit_a(n_limit);
unblock_by_time_b();
/* Освободить память, занятую под массивы */
free(a_ip);
free(a_count);
free(a_f);
free(a_time);
free(b_ip);
free(b_count);
free(b_time);
/* Выгрузить данные из массива b в базу данных статистики блокировок */
save_database_b(p_filename_blocked);
return 0;
}
> Заглавная <