Netflow-искания Часть 4 (Netflow v9 collector)

Блог им. dreamhunter

Предисловие:

Опасное это дело, Фродо, — выходить за порог: стоит ступить на дорогу и, если дашь волю ногам, неизвестно куда тебя занесёт.

Задача: Получить и обработать данные с сенсора netflow v9.

Подготовка
Запускаем уже полюбившийся скрипт и захватываем первый пакет, переданный от маршрутизатора:
#!/usr/bin/perl
use IO::Socket::INET;
$| = 1;
my ($socket,$received_data);
my ($peeraddress,$peerport);
$socket = new IO::Socket::INET (
LocalPort => '9999',
Proto => 'udp',
) or die "ERROR in Socket Creation : $!\n";
while(!$recieved_data)
{
$socket->recv($recieved_data,4096);
}

open (MYFILE, '>data.txt');
print MYFILE $recieved_data;
close (MYFILE);

$socket->close();

Анализ

Видим, что нам передалось 1412 байт информации. Вооружаемся спецификацией протокола v9 и смотрим:
00 09 00 1C │ 93 70 9C B4 │ 51 6B 86 18 │ 00 00 43 6B │ 00 00 00 00 │ 00 00 00 5C │ 01 00 00 15 │ 00 15 00 04 │ 00 16 00 04 │ 00 01 00 04 │ 00 02 00 04 │ 00 0A 00 02
00 0E 00 02 │ 00 08 00 04 │ 00 0C 00 04 │ 00 04 00 01 │ 00 05 00 01 │ 00 07 00 02 │ 00 0B 00 02 │ 00 30 00 01 │ 00 33 00 01 │ 00 0F 00 04 │ 00 0D 00 01 │ 00 09 00 01
00 06 00 01 │ 00 3D 00 01 │ 00 11 00 02 │ 00 10 00 02 │ 01 00 05 14 │ 93 70 5B 78 │ 93 70 5B 78 │ 00 00 00 3B │ 00 00 00 01 │ 00 0B 00 06 │ 0A 4F 24 DD │ 0A 4F E2 33
11 00 E1 B9 │ 00 35 00 00 │ C0 A8 22 1D │ 14 18 10 01 │ 00 00 00 00 │ 93 70 5B 80 │ 93 70 5B 80 │ 00 00 00 96 │ 00 00 00 01 │ 00 06 00 0B │ 0A 4F E2 33 │ 0A 4F 24 DD
11 00 00 35 │ E1 B9 00 00 │ 0A 4F 24 DD │ 18 14 10 00 │ 00 00 00 00 │ 93 70 5B 80 │ 93 70 5B 80 │ 00 00 00 3B │ 00 00 00 01 │ 00 0B 00 06 │ 0A 4F 24 DD │ 0A 4F E2 33
11 00 F9 3B │ 00 35 00 00 │ C0 A8 22 1D │ 14 18 10 01 │ 00 00 00 00 │ 93 70 5B 84 │ 93 70 5B 84 │ 00 00 00 96 │ 00 00 00 01 │ 00 06 00 0B │ 0A 4F E2 33 │ 0A 4F 24 DD
11 00 00 35 │ F9 3B 00 00 │ 0A 4F 24 DD │ 18 14 10 00 │ 00 00 00 00 │ 93 70 5F 94 │ 93 70 5B 88 │ 00 00 00 90 │ 00 00 00 03 │ 00 0B 00 06 │ 0A 4F 24 DD │ 83 FD 22 CF
06 00 CE 7E │ 00 50 00 00 │ C0 A8 22 1D │ 00 18 02 01 │ 00 00 00 00 │ 93 70 5C 50 │ 93 70 5B C4 │ 00 00 02 BC │ 00 00 00 02 │ 00 0E 00 06 │ 0A 4F 27 51 │ 0A 4F E3 67
06 00 07 71 │ 1F 90 00 00 │ C0 A8 22 1D │ 14 18 18 01 │ 00 00 00 00 │ 93 70 5F 44 │ 93 70 5B C4 │ 00 00 00 9C │ 00 00 00 03 │ 00 0D 00 06 │ 0A 4F 26 EF │ 5F A7 8B 06
06 00 0F D9 │ 00 50 00 00 │ C0 A8 22 1D │ 00 18 02 01 │ 00 00 00 00 │ 93 70 5B E0 │ 93 70 5B D0 │ 00 00 0C 81 │ 00 00 00 05 │ 00 06 00 0B │ 0A 4F E3 67 │ 0A 4F 24 14
06 00 1F 90 │ 0D 56 00 00 │ 0A 4F 24 14 │ 18 14 1A 00 │ 00 00 00 00 │ 93 70 5C 04 │ 93 70 5B EC │ 00 00 01 80 │ 00 00 00 04 │ 00 0D 00 06 │ 0A 4F 26 5C │ 0A 4F E2 32
11 00 00 89 │ 00 89 00 00 │ C0 A8 22 1D │ 14 18 10 01 │ 00 00 00 00 │ 93 70 5C 0C │ 93 70 5B F4 │ 00 00 01 68 │ 00 00 00 04 │ 00 06 00 0D │ 0A 4F E2 32 │ 0A 4F 26 5C
11 00 00 89 │ 00 89 00 00 │ 0A 4F 26 5C │ 18 14 10 00 │ 00 00 00 00 │ 93 70 5C 4C │ 93 70 5C 4C │ 00 00 03 06 │ 00 00 00 02 │ 00 06 00 0E │ 0A 4F E3 67 │ 0A 4F 27 51
06 00 1F 90 │ 07 71 00 00 │ 0A 4F 27 51 │ 18 14 18 00 │ 00 00 00 00 │ 93 70 5C 54 │ 93 70 5C 54 │ 00 00 00 45 │ 00 00 00 01 │ 00 0E 00 06 │ 0A 4F 27 7D │ 0A 4F E2 32
11 00 F3 0C │ 00 35 00 00 │ C0 A8 22 1D │ 14 18 10 01 │ 00 00 00 00 │ 93 70 60 DC │ 93 70 5C 58 │ 00 00 00 98 │ 00 00 00 03 │ 00 0D 00 06 │ 0A 4F 26 37 │ 3E 80 64 4F
06 00 CC 7E │ 00 50 00 00 │ C0 A8 22 1D │ 00 18 02 01 │ 00 00 00 00 │ 93 70 5C 5C │ 93 70 5C 5C │ 00 00 00 55 │ 00 00 00 01 │ 00 06 00 0E │ 0A 4F E2 32 │ 0A 4F 27 7D
11 00 00 35 │ F3 0C 00 00 │ 0A 4F 27 7D │ 18 14 10 00 │ 00 00 00 00 │ 93 70 5C 64 │ 93 70 5C 60 │ 00 00 01 73 │ 00 00 00 02 │ 00 06 00 0E │ 0A 4F E3 67 │ 0A 4F 27 4E
06 00 1F 90 │ 05 89 00 00 │ 0A 4F 27 4E │ 18 14 18 00 │ 00 00 00 00 │ 93 70 5C 64 │ 93 70 5C 64 │ 00 00 00 28 │ 00 00 00 01 │ 00 0E 00 06 │ 0A 4F 27 4E │ 0A 4F E3 67
06 00 05 89 │ 1F 90 00 00 │ C0 A8 22 1D │ 14 18 10 01 │ 00 00 00 00 │ 93 70 5C 74 │ 93 70 5C 70 │ 00 00 02 B6 │ 00 00 00 02 │ 00 0E 00 06 │ 0A 4F 27 4E │ 0A 4F E3 67
06 00 05 3C │ 1F 90 00 00 │ C0 A8 22 1D │ 14 18 18 01 │ 00 00 00 00 │ 93 70 5C 7C │ 93 70 5C 7C │ 00 00 00 28 │ 00 00 00 01 │ 00 06 00 0E │ 0A 4F E3 67 │ 0A 4F 27 4E
06 00 1F 90 │ 05 3C 00 00 │ 0A 4F 27 4E │ 18 14 10 00 │ 00 00 00 00 │ 93 70 60 54 │ 93 70 5C 88 │ 00 00 00 9C │ 00 00 00 03 │ 00 0D 00 06 │ 0A 4F 26 24 │ 53 E5 AD 03
06 00 0D BE │ 00 50 00 00 │ C0 A8 22 1D │ 00 18 02 01 │ 00 00 00 00 │ 93 70 5C 94 │ 93 70 5C 94 │ 00 00 00 1E │ 00 00 00 01 │ 00 0E 00 06 │ 0A 4F 27 68 │ CA B1 D8 EC
11 00 DB A4 │ 07 D1 00 00 │ C0 A8 22 1D │ 00 18 10 01 │ 00 00 00 00 │ 93 70 5C C4 │ 93 70 5C B0 │ 00 00 0C 9D │ 00 00 00 06 │ 00 06 00 0B │ 0A 4F E3 67 │ 0A 4F 24 14
06 00 1F 90 │ 0D 5A 00 00 │ 0A 4F 24 14 │ 18 14 1A 00 │ 00 00 00 00 │ 93 70 5C B8 │ 93 70 5C B8 │ 00 00 00 3B │ 00 00 00 01 │ 00 0B 00 06 │ 0A 4F 24 D1 │ 0A 4F E2 0A
11 00 FA 7D │ 00 35 00 00 │ C0 A8 22 1D │ 14 18 10 01 │ 00 00 00 00 │ 93 70 60 C8 │ 93 70 5C C0 │ 00 00 00 98 │ 00 00 00 03 │ 00 0E 00 06 │ 0A 4F 27 70 │ 83 FD 22 CF
06 00 C6 F1 │ 00 50 00 00 │ C0 A8 22 1D │ 00 18 02 01 │ 00 00 00 00 │ 93 70 5E 80 │ 93 70 5C D4 │ 00 00 01 65 │ 00 00 00 02 │ 00 0D 00 06 │ 0A 4F 26 D3 │ 0A 4F E3 67
06 00 0B B0 │ 1F 90 00 00 │ C0 A8 22 1D │ 14 18 18 01 │ 00 00 00 00 │ 93 70 60 FC │ 93 70 5C E0 │ 00 00 00 98 │ 00 00 00 03 │ 00 0B 00 06 │ 0A 4F 24 E2 │ 83 FD 22 CF
06 00 DA 21 │ 00 50 00 00 │ C0 A8 22 1D │ 00 18 02 01 │ 00 00 00 00 │ 93 70 60 F4 │ 93 70 5C EC │ 00 00 00 98 │ 00 00 00 03 │ 00 0E 00 06 │ 0A 4F 27 6C │ 83 FD 22 CF
06 00 F2 E0 │ 00 50 00 00 │ C0 A8 22 1D │ 00 18 02 01 │ 00 00 00 00 │ 93 70 60 F8 │ 93 70 5C F0 │ 00 00 00 98 │ 00 00 00 03 │ 00 0B 00 06 │ 0A 4F 24 D8 │ 83 FD 22 CF
06 00 C6 CC │ 00 50 00 00 │ C0 A8 22 1D │ 00 18 02 01 │ 00 00 00 00

