Подробное руководство по ipfw nat во FreeBSD


Автор: terminus.
Версия статьи 1.3.6

По странному стечению обстоятельств в Сети встречается очень мало внятных руководств описывающих функции и сценарии применения механизма ipfw nat, несмотря на то, что его поддержка появилась уже давно - начиная с версии FreeBSD 7.0. Самое обидное, что удручающе выглядит секция man ipfw посвященная nat. А ведь именно ее начинает читать любой кто хотел бы попробовать в деле "ядерный нат", и, что закономерно - не всегда разбирается. В итоге люди переходят на natd, а некоторый горячие головы начинают использовать даже альтернативные фаерволы такие как pf/ipf. Целью заметки является детализированное (по мере сил) описание ipfw nat, а так же опыта использования этого инструмента в различных сетевых конфигурациях. В заметке будет много смысловых отсылок на замечательную статью Вадима Гончарова (nuclight) где давалось подробное описание внутреннего устройства ipfw и сетевой подсистемы FreeBSD.

Примеры приводимые в моей заметке отрабатывались на FreeBSD 7.2-STABLE.

Прежде всего необходимо детально описать как именно происходит движение трафика в сетевом стеке FreeBSD. Без этого понимания целостной картины того "что же мы делаем", не сложится и все скатится просто к набору "хавтушек". На рисунках 1-а и 1-б показана гипотетическая система оборудованная двумя сетевыми адаптерами fxp0 и fxp1. Схема 1-а показывает физическое подключение, а схема 1-б уже логическое. Нас больше интересует схема логическая - на ней мы видим, что в каждом сетевом поключении для трафика существует два прохода - IN и OUT. Как легко можно догадаться в проход IN поступает трафик пришедший к нам из сети, а в проход OUT уходит то, что адресовано уйти в сеть к которой подлючен данный сетевой адаптер. Таким образом, возвращаясь к примеру системы с двумя сетевыми адаптерами, мы видим, что у нее существует четыре прохода для трафика. При добавлении к системе дополнительных сетевых адаптеров схема масштабируется.

Но предыдущая схема не главная. На самом деле схема проходжения трафика через сетевой стек FreeBSD имеет только одну логическую сторону с проходами IN и OUT, независимо от количества сетевых адаптеров. Просто при движении трафика через стек, операционная система самостоятельно избирет какой именно из доступных IN и/или OUT проходов были и/или будут задействованы для передачи трафика. В соответствии с этим, к пакетам находящимся внутри сетевой подсистемы, добавляются метки указывающие на то, через какой IN проход конкретного сетевого адаптера к нам пришел трафик, и через какой OUT какого сетевого адаптера он уйдет из машины.

Указанные два прохода IN и OUT важны тем, что при поступлении в них сетевого трафика, этот трафик попадает в фаервол где проходит через правила фильтрации ipfw. Таким образом, трафик проходящий через маршрутизатор проходит через правила ipfw два раза - на проходе IN сетевого адаптера через который он был получен, и на проходе OUT сетевого адаптера через который он будет передан (сейчас не рассматриваем случай включение фильтрации на уровне Ethernet - в таком случае трафик проходит через ipfw четыре раза). Точно так же трафик исходящий от самого маршрутизатора обязательно будет иметь свой OUT проход, а трафик адресованный непосредственно маршрутизатору войдет через свои IN проход.

Ниже я привожу перепечатку части материала статьи Вадима Гончарова - того кусочка где объясняется движение трафика через Схему 2. Лучше самого автора я не напишу, а включить в статью это обьяснение очень-очень надо.

...Допустим, набор правил у нас такой:

ipfw add 1 deny tcp from any to any 135,445
ipfw add 2 divert 8668 all from any to any via ext0
ipfw add 3 count icmp from any to any
ipfw add 4 allow all from any to any
# в 65535 по умолчанию deny

Когда пакет проходит через машину, к нему системой прикрепляется дополнительная информация, помимо собственно его содержимого, видного в tcpdump. Например, на каком интерфейсе он был получен, через какой отправляется, и т.п. Их можно проверять соответствующими опциями в правилах ipfw, на рисунке показаны места, где будут срабатывать соответствующие указания in/out и recv/xmit для сетевух.

Рассмотрим одну сторону роутера с рисунка выше более подробно, с точки зрения вызовов функций в ядре (чуть более подробная картинка из мана). На самом деле, с точки зрения функций, на рисунке выше нет двух сторон, она только одна, и различается параметром - обрабатываемой сетевухой.

То есть, пакет на входе передается драйвером в ether_demux(), затем он попадает в ip_input(), в точку (2), где выполняются базовые проверки на корректность пакета, после чего пакет прилетает в ipfw - функция ipfw_chk(). Допустим, правила были простые, без задействования других подсистем. Тогда, вернувшись из ipfw, пакет продолжает движение по ip_input(), которая смотрит, предназначен ли пакет нашей машине ("to me" в терминах ipfw), либо кому-то другому. Если нам, то пакет уходит в точку (3), где решится, в какой сокет какой юзерлэндной программе его отправить.

Если же пакет был предназначен не нам, пакет из ip_input() направится в точку (4), где ip_forward() проверит, установлен ли sysctl, разрешающий форвардинг, произведет декремент TTL и т.п. действия, после чего пакет придет в точку (6), функцию ip_output(). Туда же он попадет напрямую, когда какая-нибудь программа решит что-то отправить в сеть и передаст данные ядру.

Функция ip_output() первым делом смотрит в таблицу маршрутизации, определяя, каков шлюз и на каком интерфейсе он находится. С этой информацией пакет вновь передается в ipfw, в котором опять пробегается по всем правилам. После выхода из ipfw_chk() в ip_output(), если ядро было скомпилировано с соответствующей опцией, проверяется, не был ли применен ipfw fwd - если да, то просмотр таблицы маршрутизации выполняется заново с целью получить MAC-адрес нового шлюза. Затем пакет в точке (7) покидает ip_output() и передается дальше, на L2 и потом к драйверам интерфейсов.

Это всё было для случая простых правил файрвола. Теперь, предположим, там появляется divert, рассмотрим на примере правил выше. Пакет из внутренней сети куда-то в Интернет на порт 80 войдет на внутреннем интерфейсе в точку (1), пройдет начальные проверки ip_input() в (2), будет передан в ipfw_chk() и начнет проходить по правилам. Под правило 1 он не подпадает, под 2 тоже, так как имя интерфеса сейчас int0, правило 3 опять-таки не срабатывает, но под правило 4 подходят все пакеты, и он выходит из ipfw_chk() дальше в ip_input(). Там выясняется, что предназначен он идти в Интернет, поэтому пакет попадает в точки (4) и затем (6), где ip_output() определяет адрес шлюза и то, что интерфейс будет ext0, с чем пакет и попадает опять в ipfw_chk() и снова идет по правилам. Правило 1 снова не подходит, но условие правила 2 срабатывает - "via ext0, проходим прямо сейчас через интерфейс ext0, в любом направлении".

И вот здесь срабатывает divert - пакет из ipfw_chk() передается в точку (8), в подсистему divert, при этом к нему предварительно прикрепляется метаинформация с направлением (out), интерфейсом (ext0) и номером правила 2. Подсистема divert передает этот пакет в указанный в правиле порт (8668), на котором в нашем случае работает natd. Тот обрабатывает пакет, метаинформацию же - не трогает, и возвращает подсистеме divert вместе с измененным пакетом как есть (так поступают большинство divert-демонов, хотя любой их них, в принципе, может поменять эту информацию, и пакет будет передан в другое место).

Подсистема divert выводит полученный из natd пакет из точки (9) в точку (6). Следует обратить внимание - пакет попадает в ip_output() ЕЩЕ РАЗ! Это необходимо, так как демон мог вернуть пакет с совершенно другими адресами или вообще создать новый пакет. Но в нашем случае пакет несет с собой диверт-тег, метаинформацию, самая важная часть которой - номер правила. При входе в ipfw_chk() пакет первым делом проверяется на наличие этого самого тега. Он найден, и в нем содержится номер правила - 2. Поэтому ipfw_chk() пропускает правила номер 1 и 2, и начинает с номера 2+1, то есть 3 (если бы пакет применялся к указанному номеру, а не следующему, то он снова попал бы в divert, то есть получился бы бесконечный цикл).

На этом месте пакет продолжит движение с правила номер 3 и дальше, как обычно, уйдет в точке (7) в сеть. Таким образом, несмотря на то, что пакет попадал в ip_output() два раза, с точки зрения пользователя это выглядит так, как если бы он был там один раз и никуда из файрвола не убегал - просто на правиле 2 в нем волшебным образом поменялись адреса и порты.

Аналогичным образом, ответный пакет, возвращающийся из Интернета на интерфейс ext0, пройдет через машину по пути (1) - (2) - начало ipfw_chk() - правило 1 - правило 2 - (8) - divert - natd - divert - (9) - (2) - проверка в ipfw_chk() - правило 3 - правило 4 - (4) - (6) - начало ipfw_chk() - правило 1 - правило 2 - правило 3 - правило 4 - (7) - отправка через интерфейс int0.

Подобное выведение пакета из обработки ipfw в другую подсистему - не уникально для divert, это общая схема работы в стеке FreeBSD. Например, действия pipe и queue в dummynet, передача пакета в netgraph (а также появившийся в 7.0 ipfw nat) действуют по тому же принципу. Отличие, однако, в том, что в этих подсистемах пакет остается внутри ядра, никакому демону не передается. Поэтому, во-первых, подсистемы вместо номера правила сохраняют на него полный указатель, и пакет вернется непосредственно в следующее правило, даже если оно имеет тот же номер. Во-вторых, для таких подсистем действует настройка one_pass в соответствующем sysctl - если она включена, то при повторном входе пакета в ipfw после возврата из подсистемы dummynet (netgraph), ipfw_chk() сразу вернется без прохода по правилам, как если бы к пакету был применен allow. Это поведение позволяет упростить правила файрвола, когда известно, что если пакет попал в трубу, то он уже точно отправляется дальше, и не требуется после каждого pipe вставлять allow (чтобы пакет не попал в следующие правила и следующие pipe/queue). Если же конфигурация требует сначала ограничить трафик, а потом уже разбираться по замысловатым требованиям, что из него разрешить, а что запретить, то упрощению правил наоборот будет способствовать отключенный one_pass - поскольку с ним вместо allow, расположенных до pipe, пришлось бы делать skipto.

Итак, как уже было сказано, пакет проходит по списку правил последовательно, в порядке возрастания номеров правил. Список правил можно рассматривать как таблицу с тремя столбцами: номер, действие (и его параметры, например log), и условия, при которых пакет соответствует правилу (например, от адреса 1.1.1.1 адресу 2.2.2.2). Таблица просматривается сверху вниз, пакет сравнивается с условиями. На первом совпавшем условии смотрим в столбец действий, выполняем действие, прекращаем просмотр...

Теперь, зная, что именно и в какой последовательности происходит с пакетами на их пути через сетевой стек FreeBSD мы можем приступать к изучению правил ipfw nat.

