Программы    Заметки    Ссылки    Автор   

Программа блокировки 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;
}


> Заглавная <