Pull to refresh

Рулим трафиком в Linux. Аккаунтинг, сбор статистики

Configuring Linux *
Каждый админ должен в своей жизни настроить сендмейл, написать биллинг и обругать маздай :)
© bash.org.ru

Сендмейл мы настраивать не будем, как и ругать детище Билли, а попробуем создать что-то вроде биллинга, т.к. этот вопрос рано или поздно встает практически перед всеми системными администраторами.

Статья ориентирована на новичков в администрировании и опытным спецам навряд ли будет интересна.

И так, что мы имеем? Сеть из 50-200 компьютеров, выделенный интернет-канал (ADSL, Ethernet, etc..), linux-роутер. Требуется считать интернет-трафик пользователей (по разным направлениям), ограничивать скорость, определять лимиты трафика в месяц. В дополнение, хочется управлять всем этим через какую-нибудь веб-морду.

За основу возьмем дистрибутив ubuntu-server 8.04.

Чтобы однозначно идентифицировать пользователя и обезопасить кражи трафика при помощи подмены айпи-адресов, настроим VPN-сеть, используя PPTPD.

Практически все действия в консоли будем выполнять от рута, поэтому чтобы не писать каждый раз sudo, пишем:
rustam@srv:~$ sudo -i

Настройка PPTP-сервера


Устанавливаем:
root@srv:~# apt-get install pptpd


Открываем конфиг /etc/pptpd.conf и добавляем строчки:
localip 10.1.0.1
remoteip 10.1.0.2-254

определяем адрес сервера и диапазон адресов пользователей в виртуальной сети.

По-умолчанию pptpd хранит аккаунты пользователей в файле /etc/ppp/chap-secrets, добавляем тестовые аккаунты:
# Secrets for authentication using CHAP
# client server secret IP addresses
user1   pptpd   123   10.1.0.2
user2   pptpd   567   10.1.0.3


обратите внимание на айпи-адреса, они должны быть из диапазона, указанного в параметре remoteip файла /etc/pptpd.conf!

Перезапускаем сервис pptpd:
root@srv:~# /etc/init.d/pptpd restart


Разрешаем подключения к нашему сервису:
root@srv:~# iptables -A INPUT -s 192.168.0.0/24 -p tcp -m state --state NEW -m tcp --dport 1723 -j ACCEPT


Вместо 192.168.0.0/24 подставьте адрес своей локальной сети. Пробуем подключиться к нашей виртуальной сети, для этого создаем на машине с Win XP новое VPN — соединение («Создание нового подключения» — «Подключение к сети на рабочем месте» -«Подключение к виртуальной частной сети»). Если все получилось двигаемся дальше.

Настройка ulog-acctd


Для сбора статистики о трафике будем использовать ulog-acctd, устанавливаем:
root@srv:~# apt-get install ulog-acctd


Открываем конфиг /etc/ulog-acctd.conf. Нас интересуют параметры accounting format и fdelay.

Параметр accounting format отвечает за формат записи лога, подробное описание каждого параметра есть в комментариях, так что подробно расписывать не буду. Нам нужен такой формат:
# время отправитель получатель трафик
accounting format="%t\t%s\t%d\t%b\n"


Параметр fdelay определяет через какой промежуток времени ulog-acctd будет сбрасывать накопленную статистику в лог, выставим его в 10 секунд.
fdelay=10


Перезапускаем ulog-acctd.

Настройка iptables


Чтобы подключившиеся пользователи могли выходить в Сеть, настроим NAT для виртуальной сети:
root@srv:~# iptables -t nat -A POSTROUTING -s 10.1.0.0/24 -j MASQUERADE


Этим правилом сообщаем ulog'у какой трафик логировать:
root@srv:~# iptables -A FORWARD -d 10.1.0.0/24 -j ULOG --ulog-cprange 48 --ulog-qthreshold 50


Нас интересует транзитный трафик, идущий в VPN — сеть.

Теперь протестируем получившуюся связку, подключитесь к сети и попробуйте что-нибудь скачать, в логе /var/log/ulog-acctd/account.log должны появится такие записи:
1224187495 208.67.222.222 10.1.0.2 126
1224187534 205.188.9.192 10.1.0.2 40
1224187556 62.213.122.2 10.1.0.2 10660
1224187594 205.188.9.192 10.1.0.2 40
1224187599 62.213.122.2 10.1.0.2 22130
1224187615 62.213.122.2 10.1.0.2 1037
...

Первое значение — время в формате unix, второе — адрес отправителя, третье — адрес получателя (пользователя), четвертое — количество байт.

MySQL и схема БД


root@srv:~# apt-get install mysql-server

Создаем в MySQL пользователя ulog и импортируем схему БД:
CREATE DATABASE `ulogdb` DEFAULT CHARSET utf8;

USE `ulogdb`;