Первые 5х32 бит это заголовок, в котором указаны следующие данные:
00 09 — версия протокола
00 1D — количество записей, включая шаблон и данные.
93 70 9C B4 — Время в миллисекундах с начала запуска устройства.
51 6B 86 18 — Время в секундах с начала эпохи.
00 00 43 6B — Номер последовательности. Счетчик, который позволяет вычислить потери.
00 00 00 00 — идентификатор устройства.

Заметьте! Отказались от наносекунд!

Далее идет передача шаблонов и данных. Разберем по порядку.
00 00 — FlowSet ID. Идентификатор записи. Используется для того что бы различать заголовки от записей. 0 — означает, что это заголовок. Остальное — данные. В данном случае у нас заголовок;
00 5C — Размер заголовка. Точнее его описательной части. То есть 92 байта будут посвящены описанию данных;
01 00 — Идентификатор шаблона;
00 15 — Количество полей в таблице; Следующие 84 байта (0x15 * 4 -> 21 * 4 = 84)
  1. 00 15 00 04 — поле типа LAST_SWITCHED, 4 байта
  2. 00 16 00 04 — поле типа FIRST_SWITCHED, 4 байта
  3. 00 01 00 04 — IN_BYTES, 4 байт
  4. 00 02 00 04 — IN_PKTS, 4 байт
  5. 00 0A 00 02 — INPUT_SNMP, 2 байта
  6. 00 0E 00 02 — OUTPUT_SNMP, 2 байта
  7. 00 08 00 04 — IPV4_SRC_ADDR, 4 байт
  8. 00 0C 00 04 — IPV4_DST_ADDR, 4 байт
  9. 00 04 00 01 — PROTOCOL, 1 байт
  10. 00 05 00 01 — SRC_TOS, 1 байт
  11. 00 07 00 02 — L4_SRC_PORT, 2 байта
  12. 00 0B 00 02 — L4_DST_PORT, 2 байта
  13. 00 30 00 01 — FLOW_SAMPLER_ID, 1 байт
  14. 00 33 00 01 — *Vendor Proprietary*, 1 байт
  15. 00 0F 00 04 — IPV4_NEXT_HOP, 4 байт
  16. 00 0D 00 01 — DST_MASK, 1 байт
  17. 00 09 00 01 — SRC_MASK, 1 байт
  18. 00 06 00 01 — TCP_FLAGS, 1 байт
  19. 00 3D 00 01 — DIRECTION, 1 байт
  20. 00 11 00 02 — DST_AS, 2 байт
  21. 00 10 00 02 — SRC_AS, 2 байт
