Netgraph ipfw и гибкий учет трафика через netflow

Опубликовано admin в Пнд, 26/07/2010 - 20:44
И снова здравствуйте!

Продолжим обзор модулей FreeBSD netgraph.
Сегодня у нас в гостях ng_ipfw, ng_one2many и старый друг ng_netflow.
С их помощью попробуем посчитать трафик по определенным условиям и посмотрим как это можно использовать на практике.

Что за модули?


ng_ipfw.gif
ng_ipfw — модуль, создающий интерфейс для доступа к подсистеме netgraph из фаервола IPFW. При загрузке в ядро, модуль автоматически создает свой узел в пространстве подсистемы netgraph с именем «ipfw:», и может присутствовать только в единственном экземпляре. Ng_ipfw принимает подключения к произвольным номерным хукам, номера хуков указываются в правилах ipfw. Каждый входящий в модуль пакет тэгируется по нескольким параметрам (ipfw правило, интерфейс, направление), для того чтобы его можно было идентифицировать при возврате обратно в ipfw. Не тэгированные пакеты, возвращаемые в фаервол, уничтожаются. Контрольных сообщений модуль не принимает.

ng_one2many.gifng_one2many — модуль, позволяющий распределять пакеты по нескольким подключениям. Он принимает подключения к одному хуку «one» и к многочисленным «many0», «many1», «manyN»… Пока существует два алгоритма распределения пакетов внутри модуля.

1. Пакеты входящие в «one» поочередно выходят через разные «manyN» (xmit_roundrobin). Все пакеты входящие в «manyN» выходят в «one».
2. Пакеты входящие в «one» выходят копиями через все «manyN» (xmit_all). Все пакеты входящие в «manyN» выходят в «one». (похоже на работу ng_hub).

Так же существует алгоритмы определения up/down линков, и возможность их включения/выключения через команды, это нужно для специфичных задач, об этом подробнее в man ng_one2many.

Ng_one2many принимает контрольные сообщения: setconfig, getconfig, getstats, clrstats, getclrstats. В принципе интуитивно понятно. Подробнее в man.

Модуль ng_hole — делает элементарные действия, считает пакетики вошедшие к нему и уничтожает их.