Механизм ipfw nat можно рассматривать как своеобразный фильтр-преобразователь трафика. В зависимости от того как настроен нат, и от того какие IP адреса отправителя и получателя есть в проходящем через него пакете, нат будет преобразовывать в пакете IP адрес отправителя, либо IP адрес получателя, или же просто отвергать пакет. Важно помнить то, что нат разделяет трафик на исходящий (тот который надо маскировать) и входящий (тот который надо демаскировать) в зависимости от того на каком проходе (OUT или IN) он был получен. Так, трафик попавший в нат из прохода OUT требуется маскировать (заменить IP адрес отправителя на IP адрес нат машины), а трафик пришедший из прохода IN демаскировать. Развернуть это поведение можно используя директиву revers - тогда все поведение поменяется и можно будет запускать нат не на внешнем сетевом интерфейсе (который смотрит в интернет), а на внутреннем (том который смотрит в локальную сеть). Детальное описание того как работает механизм нат приводить, наверное, излишне. На поведение механизма nat влияет настройка параметра one_pass - в зависимости от ее значения трафик прошедший через нат может либо вернуться в проход фаервола из которого он был направлен в нат, либо выйти из фаервола. Такая гибкость дает возможность делать с трафиком много интересных вещей. ipfw nat позволяет запускать множество своих экземпляров каждый из которых имеет свою нат-таблицу и настройки. Объеденить несколько экземпляров для использования ими общей нат-таблицы - нельзя.



Возможности ipfw nat и опции конфигурации.

ipfw nat основан на коде библиотеки libalias. На ней же построенны как natd так ng_nat. Функционал libalias перенесенный в ipfw nat практически полностью повторяет все то, что есть в natd за исключением некоторых специфических вещей. К сожалению man страница для ipfw nat дает очень скупое описание его возможностей, поэтому при изучении опций конфигурации можно пользоваться man страницей для natd.

Параметры конфигурации и их перевод.

ip ip_address

Параметр предписывает IP адрес используемый при маскировании и демаскировании. Этот параметр (или же параметр "if") должны быть указаны. Установка параметра "proxy_only" отменяет действие параметров "ip" и "if". В этом параметре обычно указывается адрес назначенный "внешнему" сетевому интерфейсу. Может быть указан только один IP адрес.

Во всех уходящих во вне пакетах, исходный IP адрес отправителя будет замещен на адрес указанный в этом параметре. Все приходящие из вне пакеты будут проверяться на совпадение с параметрами для какого-либо из ранее установленных соединений, и в случае нахождения совпадения, пакет будет демаскирован. Если совпадения не будет найдено, проверяются и применяются все директивы redirect_port, redirect_proto и redirect_addr.
Если пакет не подпадает не под один из установленных критериев демаскировки и не установлен параметр "deny_in", то тогда пакет будет принят для локальной машины. Если параметр "deny_in" установлен, то тогда пакет будет удален.


if nic

Параметр предписывает IP адрес используемый при маскировании и демаскировании, на основе имеющихся настроек для сетевого интерфейса с именем "nic".
В случае динамического изменения IP адреса на сетевом интерфейсе, адрес маскировки и демаскировки будет так же динамически изменен.


log

Параметр предписывает включение накопления внутренней статистики по количеству и типу активных соединений.


deny_in

Параметр предписывает запрет пропускать через nat входящие пакеты для которых нет совпадения во внутренней таблице активных соединений, или же нет совпадения с другими правил демаскировки.

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


same_ports

Параметр предлагает механизму nat поведение при котором тот будет стараться сохранять оригинальные номера портов в уходящих (маскируемых) пакетах.

При установке данного параметра такие протоколы как RPC будут иметь больше шансов на успешную работу. Если по какой-либо причине сохранить оригинальные номера портов невозможно, то пакет будет молча приобразован без учета этого предписания.


unreg_only

Параметр предписывает проводить маскировку только для тех исходящих пакетов, чей адрес отправителя находится в классах "незарегистрированных" IP адресов.
В соответсвии с RFC 1918 к такими классам относятся адреса из диапазонов 10.0.0.0/8, 172.16.0.0/12 и 192.168.0.0/16.


reset

Параметр предписывает очищать внутреннюю таблицу активных соединений при изменениях параметра "ip" указывающего адрес используемый при маскировании и демаскировании.


reverse

Параметр предписывает поменять внутренне представление для случаев обработки входящего и исходящего трафика через проходы IN и OUT.

В случае установки этого параметра, трафик поступивший в механизм nat из прохода IN будет считаться исходящим, т.е. требующим проведения маскировки, а трафик поступивший из прохода OUT будет считаться входящим, т.е. требующем демаскировки.

Такое поведение может быть необходимо в некоторых случаях построения схем прозрачного проксирования, когда исходящий трафик необходимо перенаправлять на локальную машину, а сам механизм nat запущен на внетреннем сетевом интерфейсе (обычно nat запускают на внешнем сетевом интефейсе).


proxy_only

Параметр предписывает применять только правила прозрачного проксирования.
Правила маскировки и демаскировки при этом применяться не будут.


redirect_addr localIP publicIP

Параметр предписывает проводить перенаправление трафика поступающего на внешний IP адрес (publicIP), на машину с IP адресом во внутренней сети (localIP). Этот механизм так же известен под названием "статический NAT". Обычно статический NAT применяется в случаях, когда провайдер выделил вам небольшой диапазон IP адресов, но он так же может применяться при случаях с одним адресом:

redirect_addr 10.0.0.8 0.0.0.0

Данная команда будет пересылать весь входящий трафик на машину с адресом 10.0.0.8

В случае, когда несколько внутренних адресов назначены для пересылки с одного внешнего адреса:

redirect_addr 192.168.0.2 public_addr
redirect_addr 192.168.0.3 public_addr
redirect_addr 192.168.0.4 public_addr

весь входящий трафик будет пересылаться на последний из назначенных адресов (192.168.0.4), но трафик исходящий с первых двух адресов по прежнему будет маскироваться под адрес указанный в public_addr.


redirect_addr localIP[,localIP[,...]] publicIP

Данная форма записи для параметра redirect_addr (а так же для redirect_port) используется для прозрачного распределения сетевой нагрузки между набором из нескольких серверов. Данная функция известна под названием LSNAT (RFC 2391).

Например данная конфигурация для redirect_port:

www1:http,www2:http,www3:http www:http

означает, что входящие HTTP соединения для хоста www будут прозрачно перенаправленны на хосты www1, www2 или www3, при этом выбор хоста для перенаправления соединения будет произходить по алгоритму round-robin (по очереди), но без учета текущей нагрузки на хосты или сеть.

Пример:

redirect_addr 192.168.0.10,192.168.0.11,192.168.0.12 10.0.0.100


redirect_port proto targetIP:targetPORT[-targetPORT]
[aliasIP:]aliasPORT[-aliasPORT]
[remoteIP[:remotePORT[-remotePORT]]]

Параметр предписывает проводить перенаправление входящих соединений поступающих на указанный номер порта(ов), на другой хост и порт(ы).

Аргумент "proto" может устанавливаться либо как tcp, либо как udp.

Аргумент "targetIP" указывает IP адрес на который должна происходить пересылка трафика, а аргумент "targetPORT" порт (или диапазон портов) на который будет перенаправлен трафик.

Аргумент "aliasIP" указывает IP адрес на который будут поступать входящие соединения, а аргумент "aliasPORT" указывает номер порта
(или диапазон портов) на которые будут поступать входящие соединения.


Аргумены "remoteIP" и "remotePORT" при необходимости могут быть использованы для более точного указания того какие именно входящие соединения необходимо обрабатывать указанным правилом, устанавливая соотвествие с удаленным источником/назначением трафика.
Если аргумент "remotePORT" не указан, то подразумевается, что имеются в виду номера всех портов.

Аргументы targetIP, aliasIP и remoteIP могут указываться как в виде IP адресов, так и в виде имен хостов. Аргументы targetPORT, aliasPORT и remotePORT указывающие диапазоны портов, не обязательно должны полностью совпадать по начальным и конечным значениям диапазонов, но обязаны иметь одинаковый размер диапазонов.
Когда арументы targetPORT, aliasPORT или remotePORT указывают на единственный порт (не диапазон), то их значения могут указываться как имена соответвующих сервисов перечисленных в services(5).

Примеры:

redirect_port tcp inside1:telnet 6666

это означает, что входящие TCP пакеты направляемые на порт 6666 данной машины будут перенаправленны на порт 23 (telnet) машины с именем inside1

redirect_port tcp inside2:2300-2399 3300-3399

это означает перенаправление входящих соединений поступающих на диапазон портов 3300-3399 на машину с именем inside2, в диапазон портов 2300-2399. В данном случае произойдет сопоставление портов 1:1 - это значит, что порт 3300 будет перенаправляться в 2300, порт 3301 в 2301 и так далее.

Примеры:

redirect_port tcp 192.168.0.1:80 500
redirect_port tcp 192.168.0.1:80,192.168.0.10:22 500
redirect_port tcp 192.168.1.2:20-25 70-75
redirect_port tcp 192.168.0.1:80 1.2.3.4:500 5.6.7.8:1024
redirect_proto proto localIP [publicIP [remoteIP]]

Параметр предписывает проводить перенаправление входящих соединений протокола "proto" поступающих на внешний IP адрес "publicIP", на
машину с IP адресом "localIP" во внутренней сети. Аргумены "remoteIP" при необходимости может быть использован для более точного указания того какие именно входящие соединения необходимо обрабатывать указанным правилом, устанавливая соотвествие с удаленным источником/назначением трафика.

Если аргумент publicIP не указан, то по-умолчанию будет использоваться адрес настроенный для маскировки/демаскировки.

Пример:

redirect_proto udp 192.168.1.43 1.2.3.4

Примеры и сценарии применения механизма ipfw nat

Теперь, когда мы познакомились как с описанием принципов прохождения трафика через стек, так и с описание доступных опций конфигурирования механизма ipfw nat, можно приступать к составлению правил и рассмотрению их работы. Дальнейшее описание будет строиться по принципу "задача - решение", а так же по принципу постепенного усложнения конфигурации с детальными описаниями того как работает предлагаемое решение. Есть мнение, что на примерах проще понять изучаемый материал, а кроме того эти примеры можно будет использовать в работе.

Включение ipfw nat и Dummynet

Либо пересборка ядра с дополнительными параметрами:options IPFIREWALL
options IPFIREWALL_VERBOSE
options IPFIREWALL_VERBOSE_LIMIT=50
options IPFIREWALL_NAT
options LIBALIAS
options ROUTETABLES=2
options DUMMYNET
options HZ="1000"


Либо включение без пересборки (но так меньше возможностей для конфигурации)

/etc/rc.conf

firewall_nat_enable="YES"
dummynet_enable="YES"

Пример 1

Простейший случай: рутер с двумя сетевыми адаптерами подключенными в локальную сеть и в интернет. Адрес в локальной сети 192.168.1.1 (сетевой адаптер fxp0), адрес в интернете 1.2.3.4 (сетевой адаптер em0).

Задача: "раздавать интернет" для локальной сети, обеспечить проброс портов для внутренних адресов, разрешить прохождение трафика на отдельные порты самого рутера.

Конфигурация:

/etc/rc.conf
gateway_enable="YES"
ifconfig_em0="inet 1.2.3.4 netmask 255.255.255.0 -rxcsum"
ifconfig_fxp0="inet 192.168.1.1 netmask 255.255.255.0"
defaultrouter="1.2.3.254"
firewall_enable="YES"
firewall_type="/etc/firewall"

/etc/sysctl.conf
net.inet.ip.fw.one_pass=1

/etc/firewall# правила разрешающие трафик через локальный интерфейс lo0
# будут добавляться автоматически сами при старте фаервола
# 100 allow ip from any to any via lo0
# 200 deny ip from any to 127.0.0.0/8
# 300 deny ip from 127.0.0.0/8 to any

# разрешаем все через интерфейс локальной сети
add 1040 allow ip from any to any via fxp0