Данные взяты из таблицы соответствий протокола netflow v9 (см табл №6)
Итак фактически мы выхватили описательную часть шаблона для протокола IPv4. По структуре очень похоже на структуру таблицы netflow v5 с некоторыми дополнениями.

Далее очевидно идут данные. Так как в заголовке 01 00 — номер шаблона, который был только что описан (вполне себе ожидаемо). Итак разберем структуру данных.
01 00 — Номер шаблона;
05 14 — Размер данных (1300 байт);
  1. 93 70 5B 78 — время окончания передачи данных;
  2. 93 70 5B 78 — время начала передачи данных;
  3. 00 00 00 3B — байты;
  4. 00 00 00 01 — пакеты;
  5. 00 0B — входящий интерфейс;
  6. 00 06 — исходящий интерфейс;
  7. 0A 4F 24 DD — IP источника (отправитель);
  8. 0A 4F E2 33 — IP назначения (получатель);
  9. 11 — Протокол (в данном случае 17 — UDP);
  10. 00 — TOS (тип сервиса);
  11. E1 B9 — порт источника (отправитель);
  12. 00 35 — порт назначения (получатель);
  13. 00 — идентификатор сэмплера;
  14. 00 — типа байт, добавленный производителем, но мы то знаем что добавили чисто для эстетики и удобочитаемости В данном случае будет 0 всегда.
  15. C0 A8 22 1D — IP адрес маршрутизатора;
  16. 14 — маска назначения (получатель);
  17. 18 — маска источника (отправитель);
  18. 10 — TCP флаг;
  19. 01 — направление пакета. 0 — входяший, 1 — исходящий;
  20. 00 00 — номер автономной системы назначения
  21. 00 00 — номер автономной системы источника

