Введение
Многие пользователи (а тем более администраторы) сталкивались с проблемой контроля и учета трафика. Готовых решений позволяющих решить эти вопросы довольно много, но как часто бывает, очень сложно отыскать среди них те, которые полностью удовлетворяют нашим требованиям.
Можно конечно долго и нудно искать, пробовать, тестировать... Но есть и еще один путь. Можно создать такую систему самому. Как известно, самый главный плюс такого подхода в том, что такая система будет оптимизирована именно под требования вас и вашего предприятия.
Единственное что отпугивает - кажущаяся сложность такого подхода. Вроде смотришь на громоздкие системы подобные ISA Server 2006 и думаешь, не, такое своими рукми сделать будет сложно. Это в корне не верно. В настоящее время довольно много библиотек позволяющих реализовывать все функции встречающиеся в корпоративных программах.
Например есть очень хорошо зарекомендовавшая себя утилита iptables. Она позволяет блокировать или пропускать проходящие через компьютер пакеты. У iptables есть интересное правило - QUEUE, которое ставит пакет в очередь на обработку пользовательскому процессу. Который, в свою очередь, может занести данные о нем в БД и принять решение что с ним делать (например заблокировать).
IPTABLESИ так, iptables - распространенное и удобное средство контроля и учета сетевой активности в linux. Благодаря тому что он, можно сказать, встраивается напрямую в ядро ОС, скорость и качество его работы на много лучше любых аналогов в тех же Windows. Но в то же время, на мой взгляд у iptables есть и существенные недостатки. Не смотря на огромнейшую гибкость и функциональность, он не не может считаться полноценным билинговым средством.
Я надеюсь что вы знакомы с логикой работы и методами настройки этой программы, поэтому не буду останавливаться на этом более подробно. Вкратце iptables работает по следующему алгоритму: Сетевой пакет, попадая на наш компьютер, передается ядру, где они проходят через несколько таблиц. Каждая из таблиц содержит несколько цепочек. Цепочки же содержат наборы правил, которые определяют что необходимо сделать с поступившим на них пакетом в зависимости от его свойств.
Если вы до сих пор незнакомы с этим мощным и гибким средством, то советую вам прочитать руководство по iptables http://www.opennet.ru/docs/RUS/iptables/
Мы же приступим к созданию нашей упрощенной биллинговой системы. В первую очередь нужно загрузить необходимые модуля (если они не собраны статически):
modprobe iptable_filtermodprobe 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

linux не говно,при правильном обращении все будет гладко. 1) Для нее не...
Linux vs Windows