# боимся непонятного
add 1050 deny ip from any to 192.168.0.0/16 in recv em0
add 1060 deny ip from 192.168.0.0/16 to any in recv em0
add 1070 deny ip from any to 172.16.0.0/12 in recv em0
add 1080 deny ip from 172.16.0.0/12 to any in recv em0
add 1090 deny ip from any to 10.0.0.0/8 in recv em0
add 10100 deny ip from 10.0.0.0/8 to any in recv em0
add 10110 deny ip from any to 169.254.0.0/16 in recv em0
add 10120 deny ip from 169.254.0.0/16 to any in recv em0

# настройка ната.
# опции переноса строк "" надо убрать все должно быть в одну строчку
# опции redirect_port приведены для примера - как делать "проброс портов"
nat 1 config log if em0 reset same_ports deny_in
redirect_port tcp 1.2.3.4:6881 6881
redirect_port udp 1.2.3.4:4444 4444
redirect_port tcp 192.168.1.24:25 25

# заварачиваем все что проходит через внешний интерфейс в нат
add 10130 nat 1 ip from any to any via em0

# боимся непонятного
add 65534 deny all from any to any


Детальное описание прохождения трафика через фаервол в данной конфигурации:

Для рутера:

Трафик инициализированный непосредственно от рутера из точки (5) рис.2 пройдет в точку (6). Попав ip_output() ему будет присвоен шлюз по-умолчанию и тег с сетевым интерфейсом em0 через который он должен выйти в интернет (via em0, а точнее xmit em0). Трафик войдет в фаервол и начнет проходить через цепочку правил. Трафик дойдет до правила 10130 и будет передан в точку (8) то есть на обработку в нат как выходящий трафик так как поступит он из прохода OUT. В подсистеме нат для трафика в таблице соединений будет создана запись соответствия пар IP_локальный:порт <-> IP_удаленный:порт. После этого в точке (9) трафик выйдет из подсистемы нат и будет передан обратно в точку (6), заново попав в ip_output(). Так как значение one_pass выставлено в 1 (то есть разрешено) то трафик выйдет из ipfw (как если бы для него было применено правило allow) и уйдет в интернет через em0.

Трафик возвращающийся в виде ответа зайдет в точку (1) и попадет в проход IN имея при этом тег с сетевым интерфейсом em0 (via em0, а точнее recv em0) через который он был получен. Далее трафик зайдет в точку (2) и попадет в функцию ip_input() после чего войдет в фаервол и начнет проходить через цепочку правил. Трафик дойдет до правила 10130 и будет передан в точку (8) то есть на обработку в нат как входящий трафик так как поступит он из прохода IN. В подсистеме нат для трафика в таблице соединений будет найдена ранее созданая запись соответствия пар IP_локальный:порт <-> IP_удаленный:порт, после чего он будет демаскирован (в данном случае изменения фактически не будет так как адрес маскировки/демаскировки один и тот же - локальный 1.2.3.4). После этого в точке (9) трафик выйдет из подсистемы нат и будет передан обратно в точку (2) заново попав ip_input(). Так как значение one_pass выставлено в 1 (то есть разрешено) то трафик выйдет из ipfw (как если бы для него было применено правило allow) и поступит в локальный сокет на котором работает программа ранее инициализировавшая соединение.

Если из интернета приходит трафик не имеющий в таблице соединений записи соответсвия, то такой трафик при поступлении в нат будет отвергнут, так как в настройках выставлен параметр deny_in.

Если из интернета приходит трафик адресованный для tcp 1.2.3.4:6881 то он дойдя до точки (8) и попав в нат, будет успешно демаскирован так как в таблице для него уже будет иметься статическая запись соответствия 1.2.3.4:6881 <-> IP_любой:любой_порт. После чего он так же как и в случае с любым трафиком возвращающимся из интернета, будет принят и поступит в локальный сокет на порту 6881.
Ответный, уходящий с этого сокета трафик, пройдет через то же статическое правило и будет успешно обработан как это было в случае с любым трафиком уходящим в интернет.

Для сети 192.168.1.0/24:

Трафик инициализированный от хоста 192.168.1.56 из сети 192.168.1.0/24 войдет в рутер через ссетевой интерфейс fxp0 и пройдя через точку (1) получит тег с сетевым интерфейсом fxp0 (via fxp0, а точнее recv fxp0) через который он был получен. Далее трафик зайдет в точку (2) и попадет в функцию ip_input() после чего войдет в фаервол и начнет проходить через цепочку правил. Трафик дойдет до правила 1040 после чего выйдет из прохода IN фаервола, и попадет обратно в ip_input() где будет принято решение о том, что он предназначен не нам, а следовательно должен быть направлен в точку (4), в функцию ip_forward(). Попав в ip_forward() у IP трафика будет уменьшено значение TTL, а кроме того, после проверки разрешено ли проводить маршрутизацию (установка соответвующего значения sysctl выполненная из rc.conf через gateway_enable="YES") он будет передан в точку (6) в функцию ip_output(). Попав ip_output() ему будет присвоен шлюз по-умолчанию и тег с сетевым интерфейсом em0 через который он должен выйти в интернет (via em0, а точнее xmit em0). Трафик войдет в фаервол и начнет проходить через цепочку правил. Трафик дойдет до правила 10130 и будет передан в точку (8) то есть на обработку в нат как выходящий трафик так как поступит он из прохода OUT. В подсистеме нат для трафика в таблице соединений будет создана запись соответствия пар 192.168.1.56:порт <-> 1.2.3.4:порт <-> IP_удаленный:порт, а оригинальный адрес отправителя (192.168.1.56) в заголовке IP пакета будет заменен на IP адрес указанный для параметре "if" в настройках нат. После этого в точке (9) трафик выйдет из подсистемы нат и будет передан обратно в точку (6), заново попав в ip_output(). Так как значение one_pass выставлено в 1 (то есть разрешено) то трафик выйдет из ipfw (как если бы для него было применено правило allow) и уйдет в интернет через em0 имея IP адрес отправителя 1.2.3.4.

Трафик возвращающийся в виде ответа зайдет в точку (1) и попадет в проход IN имея при этом тег с сетевым интерфейсом em0 (via em0, а точнее recv em0) через который он был получен. Далее трафик зайдет в точку (2) и попадет в функцию ip_input() после чего войдет в фаервол и начнет проходить через цепочку правил. Трафик дойдет до правила 10130 и будет передан в точку (8) то есть на обработку в нат как входящий трафик так как поступит он из прохода IN. В подсистеме нат для трафика в таблице соединений будет найдена ранее созданая запись соответствия пар 192.168.1.56:порт <-> 1.2.3.4:порт <-> IP_удаленный:порт, после чего он будет демаскирован - адрес получателя в заголовке IP пакета будет заменен с 1.2.3.4 на 192.168.1.56. После этого в точке (9) трафик выйдет из подсистемы нат и будет передан обратно в точку (2) заново попав ip_input() Так как значение one_pass выставлено в 1 (то есть разрешено) то трафик выйдет из прохода IN фаервола (как если бы для него было применено правило allow) и попадет обратно в ip_input() где будет принято решение о том, что он предназначен не нам, а следовательно должен быть направлен в точку (4), в функцию ip_forward(). Попав в ip_forward() у IP трафика бцдет уменьшено значение TTL, а кроме того, после проверки разрешено ли проводить маршрутизацию (установка соответвующего значения sysctl выполненная из rc.conf через gateway_enable="YES") он будет передан в точку (6) в функцию ip_output(). Попав ip_output() ему будет присвоен шлюз по-умолчанию и тег с сетевым интерфейсом fxp0 через который он должен выйти в локальную сеть (via fxp0, а точнее xmit fxp0). Трафик войдет в фаервол и начнет проходить через цепочку правил. Трафик дойдет до правила 1040. После этого трафик выйдет из ipfw и уйдет в локальную сеть через fxp0 имея IP адрес получателя 192.168.1.56.

Если из интернета приходит трафик не имеющий в таблице соединений записи соответсвия, то такой трафик при поступлении в нат будет отвергнут, так как в настройках нат выставлен параметр deny_in.

Если из интернета приходит трафик адресованный для tcp 1.2.3.4:25 то он дойдя до точки (8) и попав в нат, будет успешно демаскирован так как в таблице для него уже будет иметься статическая запись соответсвия 192.168.1.24:25 <-> 1.2.3.4:25 <-> IP_любой:любой_порт. После чего он так же как и в случае с любым трафиком возвращающимся из интернета, будет принят и пройдя по тому же маршруту через IN и OUT уйдет в локальную сеть на машину с IP 192.168.1.24.
Ответный, уходящий с этого IP 192.168.1.24 трафик, пройдет через то же статическое правило и будет успешно обработан как это было в случае с любым трафиком уходящим в интернет.


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


Пример 2

Более сложный случай: рутер с двумя сетевыми адаптерами подключенными в локальную сеть и в интернет. Адрес в локальной сети 192.168.1.1 (сетевой адаптер fxp0), адреса в интернете 1.2.3.4 и 1.2.3.5 (сетевой адаптер em0).

Задача: "раздавать интернет" для локальной сети, обеспечить проброс портов для внутренних адресов, разрешить прохождение трафика на отдельные порты самого рутера, а главное - используя шейпер Dummynet обеспечить честное распределение пропускной способности между клиентами локальной сети. Так же обеспечить проброс всего трафика для 1.2.3.5 на 192.168.1.36 используя redirect_addr.

/etc/rc.conf
gateway_enable="YES"
ifconfig_em0="inet 1.2.3.4 netmask 255.255.255.0 -rxcsum"
ifconfig_em0_alias0="inet 1.2.3.5 netmask 255.255.255.255"
ifconfig_fxp0="inet 192.168.1.1 netmask 255.255.255.0"
defaultrouter="1.2.3.254"
firewall_enable="YES"
firewall_type="/etc/firewall"




/etc/sysctl.conf
net.inet.ip.fw.one_pass=0




/etc/firewall
add 1040 allow ip from any to any via fxp0

add 1050 deny ip from any to 192.168.0.0/16 in recv em0
add 1060 deny ip from 192.168.0.0/16 to any in recv em0
add 1070 deny ip from any to 172.16.0.0/12 in recv em0
add 1080 deny ip from 172.16.0.0/12 to any in recv em0
add 1090 deny ip from any to 10.0.0.0/8 in recv em0
add 10100 deny ip from 10.0.0.0/8 to any in recv em0
add 10110 deny ip from any to 169.254.0.0/16 in recv em0
add 10120 deny ip from 169.254.0.0/16 to any in recv em0

pipe 1 config bw 10Mbit/s queue 60 gred 0.002/10/30/0.1
queue 1 config pipe 1 queue 60 mask src-ip 0xffffffff gred 0.002/10/30/0.1

pipe 2 config bw 10Mbit/s queue 60 gred 0.002/10/30/0.1
queue 2 config pipe 2 queue 60 mask dst-ip 0xffffffff gred 0.002/10/30/0.1

nat 1 config log if em0 reset same_ports deny_in
redirect_port tcp 1.2.3.4:6881 6881
redirect_port udp 1.2.3.4:4444 4444
redirect_port tcp 192.168.1.24:25 25

nat 2 config log same_ports redirect_addr 192.168.1.36 1.2.3.5

add 10130 skipto 10190 ip from 192.168.1.36 to any out xmit em0
add 10140 skipto 10210 ip from any to 1.2.3.5 in recv em0
add 10150 queue 1 ip from any to any out xmit em0
add 10160 nat 1 ip from any to any via em0
add 10170 queue 2 ip from any to any in recv em0
add 10180 allow all from any to any
add 10190 queue 1 ip from any to any out xmit em0
add 10200 nat 2 ip from 192.168.1.36 to any out xmit em0
add 10210 nat 2 ip from any to 1.2.3.5 in recv em0
add 10220 queue 2 ip from any to any in recv em0
add 10230 allow all from any to any
add 65534 deny all from any to any