Дополнительные данные

У меня под рукой был ASA5520, раздающий интернет. И для закрепления я решил снять данные и с него. Сначала полученные данные меня очень смутили. Но затем они мне дали более полное понимание сути netflow v9.

00 09 00 0D │ ED 3D DD 50 │ 51 72 08 7D │ 00 00 00 0D │ 00 00 00 00 │ 00 00 03 E0 │ 01 00 00 15 │ 00 94 00 04 │ 00 08 00 04 │ 00 07 00 02 │ 00 0A 00 02
00 0C 00 04 │ 00 0B 00 02 │ 00 0E 00 02 │ 00 04 00 01 │ 00 B0 00 01 │ 00 B1 00 01 │ 9C 41 00 04 │ 9C 42 00 04 │ 9C 43 00 02 │ 9C 44 00 02 │ 9C 45 00 01
80 EA 00 02 │ 01 43 00 08 │ 00 55 00 04 │ 80 E8 00 0C │ 80 E9 00 0C │ 9C 40 00 14 │ 01 01 00 15 │ 00 94 00 04 │ 00 08 00 04 │ 00 07 00 02 │ 00 0A 00 02
00 0C 00 04 │ 00 0B 00 02 │ 00 0E 00 02 │ 00 04 00 01 │ 00 B0 00 01 │ 00 B1 00 01 │ 9C 41 00 04 │ 9C 42 00 04 │ 9C 43 00 02 │ 9C 44 00 02 │ 9C 45 00 01
80 EA 00 02 │ 01 43 00 08 │ 00 55 00 04 │ 80 E8 00 0C │ 80 E9 00 0C │ 9C 40 00 41 │ 01 02 00 11 │ 00 94 00 04 │ 00 1B 00 10 │ 00 07 00 02 │ 00 0A 00 02
00 1C 00 10 │ 00 0B 00 02 │ 00 0E 00 02 │ 00 04 00 01 │ 00 B2 00 01 │ 00 B3 00 01 │ 9C 45 00 01 │ 80 EA 00 02 │ 01 43 00 08 │ 00 55 00 04 │ 80 E8 00 0C
80 E9 00 0C │ 9C 40 00 14 │ 01 03 00 11 │ 00 94 00 04 │ 00 1B 00 10 │ 00 07 00 02 │ 00 0A 00 02 │ 00 1C 00 10 │ 00 0B 00 02 │ 00 0E 00 02 │ 00 04 00 01
00 B2 00 01 │ 00 B3 00 01 │ 9C 45 00 01 │ 80 EA 00 02 │ 01 43 00 08 │ 00 55 00 04 │ 80 E8 00 0C │ 80 E9 00 0C │ 9C 40 00 41 │ 01 04 00 12 │ 00 08 00 04
00 07 00 02 │ 00 0A 00 02 │ 00 0C 00 04 │ 00 0B 00 02 │ 00 0E 00 02 │ 00 04 00 01 │ 00 B0 00 01 │ 00 B1 00 01 │ 9C 41 00 04 │ 9C 42 00 04 │ 9C 43 00 02
9C 44 00 02 │ 9C 45 00 01 │ 80 EA 00 02 │ 01 43 00 08 │ 80 E8 00 0C │ 80 E9 00 0C │ 01 05 00 0E │ 00 08 00 04 │ 00 07 00 02 │ 00 0A 00 02 │ 00 0C 00 04
00 0B 00 02 │ 00 0E 00 02 │ 00 04 00 01 │ 00 B0 00 01 │ 00 B1 00 01 │ 9C 45 00 01 │ 80 EA 00 02 │ 01 43 00 08 │ 80 E8 00 0C │ 80 E9 00 0C │ 01 06 00 0E
00 1B 00 10 │ 00 07 00 02 │ 00 0A 00 02 │ 00 1C 00 10 │ 00 0B 00 02 │ 00 0E 00 02 │ 00 04 00 01 │ 00 B2 00 01 │ 00 B3 00 01 │ 9C 45 00 01 │ 80 EA 00 02
01 43 00 08 │ 80 E8 00 0C │ 80 E9 00 0C │ 01 07 00 12 │ 00 94 00 04 │ 00 08 00 04 │ 00 07 00 02 │ 00 0A 00 02 │ 00 0C 00 04 │ 00 0B 00 02 │ 00 0E 00 02
00 04 00 01 │ 00 B0 00 01 │ 00 B1 00 01 │ 9C 41 00 04 │ 9C 42 00 04 │ 9C 43 00 02 │ 9C 44 00 02 │ 9C 45 00 01 │ 80 EA 00 02 │ 01 43 00 08 │ 00 55 00 04
01 08 00 0E │ 00 94 00 04 │ 00 1B 00 10 │ 00 07 00 02 │ 00 0A 00 02 │ 00 1C 00 10 │ 00 0B 00 02 │ 00 0E 00 02 │ 00 04 00 01 │ 00 B2 00 01 │ 00 B3 00 01
9C 45 00 01 │ 80 EA 00 02 │ 01 43 00 08 │ 00 55 00 04 │ 01 09 00 16 │ 00 94 00 04 │ 00 08 00 04 │ 00 07 00 02 │ 00 0A 00 02 │ 00 0C 00 04 │ 00 0B 00 02
00 0E 00 02 │ 00 04 00 01 │ 00 B0 00 01 │ 00 B1 00 01 │ 9C 41 00 04 │ 9C 42 00 04 │ 9C 43 00 02 │ 9C 44 00 02 │ 9C 45 00 01 │ 80 EA 00 02 │ 01 43 00 08
00 55 00 04 │ 00 98 00 08 │ 80 E8 00 0C │ 80 E9 00 0C │ 9C 40 00 14 │ 01 0A 00 16 │ 00 94 00 04 │ 00 08 00 04 │ 00 07 00 02 │ 00 0A 00 02 │ 00 0C 00 04
00 0B 00 02 │ 00 0E 00 02 │ 00 04 00 01 │ 00 B0 00 01 │ 00 B1 00 01 │ 9C 41 00 04 │ 9C 42 00 04 │ 9C 43 00 02 │ 9C 44 00 02 │ 9C 45 00 01 │ 80 EA 00 02
01 43 00 08 │ 00 55 00 04 │ 00 98 00 08 │ 80 E8 00 0C │ 80 E9 00 0C │ 9C 40 00 41 │ 01 0B 00 12 │ 00 94 00 04 │ 00 1B 00 10 │ 00 07 00 02 │ 00 0A 00 02
00 1C 00 10 │ 00 0B 00 02 │ 00 0E 00 02 │ 00 04 00 01 │ 00 B2 00 01 │ 00 B3 00 01 │ 9C 45 00 01 │ 80 EA 00 02 │ 01 43 00 08 │ 00 55 00 04 │ 00 98 00 08
80 E8 00 0C │ 80 E9 00 0C │ 9C 40 00 14 │ 01 0C 00 12 │ 00 94 00 04 │ 00 1B 00 10 │ 00 07 00 02 │ 00 0A 00 02 │ 00 1C 00 10 │ 00 0B 00 02 │ 00 0E 00 02
00 04 00 01 │ 00 B2 00 01 │ 00 B3 00 01 │ 9C 45 00 01 │ 80 EA 00 02 │ 01 43 00 08 │ 00 55 00 04 │ 00 98 00 08 │ 80 E8 00 0C │ 80 E9 00 0C │ 9C 40 00 41

