Как считать трафик на FreeBSD. Часть первая. Настройка trafd
Автор TMeter
Один из самых элегантных способов подсчета трафика в UNIX-системах -
это использование программы trafd. Наша задача, состоит в том, чтобы получить систему,
которая бы автоматически считала трафик для определенного диапазона ip-адресов,
и заносила результаты в MySQL таблицу формата:
Имя поля | Тип поля | Назначение поля |
dt | date | Дата в формате 2002-12-31 |
ip | char(16) | IP адрес, например 192.168.3.2 |
sent | bigint | Количество посланных байт для данного IP адреса |
recv | bigint | Количество принятых байт для данного IP адреса |
Кроме того, необходимо выполнить следующие условия:
- Не учитывать трафик с определенными признаками, например, трафик
компьютеров из локальной сети к локальному веб-серверу
- При появлении нового IP адреса в заранее известном диапазоне
автоматически учитывать его трафик
Установка программ
Первое, что придется сделать - это установить MySQL и создать базу
данных, в которой будут находиться таблицы, используемые в процессе биллинга.
Пусть название базы данных для этих целей будет ipacc. Далее - установить
пакет trafd. В FreeBSD программа trafd находится в портах, что делает установку
достаточно простой:
cd /usr/ports/net/trafd
make
make install
|
Фактически, trafd представляет собой набор нескольких программ, но нам потребуются следующие утилиты:
- trafd - демон сбора трафика
- trafdump/trafsave - программы для записи собранного трафика демоном trafd в бинарный формат
- traflog - программа для преобразования бинарных файлов трафика в тестовый формат
Шаг 1. Запуск демона trafd
Демон trafd должен запускаться после загрузки системы. Для этого создадим файл /usr/local/etc/rc.d/trafd.sh:
#!/bin/sh
/usr/local/bin/trafd -r -p -i ed0
|
где, -i ed0 указывает на имя сетевого адаптера, с которого будет собирать трафик; ключ -p указывает на то, что переводить сетевой адаптер в promiscuous mode не надо (по умолчанию, демон trafd переводит сетевой адаптер в promiscuous mode); ключ -r будет указывать на попытку восстановления данных из существующего бинарного файла при рестарте демона. Если техническое задание указывает на подсчет трафика с нескольких сетевых интерфейсов одновременно, то можно запустить одновременно несколько экземпляров демона trafd из файла /usr/local/etc/rc.d/trafd.sh, например:
#!/bin/sh
/usr/local/bin/trafd -r -p -i ed0
/usr/local/bin/trafd -r -p -i ed1
/usr/local/bin/trafd -r -p -i ed2
|
Шаг 2. Создание бинарных файлов трафика
Демон trafd хранит информацию о собранном трафике в оперативной памяти,
поэтому ему необходимо периодически посылать сигнал для "сброса"
накопленных данных в временный файл чтобы в случае перезагрузки компьютера
восстановить накопленный трафик из временного файла (ключ -r демона trafd).
Делать это будем из скрипта /usr/local/etc/traffic/save_tmp.sh.
#!/bin/sh
/usr/local/bin/trafdump ed0
/usr/local/bin/trafdump ed1
/usr/local/bin/trafdump ed2
|
Раз в сутки нужно записывать накопленный трафик демоном
trafd в бинарные файлы /usr/local/var/trafd/trafd.ed0(1,2) при помощи
программы trafsave вызывая ее из скрипта /usr/local/etc/traffic/periodic.sh:
#!/bin/sh
/usr/local/bin/trafsave ed0
/usr/local/bin/trafsave ed1
/usr/local/bin/trafsave ed2
|
Итак, скрипт save_tmp.sh будем вызывать раз в 15 минут,
скрипт periodic.sh вызываем в конце каждых суток:
# /etc/crontab
#minute hour mday month wday who command
*/15 * * * * root /usr/local/etc/traffic/save_tmp.sh
59 23 * * * root /usr/local/etc/traffic/periodic.sh
|
Шаг 3. Копирование бинарных файлов трафика за предыдущий день
Сразу после наступления новых суток, бинарные файлы трафика /usr/local/var/trafd/trafd.ed0(1,2)
необходимо сохранить в надежное место. Таким местом для нас будет являться директория
/usr/local/var/traffic. Для копирования бинарных файлов трафика за прошедшие
сутки напишем следующий скрипт, и сохраним его под именем /usr/local/etc/traffic/daily.sh:
#!/bin/sh
date=`date -v-1d '+%Y%m%d'`
fp=/usr/local/var/traffic/$date
cp /usr/local/var/trafd/trafd.ed0 $fp.ed0
rm /usr/local/var/trafd/trafd.ed0
cp /usr/local/var/trafd/trafd.ed1 $fp.ed1
rm /usr/local/var/trafd/trafd.ed1
cp /usr/local/var/trafd/trafd.ed2 $fp.ed2
rm /usr/local/var/trafd/trafd.ed2
|
В результате чего, в директории /usr/local/var/traffic будут появляться
бинарные файлы трафика с именами, обозначающими дату и название интерфейса, например,
20021231.ed0 и т.д. Естественно, что запуск этого скрипта необходимо выполнять
через крон:
# /etc/crontab
#minute hour mday month wday who command
5 0 * * * root /usr/local/etc/traffic/daily.sh
|
Шаг 4. Преобразование бинарных файлов трафика в текстовый вид
Итак, после 3-его шага, мы имеем бинарные файлы трафика за прошедшие сутки,
которые необходимо начинать обрабатывать. Для начала их нужно преобразовать из бинарного
формата в текстовый вид. Для этих целей существует программа traflog,
которую будем вызывать из скрипта /usr/local/etc/traffic/expandplain.sh:
#!/bin/sh
date=`date -v-1d '+%Y%m%d'`
fp=/usr/local/var/traffic/$date
fn=/usr/local/var/traffic_plain/$date
/usr/local/bin/traflog -a -n -i $fp.ed0 >> $fn.ed0
/usr/local/bin/traflog -a -n -i $fp.ed1 >> $fn.ed1
/usr/local/bin/traflog -a -n -i $fp.ed2 >> $fn.ed2
|
Как вы уже наверно догадались, текстовые файлы трафика будут складывать
в директорию /usr/local/var/traffic_plain. Вызов скрипта expandplain.sh также
будет происходить по расписанию.
# /etc/crontab
#minute hour mday month wday who command
10 0 * * * root /usr/local/etc/traffic/expandplain.sh
|
Текстовый формат файла трафика достаточно прост для восприятия
его человеком:
(/usr/local/var/traffic/20021106.ed2) xeon.tmeter.ru at Nov 6 21:59:08
Summary: 48191 data bytes, 53659 all bytes, 5 records
From Port To Port Proto Data All
12.129.206.119 80 192.168.100.2 client tcp 24624 25672
216.26.160.8 110 192.168.100.2 client tcp 10217 11673
192.168.100.2 client 216.34.191.52 25 tcp 6307 6991
192.168.100.2 client 66.185.95.98 25 tcp 6295 6795
192.168.100.2 client 12.129.206.119 80 tcp 748 2528
|
Шаг 5. Помещаем вчерашний трафик в MySQL базу данных
Трафик за прошедшие сутки необходимо загнать с таблицу MySQL
базы данных для того, чтобы с ним было легче работать. Вчерашний трафик поместим
в таблицу под названием yesterday следующего формата:
create table yesterday (src_ip char(16), src_port int,
dst_ip char(16), dst_port int, proto int, bytes bigint);
|
Для обработки текстовых файлов трафика будем использовать
следующий скрипт (назовем его /usr/local/etc/traffic/db_step1.pl):
#!/usr/bin/perl
use Mysql;
$plain_path='/usr/local/var/traffic_plain/';
$yesterday=`date -v-1d '+%Y%m%d'`;
chomp($yesterday); $full_path=$plain_path.$yesterday.".ed0";
$dbh = Mysql->Connect("","ipacc");
$del_query="delete from yesterday;";
$dbh->Query($del_query);
open(F,$full_path) || die "Can't open traffic plain file";
while ($str=)
{
chomp($str);
$fi=substr($str,0,1);
if (($fi>='1') && ($fi<='9'))
{
@a=split('\s+',$str);
# @a[0] - src_ip, @a[1] - src_port, @a[2] - dst_ip, @a[3] - dst_port
# @a[4] - proto, @a[6] - size of packets
if (@a[1] eq 'client') {
$src_port=65535;
} else {
if (@a[1] eq 'none') {
$src_port=0;
} else {
$src_port=@a[1];
}
}
if (@a[3] eq 'client') {
$dst_port=65535;
} else {
if (@a[3] eq 'none') {
$dst_port=0;
} else {
$dst_port=@a[3];
}
}
$proto=0;
if (@a[4] eq 'icmp') {
$proto=1; }
if (@a[4] eq 'tcp') {
$proto=6; }
if (@a[4] eq 'udp') {
$proto=17; }
$ins_query="insert into yesterday (src_ip, src_port, dst_ip,\
dst_port, proto, bytes) values ('".@a[0]."',".$src_port.\
",'".@a[2]."',".$dst_port.",".$proto.\",".$a[6].");";
$dbh->Query($ins_query);
};
};
close(F);
|
Совершенно естественно, что и этот скрипт будет вызываться по крону:
# /etc/crontab
#minute hour mday month wday who command
15 0 * * * root /usr/local/etc/traffic/db_step1.pl
|
Шаг 6 (последний). Обработка и суммирование вчерашнего трафика
Итак, мы имеем таблицу yesterday. В ней собраны адреса сетевых
пакетов, которые демон trafd "поймал" вчера. Из этой таблицы нам необходимо исключить
из подсчета определенные пакеты, вычислить итоговое значение трафика в байтах
для определенной части ip-адресов и записать результат в таблицу traffic.
Делать это мы будет через промежуточную таблицу traffic_tmp:
create table traffic_tmp (ip char(16),
sent bigint default 0, recv bigint default 0);
create table traffic (dt date, ip char(16),
sent bigint default 0, recv bigint default 0);
|
Ниже приведенный скрипт /usr/local/etc/traffic/db_step2.pl
производит калькуляцию итога за прошедшие сутки:
#!/usr/bin/perl
use Mysql;
$dbh = Mysql->Connect("","ipacc");
$del_query="delete from traffic_tmp;";
$dbh->Query($del_query);
#
# exclude NetBIOS packets
#
$q="delete from yesterday where ((src_port>=137) AND (src_port<=139))";
$dbh->Query($q);
$q="delete from yesterday where ((dst_port>=137) AND (dst_port<=139))";
$dbh->Query($q);
#
# Here you can put another rules to exclude an accounting of useless traffic
#
#
# Calculate amount of traffic for each host in 192.168.3.x subnet
#
$query_sent="insert into traffic_tmp (ip,sent) \
select src_ip as ip, sum(bytes) as sent \
from yesterday where src_ip like '192.168.3.%' group by src_ip;";
$dbh->Query($query_sent);
$query_recv="insert into traffic_tmp (ip,recv) \
select dst_ip as ip, sum(bytes) as recv \
from yesterday where dst_ip like '192.168.3.%' group by dst_ip;";
$dbh->Query($query_recv);
#
# Here you can put operators to count the traffic of another subnet
#
#
# itogo. Insert itogo in the main "traffic" table
#
$yesterday=`date -v-1d '+%Y-%m-%d'`; chomp($yesterday);
$qi="insert into traffic (dt,ip,sent,recv) \
select '".$yesterday."',ip, sum(sent) as sent, sum(recv) as recv \
from traffic_tmp group by ip;";
$dbh->Query($qi);
|
Куда вставить запуск этого скрипта, я, надеюсь, вы уже догадались ;)
Загрузить набор скриптов из этой статьи ipacc_trafd.tgz (1,5кбайт)