Детальное описание прохождения трафика через фаервол в данной конфигурации:

Настройки параметров queue mask 0xffffffff указывают на то, что динамические очереди будут создаваться на основании разницы любого бита из IP адресов отправителя или получателя. Скорость исходящего и входящего трафика будет ограничеваться под 10 Mbit/s.

Для рутера:

Трафик инициализированный непосредственно от рутера пройдет через цепочку правил ipfw прохода OUT до правила с номером 10150. Трафик попадет в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость выходящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол (так как выключен параметр one_pass) и попадет в правило 10160. После прохождения ната трафик сново вернется в фаервол и дойдя до правила 10180 будет выведен из фаервола и уйдет во внешнюю сеть.

Ответный трафик пройдет через проход IN фаервола до правила 10160 где попадет в нат и будет демаскирован (в данном случае не сущесвенно) и принят так как в нате уже есть активное соединение. Выйдя из ната трафик попадет в правило 10170 и будет передан в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость входящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол и попадет в правило 10180 где будет разрешен и выйдет из фаервола направившись в сокет программы инициализировавшей соединение.

Если на рутер из интернета придет случайный трафик, то он пройдя через правило 10160 будет отвергнут так как нат запущен с опцией deny_in.

Трафик идущий на порты 6881, 4444 и 25 будет заведен в нат на правиле 10160, потом в Dummynet на правиле 10170, а потом пропущен либо на сам рутер, либо на хост 192.168.1.24 соответственно.

Для IP адресов 192.168.1.36 и 1.2.3.5

Трафик приходящий из интернета на внешний IP адрес 1.2.3.5 должен быть проброшен на 192.168.1.36. Когда такой трафик пойдет через проход IN фаервола, он будет обработан на правиле 10140 которое перенесет обработку на 10210 - трафик будет передан во второй экземпляр ната где произойдет демаскировка (1.2.3.5 в IP адресе получателя будет заменено на 192.168.1.36). После этого трафик вернется в фаервол и через правило 10220 попадет в Dummynet. Выйдя от туда он вернется в фаервол и дойдет до правила 10230 после чего будет выведен из прохода IN фаервола и уйдет в проход OUT где дойдя до правила 1040 будет принят, выйдет из фаервола, и будет отправлен на 192.168.1.36.

Ответный трафик (а так же любой другой исходящий трафик) от 192.168.1.36 пройдет на проходе IN через 1040, потом на проходе OUT через 10130, 10190, 10200 и 10230 после чего уйдет во внешнюю сеть.

Для сети 192.168.1.0/24:

Трафик исходящий из сети 192.168.1.0/24 (например от хоста с IP 192.168.1.24), зайдет через сетевой адаптер fxp0 в проход IN фаервола и дойдет до правила 1040 после чего выйдет из IN и будет разрешен. Трафик зайдет в проход OUT где пройдет по правилам до 10150. Трафик попадет в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость выходящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол (так как выключен параметр one_pass) и попадет в правило 10160. После прохождения ната трафик сново вернется в фаервол и дойдя до правила 10180 будет выведен из фаервола и уйдет во внешнюю сеть.

Ответный трафик пройдет через проход IN фаервола до правила 10160 где попадет в нат, будет демаскирован и принят так как в нате уже есть активное соединение. Выйдя из ната трафик попадет в правило 10170 и будет передан в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость входящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол и попадет в правило 10180 где будет разрешен и выйдет из прохода IN фаервола. Трафик попадет в проход OUT фаервола где дойдет до правила 1040, после чего выйдет из IN и будет передан в сеть 192.168.1.0/24 через адаптер fxp0.

Если на рутер из интернета придет случайный трафик, то он пройдя через правило 10160 будет отвергнут так как нат запущен с опцией deny_in.


Пример 3

В локальной сети из примера 2 появилась машина 192.168.1.80 на которой работает web сервер. Кроме того появился еще один компьютер с FreeBSD у которого есть два сетевых интерфейса (em1 со стороны интернета, и fxp1 со стороны локальной сети). Адаптер fxp1 подключен в ту же сеть и имеет IP 192.168.1.254, а адаптер em1 подключен ко второму провайдеру и имеет IP 5.6.7.8.

Задача: не меняя настройки фаервола у имеющегося рутера 192.168.1.1 и TCP/IP настройки машины 192.168.1.80, обеспечить доступ к веб серверу работающему на 192.168.1.80 через подключение 5.6.7.8 используя два экземпляра ната.

/etc/rc.conf
gateway_enable="YES"
ifconfig_em1="inet 5.6.7.8 netmask 255.255.255.0 -rxcsum"
ifconfig_fxp1="inet 192.168.1.254 netmask 255.255.255.0"
defaultrouter="5.6.7.254"
firewall_enable="YES"
firewall_type="/etc/firewall"


/etc/sysctl.conf
net.inet.ip.fw.one_pass=1


/etc/firewallnat 2 config log if fxp1 same_ports reset deny_in

add 1040 nat 2 ip from any to any via fxp1

add 1050 deny ip from any to 192.168.0.0/16 in recv em1
add 1060 deny ip from 192.168.0.0/16 to any in recv em1
add 1070 deny ip from any to 172.16.0.0/12 in recv em1
add 1080 deny ip from 172.16.0.0/12 to any in recv em1
add 1090 deny ip from any to 10.0.0.0/8 in recv em1
add 10100 deny ip from 10.0.0.0/8 to any in recv em1
add 10110 deny ip from any to 169.254.0.0/16 in recv em1
add 10120 deny ip from 169.254.0.0/16 to any in recv em1

nat 1 config log if em1 reset same_ports deny_in
redirect_port tcp 192.168.1.80:80 80

add 10130 nat 1 ip from any to any via em1
add 65534 deny all from any to any


Детальное описание прохождения трафика через фаервол в данной конфигурации:

Для рутера:

Трафик инициализированный непосредственно от рутера пройдет через цепочку правил ipfw прохода OUT до правила с номером 10130 где попадет в нат. После прохождения ната трафик выйдет из фаервола и будет передан в интернет.

Ответный трафик пройдет через цепочку правил прохода IN фаервола до правила 10130 где попав в нат будет демаскирован (в данном случае не существенно) и выйдет из фаервола попав в сокет программы инициировавшей соединение.

Если на рутер из интернета придет случайный трафик, то он пройдя через правило 10130 будет отвергнут так как нат запущен с опцией deny_in.

Для 192.168.1.80 и 5.6.7.8:

Трафик приходящий из интернета на 5.6.7.8:80 пройдет через цепочку правил ipfw прохода IN до правила с номером 10130 где попадет в первый экземпляр ната и будет демаскирован на основании статического правила (внешний_IP <-> 5.6.7.8 будет заменено на внешний_IP <-> 192.168.1.80). После этого трафик попадет в проход OUT фаервола и дойдет до правила 1040 где попадет во второй экземпляр ната. Трафик будет маскирован (внешний_IP <-> 192.168.1.80 будет заменено на 192.168.1.254 <-> 192.168.1.80). После этого трафик выйдет из ната и будет передан на 192.168.1.80:80

Цепочка преобразования адресов при проходе была следующей:
отправитель: получатель:
внешний_IP 5.6.7.8
внешний_IP 192.168.1.80
192.168.1.254 192.168.1.80


Ответный трафик уходящий от 192.168.1.80:80 будет передан на 192.168.1.254 где попав в фаервол пройдет до правила 1040. Попав во второй экземпояр ната трафик будет демаскирован (192.168.1.80 <-> 192.168.1.254 будет заменено на 192.168.1.80 <-> внешний_IP) и выйдя из прохода IN перейдет в проход OUT где пройдя через правила дойдет до 10130. Попав в первый экземпляр ната трафик будет повторно маскирован (192.168.1.80 <-> внешний_IP будет заменено на 5.6.7.8 <-> внешний_IP) после чего трафик уйдет в интернет.

Цепочка преобразования адресов при проходе была следующей:
отправитель: получатель:
192.168.1.80 192.168.1.254
192.168.1.80 внешний_IP
5.6.7.8 внешний_IP

Если на рутер из интернета придет случайный трафик, то он пройдя через правило 10130 будет отвергнут так как нат запущен с опцией deny_in.


Пример 4

Есть два подключения к двум независимым провайдерам и две локальных сети. На рутере имеется четыре сетевых адаптера каждый из которых подключен к своему провайдеру или к своей локальной сети. Адаптеры em0 и em1 подключены к провайдерам ISP1 (IP 11.22.33.44) и ISP2 (IP 55.66.77.88). Адаптеры fxp0 и fxp1 подключены к локальным сетям LAN1 (IP 192.168.1.1) и LAN2 (IP 10.1.1.1). Ядро рутера собранно с поддержкой двух таблиц маршрутизации (options ROUTETABLES=2).

Задача: обеспечить выход в интернет первой сети через первого провайдера, а второй сети через второго. Все это через нат и используя шейпер Dummynet обеспечить честное распределение пропускной способности между клиентами в локальных сетях.

/etc/rc.conf
gateway_enable="YES"
ifconfig_em0="inet 11.22.33.44 netmask 255.255.255.0 -rxcsum"
ifconfig_em1="inet 55.66.77.88 netmask 255.255.255.0 -rxcsum"
ifconfig_fxp0="inet 192.168.1.1 netmask 255.255.255.0"
ifconfig_fxp1="inet 10.1.1.1 netmask 255.255.255.0"

defaultrouter="11.22.33.254"

setfib1_enable="YES"
setfib1_defaultroute="55.66.77.254"

firewall_enable="YES"
firewall_type="/etc/firewall"


/etc/sysctl.conf
net.inet.ip.fw.one_pass=0


/etc/firewalladd 1040 setfib 0 ip from any to any in recv fxp0
add 1050 setfib 1 ip from any to any in recv fxp1

add 1060 allow ip from any to any via fxp0
add 1070 allow ip from any to any via fxp1

add 1080 deny ip from any to 192.168.0.0/16 in recv em0
add 1081 deny ip from any to 192.168.0.0/16 in recv em1
add 1090 deny ip from 192.168.0.0/16 to any in recv em0
add 1091 deny ip from 192.168.0.0/16 to any in recv em1
add 10100 deny ip from any to 172.16.0.0/12 in recv em0
add 10101 deny ip from any to 172.16.0.0/12 in recv em1
add 10110 deny ip from 172.16.0.0/12 to any in recv em0
add 10111 deny ip from 172.16.0.0/12 to any in recv em1
add 10120 deny ip from any to 10.0.0.0/8 in recv em0
add 10121 deny ip from any to 10.0.0.0/8 in recv em1
add 10130 deny ip from 10.0.0.0/8 to any in recv em0
add 10131 deny ip from 10.0.0.0/8 to any in recv em1
add 10140 deny ip from any to 169.254.0.0/16 in recv em0
add 10141 deny ip from any to 169.254.0.0/16 in recv em1
add 10150 deny ip from 169.254.0.0/16 to any in recv em0
add 10151 deny ip from 169.254.0.0/16 to any in recv em1

pipe 1 config bw 10Mbit/s queue 60 gred 0.002/10/30/0.1
queue 1 config pipe 1 queue 60 mask src-ip 0xffffffff gred 0.002/10/30/0.1

pipe 2 config bw 10Mbit/s queue 60 gred 0.002/10/30/0.1
queue 2 config pipe 2 queue 60 mask dst-ip 0xffffffff gred 0.002/10/30/0.1

pipe 3 config bw 20Mbit/s queue 60 gred 0.002/10/30/0.1
queue 3 config pipe 3 queue 60 mask src-ip 0xffffffff gred 0.002/10/30/0.1