Первые 5х32 бит заголовка сами по себе ничего нового мне не сказали.
00 09 — версия протокола
00 0D — количество записей, включая шаблон и данные.
ED 3D DD 50 — Время в миллисекундах с начала запуска устройства.
51 72 08 7D — Время в секундах с начала эпохи.
00 00 00 0D — Номер последовательности. Счетчик, который позволяет вычислить потери.
00 00 00 00 — идентификатор устройства.
Тут все понятно. Но дальнейшее ввело меня в ступор:
00 00 — это передача шаблона
03 E0 — размер шаблона 992 байта. То есть весь пакет это шаблон!
01 00 — описание шаблона 0x100
00 15 — кол-во записей 21
… после описания полей шаблона, началось описание другого шаблона. 01 01 00 15 — шаблон 0х101, в котором тоже 21 поле.
В общем этот пакет содержал в себе только заголовки шаблонов. Учтем это на будущее. Так же стоит учесть, что одинаковые номера шаблонов даже в Cisco совсем не означают какую то схожесть. Фактически нам Router в 9й версии протокола передал данные для IPv4 почти в том же виде, что в версии 5. — Разработчики особо морочиться не стали. Зато шаблон ASA отличается весьма ощутимо.
Кстати кое что общее между шаблонами 0х100 есть. Они оба описывают IPv4.
И да: Похоже к разработке netflow v9 подпустили SQL разработчиков. т.к. чувствуется рука мастера.

