Сбор данных через NETGRAPH
Начиная с версии NETAMS-CURRENT build 2340 (03 марта 2005 г.) работает метод сбора статистики и фильтрация трафика через модуль NETGRAPH.
Технология NETGRAPH доступна для операционной системы
FreeBSD версий 4.хх и 5.хх. Входящий в поставку модуль совместим с веткой 5.хх. NETGRAPH представляет собой механизм объединения различных сетевых модулей ядра FreeBSD в произвольные структуры (образующие граф), для последовательной обработки пакетов данных. Таким образом, представляется возможным написать и использовать достаточно произвольную схему обработки данных в ядре ОС, пользуясь стандартным интерфейсом программирования. Более того, легко осуществить связь модуля ядра с user-level программой. Через NETGRAPH работают, например, ng_netflow, user-level ppp, различные механизмы инкапсуляции пакетов, и многое другое. Для более глубокого ознакомления можно порекомендовать следующие источники:
http://www.daemonnews.org/200003/netgraph.html и
man 4 netgraph
Пользователей других операционных систем вынуждены огорчить: ничего подобного у вас нет. Тем же, кому повезло, могут читать дальше:
- Принципы работы
- Как настроить
- Как проверить
- Результаты испытаний
- Заключение
Работа netams в случае использования модуля NETGRAPH (далее-модуль) заключается в установке модуля в ядро (и подключения его к интерфейсу, через который идет трафик), и настройке программы netams (далее-демона) для корректного соединения с модулем.
Модуль и демон могут работать в двух режимах (они должны быть одинаковы в настройках!):
tee и
divert.