pipe 4 config bw 35Mbit/s queue 60 gred 0.002/10/30/0.1
queue 4 config pipe 4 queue 60 mask dst-ip 0xffffffff gred 0.002/10/30/0.1

nat 1 config log if em0 same_ports reset deny_in
nat 2 config log if em1 same_ports reset deny_in

add 10160 queue 1 ip from any to any out xmit em0
add 10170 nat 1 ip from any to any via em0
add 10180 queue 2 ip from any to any in recv em0
add 10190 queue 3 ip from any to any out xmit em1
add 10200 nat 2 ip from any to any via em1
add 10210 queue 4 ip from any to any in recv em1
add 10220 allow all from any to any
add 65534 deny all from any to any


/usr/local/etc/rc.d/setfib1
#!/bin/sh
# PROVIDE: SETFIB1
# REQUIRE: NETWORKING
# BEFORE: DAEMON
#
# Add the following lines to /etc/rc.conf to enable setfib -1 at startup
# setfib1 (bool): Set to "NO" by default.
# Set it to "YES" to enable setfib1
# setfib1_defaultroute (str): Set to "" by default
# Set it to ip address of default gateway for use in fib 1

. /etc/rc.subr

name="setfib1"
rcvar=`set_rcvar`

load_rc_config $name

[ -z "$setfib1_enable" ] && setfib1_enable="NO"
[ -z "$setfib1_defaultroute" ] && setfib1_defaultroute=""

start_cmd="${name}_start"
stop_cmd="${name}_stop"

setfib1_start()
{
if [ ${setfib1_defaultroute} ]
then
setfib 1 route delete default
setfib 1 route add default ${setfib1_defaultroute}
else
echo "Can not set default route for fib 1!"
fi
}

setfib1_stop()
{
setfib 1 route delete default
}
run_rc_command "$1"


Напомининие.
Скрипт setfib1 необходимо сделать исполняемым. Для этого после его создания надо выполнить:
chmod 755 /usr/local/etc/rc.d/setfib1


Детальное описание прохождения трафика через фаервол в данной конфигурации:

Для рутера:

Трафик инициализированный непосредственно от рутера пройдет через цепочку правил ipfw прохода OUT до правила с номером 10160. Трафик попадет в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость выходящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол (так как выключен параметр one_pass) и попадет в правило 10170. После прохождения ната трафик сново вернется в фаервол и дойдя до правила 10220 будет выведен их фаервола и уйдет во внешнюю сеть через 11.22.33.254..

Ответный трафик пройдет через проход IN фаервола до правила 10170 где попадет в нат и будет демаскирован (в данном случае не сущесвенно) и принят так как в нате уже есть активное соединение. Выйдя из ната трафик попадет в правило 10180 и будет передан в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость входящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол и попадет в правило 10220 где будет разрешен и выйдет из фаервола направившись в сокет программы инициализировавшей соединение.

Если на рутер из интернета придет случайный трафик, то он пройдя через правило 10170 будет отвергнут так как нат запущен с опцией deny_in.

Для 192.168.1.0/24:

Трафик исходящий из сети 192.168.1.0/24 (например от хоста с IP 192.168.1.24), зайдет через сетевой адаптер fxp0 в проход IN фаервола и дойдет до правила 1040 где ему будет назначенна таблица маршрутизации 0 (таблица по-умолчанию). Далее трафик пройдет до правила 1060 после чего выйдет из IN и будет разрешен. Трафик зайдет в проход OUT где пройдет по правилам до 10160. Трафик попадет в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость выходящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол (так как выключен параметр one_pass) и попадет в правило 10170. После прохождения ната трафик сново вернется в фаервол и дойдя до правила 10220 будет выведен их фаервола и уйдет во внешнюю сеть через 11.22.33.254.

Ответный трафик пройдет через проход IN фаервола до правила 10170 где попадет в нат, будет демаскирован и принят так как в нате уже есть активное соединение. Выйдя из ната трафик попадет в правило 10180 и будет передан в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость входящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол и попадет в правило 10220 где будет разрешен, и выйдет из прохода IN фаервола. Трафик попадет в проход OUT фаервола где дойдет до правила 1060, после чего выйдет из IN и будет передан в сеть 192.168.1.0/24 через адаптер fxp0.

Если на рутер из интернета придет случайный трафик, то он пройдя через правило 10170 будет отвергнут так как нат запущен с опцией deny_in.

Для 10.1.1.0/24:

Трафик исходящий из сети 10.1.1.0/24 (например от хоста с IP 10.1.1.24), зайдет через сетевой адаптер fxp1 в проход IN фаервола и дойдет до правила 1050 где ему будет назначенна таблица маршрутизации 1 (вторая таблица имеющая default gateway 55.66.77.254). Теперь для трафика в качестве сетевого адаптера через который он должен быть передан наружу, будет установлено em1. Далее трафик пройдет до правила 1070 после чего выйдет из IN и будет разрешен. Трафик зайдет в проход OUT где пройдет по правилам до 10190. Трафик попадет в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость выходящего трафика будет ограничена в 20Mbit/s. После выхода из Dummynet трафик вернется в фаервол (так как выключен параметр one_pass) и попадет в правило 10200. После прохождения ната трафик сново вернется в фаервол и дойдя до правила 10220 будет выведен из фаервола и уйдет во внешнюю сеть через 55.66.77.254.

Ответный трафик пройдет через проход IN фаервола до правила 10200 где попадет в нат, будет демаскирован и принят так как в нате уже есть активное соединение. Выйдя из ната трафик попадет в правило 10210 и будет передан в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость входящего трафика будет ограничена в 35Mbit/s. После выхода из Dummynet трафик вернется в фаервол и попадет в правило 10220 где будет разрешен, и выйдет из прохода IN фаервола. Трафик попадет в проход OUT фаервола где дойдет до правила 1070, после чего выйдет из IN и будет передан в сеть 10.1.1.0/24 через адаптер fxp1.

Если на рутер из интернета придет случайный трафик, то он пройдя через правило 10200 будет отвергнут так как нат запущен с опцией deny_in.


Пример 5

Есть два подключения к двум независимым провайдерам и одна локальная сеть. На рутере имеется три сетевых адаптера два из которых подключены провайдерам, а третий к своей локальной сети. Адаптеры em0 и em1 подключены к провайдерам ISP1 (IP 11.22.33.44) и ISP2 (IP 55.66.77.88). Адаптер fxp0 подключен к локальной сети LAN1 (IP 192.168.1.1). Ядро рутера собранно с поддержкой двух таблиц маршрутизации (options ROUTETABLES=2).

Задача: обеспечить выход в интернет локальной сети через обоих провайдеров используя балансировку трафика между подключениями. Все это через нат и используя шейпер Dummynet обеспечить честное распределение пропускной способности между клиентами в локальной сети.

/etc/rc.conf
gateway_enable="YES"
ifconfig_em0="inet 11.22.33.44 netmask 255.255.255.0 -rxcsum"
ifconfig_em1="inet 55.66.77.88 netmask 255.255.255.0 -rxcsum"
ifconfig_fxp0="inet 192.168.1.1 netmask 255.255.255.0"

defaultrouter="11.22.33.254"

setfib1_enable="YES"
setfib1_defaultroute="55.66.77.254"

firewall_enable="YES"
firewall_type="/etc/firewall"


/etc/sysctl.conf
net.inet.ip.fw.one_pass=0


/etc/firewalladd 1010 prob 0.5 skipto 1060 ip from any to any in recv fxp0

add 1040 setfib 0 ip from any to any in recv fxp0 keep-state
add 1050 allow ip from any to any via fxp0

add 1060 setfib 1 ip from any to any in recv fxp0 keep-state
add 1070 allow ip from any to any via fxp0
add 1080 deny ip from any to 192.168.0.0/16 in recv em0
add 1081 deny ip from any to 192.168.0.0/16 in recv em1
add 1090 deny ip from 192.168.0.0/16 to any in recv em0
add 1091 deny ip from 192.168.0.0/16 to any in recv em1
add 10100 deny ip from any to 172.16.0.0/12 in recv em0
add 10101 deny ip from any to 172.16.0.0/12 in recv em1
add 10110 deny ip from 172.16.0.0/12 to any in recv em0
add 10111 deny ip from 172.16.0.0/12 to any in recv em1
add 10120 deny ip from any to 10.0.0.0/8 in recv em0
add 10121 deny ip from any to 10.0.0.0/8 in recv em1
add 10130 deny ip from 10.0.0.0/8 to any in recv em0
add 10131 deny ip from 10.0.0.0/8 to any in recv em1
add 10140 deny ip from any to 169.254.0.0/16 in recv em0
add 10141 deny ip from any to 169.254.0.0/16 in recv em1
add 10150 deny ip from 169.254.0.0/16 to any in recv em0
add 10151 deny ip from 169.254.0.0/16 to any in recv em1

pipe 1 config bw 10Mbit/s queue 60 gred 0.002/10/30/0.1
queue 1 config pipe 1 queue 60 mask src-ip 0xffffffff gred 0.002/10/30/0.1

pipe 2 config bw 10Mbit/s queue 60 gred 0.002/10/30/0.1
queue 2 config pipe 2 queue 60 mask dst-ip 0xffffffff gred 0.002/10/30/0.1

pipe 3 config bw 20Mbit/s queue 60 gred 0.002/10/30/0.1
queue 3 config pipe 3 queue 60 mask src-ip 0xffffffff gred 0.002/10/30/0.1

pipe 4 config bw 35Mbit/s queue 60 gred 0.002/10/30/0.1
queue 4 config pipe 4 queue 60 mask dst-ip 0xffffffff gred 0.002/10/30/0.1

nat 1 config log if em0 same_ports reset deny_in
redirect_port tcp 192.168.1.5:25 25
nat 2 config log if em1 same_ports reset deny_in

add 10160 queue 1 ip from any to any out xmit em0
add 10170 nat 1 ip from any to any via em0
add 10180 queue 2 ip from any to any in recv em0
add 10190 queue 3 ip from any to any out xmit em1
add 10200 nat 2 ip from any to any via em1
add 10210 queue 4 ip from any to any in recv em1
add 10220 allow all from any to any
add 65534 deny all from any to any


/usr/local/etc/rc.d/setfib1
#!/bin/sh
# PROVIDE: SETFIB1
# REQUIRE: NETWORKING
# BEFORE: DAEMON
#
# Add the following lines to /etc/rc.conf to enable setfib -1 at startup
# setfib1 (bool): Set to "NO" by default.
# Set it to "YES" to enable setfib1
# setfib1_defaultroute (str): Set to "" by default
# Set it to ip address of default gateway for use in fib 1

. /etc/rc.subr

name="setfib1"
rcvar=`set_rcvar`

load_rc_config $name

[ -z "$setfib1_enable" ] && setfib1_enable="NO"
[ -z "$setfib1_defaultroute" ] && setfib1_defaultroute=""

start_cmd="${name}_start"
stop_cmd="${name}_stop"

setfib1_start()
{
if [ ${setfib1_defaultroute} ]
then
setfib 1 route delete default
setfib 1 route add default ${setfib1_defaultroute}
else
echo "Can not set default route for fib 1!"
fi
}

setfib1_stop()
{
setfib 1 route delete default
}
run_rc_command "$1"


Напомининие.
Скрипт setfib1 необходимо сделать исполняемым. Для этого после его создания надо выполнить:
chmod 755 /usr/local/etc/rc.d/setfib1


Детальное описание прохождения трафика через фаервол в данной конфигурации:

Для рутера:

Трафик инициализированный непосредственно от рутера пройдет через цепочку правил ipfw прохода OUT до правила с номером 10160. Трафик попадет в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость выходящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол (так как выключен параметр one_pass) и попадет в правило 10170. После прохождения ната трафик сново вернется в фаервол и дойдя до правила 10220 будет выведен их фаервола и уйдет во внешнюю сеть через 11.22.33.254..

Ответный трафик пройдет через проход IN фаервола до правила 10170 где попадет в нат и будет демаскирован (в данном случае не сущесвенно) и принят так как в нате уже есть активное соединение. Выйдя из ната трафик попадет в правило 10180 и будет передан в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость входящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол и попадет в правило 10220 где будет разрешен и выйдет из фаервола направившись в сокет программы инициализировавшей соединение.

Если на рутер из интернета придет случайный трафик, то он пройдя через правило 10170 будет отвергнут так как нат запущен с опцией deny_in.

Для 192.168.1.0/24:

Трафик исходящий из сети 192.168.1.0/24 (например от хоста с IP 192.168.1.24), зайдет через сетевой адаптер fxp0 в проход IN фаервола и дойдет до правила 1010 на котором с вероятностью 1/2 будет либо произведен переход на правило 1060, либо переход к следующему по списку правилу 1040. Правила 1040 и 1060 имеют директиву keep-state (это динамические правила). Допустим, что 192.168.1.24 инициировал две сессии - первую на адрес IP 3.3.3.3:80, а вторую на адрес IP 9.9.9.9:22.

Допустим, что первая сессия на правиле 1010 не была обработана. В таком случае трафик адресованный на IP 3.3.3.3 пройдет до правила 1040 где ему будет назначена таблица маршрутизации по-умолчанию, а кроме того в таблицу динамических правил будет занесена информация о сессии 192.168.1.24:12345 <-> 3.3.3.3:80 и о действии родительского правила (setfib 0) которое надо применять к трафику в рамках сессии. Далее, дойдя до правила 1050 трафик будет принят, выйдет из прохода IN и будет разрешен.

Допустим, что вторая сессия на правиле 1010 была обработана. В таком случае трафик адресованный на IP 9.9.9.9 пройдет до правила 1060 где ему будет назначена вторая таблица маршрутизации, а кроме того в таблицу динамических правил будет занесена информация о сессии 192.168.1.24:56789 <-> 9.9.9.9:22 и о действии родительского правила (setfib 1) которое надо применять к трафику в рамках сессии. Далее, дойдя до правила 1070 трафик будет принят, выйдет из прохода IN и будет разрешен.

Трафик первой сессии зайдет в проход OUT где пройдет по правилам до 10160. Трафик попадет в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость выходящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол (так как выключен параметр one_pass) и попадет в правило 10170. После прохождения ната трафик сново вернется в фаервол и дойдя до правила 10220 будет выведен их фаервола и уйдет во внешнюю сеть через 11.22.33.254.

Трафик второй сессии зайдет в проход OUT где пройдет по правилам до 10190. Трафик попадет в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость выходящего трафика будет ограничена в 20Mbit/s. После выхода из Dummynet трафик вернется в фаервол (так как выключен параметр one_pass) и попадет в правило 10200. После прохождения ната трафик сново вернется в фаервол и дойдя до правила 10220 будет выведен их фаервола и уйдет во внешнюю сеть через 55.66.77.254.

Ответный трафик в рамках первой сессии пройдет через проход IN фаервола до правила 10170 где попадет в нат, будет демаскирован и принят так как в нате уже есть активное соединение. Выйдя из ната трафик попадет в правило 10180 и будет передан в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость входящего трафика будет ограничена в 10Mbit/s. После выхода из Dummynet трафик вернется в фаервол и попадет в правило 10220 где будет разрешен, и выйдет из прохода IN фаервола. Трафик попадет в проход OUT фаервола и дойдет до правила 1040 - там он будет обработан на неявном check-state где к нему будет применено действие родительского правила (setfib 0). Далее трафик перейдет на правило 1050 после чего выйдет из IN и будет передан в сеть 192.168.1.0/24 через адаптер fxp0.

Если на рутер из интернета придет случайный трафик, то он пройдя через правило 10170 будет отвергнут так как нат запущен с опцией deny_in.

Ответный трафик в рамках второй сессии пройдет через проход IN фаервола до правила 10200 где попадет в нат, будет демаскирован и принят так как в нате уже есть активное соединение. Выйдя из ната трафик попадет в правило 10210 и будет передан в Dummynet - зайдет в динамически созданную очередь и трубу в результате чего скорость входящего трафика будет ограничена в 35Mbit/s. После выхода из Dummynet трафик вернется в фаервол и попадет в правило 10220 где будет разрешен, и выйдет из прохода IN фаервола. Трафик попадет в проход OUT фаервола и дойдет до правила 1040 - там он будет обработан на неявном check-state где к нему будет применено действие родительского правила (setfib 1). Далее трафик перейдет на правило 1070 после чего выйдет из IN и будет передан в сеть 192.168.1.0/24 через адаптер fxp0.

Если на рутер из интернета придет случайный трафик, то он пройдя через правило 10200 будет отвергнут так как нат запущен с опцией deny_in.

Если при следующем обмене трафиком в рамках первой сессии (192.168.1.24:12345 <-> 3.3.3.3:80) исходящий от 192.168.1.24 трафик на правиле 1010 будет передан не на 1040, а на 1060 то сессия не поломается так как при попадании на 1060 к трафику сессии на неявном check-state будет применено действие его родительского правила (setfib 0).

Если при следующем обмене трафиком в рамках второй сессии (192.168.1.24:56789 <-> 9.9.9.9:22) исходящий от 192.168.1.24 трафик на правиле 1010 будет передан не на 1060, а на 1040 то сессия не поломается так как при попадании на 1040 к трафику сессии на неявном check-state будет применено действие его родительского правила (setfib 1).

Замечания:
Пример абсолютно не учитавает распределение DNS/UDP трафика между провайдерами. Я предрологаю, что на самом нат-рутере запущен локальный DNS-кеш сервер (Unbound, BIND, etc...) - в таком случае клиенты запрашивают разрешение имен у него, а он сам выходит в интернет только по одному каналу... Иначе DNS запросы к серверам одного провайдера могут уходить через сеть другого провайдера и не разрешаться.

Подобная "балансировка" трафика не учитывает такие факторы как выход из строя одного из провайдеров. Если есть необходимость обрабатывать еще и это, то надо делать систему мониторинга (пинговалку?) обоих провайдеров, и в случае выпадения одного из них, перестраевать правила фаервола на задействование только одного канала как в примере 1. Подобное поведение можно удобно реализовать используя функцию наборов правил (set) ipfw.

Так как таблица динамических правил не хранит их у себя вечно ;) то при неактивности сессии в течение какого-то времени, и после ее возобновления позже, эта сессиия может получить другую fib и уйти к получателю с другого IP. Если возникнут проблемы такого плана с какими-нибудь конкретными IP адресами (например с однокамерника... то есть с одноклассниками), то для этих конкретных IP можно создать отдельную таблицу и повесить ее обработку на отдельное правило жестко назначающее таким IP одну единственную таблицу маршрутизации.

В данном примере "пробрасывать порты" можно только с того внешнего IP адреса который имеет default route в таблице маршрутизации по-умолчанию (fib 0). В данном случае это сетевой адаптер em0 с IP 11.22.33.44 - в экземпляре его ната выставлен проброс соединений приходящих на 25 порт на внутреннюю машину 192.168.1.5. Чтобы иметь возможность делать это с обеих внешних IP адресов, надо добавить в пример еще и такие правила:
add 1011 skipto 1040 ip from any to any out xmit fxp0 tagged 1
add 1012 skipto 1060 ip from any to any out xmit fxp0 tagged 2
add 10171 skipto 10180 tag 1 ip from any to any in recv em0
add 10201 skipto 10210 tag 2 ip from any to any in recv em1


Пример 6

Конфигурация практически полностью повторяет Пример 1: рутер с двумя сетевыми адаптерами подключенными в локальную сеть и в интернет. Адрес в локальной сети 192.168.1.1 (сетевой адаптер fxp0), адрес в интернете 1.2.3.4 (сетевой адаптер em0).

Задача: "раздавать интернет" для локальной сети, обеспечить проброс портов для внутренних адресов, разрешить прохождение трафика на отдельные порты самого рутера, а главное - ввести жесткий лимит на количество одновременных исходящих TCP соединений для всего трафика уходящего в интернет (максимум 20 одновременных соединений с каждого внутреннего IP адреса).

Конфигурация:

/etc/rc.conf
gateway_enable="YES"
ifconfig_em0="inet 1.2.3.4 netmask 255.255.255.0 -rxcsum"
ifconfig_fxp0="inet 192.168.1.1 netmask 255.255.255.0"
defaultrouter="1.2.3.254"
firewall_enable="YES"
firewall_type="/etc/firewall"


/etc/sysctl.conf
net.inet.ip.fw.one_pass=1


/etc/firewalladd 1040 allow ip from any to any via fxp0

add 1050 deny ip from any to 192.168.0.0/16 in recv em0
add 1060 deny ip from 192.168.0.0/16 to any in recv em0
add 1070 deny ip from any to 172.16.0.0/12 in recv em0
add 1080 deny ip from 172.16.0.0/12 to any in recv em0
add 1090 deny ip from any to 10.0.0.0/8 in recv em0
add 10100 deny ip from 10.0.0.0/8 to any in recv em0
add 10110 deny ip from any to 169.254.0.0/16 in recv em0
add 10120 deny ip from 169.254.0.0/16 to any in recv em0

nat 1 config log if em0 reset same_ports deny_in
redirect_port tcp 1.2.3.4:6881 6881
redirect_port udp 1.2.3.4:4444 4444
redirect_port tcp 192.168.1.24:25 25

add 10130 nat 1 tcp from any to any out xmit em0 limit src-addr 20
add 10140 nat 1 ip from any to any out xmit em0
add 10150 nat 1 ip from any to any in recv em0
add 65534 deny all from any to any


Детальное описание прохождения трафика через фаервол в данной конфигурации:

Для рутера:
Исходящий TCP трафик инициализированный непосредственно от рутера пройдет через цепочку правил ipfw прохода OUT до правила с номером 10130. В следствии наличия директивы limit, трафик попадет в функцию обработки динамических (keep-state) правил - на неявный обработчик check-state. Будет проверено: нет ли соответсвия с какой-либо ранее инициализированной сессией (это окончется неудачей так как в таблице динамических правил, конечно же, не будет никаких совпадений). После неудачной проверки на активную сессию обработка вернется на тело правила 10130 где будет установлено, что пакет подпадает под критерий отбора (tcp from any to any out xmit em0). После этого в таблицу динамических правил будет помещена первая запись соответствия локальный_IP:порт <-> удаленный_IP:порт (по правилам в таблице может быть максимум 20 записей с одинаковым значением "локальный_IP") имеющая таймер устанавливающий время ее жизни. После этого трафик выйдет из функции обработки динамических правил и будет передан на поле действия правила 10130 - трафик будет перенаправлен в нат. Выйдя из ната трафик будет передан в интернет так как значение параметра one_pass=1.

Для каждого из последующих 19 TCP соединений инициализированных от рутера обработка будет такой же. Когда таблица динамических правил заполниться, то следующие TCP соединения будут молча отвергаться до тех пор пока в динамической таблице активных соединений не освободиться место.

Любой IP (UDP, ICMP, etc) трафик инициализированный непосредственно от рутера пройдет через цепочку правил ipfw прохода OUT до 10140 где попадет в нат и выйдя из него будет отправлен в интернет. Лимиты на данный трафик не распространяются.