Кодирование

С чего начать?.. Проще и правильней разбить программу на составляющие. Для начала считаем данные.
#!/usr/bin/perl
use IO::Socket::INET;
use POSIX;
use feature qw { switch };
# Initialisation
$| = 1;
%templates_fmt = ();
%templates = ();
my ($socket,$received_data);
my ($peeraddress,$peerport);
#Opening Socket
$socket = new IO::Socket::INET (
	LocalPort => '9999',
	Proto => 'udp',
) or die "ERROR in Socket Creation : $!\n";
#Reading socket
while(1) {
my ($recieved_data);
#while((!$recieved_data) || (length($recieved_data) == 1416)) {
while(!$recieved_data) {
	$socket->recv($recieved_data,4096);
	$peer_address = unpack("N", $socket->peeraddr());
}
	$version = unpack("n", substr($recieved_data,0,2));
	given ( $version ) {
		when ( 5 ) { fv5($recieved_data,$peer_address); }
		when ( 9 ) { fv9($recieved_data,$peer_address); }
	}
}
#Closing Socket
$socket->close();

Тут я думаю все просто и расшифровывать не надо. Порт читается, данные направляются в зависимости от версии протокола. При такой конструкции мы можем принимать на порт все что ни попадя.

Далее обработаем пятую версию. По ней я особо комментировать ничего не буду. Тема достаточно раскрыта в предыдущей статье.
sub fv5 {
	my (@header) = unpack("nnN4NNNHHH2", substr($_[0],0,24));
	for ($i=0; substr($_[0], 24+48*$i, 48); $i++) {
		@substr = unpack("N3n2N4n2C4n2C2n", substr($_[0], 24+48*$i, 48));
		$fmt = "%08x0100 %08x%08x %08x %08x %08x %02x %02x %08x %08x %s %s %04x %04x %02x %02x %02x %02x %04x %04x %02x %02x %02x\n";
		printf ("$fmt", $_[1], $header[3], $header[2], @substr);
	}
}

