Есть два подключения к двум независимым провайдерам и две локальных сети. На рутере имеется четыре сетевых адаптера каждый из которых подключен к своему провайдеру или к своей локальной сети. Адаптеры 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 обеспечить честное распределение пропускной способности между клиентами в локальных сетях.
add 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 1090 deny ip from 192.168.0.0/16 to any in recv em0 add 10100 deny ip from any to 172.16.0.0/12 in recv em0 add 10110 deny ip from 172.16.0.0/12 to any in recv em0 add 10120 deny ip from any to 10.0.0.0/8 in recv em0 add 10130 deny ip from 10.0.0.0/8 to any in recv em0 add 10140 deny ip from any to 169.254.0.0/16 in recv em0 add 10150 deny ip from 169.254.0.0/16 to any in recv em0
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
setfib1_start() { if [ ${setfib1_defaultroute} ] then setfib 1 route add -net default ${setfib1_defaultroute} else echo "Can't set defaultroute for fib 1 (setfib1_defaultroute is not set)" fi }
Детальное описание прохождения трафика через фаервол в данной конфигурации:
Для рутера:
Трафик инициализированный непосредственно от рутера пройдет через цепочку правил 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
Конфигурация практически полностью повторяет Пример 1: рутер с двумя сетевыми адаптерами подключенными в локальную сеть и в интернет. Адрес в локальной сети 192.168.1.1 (сетевой адаптер fxp0), адрес в интернете 1.2.3.4 (сетевой адаптер em0).
Задача: "раздавать интернет" для локальной сети, обеспечить проброс портов для внутренних адресов, разрешить прохождение трафика на отдельные порты самого рутера, а главное - ввести жесткий лимит на количество одновременных исходящих TCP соединений для всего трафика уходящего в интернет (максимум 20 одновременных соединений с каждого внутреннего IP адреса).
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
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. Будет проверено: нет ли соответсвия с какой-либо из ранее инициализированных сессий, и такое совпадение будет обнаружено. В следствии этого таймер устанавливающий время жизни динамической записи будет сброшен. После этого трафик выйдет из функции обработки динамических правил и будет передан на поле действия правила 10130 - трафик будет перенаправлен в нат. Выйдя из ната трафик покинет проход 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
Дополнительная информация.
Модули протоколов:
Для ipfw nat есть возможность запускать модули помогающие улучшать работу через нат различных протоколов прикладного уровня. На момент написания заметки в FreeBSD 7.2-STABLE были достуаны следующие модули:
Так, например, после запуска модуля 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:000.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:140.00% dummynet
Так как ipfw nat работает в ядре, то память для своей работы он потребляет из диапазона доступного для аллокации ядру. Объем памяти ядра устанавливается через переменную sysctl vm.kmem_size. На момент написания заметки для FreeBSD 7.2-STABLE i386 это значение по-умолчанию было:
Потолок для i386 - 2Гб. Чтобы выставить его, необходимо пересобрать ядро с опцией options KVA_PAGES=512.
Для архитектуры AMD64 этот параметр может быть выставлен вплоть до 6 Гб и для этого нет необходимости в пересборке ядра - достаточно установить соответсвующие sysctl значения через /boot/loader.conf.
Описание известных проблем:
Чем лучше какая-либо штука, тем она сложнее, а в сложных вещах бывают и свои проблемы :) Механизм ipfw nat тоже не идеален. Далее я просто перечислю некоторые его косячки и возможные решения.
- При больших нагрузках (когда через нат создается очень много соединений) внутренняя таблица ната может разростись до очень больших размеров и исчерпать всю доступную память ядра. Такая проблема встречалась в первых релизах версии FreeBSD 7, и насколько мне известно ее решением занимались путем оптимизации механизма создания/удаления записей в таблице - патчили его чтобы он более агрессивно следил за используемой памятью. Насколько проблема актуальна сейчас я проверить не могу так как не обладаю каналом с трафиком в сотни мегабайт/сек и тысячами юзеров. В качестве меры профилактики можно удалять экземпляр старого ната, а потом создавать новый. Удаление делается через:
ipfw nat delete 1
Нет возможности получить дамп внутренне таблицы ната. При включении параметра конфигурации log накапливается довольно скудная статистика которую можно просмотреть с помощью команды:
- Библиотека libalias, на которой основан ipfw nat, плохо дружит с функциями аппаратного ускорения расчета контрольных сумм и управления потоками, которые доступны на некотроых сетевых адаптерах. Рекомендуется выключать tcp segmentation offloading (TSO), а так же rxcsum и txcsum на том сетевом адаптере где работает нат.
- Тайной осталось то, каким образом применять директиву proxy_only. В natd кроме нее есть так же и деректива proxy_rule с помощью которой можно настраевать правила. Здесь же ее нет (недопилили?) и как завести правила проксирования - непонятно.
- На момент написания заметки в FreeBSD 7.2-STABLE не работали правила redirect_proto.
- В FreeBSD 7.1 и более ранних версиях размер redirect_port имел какое-то внутреннее ограничение приводящее к ошибкам "logical string limit" при составлении длиных правил. В FreeBSD 7.2-STABLE этой проблемы больше нет.
- ipfw nat любит перезагрузки при изменениях в конфигурации. Я не могу привести конкретных примеров, но иногда случается, что после больших изменений в конфигурации экземпляра ната, новые настроки могут вступить в действие только после перезагрузки модуля ipfw, или же после перезагрузки машины. Бездоказательное утверждение из опыта и на уровне "ощущений".