О модуле ng_netgraph я писал в своем прошлом топике FreeBSD Netgraph на примере Ethernet тоннеля. (_http://alexandr.sysoev.ru/node/71).


IPFW


Системным администраторам работающим с FreeBSD рассказывать об ipfw смысла нет, а кто не знает что это может без проблем прочитать в Википедии (_http://ru.wikipedia.org/wiki/Ipfw), да и гугл выдаст тонны информации с примерами и готовыми наработками. Скажу лишь то, что для взаимодействия ipfw и netgraph в ipfw существует два правила ngtee и netgraph, пример:

ipfw add netgraph 10 all from any to any — направит весь трафик попавший в правило в хук ipfw:10 модуля ng_ipfw

ipfw add ngtee 20 ip from any to any — скопирует весь трафик попавший в правило в хук ipfw:20 модуля ng_ipfw

Немного посчитаем


Мы хотим посчитать трафик, проходящий через интерфейс каждого пользователя в обоих направлениях. При этом исключить подсети 192.168.5.0/24 и 192.168.10.0/24 из учета, т.к в них у нас живут бесплатные ресурсы. Пользователи терминируются на интерфейсы «ngX» демоном mpd.

В ipfw это будет выглядеть так:
ipfw table 1 flush
ipfw table 1 add 192.168.5.0/24
ipfw table 1 add 192.168.10.0/24

ipfw add ngtee 10 ip from not "table(1)" to any in via "ng*"
ipfw add ngtee 10 ip from any to not "table(1)" out via "ng*"

Т.е мы копируем в ipfw:10 подсистемы netgraph весь трафик, не принадлежащий сетям, перечисленным в таблице 1.

ng_ipfw_netflow.gif Нарисуем граф. Он достаточно прост.

Создаем простую структуру из модулей ng_ipfw и ng_netflow и ng_ksocket. Т.к это сбор статистики, данные будут идти только в одну сторону, обрабатываться и отправляться в нужное место.

Копируем весь трафик проходящий через IPFW правила в ng_ipfw, который в свою очередь подключим к ng_netflow, подключенный к ksocket и далее к netflow коллектору.

Собираем в системе


Создаем модуль netflow и подключаем его хук iface0 к хуку ipfw 10.
ngctl mkpeer ipfw: netflow 10 iface0
Называем созданный модуль нетфлоу «netflow», он находится по адресу создания «ipfw:10»
ngctl name ipfw:10 netflow
Отправляем модулю «netflow:» команду, принимать через iface0 чистые IP пакеты. (подробнее в моём прошлом топике о модуле ng_netflow)
ngctl msg netflow: setdlt {iface=0 dlt=12}
Созадем модуль ksocket и подключаем его хук inet/dgram/udp к «netflow:» хуку «export».
ngctl mkpeer netflow: ksocket export inet/dgram/udp
Называем созданный модуль «netflow_socket»
ngctl name netflow:export netflow_socket
Отправляем модулю «netflow_socket» команду подключиться к коллектору.
ngctl msg netflow_socket: connect inet/3.3.3.3:9996

Казалось бы все просто — это будет работать, но оказывается не совсем так, как нужно. Пакет вышедший с интерфейса пользователя и вошедший в интерфейс нашего соседнего пользователя посчитается дважды. Тут мы можем перестать считать, например, исходящий трафик… но к нам на помощь приходит модуль ng_one2many.

Новый граф


ng_ipfw_one2many_netflow.gif Т.к ng_one2many умеет работать в режиме roundrobin, и даже делает это по-умолчанию, мы можем вставить его между ng_ipfw и ng_netflow.

Для разделения трафика пополам потребуется подключить one2many хуками many0 и many1 к ng_netflow и ng_hole, т.к обязательное условие распределения пакетов для one2many являются хуки в активном состоянии.

Получится что в учет пойдет только половина пакетов, с маленькой погрешностью, а вторая часть будет уничтожена.

Для второго графа будем использовать новый хук модуля ipfw — 20. Так же при создании графа нужно будет давать узлам новые имена, чтобы они не пересекались с уже созданным выше графом. Создание его аналогично, и подробно описывать его не буду.

В итоге мы получаем итоговые скрипты для работы.


Первый скрипт для сбора графа полного netflow.
#!/bin/sh
case "$1" in
start)
echo "Starting full netflow."
ngctl mkpeer ipfw: netflow 10 iface0
ngctl name ipfw:10 netflow
ngctl msg netflow: setdlt {iface=0 dlt=12}
ngctl mkpeer netflow: ksocket export inet/dgram/udp
ngctl name netflow:export netflow_socket
ngctl msg netflow_socket: connect inet/3.3.3.3:9996
echo "Ok."
exit 0
;;
stop)
echo "Stopping full netflow."
ngctl shutdown netflow_socket:
ngctl shutdown netflow:
echo "Ok."
exit 0
;;
restart)
sh $0 stop
sh $0 start
;;
*)
echo "Usage: `basename $0` { start | stop | restart }"
exit 64
;;
esac


Второй скрипт создает граф учета половины трафика.
#!/bin/sh
case "$1" in
start)
echo "Starting half netflow."
ngctl mkpeer ipfw: one2many 20 one
ngctl name ipfw:20 one2many
ngctl mkpeer one2many: netflow many0 iface0
ngctl name one2many:many0 netflow_half
ngctl mkpeer one2many: hole many1 black
ngctl name one2many:many1 blackhole
ngctl mkpeer netflow_half: ksocket export inet/dgram/udp
ngctl name netflow_half:export netflow_socket_half
ngctl msg netflow_half: setdlt {iface=0 dlt=12}
ngctl msg netflow_socket_half: connect inet/3.3.3.3:9996
echo "Ok."
exit 0
;;
stop)
echo "Stopping half netflow."
ngctl shutdown netflow_socket_half:
ngctl shutdown blackhole:
ngctl shutdown netflow_half:
ngctl shutdown one2many:
echo "Ok."
exit 0
;;
restart)
sh $0 stop
sh $0 start
;;
*)
echo "Usage: `basename $0` { start | stop | restart }"
exit 64
;;
esac


В IPFW мы получаем новую конструкцию. Во вторую табличку добавляем сети, трафик между которыми надо уменьшать в 2 раза.

ipfw table 1 flush
ipfw table 1 add 192.168.5.0/24
ipfw table 1 add 192.168.10.0/24

ipfw table 2 flush
ipfw table 2 add 192.168.50.0/24
ipfw table 2 add 192.168.51.0/24
ipfw table 2 add 192.168.52.0/24
ipfw table 2 add 192.168.53.0/24

ipfw add ngtee 10 ip from not "table(1)" to any in via "ng*"
ipfw add ngtee 10 ip from any to not "table(1)" out via "ng*"

ipfw add ngtee 20 ip from "table(2)" to "table(2)" via "ng*"


Вот и всё


Спасибо всем, кто героически прочел всю статью.

Статья взята с http://habrahabr.ru (_http://habrahabr.ru/blogs/bsdelniki/88022/)