Теперь начинается основная часть. Первым делом разберем заголовок пакета, в которой содержится необходимая нам инфомация о пакете. Потом начнем считывать данные, передавая данные другим процедурам, которые отвечают за обработку шаблонов или данных.
sub fv9 {
	@header = unpack("n2N4", substr($_[0],0,20));
	my ($marker) = 20;
	for ($i=0; $i < $header[1]; $i++) {
		@template_id = unpack("n2", substr($_[0],$marker,4));
		if ($template_id[0] == 0) {
			$i += &fv9_templates(substr($_[0], $marker+4, $template_id[1]-4),$_[1], $template_id[1]);
		} else {
			$i += &fv9_records(substr($_[0], $marker+4, $template_id[1]-4), $_[1], $template_id[0], $template_id[1], $header[3], $header[2]);
		}
		$marker += $template_id[1];
	}
#	print $marker . " bytes in ", $i+1, " records processed\n";
}

Если у нас пришли данные по шаблонам, обработаем их в следующей процедуре.
sub fv9_templates {
	my (@template_id, @template_val, @templates_id, @templates_val);
	my ($marker, $field_count) = 0;
	my ($i,$j) = -1;
	if (exists($templates{"$_[1]"})) {
		@templates_val = split(",", $templates{"$_[1]"});
		@template_val = split(",", $templates_fmt{"$_[1]"});
	} else {
		@templates_val = ();
		@template_val = ();
	}
	my ($count) = $_[2];
	while ($count>4) {
		@templates_id = unpack("n2", substr($_[0],$marker,4));
		$templates_val[$templates_id[0]] = unpack("H".($templates_id[1]*8+8), substr($_[0],$marker,($templates_id[1]*4+4)));
		$templates {"$_[1]"} = join (",", @templates_val);
		my ($template, $template_len) = "";
		$field_count = hex(substr($templates_val[$templates_id[0]],4,4));
		for ($j=1; $j<=$field_count; $j++) {
			$template .= "H".(hex(substr($templates_val[$templates_id[0]],$j*8+4,4))*2);
			$template_len += hex(substr($templates_val[$templates_id[0]],$j*8+4,4));
		}
		$template_val[$templates_id[0]] = sprintf "%04x$template", $template_len;
		$templates_fmt {"$_[1]"} = join(",", @template_val);
		$count -= $templates_id[1]*4+4;
		$marker += $templates_id[1]*4+4;
		$i++;
	}
return $i;
};

