Все о NetFlow и sFlow от Web Control
Меню сайта


Категории раздела
Cisco [1]
Lancope StealthWatch [3]
Решения компании Lancope для анализа сети и контроля сетевых процессов
NetFlow [6]
Статьи о технологиях


Партнеры



Вход


Поиск


Приветствую Вас, Гость · RSS 15.06.2025, 22:13
Главная » Статьи » NetFlow

NetFlow-искания. Часть 3. Изобретаем велосипед.
Итак поигрались с готовыми решениями — красота. Что дальше? Лично меня очень беспокоила информация о потерянных пакетах в логах:
# /var/log/flow-capture.log:
ftpdu_seq_check(): src_ip=xxx.xxx.xxx.xxx dst_ip=0.0.0.0 d_version=5 expecting=1531521097 received=1531521307 lost=210
ftpdu_seq_check(): src_ip=xxx.xxx.xxx.xxx dst_ip=102.0.0.0 d_version=5 expecting=1730244036 received=1730300526 lost=56490
ftpdu_seq_check(): src_ip=xxx.xxx.xxx.xxx dst_ip=99.111.117.110 d_version=5 expecting=1730207586 received=1730300556 lost=92970
ftpdu_seq_check(): src_ip=xxx.xxx.xxx.xxx dst_ip=102.0.0.0 d_version=5 expecting=1730300556 received=1730300586 lost=30
Плюс ко всему какие то нелепые dst_ip.

Что бы не мешать основному процессу, добавляю на сенсоре строчку для дублирования инфы на другой порт:
# conf t
# ip flow-export destination xxx.xxx.xxx.xxx 9997
Циска ругнется, что мы дублируем инфу, но послушно выполнит.

Теперь надо как-то взять информацию:
Пишем на перле (!PhP) скрипт:

#!/usr/bin/perl
use IO::Socket::INET;
$| = 1;
my ($socket,$received_data);
my ($peeraddress,$peerport);
$socket = new IO::Socket::INET (
LocalPort => '9997',
Proto => 'udp',
) or die "ERROR in Socket Creation : $!\n";
while(!$recieved_data)
{
$socket->recv($recieved_data,1464);
$peer_address = $socket->peerhost();
$peer_port = $socket->peerport();
}

open (MYFILE, '>data.txt');
# print MYFILE "$peer_address:$peer_port\n"; # <- строчка просто что бы показать метод.
print MYFILE $recieved_data;
close (MYFILE);

$socket->close();
Запускаем скрипт на сервере и он выхватывает один пакет с информацией по netflow.
Открываем официальную спецификацию по протоколу NetFlow v 5 и анализируем:

00000000 00 05 00 1E │ 64 DB 5B 60 │ 51 23 3F B6 │ 38 E5 5D 5A │ 67 33 DF D6 │ 00 00 00 00  ....dш[`Q#?І8Е]Zg3ъж....
00000018 0A 4F E2 0A │ 0A 01 FF 01 │ C0 A8 22 02 │ 00 14 00 02 │ 00 00 00 02 │ 00 00 01 96  .OБ...Ъ.ю╗".............
00000030 64 DA F4 3C │ 64 DB 11 88 │ E9 96 01 85 │ 00 10 11 00 │ 00 00 00 00 │ 14 10 00 08  dзТ<dш..И...............
00000048 0A 5C 03 04 │ 0A 4F E2 32 │ C0 A8 22 56 │ 00 02 00 14 │ 00 00 00 02 │ 00 00 00 D9  .\...OБ2ю╗"V...........ы
00000060 64 DA F4 40 │ 64 DB 1B 68 │ 00 35 C0 19 │ 00 10 11 24 │ 00 00 00 00 │ 10 14 00 00  dзТ@dш.h.5ю....$........
00000078 0A 49 D0 08 │ 0A 4F E2 0A │ C0 A8 22 56 │ 00 02 00 14 │ 00 00 00 02 │ 00 00 02 8E  .Iп..OБ.ю╗"V............
00000090 64 DA F4 C0 │ 64 DB 14 2C │ 00 8A 00 8A │ 00 10 11 24 │ 00 00 00 00 │ 10 14 00 00  dзТюdш.,.......$........
000000A8 0A 4F A5 0A │ 0A 02 1A 8C │ C0 A8 22 02 │ 00 08 00 02 │ 00 00 00 03 │ 00 00 00 90  .O╔.....ю╗".............
000000C0 64 DA F5 08 │ 64 DB 17 E4 │ 0E 8B 16 13 │ 00 02 06 00 │ 00 00 00 00 │ 14 10 00 08  dзУ.dш.Д................
*********************************************************************************
000003D8 0A 4F A1 46 │ 0A 48 38 1B │ C0 A8 22 02 │ 00 08 00 02 │ 00 00 00 0A │ 00 00 04 F2  .O║F.H8.ю╗"............Р
000003F0 64 DB 3E 50 │ 64 DB 48 18 │ 11 9A 01 BB │ 00 1B 06 00 │                            dш>PdшH....╩....



Каждый такой пакет — таблица.
Первые 24 байта (0-23) — это шапка:

00 05 - версия протокола 5
00 1E - в таблице будет 30 записей
64 DB 5B 60 - System uptime в милисекундах (переводить надо?)
51 23 3F B6 - Текущее время в секундах (с начала эпохи)
38 E5 5D 5A - Остаточная часть в НАНОСЕКУНДАХ!!! (с начала эпохи)
67 33 DF D6 - Номер последовательности.
00 - тип движка
00 - идентификатор движка
00 00 - sampling_interval - Если честно я так и не понял что это за фигня.


Остальные данные — записи, которые разбиты по 48 байт.

Здесь я наблюдаю некоторую избыточность:
1) Зачем под версию выделено 2 байта?! То есть планируется больше 255 версий?
2) Очень странно, что под остаточную часть выделяется 4 байта Зачем такая точность?!.. Мне бы хватило 2.
3) Тип движка и его идентификатор. Что то мне подсказывает, что это тоже не очень важные данные.
4) Sampling interval — еще 2 байта, заполненные нулями.
Итого около 7 байт ненужной информации, которую врятли кому понядобятся. Учитывая тот факт, что пятая версия модифицироваться не будет, сомнений о бесполезности не остается.
7 байт тебе жалко что ли? — Умножаем эти 7 байт на миллионы пакетов, которые передаются и получаем ощутимое множество тактов оборудования. NetFlow и так очень прожорливый протокол.
В самих записях есть все необходимые данные по потокам. Вероятно инженеры Cisco разрабатывали такой вид таблиц для какой то своей базы с особой структурой, в которой имеют значения, взятые из заголовков. Но в обычном быту системного администратора (вот только не надо ржать) это не очень нужно. С другой стороны если мы захотим, например следить за устройством, нам все равно придется выкачивать весь объем данных. Вероятно в этом вопросе решили подойти со всех сторон. Вспомним хотя бы тот факт, что маршрутизатор не принимает никаких запросов, а просто шлет накопленную информацию.
Теперь давайте подумаем что мы можем сделать?..
Для начала декодируем пакет в удобоваримый вид:

#!/usr/bin/perl
use IO::Socket::INET;
use POSIX;

$| = 1;

my ($socket,$received_data);
my ($peeraddress,$peerport);

$socket = new IO::Socket::INET (
LocalPort => '9997',
Proto => 'udp',
) or die "ERROR in Socket Creation : $!\n";

while(!$recieved_data) {
    $socket->recv($recieved_data,1464);
    $peer_address = $socket->peerhost();
    $peer_port = $socket->peerport();
}

@header = unpack("nnN4NNNHHH2", substr($recieved_data,0,24));

open (MYFILE, '>data.txt');
print MYFILE "Source: $peer_address:$peer_port\n",
             "Protocol version: $header[0]\n",
             "Records count: $header[1]\n",
             "System uptime: $header[2] ms\n",
             "Timer: " . strftime ("%Y/%m/%d %H:%M:%S", localtime($header[3])) . "\n";
print MYFILE "\nsource_ip\t",
             "dst_ip\t\t",
             "next_hop\t",
             "if_in\t",
             "if_out\t",
             "Pkts\t",
             "Octets\t",
             "Time start\t\t",
             "Time end\t\t",
             "S_port\t",
             "D_port\t",
             "pad1\t",
             "Flag\t",
             "Prot\t",
             "TOS\t",
             "src_as\t",
             "dst_as\t",
             "s_mask\t",
             "d_mask\t",
             "pad2\n\n";
foreach $i (0..$header[1]) {
    if (substr($recieved_data, 24+48*$i, 48)) {
    @substr = unpack("A4 A4 A4 n n N N x8 n n C C C C n n C C n X24 N N", substr($recieved_data, 24+48*$i, 48));
    if ($substr[18] && $substr[19]) {
        @t = localtime($header[3]+floor(($header[4]/1000000+$substr[18]-$header[2])/1000));
        $substr[20] = fmod((floor($header[4]/1000000)+$header[2]-$substr[18]), 1000);
        $start = strftime "%Y-%m-%d.%H:%M:%S", @t;
        $start .= ".$substr[20]";
        @t = localtime($header[3]+floor(($header[4]/1000000+$substr[19]-$header[2])/1000));
        $substr[21] = fmod((floor($header[4]/1000000)+$header[2]-$substr[19]), 1000);
        $stop = strftime "%Y-%m-%d.%H:%M:%S", @t;
        $stop .= ".$substr[21]";
    };
    $fmt = "%vd\t%vd\t%vd\t\%d\t%d\t%d\t%d\t$start\t$stop\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n";
    printf MYFILE ($fmt, @substr);
    }
};

#print MYFILE $recieved_data; #debug info
close (MYFILE);

$socket->close();
Если честно я очень лоханулся, не ознакомившись с работой сокетов Perl. Грабли меня ждали в строчке:

$socket->recv($recieved_data,1464);
У меня было подозрение, что маршрутизатор работает неправильно, говоря в заголовке о том, что передает 30 записей, а передавая на самом деле 20. Дак вот я просто скопировал из примера неправильное число байт, которое будет ожидать коллектор (1024). На самом деле мы должны ожидать 24+48*30=1464 байт. Только постоянные сомнения в себе помогли мне исправиться.

Вдогонку скрипт просмотра потока netflow в реальном внемени. Для него не нужно вообще ничего кроме установленного Perl. Возможно пригодится для диагностики.

#!/usr/bin/perl
#Realtime viewer made by DreamHunter
use IO::Socket::INET;
use POSIX;

$| = 1;

my ($socket,$received_data);
my ($peeraddress,$peerport);

$socket = new IO::Socket::INET (
LocalPort => '9997',
Proto => 'udp',
) or die "ERROR in Socket Creation : $!\n";

while(1) {
    $socket->recv($recieved_data,1464);
    $peer_address = $socket->peerhost();
    $peer_port = $socket->peerport();
    @header = unpack("nnN4NNNHHH2", substr($recieved_data,0,24));
    for ($i=0; substr($recieved_data, 24+48*$i, 48); $i++) {
        @substr = unpack("A4 A4 A4 n n N N x8 n n C C C C n n C C n X24 N N", substr($recieved_data, 24+48*$i, 48));
        if ($substr[18] && $substr[19]) {
            @t = localtime($header[3]+floor(($header[4]/1000000+$substr[18]-$header[2])/1000));
            $substr[20] = fmod((floor($header[4]/1000000)+$header[2]-$substr[18]), 1000);
            $start = strftime "%Y-%m-%d.%H:%M:%S", @t;
            $start .= ".$substr[20]";
            @t = localtime($header[3]+floor(($header[4]/1000000+$substr[19]-$header[2])/1000));
            $substr[21] = fmod((floor($header[4]/1000000)+$header[2]-$substr[19]), 1000);
            $stop = strftime "%Y-%m-%d.%H:%M:%S", @t;
            $stop .= ".$substr[21]";
            $fmt = "%vd\t%vd\t%vd\t\%d\t%d\t%d\t%d\t$start\t$stop\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n";
            printf ($fmt, @substr);
        }
    }
};
$socket->close();
Послесловие: Лично для себя я считаю тему NetFlow версии 5 раскрытой. Предыдущие версии рассматривать нет смысла — там практически тоже самое. То есть работа для спинного мозга (тупо долбить код). Впереди ждет 9-я версия, которая призвана работать с протоколом IPv6.

Материал с сайта SysAdminBlog
Категория: NetFlow | Добавил: SMG (05.03.2013)
Просмотров: 1320 | Теги: Perl, cisco netflow, freebsd, netflow | Рейтинг: 0.0/0