Возник интерес к подсистеме netgraph. Решил начать изучать ее с наиболее актуальной и нужной лично мне возможности - с ната реализованного в ноде ng_nat. За время использования ipfw nat выяснилось, что у него есть некоторые недостатки, а ответственные за него люди из проекта FreeBSD что-то не спешат исправлять найденные проблемы. Поэтому захотелось освоить параллельно еще и ng_nat чтобы применять его при случае там, где ipfw nat не дает нужного результата. Да и просто ng_nat (как и сам netgraph) - это прикольная штука играться с которой уже само по себе интересно!
С того времени как ng_nat впервые был закоммичен в FreeBSD в нем многое изменилось. В первых реализациях не было некоторых важных функций (типа проброса портов), а теперь его функционал даже больше чем у ipfw nat! Нода ng_nat построена все на той же библиотеке libalias на которой работают как ipfw nat, так и natd и все что мы знаем о natd применимо к ng_nat. К слову сказать - даже модули протоколов libalias (alias_ftp.ko, alias_irc.ko, etc...) работают с ng_nat так же как они работали с ipfw nat.
Если провести сравнение с ipfw nat, то на вскидку получится следующий список фич в пользу ng_nat: - работают правила проксирования proxy_rule, proxy_only - менее "глючен" в работе, позволяет налету изменять правила проброса портов - нет неприятных ошибок типа ограничений на количество редиректов (NAT_BUF_LEN) - возможность добавлять к правилам проброса портов текстовые описания - дополнительные, интересные настройки не реализованные в ipfw nat
Я планирую позже оформить эту статью по такому же принципу как было с обзором ipfw nat - с кучей примеров и детальных описаний. Пока же только самое основное и один интересный пример.
В процессе освоения ng_nat возникли некоторые вопросы по его настройке. Хотелось бы выразить благодарность Вадиму Гончарову ака nuclight за то, что он дал полезную информацию о принципах настройки ng_nat, а так же за то, что он подготовил специальный rc.d скрипт ng_nat.sh для настройки экземпляров ng_nat - этот скрипт значительно облегчает работу с netgraph для тех кто с ним никогда не имел дела. Примеры в заметке будут использовать ng_nat.sh Скрипт этот был сделан уже давно, но по непонятным причинам в базовую систему не вошел (снова хотелось бы передать ПРЕВЕД людям отвечающим за ipfw в FreeBSD - почему ng_nat принят, а этот полезный дополнительный функционал к нему не принят?!)
Ручное создание ноды ng_nat и связь ее с ipfw
Для того чтобы вручную создать экземпляр ng_nat и связать его с ipfw, надо проделать следующие шаги:
- загрузить через kldload (или /boot/loader.conf) модули netgraph.ko, ng_ipfw.ko и ng_nat.ko - создать с помощью ngctl экземпляр ноды ng_nat и связать ее своими хуками с нодой ng_ipfw и ее хуками - с помощью управляющих сообщений netgraph, настроить ноду ng_nat - с помощью ipfw настроить пересылку входящего и исходящего трафика в netgraph через соответствующие хуки
Создание ноды ng_nat под именем nat1 и связь ее с ipfw:
ngctl mkpeer ipfw: nat 100 in ngctl name ipfw:100 nat1 ngctl connect ipfw: nat1: 200 out ngctl msg nat1: setaliasaddr 192.168.1.1
Была создана нода ng_nat под именем nat1, и в качестве первичной настройки ей было указано использовать IP адрес 192.168.1.1 для алиасинга. Нода под именем nat1 имеет два хука. Хук in, через который в ноду поступает трафик для демаскировки, соединен с хуком под номером 100 у ноды ipfw. Хук out, через который в ноду поступает трафик для маскировки, соединен с хуком под номером 200 у ноды ipfw.
Настройка ipfw для пересылки трафика в ng_nat:
ipfw add netgraph 100 all from any to any in via fxp0 ipfw add netgraph 200 all from any to any out via fxp0
Правила пересылают весь трафик приходящий к нам через внешний интефейс fxp0 в netgraph через хук 100, а весь исходящие трафик пересылается в netgraph через хук 200. Таким образом трафик из ipfw перенаправляется на обработку в экземпляр ng_nat.
Показанный выше пример дает общее представление о принципах настройки связки ipfw + ng_nat. Ниже будут приводиться примеры более детальной ручной настройки ng_nat, а в конце заметки будет пример использования rc.d скрипта ng_nat.sh с помощью которого можно отказаться от рутины и автоматизировать настройку через /etc/rc.conf
Возможности ng_nat и опции конфигурации.
Ниже приводятся ключевые слова используемые при настройке ng_nat через сообщения netgraph, примеры сообщений netgraph, а так же их эквиваленты применяемые для настройки ноды ng_nat через rc.d интерфейс реализованный в ng_nat.sh.
Сообщения netgraph для настройки ng_nat:
setaliasaddr
Параметр указывает IP адрес используемый при маскировании и демаскировании.
Пример сообщения netgraph: ngctl msg nat1: setaliasaddr 192.168.1.1
Пример настройки через rc.conf/ng_nat.sh: ng_nat_nat1_interface="192.168.1.1" ng_nat_nat1_interface="fxp0"
setmode
Устанавливает режимы работы экземпляра ната. Для настройки доступны следующие опции (опция - шестнадцатеричное значение - [десятичный эквивалент]):
Каждое из этих значений в бинарном виде выражается с помощью установки одного разряда. Например NG_NAT_PROXY_ONLY [64]= 01000000. Комбинация этих значений задает режим работы экземпляра ната. Например для установки режимов NAT_LOG + DENY_INCOMING + SAME_PORTS + UNREGISTERED_ONLY + RESET_ON_ADDR_CHANGE мы должны передать управляющее сообщение содержащее десятичное значение 1+2+4+16+32=55
Пример сообщения netgraph: ngctl msg nat1: setmode { flags=55 mask=55 }
Пример настройки через rc.conf/ng_nat.sh: ng_nat_nat1_set_mode="log deny_incoming same_ports unregistered_only reset_on_addr_change"
settarget
Устанавливает адрес по-умолчанию на который должна происходить передача входящего трафика, когда в таблице переадресации нет полного совпадения для какой-либо из ранее установленных сессий или правил пересылки установленных вручную. Функционал аналогичный режиму "natd -t".
Пример сообщения netgraph: ngctl msg nat1: settarget 172.16.0.100
Пример настройки через rc.conf/ng_nat.sh: ng_nat_nat1_set_target="172.16.0.100"
redirectport
Проброс входящих TCP/UDP соединений на внутренние хосты.
Пример сообщения netgraph: ngctl msg nat1: redirectport { local_addr=172.16.0.100 alias_addr=192.168.1.1 remote_addr=8.8.8.8 local_port=8080 alias_port=80 remote_port=12345 proto=6 description="this is test" }
Пример настройки через rc.conf/ng_nat.sh: ng_nat_nat1_redirect_port0="tcp 172.16.0.100:8080 192.168.1.1:80 8.8.8.8:12345" ng_nat_nat1_redirect_port0_description="this is test"
redirectaddr
Проброс входящих TCP/UDP соединений на внутренние хосты.
Пример сообщения netgraph: ngctl msg nat1: redirectaddr { local_addr=172.16.0.100 alias_addr=192.168.1.1 description="this is test" }
Пример настройки через rc.conf/ng_nat.sh: ng_nat_nat1_redirect_address0="172.16.0.100 192.168.1.1" ng_nat_nat1_redirect_address0_description="this is test"
redirectproto
Проброс входящих TCP/UDP соединений на внутренние хосты.
Пример сообщения netgraph: ngctl msg nat1: redirectproto { local_addr=172.16.0.100 alias_addr=192.168.1.1 remote_addr=8.8.8.8 proto=6 description="this is test" }
Пример настройки через rc.conf/ng_nat.sh: ng_nat_nat1_redirect_proto0="tcp 172.16.0.100 192.168.1.1 18.8.8.8" ng_nat_nat1_redirect_proto0_description="this is test"
redirectdynamic
Делает ранее установленное правило редирекции динамическим. После первого обмена пакетами через динамическое правило, оно будет автоматически удалено
Пример сообщения netgraph: ngctl msg nat1: redirectdynamic 1
redirectdelete
Удаляет правило редирекции
Пример сообщения netgraph: ngctl msg nat1: redirectdelete 1
Пример настройки через ng_nat.sh: ng_nat.sh delete_redirect nat1 1
addserver
Реализация LSNAT - распределения коннекций на несколько серверов по round-robin. Сообщение netgraph принимает id номер ранее установленной редирекции и адрес + порт назначения которые надо добавить для данной редирекции.
Пример сообщения netgraph: ngctl msg nat1: addserver { id=1 addr=1.2.3.4 port=8080}
Пример настройки через rc.conf/ng_nat.sh: ng_nat_nat1_redirect_port0="tcp 172.16.0.100:80,172.16.0.102:80 192.168.1.1:80"
listredirects
Показывает список со всеми сконфигурированными статическими редирекциями для конкретной ноды.
Пример сообщения netgraph: ngctl msg nat1: listredirects
Пример настройки через rc.conf/ng_nat.sh: ng_nat.sh list_redirects nat1
proxyrule
Конфигурация правила проксирования проходящего трафика. Правила применяются к трафику полученному через хук in. Возможные значения type: encode_ip_hdr | encode_tcp_stream | no_encode
Пример сообщения netgraph: msg nat1: proxyrule "type no_encode port 81 server 172.16.1.1:8080 proto tcp dst 7.7.7.7"
Пример настройки через rc.conf/ng_nat.sh: ng_nat_nat1_proxy_rule0="type no_encode port 80 server 172.16.1.1:8080 proto tcp dst 7.7.7.7"
Дополнительная информация о способах конфигурации режимов работы ng_nat
В примере использования сообщения setmode была показана конструкция такого вида:
ngctl msg nat1: setmode { flags=55 mask=55 }
Про способ подбора значения параметра flags было сказано (плюсуются десятичные значения соответствующих опций), но не было сказано про второй параметр - mask. Маска используется для указания какие именно биты конфигурации необходимо изменить. Как объяснил nuclight - в C коде есть такая часть в которой и происходит обработка флагов и маски. Там-то как раз и задается режим работы ноды ng_nat:
Это значит, что когда заданы как флаги так и маска, между ними проводится логическая операция AND после чего в качестве конфигурации ноды ng_nat применяется полученное значение. В нашем случае применение AND между flags=55 и mask=55 даст то же самое 55 - режим который мы хотим установить. С тем же успехом можно было применять flags=55 mask=0xffffffff - это бы дало те же самые 55. Если задать flags=55 и mask=54 то будет отключен режим логирования (убрали единицу в последнем разряде).
Маску можно использовать для управления режимом работы ната, когда не известно эталонное значение flags. Скажем, если мы хотим отключить режим DENY_INCOMING [2] то можно послать такое сообщение:
ngctl msg nat1: setmode { flags=0 mask=2 }
так как флаги равны 0 (false), то сработает вторая часть кода в которой будет взято теперешнее значение флагов, инвертирована переданная нами маска, и между ними будет проведена операция AND. В итоге флаги станут 53 и это значение будет применено к ноде ng_nat.
Примеры и сценарии применения механизма ng_nat
Пример 1
Случай использования правил проксирования для передачи http трафика на прозрачный прокси сервер. Прокси сервер работает на машине 192.168.1.80 у которой свое подключение к интернету. Весь проходящий через наш рутер трафик адресованный на 80 порт любого адреса получателя надо пересылать на 192.168.1.80:8080. Наш нат рутер имеет адреса 192.168.1.1 со стороны локальной сети и 1.2.3.4 со стороны интернета.
ng_nat_enable="YES" ng_nat_nodes="nat1" ng_nat_nat1_interface="em0" ng_nat_nat1_cookies="1 2" # in out ng_nat_nat1_ipfw_rules="20005 20008" # in out ng_nat_nat1_ipfw_rule0="20005 netgraph 1 ip from any to me in recv em0" ng_nat_nat1_ipfw_rule1="20008 netgraph 2 ip from any to any out xmit em0" ng_nat_nat1_set_mode="same_ports reset_on_addr_change log deny_incoming" ng_nat_nat1_proxy_rule0="type no_encode port 80 server 192.168.1.80:8080 proto tcp"
/etc/sysctl.conf
net.inet.ip.fw.one_pass=1
/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
# здесь в правилах с номерами 20005 и 20008 автоматически появятся # директивы пересылающие трафик в ng_nat
add 65534 deny all from any to any
Файл ng_nat.sh надо скачать от сюда, поместить в /usr/local/etc/rc.d/ и сделать выполняемым через chmod 555 ng_nat.sh