Если пришли данные, то обработаем их в следующей подпрограмме:
sub fv9_records {
	my (@template_id);
	my ($template, $template_len, $j);
	if (exists($templates{"$_[1]"})) {
		my (@template_val) = split(",", $templates_fmt{"$_[1]"});
		if (exists($template_val[$_[2]])) {
			$template_len = hex(substr($template_val[$_[2]],0,4));
			$template = substr($template_val[$_[2]],4,length($template_val[$_[2]]));
			$record_count = (length($_[0])-4)/$template_len;
			for ($j=0; $j<$record_count; $j++) {
				my (@mass) = unpack("$template", substr($_[0],$j*$template_len,$template_len)), "\n";
				printf "%08x%04x %04x%04x @mass\n",$_[1],$_[2],$_[4],$_[5];
			}
		};
	} else {
		last;
	}
return $j-1;
};

Ну вот собственно и весь коллектор. Конечно это не совсем коллектор. Эта программа будет выводить все на экран в шеснадцатеричном виде. Но в функционале заложен задел под SQL базу.
На мой взгляд крайне желательно хранить данные упакованными, так они будут занимать меньше дискового пространства.
Еще пришлось добавить дополнительное поле, в котором содержится время получения данных. Это связанно с тем, что далеко не всегда в полученных данных может иметься информация о времени и вычисление таких полей даст дополнительную нагрузку на процессор.
Скрипт работал всю ночь, на него спаммили устройства с разными версиями Netflow и вроде он отработал нормально. (память не съел). Признаюсь честно: код весьма далек от совершенства. И оптимизировать его очень желательно. т.к. когда я посмотрел чем сыпет Asa5520 плюс пара роутеров, я серьезно задумался об этом — поток был нешуточный, а средняя нагрузка на процессор — 10-15%.
Еще я думал о утилизации оперативной памяти. Думаю, что неплохо будет сперва загонять данные в память, а при наборе нужного объема — выгружать их. В общем будем думать…

Вывод:
Netflow v9 обладает огромным потенциалом и гибкостью, но он очень тяжел для сети и процессора. Лично я бы рекоммендовал использовать пятую версию там/пока это возможно. Конечно, если у вас ASA (в функционале которой только 9 версия) или вы уже работаете с протоколом IPv6, ну или вам необходима статистика не только по переданным данным, то конечно без Netflow v9 вам не обойтись.
Пятой версии протокола я пожалуй дам сроку 3-4 года. Потом он потеряет свою востребованность.

Список литературы:
1. NetFlow Version 9 Flow-Record Format
2. IP Flow Information Export (IPFIX) Entities
3. http://perldoc.perl.org/

З.Ы. Если вы будете пользоваться скриптом в коммерческих целях — не забудте выслать мне пива.

З.Ы.З.Ы. Админ… Не могу прикрепить весь скрипт как файл. Говорит .pl формат — не есть истинная вера (tar.gz тоже не ест). Я понимаю конечно проблемы с безопасностью, но может что-то как то можно сделать?..

2 комментария

avatar
Очень хорошо все описал!
Но не мог не написать, что есть пакет Net::Flow, который справляется с Netflow v5, v9, IPFIX.
А вообще в статье очень хорошие примеры, как работать с сетью и бинарными файлами! В избранное!
avatar
Если честно я и не подозревал о этом пакете. С другой стороны моей целью и было полностью разобраться с протоколом. А вообще спасибо за ссылку.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.