Контакты

Для связи с нами можно использовать:
irc://irc.chatnet.ru:#gfs
icq://546460
email://cobalt[@]gfs-team.ru

Все материалы предоставлены только с ознакомительной целью
ГлавнаяСтатьиКонкурсМини биллинг (1-е место)
© InCoder 01.02.2008

Введение

Многие пользователи (а тем более администраторы) сталкивались с проблемой контроля и учета трафика. Готовых решений позволяющих решить эти вопросы довольно много, но как часто бывает, очень сложно отыскать среди них те, которые полностью удовлетворяют нашим требованиям.

Можно конечно долго и нудно искать, пробовать, тестировать... Но есть и еще один путь. Можно создать такую систему самому. Как известно, самый главный плюс такого подхода в том, что такая система будет оптимизирована именно под требования вас и вашего предприятия.

Единственное что отпугивает - кажущаяся сложность такого подхода. Вроде смотришь на громоздкие системы подобные ISA Server 2006 и думаешь, не, такое своими рукми сделать будет сложно. Это в корне не верно. В настоящее время довольно много библиотек позволяющих реализовывать все функции встречающиеся в корпоративных программах.

Например есть очень хорошо зарекомендовавшая себя утилита iptables. Она позволяет блокировать или пропускать проходящие через компьютер пакеты. У iptables есть интересное правило - QUEUE, которое ставит пакет в очередь на обработку пользовательскому процессу. Который, в свою очередь, может занести данные о нем в БД и принять решение что с ним делать (например заблокировать).

IPTABLES

И так, iptables - распространенное и удобное средство контроля и учета сетевой активности в linux. Благодаря тому что он, можно сказать, встраивается напрямую в ядро ОС, скорость и качество его работы на много лучше любых аналогов в тех же Windows. Но в то же время, на мой взгляд у iptables есть и существенные недостатки. Не смотря на огромнейшую гибкость и функциональность, он не не может считаться полноценным билинговым средством.

Я надеюсь что вы знакомы с логикой работы и методами настройки этой программы, поэтому не буду останавливаться на этом более подробно. Вкратце iptables работает по следующему алгоритму: Сетевой пакет, попадая на наш компьютер, передается ядру, где они проходят через несколько таблиц. Каждая из таблиц содержит несколько цепочек. Цепочки же содержат наборы правил, которые определяют что необходимо сделать с поступившим на них пакетом в зависимости от его свойств.

Если вы до сих пор незнакомы с этим мощным и гибким средством, то советую вам прочитать руководство по iptables http://www.opennet.ru/docs/RUS/iptables/

Мы же приступим к созданию нашей упрощенной биллинговой системы. В первую очередь нужно загрузить необходимые модуля (если они не собраны статически):

modprobe iptable_filter
modprobe ip_queue

Так как мы будем писать "биллинговую" систему, нас интересует в первую очередь цепочка FORWARD. На нее попадают пакеты которые следуют через наш компьютер "транзитом". Зададим для этой цепочки правило:

iptables -A FORWARD -j QUEUE

Это правили будет отправлять все пакеты проходящие через наш компьютер написанной нами программе и уже она будет принимать решения о том стоит ли пропустить пакет или нет.

Программа

Наша программа будет работать по следующему алгоритму. Она будет крутиться в памяти и принимать данные от iptables. Получив пакет, она будет вычислять его размер и ip-адрес назначения. Если пакет предназначен во внутреннюю сеть (192.168.0.0/255), то программа будет записывать текущее время, ip-адрес и размер пакета в базу. Тем самым мы будем вести учет входящего трафика.

Потом программа будет делать запрос к БД, с тем чтобы выбрать сумму трафика с этого ip за сегодняшний день. И если эта сумма будет превышать например 40Мб, то пакет будет блокирован, а если нет, то пропущен.

Вот такая незатейливая системка :).

Для того чтобы "слушать" данные приходящие от iptables, нам понадобиться библиотека libipq, поставляемая вмести с iptables. База данных мы будем использовать MySQL. Состоять она будет всего лишь из одной таблицы следущего формата:

CREATE TABLE `traff` (
	`ip` VARCHAR( 15 ) NOT NULL ,
	`dt` DATETIME NOT NULL ,
	`siz` INT( 22 ) NOT NULL DEFAULT "0",
	INDEX ( `ip` , `dt` )
) TYPE = MYISAM;

Исходный код программы состоит из двух файлов. Заголовочного minibill.h и самого исходника minibill.c. Код этих файлов приведен ниже