Ответный TCP трафик пройдет через проход IN фаервола до правила 10130. Не смотря на то, что тело правила (tcp from any to any out xmit em0) не имеет совпадения с параметрами трафика, из-за присутвия директивы limit трафик попадет в функцию обработки динамических (keep-state) правил - на неявный обработчик check-state. Будет проверено: нет ли соответсвия с какой-либо из ранее инициализированных сессий, и такое совпадение будет обнаружено. В следствии этого таймер устанавливающий время жизни динамической записи будет сброшен. После этого трафик выйдет из функции обработки динамических правил и будет передан на поле действия правила 10130 - трафик будет перенаправлен в нат. Выйдя из ната трафик будет передан в сокет процесса создавшего соединение.

Любой ответный IP (UDP, ICMP, etc) трафик пройдет через проход IN фаервола до правила 10150 где попадет в нат. Выйдя из ната трафик будет передан в локальную систему.

Трафик приходящий на порты 6881, 4444 и 25 пройдет до правила 10150 и будет принят.

Любой случайный трафик пришедший из интернета (как TCP так и IP) пройдет до правила 10150 и будет отброшен так как нат запущен с директивой deny_in


Для 192.168.1.0/24:

Исходящий TCP трафик инициализированный от хостов из сети 192.168.1.0/24 (например от 192.168.1.56) пройдет через цепочку правил ipfw прохода IN до правила 1040 после чего выйдет из фаервола и будет передан в проход OUT. Зайдя в OUT трафик дойдет до правила с номером 10130. В следствии наличия директивы limit, трафик попадет в функцию обработки динамических (keep-state) правил - на неявный обработчик check-state. Будет проверено: нет ли соответсвия с какой-либо ранее инициализированной сессией (это окончется неудачей так как в таблице динамических правил, конечно же, не будет никаких совпадений). После неудачной проверки на активную сессию обработка вернется на тело правила 10130 где будет установлено, что пакет подпадает под критерий отбора (tcp from any to any out xmit em0). После этого в таблицу динамических правил будет помещена первая запись соответствия 192.168.1.56:порт <-> удаленный_IP:порт (по правилам в таблице может быть максимум 20 записей с одинаковым значением 192.168.1.56) имеющая таймер устанавливающий время ее жизни. После этого трафик выйдет из функции обработки динамических правил и будет передан на поле действия правила 10130 - трафик будет перенаправлен в нат. Выйдя из ната трафик будет передан в интернет так как значение параметра one_pass=1.

Для каждого из последующих 19 TCP соединений инициализированный от 192.168.1.56 обработка будет такой же. Когда таблица динамических правил заполниться, то следующие TCP соединения будут молча отвергаться до тех пор пока в динамической таблице активных соединений не освободиться место.

Любой IP (UDP, ICMP, etc) трафик инициализированный непосредственно от 192.168.1.56 пройдет через цепочку правил ipfw прохода IN до 1040, а затем в проходе OUT до 10140 где попадет в нат и выйдя из него будет отправлен в интернет. Лимиты на данный трафик не распространяются.


Ответный TCP трафик пройдет через проход IN фаервола до правила 10130. Не смотря на то, что тело правила (tcp from any to any out xmit em0) не имеет совпадения с параметрами трафика, из-за присутвия директивы limit трафик попадет в функцию обработки динамических (keep-state) правил - на неявный обработчик check-state. Будет проверено: нет ли соответсвия с какой-либо из ранее инициализированных сессий, но такое совпадение не будет обнаружено так как пришедший из интернета трафик еще не прошел деалиасинг в нате. В следствии этого таймер устанавливающий время жизни динамической записи не будет сброшен. Трафик пройдет до правила 10150 на котором будет перенаправлен в нат. Выйдя из ната трафик покинет проход IN (так как one_pass=1) и перейдет в проход OUT где на правиле 1040 будет разрешен и уйдет в сеть 192.168.1.0/24.

Любой ответный IP (UDP, ICMP, etc) трафик пройдет через проход IN фаервола до правила 10150 где попадет в нат. Выйдя из ната трафик будет передан в проход OUT (так как one_pass=1), где на правиле 1040 будет разрешен и уйдет в сеть 192.168.1.0/24.

Любой случайный трафик пришедший из интернета (как TCP так и IP) пройдет до правила 10150 и будет отброшен так как нат запущен с директивой deny_in

Замечание:
Схема не очень красивая из-за того, что ответный трафик не обноляет счетчики на динамических правилах. Позже перепишу пример чтобы заставить приходящий трафик делать это.

Пример 7

Подключение к провайдеру имеет выделенный диапазон IP адресов - для использования доступно пять адресов. На рутере установлено два сетевых адаптера. Адаптер em0 подключен к провайдеру и имеет IP адреса в диапазоне от 11.22.33.[1-5]. Адаптер fxp0 подключен к локальной сети (IP 192.168.1.1).

Задача: обеспечить выход в интернет локальной сети задействуя все имеющиеся внешние адреса (использовать пул адресов). Все это через нат и используя шейпер Dummynet обеспечить честное распределение пропускной способности между клиентами в локальной сети.


/etc/rc.conf
gateway_enable="YES"

ifconfig_em0="inet 11.22.33.1 netmask 255.255.255.0 -rxcsum"
ifconfig_em0_alias0="inet 11.22.33.2 netmask 255.255.255.255"
ifconfig_em0_alias1="inet 11.22.33.3 netmask 255.255.255.255"
ifconfig_em0_alias2="inet 11.22.33.4 netmask 255.255.255.255"
ifconfig_em0_alias3="inet 11.22.33.5 netmask 255.255.255.255"

ifconfig_fxp0="inet 192.168.1.1 netmask 255.255.255.0"

defaultrouter="11.22.33.6"

firewall_enable="YES"
firewall_type="/etc/firewall"


/etc/sysctl.conf
net.inet.ip.fw.one_pass=0


/etc/firewall
add 1011 prob 0.2 skipto 1061 ip from any to any in recv fxp0
add 1012 prob 0.25 skipto 1063 ip from any to any in recv fxp0
add 1013 prob 0.333 skipto 1065 ip from any to any in recv fxp0
add 1014 prob 0.5 skipto 1067 ip from any to any in recv fxp0
add 1015 prob 1 skipto 1069 ip from any to any in recv fxp0
add 1061 skipto 1070 tag 1 ip from any to any via fxp0 keep-state
add 1063 skipto 1070 tag 2 ip from any to any via fxp0 keep-state
add 1065 skipto 1070 tag 3 ip from any to any via fxp0 keep-state
add 1067 skipto 1070 tag 4 ip from any to any via fxp0 keep-state
add 1069 skipto 1070 tag 5 ip from any to any via fxp0 keep-state
add 1070 allow ip from any to any via fxp
add 1080 deny ip from any to 192.168.0.0/16 in recv em0
add 1090 deny ip from 192.168.0.0/16 to any in recv em0
add 2000 deny ip from any to 172.16.0.0/12 in recv em0
add 2010 deny ip from 172.16.0.0/12 to any in recv em0
add 2020 deny ip from any to 10.0.0.0/8 in recv em0
add 2030 deny ip from 10.0.0.0/8 to any in recv em0
add 2040 deny ip from any to 169.254.0.0/16 in recv em0
add 2050 deny ip from 169.254.0.0/16 to any in recv em0


pipe 1 config bw 20Mbit/s queue 60 gred 0.002/10/30/0.1
queue 1 config pipe 1 queue 60 mask src-ip 0xffffffff gred 0.002/10/30/0.1

pipe 2 config bw 35Mbit/s queue 60 gred 0.002/10/30/0.1
queue 2 config pipe 2 queue 60 mask dst-ip 0xffffffff gred 0.002/10/30/0.1


nat 1 config log ip 11.22.33.1 same_ports reset deny_in
redirect_port tcp wsrv:80 80
nat 2 config log ip 11.22.33.2 same_ports reset deny_in
nat 3 config log ip 11.22.33.3 same_ports reset deny_in
nat 4 config log ip 11.22.33.4 same_ports reset deny_in
nat 5 config log ip 11.22.33.5 same_ports reset deny_in


add 10000 queue 1 ip from any to any out xmit em0
add 10190 skipto 10601 tag 1 ip from me to any out xmit em0
add 10201 nat 2 ip from any to any out xmit em0 tagged 2
add 10202 nat 2 ip from any to 11.22.33.2 in recv em0
add 10301 nat 3 ip from any to any out xmit em0 tagged 3
add 10302 nat 3 ip from any to 11.22.33.3 in recv em0
add 10401 nat 4 ip from any to any out xmit em0 tagged 4
add 10402 nat 4 ip from any to 11.22.33.4 in recv em0
add 10501 nat 5 ip from any to any out xmit em0 tagged 5
add 10502 nat 5 ip from any to 11.22.33.5 in recv em0
add 10601 nat 1 ip from any to any out xmit em0 tagged 1
add 10602 nat 1 ip from any to 11.22.33.1 in recv em0
add 20000 queue 2 ip from any to any in recv em0
add 20220 allow all from any to any
add 65534 deny all from any to any


Детальное описание прохождения трафика через фаервол в данной конфигурации:

Для рутера:
Трафик инициированный непосредственно от рутера пройдет через цепочку правил до правила номер 10000. На правиле с номером 10000 трафик будет передан в dummynet - скорость выходящего потока будет ограничена в 20Mbit/s. После выхода из шейпера, трафик пройдет по цепочке правил до 10190 где ему будет присвоен тег 1. После этого трафик перепрыгнет на 10601. На правиле 10601 произойдет совпадение с уточняющим параметром tagged 1, и трафик будет передан в нат. Выйдя из ната трафик продолжит движение по правилам, и дойдя до правила 20220 выйдет из фаервола и уйдет во внешнюю сеть через интерфейс em0.

Ответный трафик принимаемый из внешней сети через интерфейс em0, пройдет по цепочке правил до правила 10602 на котором будет передан в нат. Выйдя из ната трафик продолжит движение по правилам до правила с номером
20000 на котром он будет передан в dummynet - скорость входящего потока будет ограничена в 35Mbit/s. После выхода из шейпера, трафик пройдет по цепочке правил до правила 20220, после чего выйдет из фаервола и будет принят рутером.

Если на рутер из внешней сети, на адрес 11.22.33.1 придет случайный трафик, то он будет отвергнут так как нат запущен с опцией deny_in.

Если на рутер из внешней сети, на адрес 11.22.33.1 придет трафик адресованный для tcp порта 11.22.33.1:80, то он будет принят так как в настройках ната указанна опция форвардинга redirect_port. Адрес назначения будет заменен IP адресом машины с именем wsrv. Трафик пройдет до правила с номером 10602 где попадет в нат. Далее трафик зайдет в правило 20000 на котром он будет передан в dummynet - скорость входящего потока будет ограничена в 35Mbit/s. После выхода из шейпера, трафик пройдет по цепочке правил до правила 20220, после чего выйдет из прохода IN фаервола и перейдет в проход OUT. В проходе OUT трафик пройдет по правилам до 1061 где попадет на неявный check-state. Так как в таблице динамических правил нет соединения для пар адресов внешнего и внутреннего адреса из IP заголовка, и трафик подпадает под действие правила from any to any via fxp0 то, следовательно, запись в таблице динамических правил будет создана, к трафику будет применен параметр действия родительского правила (tag 1), а так же применено действие родительского правила (skipto 1070). Перейдя на правило 1070 трафик выйдет из фаервола и будет передан через интерфейс fxp0 во внутреннюю сеть.

