- Общие замечания
- Рабочие и вспомогательные потоки
- Объекты мониторинга и фильтры
- Как добавить в коллектор новое Netflow-поле
- Источник времени
- Фиксированные временные окна
- Скользящие средние
- IP-списки
- Базы GeoIP и AS
Netflow-данные можно рассматривать как многомерные временные ряды.
Существует достаточно много подходов и инструментов для анализа и обработки временных рядов, но главная сложность применительно к Netflow — это его количество. Даже в небольших сетях может происходить очень много сетевых событий, которые экспортируются роутерами.
Обратите внимание — многие коллекторы в названии или описании используют слова "быстрый", "высокопроизводительный" и т.п.
Для снижения нагрузки как на роутеры, так и на коллекторы используют семплирование трафика. Роутер учитывает только каждый N-й пакет (точнее, пакеты выбираются из потока случайно, но в среднем выборка дает 1:N). Частоту семплирования можно увеличить, тем самым уменьшится количество экспортируемых фловов в секунду и нагрузка. Нужно принимать во внимание, что например при частоте семплирования 1:1000 вы не увидите буквально 99.9% трафика. Чем выше частота, тем меньше точность отчетов и тем хуже видны аномалии в трафике.
Обработка netflow это обычно агрегация данных за крупные периоды времени, снижение размерности данных (выбор только нужных полей), визуализация, определение нежелательного трафика, запуск оповещений и других действий при обнаружении аномалий. Иногда данные netflow используются для прогнозирования.
Есть интересный источник практических подходов для анализа сетевого трафика — старые биржевые торговые стратегии. В трафике можно быстро детектировать превышения порогов с помощью скользящих средних, видеть циклы (суточные, недельные), паттерны, отклонения от них и т.д.
Современные netflow-коллекторы с открытым кодом как правило получают фловы, декодируют их, экспортируют в Kafka, ClickHouse, Elastic-стек и дальше обрабатывают и визуализируют другими утилитами. Например:
https://github.com/robcowart/elastiflow
https://github.com/akvorado/akvorado
https://github.com/netsampler/goflow2
Из коллекторов старой школы выжило не так много. Одни из самых известных:
https://github.com/phaag/nfdump
https://github.com/pavel-odintsov/fastnetmon - детектирует превышения порогов с помощью экспоненциальных скользящих средних
xenoeye
и по внутреннему устройству, и по способу настройки ближе к коллекторам старой школы.
Коллектор хранит всю конфигурацию в текстовых файлах. Для экспорта в базу данных генерируются тоже текстовые файлы. Идея была в том, чтобы не привязываться к какой-то определенной СУБД, а генерировать файлы экспорта для разных баз данных, не обязательно реляционных. Во время остановки СУБД (в случае ошибки или когда база данных нуждается в обслуживании), коллектор не удаляет данные. После восстановления работоспособности СУБД, можно экспортировать накопленные данные, они не потеряются.
Сначала мы хотели хранить весь сырой netflow, который получает коллектор. Но после общения с людьми из крупных телекомов поняли, что это бессмысленно. Предполагается, что в постоянном хранилище должны экспортироваться только те данные, которыми действительно будут пользоваться. В момент возникновения аномалии (пробития порогов) можно снять более расширенную статистику.
Кроме этого, стало понятно, что сетевые инженеры очень занятые люди, часто проще настроить частоту семплирования на стороне коллектора, чем просить их настроить options template.
Мы смотрели на несколько современных time-series баз данных, но по разным причинам пока остановились на обычном PostgreSQL. Он вполне приемлемо работает даже не механических жестких дисках, не требователен к памяти, широко распространен и поддерживается.
При превышении порогов для скользящих средних запускаются пользовательские скрипты. Предполагается, что основная логика обработки аномалий реализуется в этих скриптах. Почти все сети имеют свои особенности. Зашивать эти особенности в коллектор нет никакого смысла.
При старте коллектор читает общие конфиги и файлы mo.conf
. Запускаются рабочие потоки и несколько вспомогательных:
- Поток, который экспортирует агрегированные данные в файлы для СУБД
- Поток, запускающий скрипты при превышении лимитов скользящих средних и при возврате трафика в норму
- Поток, который делает дампы скользящих средних
Рабочие потоки принимают netflow и обрабатывают его.
После того, как коллектор прочитал конфигурационный файл mo.conf
, он пытается скомпилировать фильтр объекта мониторинга в байткод. Если получается, байткод сохраняется в памяти.
Каждый приходящий флов декодируется, используя шаблоны. Для флова исполняется байткод фильтров, проверяется относится ли этот флов к объекту мониторинга.
Если относится, то из флова выделяются нужные поля и обрабатываются дальше — в окнах фикисрованного размера и скользящих средних.
В Netflow v9 и IPFIX существуют и описаны несколько сотен полей. Коллектор из коробки поддерживает только самые распространенные. Поля некоторых типов можно достаточно просто добавить.
Коллектор оперирует netflow-полями двух видов. Первый вид — поля IN_BYTES
, IN_PKTS
и подобные. Эти поля считаются агрегируемыми. Если они встречаются в конфиге в массиве полей для экспорта, то данные из этих полей будут суммироваться.
Остальные поля считаются не-агрегируемыми.
Сейчас используется такой подход к описанию netflow-полей: они задаются в коде, в файлах netflow.def, filter.def и filter-ag.def.
Формат файлов такой:
netflow.def
FIELD(internal_id, "Description", FIELD_TYPE, netflow_id, min_size, max_size)
internal_id
— внутренний идентификатор, ожидается валидное имя для поля структуры CDescription
— текстовое описание поляFIELD_TYPE
— тип поля (сейчас поддерживаютсяNF_FIELD_INT
(целое) иNF_FIELD_IP_ADDR
(IP адрес))netflow_record_id
— идентификатор поляmin_size
,max_size
— минимальный и максимальный размер поля в байтах
Данные о полях (идентификатор, размер и т.д.) можно взять из NetFlow Version 9 Flow-Record Format - Cisco Systems или из IP Flow Information Export (IPFIX) Entities
Добавление поля в netflow.def
изменит только парсер netflow. Чтобы использовать это поле в фильтрах и для экспорта в СУБД, нужно добавить его еще в filter.def
FIELD(ID, "name", TYPE, src_netflow_field, dst_netflow_field)
ID
— внутренний идентификаторname
— строка, которая будет использоваться в фильтрахTYPE
— тип (сейчас поддерживаетсяRANGE
(диапазон целых),ADDR4
иADDR6
- IP адреса)src_netflow_field
,dst_netflow_field
— поля netflow с которыми нужно работать. То, что написано вinternal_id
изnetflow.def
. Если могут быть префиксы "источник" (src
) и "назначение"dst
, нужно указать соответствующие поля.
Для агрегируемых полей используется файл filter-ag.def
:
FIELD(ID, "name", netflow_field, SCALE)
SCALE
это коэффициент, на который умножается значение поля. Используется только для одного случая — можно указать в списке полей bits
и коллектор будет экспортировать значение in_bytes
* 8.
После изменения файлов программу нужно перекомпилировать.
В каждом флове может быть время, когда роутер начал и когда закончил наблюдение за ним. Коллектор не смотрит на это время. Временем флова считается текущее время на сервере. Это немного уменьшает точность, но сильно упрощает обработку, особенно обработку скользящих средних.
Для экспорта в СУБД применяется классичская схема с двумя банками данных.
В начале активен первый банк, рабочий поток записывает в него данные.
После того как подошло время экспорта, вспомогательный поток атомарно переключает банки. Активным становится тот, который был неактивным и в него начинают писаться новые данные.
После переключения неактивный банк пересортировывается. Если в параметрах указано, что для экспорта нужно только N первых записей, то отбираются первые записи, остальные суммируются. Из результата формируется текстовый файл, который записывается на диск.
Вспомогательный поток ждет нужное время и процесс повторяется.
В коллекторе используются куммулятивные скользящие средние
Значение байт или пакетов в скользящем окне пересчитывается при получении каждого нового флова по формуле:
где N — текущее количество байт/пакетов в скользящем окне
T — размер скользящего окна
V — значение величины (количество байт/пакетов) в новом пришедшем флове
Для таких скользящих средних можно использовать временные окна почти произвольной длины. Не нужно хранить все фловы за промежуток времени. Нужно хранить только две величины — количество байт или пакетов в окне и время последнего обновления
Но если использовать очень большие окна и большой трафик, то можно потерять точность. В коллекторе есть возможность повысить точность "в лоб" - можно вместо машинного double
использовать __float128
.
Для этого нужно изменить значение MAVG_TYPE
в файле monit-objects.h
Списки сетей хранятся в виде bitwise trie. Возможно, это не самый быстрый способ хранения, но достаточно простой и эффективный. В списках может храниться довольно много сетей, как IPv4, так и IPv6.
Соответствие IP-сетей и данных GeoIP/AS тоже хранится в виде bitwise trie. Для IPv4 и IPv6 создаются разные деревья.
Для парсинга CSV-файлов и генерации этих деревьев (в виде файлов) есть отдельная утилита xemkgeodb
. Такую двухэтапную загрузку сделали в основном по двум причинам:
- Владельцы GeoIP-баз могут изменить формат своих CSV-файлов (и они уже это делали). Об этом лучше узнать до загрузки данных в коллектор
- GeoIP-базы могут быть довольно большими. На системах (или виртуалках) с небольшим количеством памяти их сложно обрабатывать. Можно сгенерировать файлы на компьютере с достаточным количеством памяти и поместить их на сервер с коллектором
Коллектор с помощью mmap() отображает в память файлы с данными, и после этого обрабатывает запросы. При этом физическая память используется достаточно эффективно, с диска поднимаются только нужные страницы с данными.
Утилита xemkgeodb
строит дерево тоже с помощью отображения файла в память. Алгоритм такой: создается файл большого размера (по умолчанию 4G), отображается в память. В этом участке памяти строится дерево, после окончания процесса пустой хвост файла обрезается.