/* ********************************************************************
 * File:   minibill.h
 **********************************************************************/

// Подключаем необходимы либы    
#include <libipq/libipq.h>
#include <mysql/mysql.h>
#include <linux/netfilter.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/ip.h>

// Конфиг
#define DB_HOST "localhost"
#define DB_USER "root"
#define DB_PASS ""
#define DB_NAME "test"
#define BUFSIZE 393210
#define SHOW    1 // показывать пакеты на экране -1, нет - 0

/* END minilib.h */
/* ********************************************************************
 * File:   minibill.с
 **********************************************************************/

#include "minibill.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>


// вернем ошибку
void err(char *str){ 
    printf("%s\n",str);
    exit(0);
}

// число в строку
char* itoa(long long int_data){
        int count=0;
        long long i=int_data;
                do
                count++;
                while(i /= 10);
        char* buff=calloc(count+1,sizeof(char));
        sprintf(buff,"%lli",int_data);
        return buff;
}

// Добавим в базу трафика
void add_traff(MYSQL *Mysql, char *ip, int siz){
    char query[1024];
    sprintf(query,"INSERT INTO `traff` SET dt=SYSDATE(), ip=`%s`, siz=`%i`",
                   ip,siz);
    //printf("%s\n",query);
    int i=mysql_query(Mysql,query);
    if(i!=0){
        err((char*)mysql_error(Mysql));
    }
}

// Определяем что делать с пакетом
int test_pack(MYSQL *Mysql, char *ip){
    char query[1024];
    sprintf(query,"SELECT sum(siz) FROM traff WHERE ip=`%s` and 
                   DATE_FORMAT(dt,`%%Y-%%m-%%d`)=
                   DATE_FORMAT(SYSDATE(),`%%Y-%%m-%%d`)",ip);
    //printf("%s\n",query);
    if((mysql_query(Mysql,query))!=0){
        err((char*)mysql_error(Mysql));
    }
    MYSQL_RES *res;
    res=mysql_use_result(Mysql);
    if(!res) err("Ошибка в use");
    MYSQL_ROW row;
    int c;
    c=0;
    while((row=mysql_fetch_row(res))){
        if(row){ 
            c=atoi(row[0]);
        }
    }
    mysql_free_result(res);
    return c;
}

void sh(ipq_packet_msg_t *eth_pack,int now,int max){
printf("ID of queued packet          : %i\n",eth_pack->packet_id);
    printf("Netfilter mark value         : %s\n",eth_pack->mark);
    printf("Packet arrival time (seconds): %d\n",eth_pack->timestamp_sec);
    printf("Name of incoming interface   : %s\n",eth_pack->indev_name);
    printf("Name of outcoming interface  : %s\n",eth_pack->outdev_name);
    printf("Length of packet data        : %d\n",eth_pack->data_len);
    printf("Packet data                  : %d\n",eth_pack->payload);
    printf("SRC                          : %s\n",inet_ntoa(sddr));
    printf("DST                          : %s\n",inet_ntoa(dddr));
    printf("______________________________________________________\n","");
    printf("All=%i\nMax=%i\n",now,max);
    printf("______________________________________________________\n","");
}

int main(int argc, char** argv) {
    
    // Создаем коннект к базе
    MYSQL mysql;
    mysql_init(&mysql);
    if(!mysql_real_connect(&mysql,DB_HOST,DB_USER,DB_PASS,DB_NAME,0,NULL,0)){
        err((char*)mysql_error(&mysql));
    }
    
    // Создадим хэндлер ipq
    unsigned char buf[BUFSIZE];
    struct ipq_handle *h;          // хэндлер
    
    if(!(h=ipq_create_handle(0, PF_INET))) // инициализируем
    {
        ipq_perror("Died");
        ipq_destroy_handle(h);             // убиваем
        err("Error CREATE HANDLE IP_QUEUE");
    }
    if(ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE)<0){ // устанавливаем
                                                        // режим
        ipq_perror("Died");
        ipq_destroy_handle(h);
        err("Error SETMODE IP_QUEUE NOT LOAD modprobe ip_queue");
    }
    while(1){
        if(ipq_read(h, buf, BUFSIZE, 0)<0){ // читаем данные
            err("Error ipq_read");
            ipq_perror("Died");
            ipq_destroy_handle(h);
        }
    
        switch (ipq_message_type(buf)){
            case NLMSG_ERROR:
                ipq_get_msgerr(buf); // Выдача сообщения в лог
            break;
            case IPQM_PACKET:{
                struct iphdr* ip_pack;
                __u32 src,dst;

                ipq_packet_msg_t *eth_pack = ipq_get_packet(buf);
                ip_pack = (struct iphdr *) eth_pack->payload;
                struct in_addr dddr;
                struct in_addr sddr;
                dddr.s_addr=ip_pack->daddr;
                sddr.s_addr=ip_pack->saddr;

                if(strlen(strstr(inet_ntoa(dddr),"192.168.0."))>0){ 
             // Если входящий
                    add_traff(&mysql,inet_ntoa(dddr),
                                (int)eth_pack->data_len);
                }
                
                int now=test_pack(&mysql,inet_ntoa(dddr));
                int max=40*1024*1024;

                if(SHOW==1){ // Если стоит показывать
                    sh(eth_pack,now,max);
                }
                             
                if(now<max){ // разрешим
                    ipq_set_verdict(h, eth_pack->packet_id,NF_ACCEPT, 0, 
                                      NULL);
                }else{ // запретим
                    ipq_set_verdict(h, eth_pack->packet_id,NF_DROP, 0, 
                                           NULL);
                }
                break;
            }
        }
    }
            
}