В режиме
tee модуль ядра получает пакеты с использованием "дубликатора" ng_tee, который отсылает на обработку "копию" проходящего через интерфейс пакета. Понятное дело, в таком случае фильтрация трафика невозможна. Проходящие через модуль пакеты подвергаются анализу заголовков, формируются записи в хэш-таблице, которые периодически "устаревают" и отправляются на обработку демону. Он получает пакеты с данными о трафике и обрабатывает их примерно так же, как происходит с потоками netflow (работают учет и мониторинг).
В режиме
divert модуль ядра подключается непосредственно к ethernet-интерфейсу. Весь трафик проходит через обработку, однако НЕ IP трафик пропускается прозрачно без учета. Каждый пакет также проходит проверку на соответствие с уже имеющимся в системе потоком данных, и:
- если соответствующего потока не найдено, т.е. рассматриваемый пакет-первый в потоке данных (начало соединения), то для данного потока создается очередь. пакет помещается в конец очереди. создается запрос вида FWREQUEST, содержащий заголовки пакета, и передается через контрольный сокет демону netams. заметим, что в этот момент оригинальный IP пакет никуда не передается, он "застревает" в модуле. потоку присваивается статус QUEUED.
- если поток найден, то проверяется его статус:
- QUEUED - рассматриваемый пакет добавляется в конец цепочки пакетов данного потока. при этом делаются проверки на ряд ограничений по количеству потоков/пакетов/байт/очередей, для предотвращения атаки DoS
- PASS - пакет передается дальше
- DROP - пакет уничтожается
Возникает вопрос, что же происходит с пакетами в очереди, и откуда берутся статусы
PASS и
DROP?
Статус
DROP является единственно возможным для режима работы
TEE.
Когда демон получает запрос
FWREQUEST из модуля ядра, происходит разбор заголовков и полный анализ возможности блокировки пакета с использованием таблиц юнитов, политик, системных политик, словом всего обычного набора действий. По окончании проверки, формируется решение по данному потоку: PASS или DROP, и оно передается обратно в ядро через сообщение
FWREPLY. За время такой обработки в ядре уже может накопиться несколько пакетов в очереди для данного потока. По получении ответа от демона, модуль ядра во-первых ставит соответствующий флаг для данного потока, а затем пытается или отправить все пакеты из очереди, или очистить очередь.
Если по каким-то причинам демон недоступен, то по истечении некоторого таймаута (сейчас это NG_NETAMS_DEFAULT_TIMEOUT равный 2 секундам) производится принудительная очистка очереди для потока и принятие "решения по умолчанию" (сейчас: пропускать). Таким образом предотвращается залипание потока и выедание памяти у ядра (что может быть очень опасным!)
В режиме divert, как и в tee, проводится периодическое устаревание потоков и отправка их на учет "наверх", демону.
Рассмотренный механизм работает, по сути, аналогично
Multilayer Switching, реализованному в Cisco Catalyst 6000 и подобных ящиках. Там "быстрый" Switch Engine направляет первый пакет потока "медленному" Route Processor, который определяет, куда маршрутизировать пакет, и проводит проверку правил доступа (access lists). Все последующие после ответа пакеты идут через SE напрямую, и только через некоторое время "наверх" передается статистика о прошедшем потоке. В нашем случае решения о маршрутизации принимать не нужно, в роли "быстрого" движка выступает ядро с его механизмом форвардинга пакетов, в роли "медленного" решателя - демон NeTAMS.
Для начала, вам надо скомпилировать netams, как обычно. Получившийся модуль
src/ng_netams.ko необходимо переписать в
/boot/kernel/
В дистрибутиве есть скрипт
addon/netams-netgraph.sh, который устанавливает в ядро сам модуль
ng_netams.ko, устанавливает его режим работы (TEE или DIVERT), вывод отладочной информации, производит подключения к другим нодам NETGRAPH (интерфейсу и ng_tee, если надо)
Запускается этот скрипт через
./netams-netgraph.sh start
останавливается через
./netams-netgraph.sh stop
Для настройки самого
NeTAMS необходимо добавить соответствующий сервис в
/usr/local/etc/netams.cfg:
service data-source 1
type netgraph
source netams: divert
При этом '
netams:' - это имя модуля NETGRAPH, совпадающее с тем, что написано в скрипте
netams-netgraph.sh. Не забываем про двоеточие!
Модуль ядра должен быть запущен ДО демона. В противном случае демон не заработает, как следует. Однако, в процессе работы допускается останавливать и запускать демон NeTAMS, равно как и выгружать и загружать снова модуль ядра (при этом будет 20-секундная задержка в приеме статистики).
Если что-то будет идти совсем не так, упадет ядро :) или блокируется весь трафик!
Работу демона netams можно проверить через просмотр состояния сервиса data-source:
netamsctl show ds
Data-source ID=1 type NETGRAPH source netams::9 loop 0 average 0 mcsec
Perf: average skew delay 0 mcsec, PPS: 0, BPS: 0
IP tree: 7 nodes [12] + 4 dlinks [1024] + 4 unodes [24] = 4276 bytes
Flows: 0/0 act/inact entries (0 bytes), 3 flows sent
HASH: size=65536, 0 flows hashed, 0 nodes used, max chain= 0
FIFO: 0/2 used/ready messages, each 108, total 216 bytes
ds_netgraph data messages: 3
netams: mode=2, pkt_rx=201, pkt_tx=169
flows: active(now)=3, queued(now)=0, blocked(total)=0, total=4
Работа модуля ядра видна через ngctl:
ngctl msg netams: info
Rec'd response "info" (1) from "[3bb]:":
Args: { packets/in=254 packets/out=202 mode=2 debug=1
active_flows=3 total_flows=9 default_policy=2 }
При включенной отладке модуля (через
ngctl msg netams: debug 1) на консоли и в dmesg видно много подобных строк:
info/1109893460: sent to daemon [961] with error=0
callout/1109893461+ active 1, checked 1, queued=0, flushed 0
callout/1109893462+ active 1, checked 1, queued=0, flushed 0
callout/1109893463+ active 1, checked 1, queued=0, flushed 0
callout/1109893464+ active 1, checked 1, queued=0, flushed 0
callout/1109893465+ active 1, checked 1, queued=0, flushed 0
callout/1109893466+ active 1, checked 1, queued=0, flushed 0
callout/1109893467+ active 1, checked 1, queued=0, flushed 0
callout/1109893468+ active 1, checked 1, queued=0, flushed 0
callout/1109893469+ active 1, checked 1, queued=0, flushed 0
netams: created flow record id=14, hash=00766, time=1109893469, proto=6
netams: created queue 0xc1a15250 for id=14, hash=00766
netams fwreply for entry id=14, flags=0, queue 1/102
netams: flush queue for entry id=14, hash=766, size=1, action=1
netams: created flow record id=15, hash=00254, time=1109893469, proto=6
netams: created queue 0xc1355240 for id=15, hash=00254
netams fwreply for entry id=15, flags=0, queue 1/102
netams: flush queue for entry id=15, hash=254, size=1, action=1
Зачем все это нужно? Чтобы быстрее работало! Ниже приведены результаты небольших стендовых испытаний.
Все работы проводились с ОС FreeBSD 5.3-RELEASE, которая работала внутри виртуальной машины VmWare 4.5.2. Сама виртуальная машина работала на компьютере DUAL P4 Xeon 3.4GHz, 4Gb RAM под управлением Windows Server 2003. Виртуальная машина и хост-машина связаны через виртуальный адаптер vnmat (хотя в тестах трансляции адресов не было).
Скорость передачи данных измерялась при помощи
iperf 1.7.0
На самой машине с Windows Server 2003 запущен сервер
iperf, там же запускаем клиента:
C:\>iperf.exe -c 192.168.56.1 -t 10 -i 1
------------------------------------------------------------
Client connecting to 192.168.56.1, TCP port 5001
TCP window size: 8.00 KByte (default)
------------------------------------------------------------
[1948] local 192.168.56.1 port 3027 connected with 192.168.56.1 port 5001
[ ID] Interval Transfer Bandwidth
[1948] 0.0- 1.0 sec 97.8 MBytes 821 Mbits/sec
[1948] 1.0- 2.0 sec 96.1 MBytes 807 Mbits/sec
[1948] 2.0- 3.0 sec 97.7 MBytes 820 Mbits/sec
[1948] 3.0- 4.0 sec 93.0 MBytes 780 Mbits/sec
[1948] 4.0- 5.0 sec 93.2 MBytes 782 Mbits/sec
[1948] 5.0- 6.0 sec 96.9 MBytes 813 Mbits/sec
[1948] 6.0- 7.0 sec 98.4 MBytes 825 Mbits/sec
[1948] 7.0- 8.0 sec 97.4 MBytes 817 Mbits/sec
[1948] 8.0- 9.0 sec 96.0 MBytes 806 Mbits/sec
[1948] 9.0-10.0 sec 98.2 MBytes 824 Mbits/sec
[1948] 0.0-10.0 sec 965 MBytes 808 Mbits/sec
Как видим, скорость передачи данных через локальный виртуальный интерфейс просто гигантская. Пробуем, как передаются данные между Windows и установленной FreeBSD, через VmWare, безо всяких побочных эффектов (NeTAMS и модуль ядра выключены):
freebsd-vm:~/netams#iperf -c 192.168.56.1 -t 10 -i 1
------------------------------------------------------------
Client connecting to 192.168.56.1, TCP port 5001
TCP window size: 32.5 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.56.17 port 51925 connected with 192.168.56.1 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0- 1.0 sec 27.6 MBytes 232 Mbits/sec
[ 3] 1.0- 2.0 sec 28.4 MBytes 238 Mbits/sec
[ 3] 2.0- 3.0 sec 28.1 MBytes 236 Mbits/sec
[ 3] 3.0- 4.0 sec 28.3 MBytes 237 Mbits/sec
[ 3] 4.0- 5.0 sec 28.4 MBytes 238 Mbits/sec
[ 3] 5.0- 6.0 sec 28.3 MBytes 237 Mbits/sec
[ 3] 6.0- 7.0 sec 28.0 MBytes 235 Mbits/sec
[ 3] 7.0- 8.0 sec 28.1 MBytes 236 Mbits/sec
[ 3] 8.0- 9.0 sec 28.7 MBytes 240 Mbits/sec
[ 3] 9.0-10.0 sec 28.3 MBytes 237 Mbits/sec
[ 3] 0.0-10.0 sec 282 MBytes 237 Mbits/sec
Естественно, медленнее. Теперь запустим NeTAMS и модуль ядра вместе, в режиме
divert и убедимся, что это была не подстава:
freebsd-vm:~/netams#iperf -c 192.168.56.1 -t 10 -i 1
------------------------------------------------------------
Client connecting to 192.168.56.1, TCP port 5001
TCP window size: 32.5 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.56.17 port 56639 connected with 192.168.56.1 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0- 1.0 sec 20.9 MBytes 175 Mbits/sec
[ 3] 1.0- 2.0 sec 23.4 MBytes 196 Mbits/sec
[ 3] 2.0- 3.0 sec 23.5 MBytes 197 Mbits/sec
[ 3] 3.0- 4.0 sec 23.5 MBytes 197 Mbits/sec
[ 3] 4.0- 5.0 sec 23.6 MBytes 198 Mbits/sec
[ 3] 5.0- 6.0 sec 23.6 MBytes 198 Mbits/sec
[ 3] 6.0- 7.0 sec 23.4 MBytes 196 Mbits/sec
[ 3] 7.0- 8.0 sec 23.8 MBytes 200 Mbits/sec
[ 3] 8.0- 9.0 sec 23.6 MBytes 198 Mbits/sec
[ 3] 9.0-10.0 sec 23.3 MBytes 196 Mbits/sec
[ 3] 0.0-10.0 sec 233 MBytes 195 Mbits/sec
freebsd-vm:~/netams#ngctl msg netams: info
Rec'd response "info" (1) from "[3c5]:":
Args: { packets/in=85515 packets/out=169244 mode=2
debug=1 active_flows=4 total_flows=4 default_policy=2 }
Налицо падение производительности на 100*(237-195)/237=
17.7% или
в 1.2 раза. Теперь заменим фильтрование через модуль ядра на стандартное, через
ipfw divert и
data-source ip-traffic:
freebsd-vm:~/netams#iperf -c 192.168.56.1 -t 10 -i 1
------------------------------------------------------------
Client connecting to 192.168.56.1, TCP port 5001
TCP window size: 32.5 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.56.17 port 55410 connected with 192.168.56.1 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0- 1.0 sec 2.96 MBytes 24.8 Mbits/sec
[ 3] 1.0- 2.0 sec 3.59 MBytes 30.1 Mbits/sec
[ 3] 2.0- 3.0 sec 3.73 MBytes 31.3 Mbits/sec
[ 3] 3.0- 4.0 sec 3.62 MBytes 30.3 Mbits/sec
[ 3] 4.0- 5.0 sec 3.70 MBytes 31.0 Mbits/sec
[ 3] 5.0- 6.0 sec 3.69 MBytes 30.9 Mbits/sec
[ 3] 6.0- 7.0 sec 3.65 MBytes 30.6 Mbits/sec
[ 3] 7.0- 8.0 sec 3.71 MBytes 31.1 Mbits/sec
[ 3] 8.0- 9.0 sec 3.71 MBytes 31.1 Mbits/sec
[ 3] 9.0-10.0 sec 3.73 MBytes 31.3 Mbits/sec
[ 3] 0.0-10.0 sec 36.1 MBytes 30.2 Mbits/sec
freebsd-vm:~/netams#ipfw show 10 11
00010 26136 39197956 divert 199 tcp from any to any dst-port 5001
00011 13069 679600 divert 199 tcp from any 5001 to any
В данном случае мы видим потерю производительности на 100*(237-30.2)/237=
87.2% или
в 8 раз. Выгода налицо!
Велосипед мы не изобрели, это понятно. Результаты ожидаемы. Использование модуля ядра более опасно, чем обычного
data-source ip-traffic, а уже тем более сбора по
libpcap или
netflow. В случае ошибок или переполнения буферов зависает ядро вместе со всеми процессами, или блокируются все сокеты. Было проведено тестирование на предмет поддержки "нехороших ситуаций" вроде
ping -f или
nmap -sS -PS 80 -iR 100. Однако стабильность работы не гарантируется, тестируйте модуль со всей осторожностью!
Кто-нибудь особенно умный может спросить: "А собственно зачем вы это делали? Фильтровать можно и в ядре, через тот же
ipfw deny,
pfctl и прочее. Все будет быстро и надежно."
Возможно. Однако вам придется как-то синхронизировать таблицу юнитов и политик учета с правилами firewall, фактически городить зоопарк скриптов и дублировать одно и то же дважды. Зачем? Использование NeTAMS позволяет хранить всю информацию о правилах в одном месте, без проблем применяя всякие хитрости вроде
break flag,
prefix table и
срабатывание по времени суток. Совершенно прозрачно работают сервисы квот, системные политики, биллинг, и так далее.
Возможные направления улучшения и развития:
- Создать аналогичный продукт для Linux, видимо на базе ULOG
- Сделать поддержку RAW IP пакетов, PPP и так далее
- Проверить работоспособность в случае нескольких модулей ядра, работающих одновременно