Ответный трафик от машины с именем wsrv пройдет через любое из правил 1011-1015 с параметром prob и попадет на любое из правил 1061-1069 с параметром keep-state, в результате чего на вызове неявного check-state будет установлено, что в таблице динамических правил уже есть запись с соответсвующими параметрами для ранее установленной сессии. Трафик будет переброшен на правило впервые породившее сессию - это было правило 1061. К трафику будет применен параметр действия родительского правила (tag 1), а так же применено действие родительского правила (skipto 1070). Попав на правило 1070 трафик будет принят и выйдет из прохода IN фаервола.

Трафик зайдет в проход OUT где на правиле с номером 1061 сново попадет на неявный check-state. Так как в таблице есть данные о сессии, то трафик будет переброшен на правило впервые породившее сессию - это было правило 1061. К трафику будет применен параметр действия родительского правила (tag 1), а так же применено действие родительского правила (skipto 1070). Так как на проходе OUT правило 1070 совпадения не даст то трафик пройдет по фаерволу далее до правила с номером 10000 на котором он будет передан в dummynet - скорость выходящего потока будет ограничена в 20Mbit/s. После выхода из шейпера, трафик пройдет по цепочке правил до 10601. Так как трафик имеет присвоенный ему ранее тег равный 1, то он будет обработан на последнем экземпляре ната у которого установлен уточняющий параметр tagged 1. Выйдя из ната трафик продолжит движение по правилам, и дойдя до правила 20220 выйдет из фаервола и уйдет во внешнюю сеть через интерфейс em0.

Для 192.168.1.0/24:
Допостим, что хост 192.168.1.83 инициировал две сессии. Обе сессии будут приняты рутером и на проходе IN пройдут через секцию правил с номерами 1011-1015. Эти правила имеют параметр определяющий вероятность их срабатывания. Каждое из правил делит проходящий через него поток по вероятности обработки на части. В итоге рассматривая поток из 100 случайных пакетов получим, что каждый пакет так или иначе будет обработан на каком-либо из правил, а в итоге все 100 пакетов в среднем будут поделены на пять равновероятных групп по 20 пакетов в каждой.

Допустим, что первая сессия была обработана на правиле 1012. При этом произойдет переход на правило 1063 на котором сработает неявный check-state - будет установлено, что в таблице динамических правил еще нет записи для параметров первой сессии, в результате чего при вызове keep-state такая запись будет создана, к трафику будет применен параметр действия (tag 2) и само действие правила (skipto 1070). После перехода на правило 1070 трафик будет разрешен и выйдет из прохода IN перейдя в проход OUT фаервола. На проходе OUT трафик дойдет до первого правила с keep-state где на неявном check-state будет обработан - так как в таблице динамических правил уже есть ранее созданная запись с параметрами сессии, то к трафику будет применен параметр действия (tag 2) и само действие родительского правила (skipto 1070). После перехода на правило 1070 трафик продолжет движение до правила 10000. На правиле с номером 10000 трафик будет передан в dummynet - скорость выходящего потока будет ограничена в 20Mbit/s. После выхода из шейпера, трафик пройдет по цепочке правил до 10201. Так как трафик имеет присвоенный ему ранее тег равный 2, то произойдет совпадение с уточняющим параметром tagged 2 и трафик будет передан в экземпляр ната с номером 2. Выйдя из ната трафик продолжит движение по правилам, и дойдя до правила 20220 выйдет из фаервола и уйдет во внешнюю сеть через интерфейс em0.

Ответный трафик полученный из внешней сети через интерфейс em0 на проходе IN пройдет до праила 10202 на котором будет передан в нат. Выйдя из ната трафик продолжит движение по правилам до правила с номером 20000 на котром он будет передан в dummynet - скорость входящего потока будет ограничена в 35Mbit/s. После выхода из шейпера, трафик пройдет по цепочке правил до правила 20220, после чего выйдет из прохода IN и перейдет в проход OUT фаервола. В проходе OUT трафик дойдет до правила 1061 где на неявном check-state будет определено, что в таблице динамических правил есть совпадение с параметрами сессии в результате чего трафик перескочет на свое родительское правило 1063 где к нему будет применен параметр действия (tag 2) и само действие правила (skipto 1070). После перехода на правило 1070 трафик будет разрешен, выйдет из прохода OUT фаервола и будет передан во внутреннюю сеть на хост 192.168.1.83.

Ответный трафик в рамках сессии проходя через check-state всегда будет попадать на родительское правило 1063 где к нему будет применен параметр действия (tag 2) и само действие правила (skipto 1070). Таким образом, до тех пор пока в таблице динамических правил есть информация о сессии, проходящий трафик конкретной сессии всегда будет помечаться своим тегом, а позже направляться в свой экземпляр ната.

Аналогичное поведение будет для трафика второй сессии, но, если допустить, что на секции правил с номерами 1011-1015 эта сессия была обработана правилом 1015, то тогда вся цепочка преобразований будет давать присвоение трафику тега с номером 5 и последующую обработку его на пятом экземпляре ната.

Если на рутер из внешней сети, на адреса 11.22.33.[1-5] придет случайный трафик, то он будет отвергнут так как все экземпляры натов запущены с опцией deny_in.

Дополнительная информация.

Модули протоколов:

Для ipfw nat есть возможность запускать модули помогающие улучшать работу через нат различных протоколов прикладного уровня. На момент написания заметки в FreeBSD 7.2-STABLE были достуаны следующие модули:
alias_cuseeme.ko
alias_ftp.ko
alias_irc.ko
alias_nbt.ko
alias_pptp.ko
alias_skinny.ko
alias_smedia.ko


Так, например, после запуска модуля alias_ftp.ko (или вручную через kldload alias_ftp.ko или через /boot/loader.conf alias_ftp_load="YES") поведение ipfw nat изменяется к лучшему - он начнет пропускать через себя активные FTP сессии инициализированные от клиентов находящихся за ним. Модуль alias_ftp отслеживат FTP трафик проходящий через нат и динамически изменяет внутреннюю таблицу ната для установления соответствия номеров портов используемых в сессии. Аналогично и для прочих модулей.
Файл /etc/libalias.conf не влияет на работу ipfw nat. Этот конфигурационный файл используется только "user space" программами такими как natd.


Мониторинг:

Время процессора потребляемое ipfw и Dummynet:top -SH -n 500 | grep swi1

PID USERNAME PRI NICE SIZE RES STATE TIME WCPU COMMAND
14 root -44 - 0K 8K WAIT 0:00 0.00% swi1: net


top -SH -n 500 | grep dummynet

PID USERNAME PRI NICE SIZE RES STATE TIME WCPU COMMAND
44 root -68 - 0K 8K - 1:14 0.00% dummynet


Размер памяти съедаемый ipfw nat:vmstat -m | grep libalias

Type InUse MemUse HighUse Requests Size(s)
libalias 19 35K - 580 128


Тюнинг:

Так как ipfw nat работает в ядре, то память для своей работы он потребляет из диапазона доступного для аллокации ядру. Объем памяти ядра устанавливается через переменную sysctl vm.kmem_size. На момент написания заметки для FreeBSD 7.2-STABLE i386 это значение по-умолчанию было:sysctl -a | grep vm.kmem_size
vm.kmem_size: 335544320
vm.kmem_size_min: 0
vm.kmem_size_max: 335544320
vm.kmem_size_scale: 3


Что есть 335544320 байт = 320 Мб.

Потолок для i386 - 2Гб. Чтобы выставить его, необходимо пересобрать ядро с опцией options KVA_PAGES=512.

Для архитектуры AMD64 этот параметр может быть выставлен вплоть до 6 Гб и для этого нет необходимости в пересборке ядра - достаточно установить соответсвующие sysctl значения через /boot/loader.conf.


Описание известных проблем:

Чем лучше какая-либо штука, тем она сложнее, а в сложных вещах бывают и свои проблемы :) Механизм ipfw nat тоже не идеален. Далее я просто перечислю некоторые его косячки и возможные решения.

- При больших нагрузках (когда через нат создается очень много соединений) внутренняя таблица ната может разростись до очень больших размеров и исчерпать всю доступную память ядра. Такая проблема встречалась в первых релизах версии FreeBSD 7, и насколько мне известно ее решением занимались путем оптимизации механизма создания/удаления записей в таблице - патчили его чтобы он более агрессивно следил за используемой памятью. Насколько проблема актуальна сейчас я проверить не могу так как не обладаю каналом с трафиком в сотни мегабайт/сек и тысячами юзеров.
В качестве меры профилактики можно удалять экземпляр старого ната, а потом создавать новый. Удаление делается через: ipfw nat delete 1


- Нет возможности получить дамп внутренне таблицы ната. При включении параметра конфигурации log накапливается довольно скудная статистика которую можно просмотреть с помощью команды:# ipfw nat 1 show
nat 1: icmp=0, udp=6, tcp=3, pptp=0, proto=0, frag_id=0 frag_ptr=0 / tot=9


- Библиотека libalias, на которой основан ipfw nat, плохо дружит с функциями аппаратного ускорения расчета контрольных сумм и управления потоками, которые доступны на некотроых сетевых адаптерах. Рекомендуется выключать tcp segmentation offloading (TSO), а так же rxcsum на том сетевом адаптере где работает нат (ifconfig em0 -rxcsum -tso).


- Тайной осталось то, каким образом применять директиву proxy_only. В natd кроме нее есть так же и деректива proxy_rule с помощью которой можно настраевать правила. Здесь же ее почему-то нет (недопилили?) и как завести правила проксирования - непонятно. В natd правила проксирования позволяют организовать что-то вроде ipfw fwd - пакеты адресованные на определенный порт можно модифицировать заменив в них IP адрес и порт назначения на произвольные. Без этого правила мы можем менять в пакете только адрес отправителя на тот что указан в директиве IP, или только адрес получателя на один конкретный при использовании redirect_port или redirect_addr. Функциональность proxy_rule позволила бы менять адрес получателя без привязки к конкретному IP указанному в настройках экземпляра ната. Такое нельзя сделать через связку revers+redirect_port. Например для пересылки всего исходящего http трафика на сервер srv1 порт 81, можно было бы применить правило: proxy_only proxy_rule port 80 server srv1:81


- На момент написания заметки в FreeBSD 7.2-STABLE не работали правила redirect_proto.


- В FreeBSD 7.1 и более ранних версиях размер redirect_port имел какое-то внутреннее ограничение приводящее к ошибкам "logical string limit" при составлении длиных правил. В FreeBSD 7.2-STABLE этой проблемы больше нет.


- ipfw nat любит перезагрузки при изменениях в конфигурации. Я не могу привести конкретных примеров, но иногда случается, что после больших изменений в конфигурации экземпляра ната, новые настроки могут вступить в действие только после перезагрузки модуля ipfw, или же после перезагрузки машины. Бездоказательное утверждение из опыта и на уровне "ощущений".


- Параметры sysctl которые влияют на динамические (keep-state) правила.

срок жизни инамических правил для UDP и TCP:
net.inet.ip.fw.dyn_udp_lifetime
net.inet.ip.fw.dyn_ack_lifetime

максимальное количество динамических правил:
net.inet.ip.fw.dyn_max

размер хеш-таблицы для поиска в списке динамических правил:
net.inet.ip.fw.dyn_buckets

автоматическая отсылка TCP keep-alive сообщений для динамических правил:
net.inet.ip.fw.dyn_keepalive


- В версии FreeBSD 8.1-RELEASE не работает параметр sysctl one_pass - трафик после прохода через нат возвращается обратно в фаервол. Чтобы избавиться от этой неприятности надо делать правило "allow all from any to any" после правила ната. Эту проблему должны будут исправить в релизе 8.2.

Обсуждение http://forum.lissyara.su/viewtopic.php?t=18967

http://www.lissyara.su/articles/freebsd/tuning/ipfw_nat/

Обновлено: 12.03.2015