/* END minilib.c */

Как вы могли заметить, пакетом мы управляем через функцию ipq_set_verdict. Она находиться в библиотеке libipq. Также надо незабыть подключить библиотеку mysql. В итоге строка компиляции у нас будет выглядеть примерно так:

gcc -c -g -I/usr/include -o /minibill.o minibill.c &&
gcc -o minibill minibill.o -lz -lmysqlclient -lipq

Также прошу заметить, что программу надо запускать с привилегиями суперпользователя, а иначе вы получите сообщение Segmentation fault.

Заключение

Написанная нами программа совершенно не является "правильным" биллингом. Ведь в ней есть множество недостатков. Как то: все выполняется в одном потоке, отсюда некоторое притормаживание системы. Вовторых мы считаем только входящий трафик, и у нас нет функций позволяющих вычислять другие случаи. Например было бы не плохо чтобы система могла учитывать данные по портам, ip источника и протоколам. К томуже не помешала бы возможность анализа некоторых пакетов (например http). К томуже программу правильнее было бы сделать демоном и добавить логирование ошибок. Вообщем над приведенным примером еще работать и работать :).

Но в тоже время мы заложиди некоторую основу. Благодаря созданному нами каркасу, мы можем построить систему любой сложности и учитывающую индивидуальные задачи того или иного предприятия для которого она пишеться.

Как подобную систему вижу я:
1. База данных и веб интерфейс к ней (php, perl etc) через ктороый добавляются/удаляются пользователи, устанавливаются разрешенные/запрещенные хосты, лимиты трафика или времени и т.д
2. Система авторизации (например опять же на базе web). Логин пользователя приводиться в соответствии с ip адресом.
3. Система статистики (опять же можно на веб).
4. Демон состоящий из нескольких потоков:
а) Периодически читает БД и выбирает оттуда правила разрешающие и заносит их в общую переменную (структуру)
б) Заносит в базу данные о проходящих пакетах
в) Проверяет пакет на соответствие правилам из общей структуры считанной патоком а) и принемает решение что делать с этим пакетом.

Этим естественно ограничиваться не обязательно. Вы можете создать свой алгоритм отвечающий именно вашим требованиям.

В принципе, библиотека libipq довольно-таки простая. Если вы хотите познакомится с ней подробнее, советую прочитать статью по этой ссылке: http://www.imchris.org/projects/libipq.html

Документация по библиотеке mysql может быть получена с официального сайта по сслыке: http://dev.mysql.com/doc/refman/5.0/en/c.html

by InCoder




© InCoder 01.02.2008

e-Commerce Partners Network
Ник:

Текст:
P Br B I Qute



Код: обновить
Последние комментарии
26.06.2017 07:33:17 DeweyAloma написал:
If you have a desire to learn how to earn from...
XSS Часть II
26.06.2017 07:09:03 DeweyAloma написал:
If you have a desire to learn how to earn from...
Взлом приложений WEB 2.0 из Firefox
26.06.2017 04:42:47 DeweyAloma написал:
If you have a desire to learn how to earn from...
HTTP. Описание протокола
Реклама

Тут должна была быть ваша реклама, но мы потеряли глиняную табличку с ее текстом. SapeId: 665044

Rambler's Top100