CREATE TABLE `data` (
    `id` int(11) NOT NULL auto_increment,
    `id_user` int(11) NOT NULL,
    `ts` int(11) NOT NULL,
    `bytes` int(11) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET utf8;

CREATE TABLE `users` (
    `id` int(11) NOT NULL auto_increment,
    `login` varchar(32) NOT NULL,
    `password` varchar(64) NOT NULL,
    `ip` varchar(15) NOT NULL,
    PRIMARY KEY (`id`),
    KEY `ip` (`ip`)
) ENGINE=MyISAM DEFAULT CHARSET utf8;


Это упрощенная схема, позже мы ее модернизируем. В таблице data храним агрегированную статистику по трафику, в users информация о пользователях. Добавьте в таблицу users пользователей, которых указали в /etc/ppp/chap-secrets.
INSERT INTO `users` (`login`, `password`, `ip`) VALUES
('user1', '123', '10.1.0.2');
INSERT INTO `users` (`login`, `password`, `ip`) VALUES
('user2', '567', '10.1.0.3');


Парсим лог


Писать будем на perl, поэтому необходимо доустановить модуль для работы с MySQL:
root@srv:~# apt-get install libdbd-mysql-perl


Привожу полный текст срипта:
#!/usr/bin/perl
   
use DBI;

# определяем имя БД, пользователя и пароль
my $db_name = "ulogdb";
my $db_user = "ulog";
my $db_pass = "1234";

# путь к лог-файлу
$account_log = "/var/log/ulog-acctd/account.log";

# подключаемся к нашей базе
my $DBH = DBI->connect("DBI:mysql:$db_name:localhost",$db_user,$db_pass) or die "Error connecting to database";

# получаем список пользователей в связке ip+id_user
my $STH = $DBH->prepare("select ip,id from users");
$STH->execute;
while (@tmp = $STH->fetchrow_array()) {
    $users{$tmp[0]} = $tmp[1];
}
$STH->finish;

# делаем временную копию лога и очищаем оригинальный файл
system "cp $account_log /tmp/ulog-parser.tmp && cat /dev/null > $account_log";
open LOGFILE,"< /tmp/ulog-parser.tmp";
while (<LOGFILE>) {
    chomp;
   
    # переменную $saddr пока не используем,
    # она пригодится позже

    ($ts,$saddr,$daddr,$bytes) = split /\t/;

    # создаем новую временную метку, необходимо для агрегирования
    # статистки пользователя за определенный интервал времени
    # в одну запись. интервалом будем считать 1 минуту

    $ts = $ts - $ts % 60;

    # сопоставляем айпи из лога со списком пользователей
    # если айпи имеется в базе - наш клиент
    # массив со статистикой имеет древовидную структуру:
    # метка времени -> id пользователя -> полученный трафик

    if (exists($users{$daddr})) {
       $data{$ts}{$users{$daddr}} += $bytes;
    }
}
close LOGFILE;
unlink("/tmp/ulog-parser.tmp");

# подготавливаем запросы к БД
my $STH_CHK = $DBH->prepare("select id_user,id from data where ts=?");
my $STH_ADD = $DBH->prepare("insert into data (id_user,ts,bytes) values(?,?,?)");
my $STH_UPD = $DBH->prepare("update data set bytes=bytes+? where id=?");

# проходим по всему массиву статистики вложенным циклом
#
for $ts (keys %data) {
    # проверяем, если ли в базе записи с такой меткой времени
    $STH_CHK->execute($ts);
    %ex_data = ();
    while (@tmp = $STH_CHK->fetchrow_array()) {
       $ex_data{$tmp[0]} = $tmp[1];
    }
    $STH_CHK->finish;

    # выбираем данные о пользователях в текущем периоде
    for $id_user (keys %{$data{$ts}}) {
       if (exists($ex_data{$id_user})) {
          # если запись уже есть, то просто обновляем значение поля bytes
          $STH_UPD->execute($data{$ts}{$id_user},$ex_data{$id_user});
          $STH_UPD->finish;
       } else {
          # иначе добавляем новую запись
          $STH_ADD->execute($id_user,$ts,$data{$ts}{$id_user});
          $STH_ADD->finish;
       }
    }
}
# отключаемся от БД
$DBH->disconnect;


Сохраним скрипт как /usr/bin/ulog-parser.pl и установим атрибуты:
root@srv:~# chmod 700 /usr/bin/ulog-parser.pl


Пробуем запустить, и смотрим, появились ли данные в таблице data:
mysql> select count(id) from data;
+-----------+
| count(id) |
+-----------+
|       118 |
+-----------+

При минимальных знаниях PHP и SQL сделать вывод статистики не составит труда ;)

На этом я пока закончу, статья и так слишком большая получилась. Если данная тема будет Вам интересна — напишу продолжение (группировка по трафика по направлениям, ограничение скорости).

P.S.: Спасибо ISVir и thestorm за карму. Первый пост.
Tags:
Hubs:
Total votes 67: ↑65 and ↓2 +63
Views 20K
Comments Comments 61