ipfw

ipfw — это утилита для управления встроенным в систему файерволлом.

Файерволл — часть системы, часть сетевого стека, часть ядра и без него управление трафиком было бы невозможно.

В статье пойдет речь о том, какими возможностями он обладает и как их использовать.

Скучный мануал такой скучный, поэтому я его написал, но он будет в конце специально для того, чтобы желающие могли разобраться как все устроено и как все работает, сейчас же я приведу пример не очень сложного рабочего скрипта с шейпером для настройки:

#!/bin/sh

#Хороший тон при написании скриптов — использовать переменные, в противном случае при изменении скрипта/конфигурации #системы/обновлении/любом случайном стечении обстоятельств вы рискуете переписывать весь свой огромный скрипт, да и #ошибиться несложно.
wan_if=»re0″
lan_if=»re1″
localnet=»192.168.0.0/24″

#сброс предыдущих настроек
ipfw -fq flush

#разрешаем локальный обмен
ipfw -q add 130 allow all from $localnet to $localnet via $lan_if

#задаем каналы, для протокола udp чуть уже
ipfw -q pipe 3 config bw 14000Kbit/s queue 20
ipfw -q pipe 4 config bw 14000Kbit/s queue 20
ipfw -q pipe 5 config bw 17000Kbit/s queue 20
ipfw -q pipe 6 config bw 17000Kbit/s queue 20

#конфигурируем очереди для равномерного распределения трафика
ipfw -q queue 30 config pipe 3 weight 10 queue 20 mask dst-ip 0xffffffff
ipfw -q queue 40 config pipe 4 weight 10 queue 20 mask src-ip 0xffffffff
ipfw -q queue 50 config pipe 5 weight 1 queue 20 mask dst-ip 0xffffffff
ipfw -q queue 60 config pipe 6 weight 1 queue 20 mask src-ip 0xffffffff

#добавляем очереди для udp в правила, обрабатываем их отдельно
ipfw -q add 230 queue 30 udp from not $localnet to $localnet out via $lan_if
ipfw -q add 240 queue 40 udp from $localnet to not $localnet in via $lan_if

#пакеты udp уходят из файервола

ipfw -q add 250 allow udp from not $localnet to $localnet out via $lan_if
ipfw -q add 260 allow udp from $localnet to not $localnet in via $lan_if

#добавляем очереди для оставшихся пакетов в правила, обрабатываем их отдельно
ipfw -q add 350 queue 50 all from not $localnet to $localnet out via $lan_if
ipfw -q add 360 queue 60 all from $localnet to not $localnet in via $lan_if

#остальные пакеты уходят из файервола

ipfw -q add 370 allow all from not $localnet to $localnet out via $lan_if
ipfw -q add 380 allow all from $localnet to not $localnet in via $lan_if

#добавляем НАТ

ipfw -q nat 1 config if $wan_if log redirect_port tcp 192.168.0.2:40001-41000 40001-41000 redirect_port udp 192.168.0.2:40001-41000

#оставшиеся правила нужны, чтобы пакеты нормально проходили через НАТ

ipfw -q add 410 nat 1 all from any to any via $wan_if

ipfw -q add 510 allow all from me to not $localnet out via $wan_if
ipfw -q add 520 allow all from not $localnet to me in via $wan_if
ipfw -q add 540 allow all from any to $localnet via $wan_if

Скрипт проверял, работает =)

 

Сразу оговорюсь, есть ряд мелких, но важных деталей:

1. переменная ядра net.inet.ip.fw.one_pass должна быть установлена в ноль, т.е. занесите «net.inet.ip.fw.one_pass=0» в sysctl.conf, это дает возможность более гибкой настройки файервола: пакеты будут покидать файервол только в случае попадания в правило allow/deny, т.о. вы можете сделать так, чтобы пакет проходил несколько правил и обрабатывался ими, а затем уже покидал файервол по вашему желанию.

2. опция -q, стоящая после каждой команды ipfw означает режим quite, т.е. файервол не будет писать никаких сообщений для отчетности, это необходимо для удаленного администрирования, иначе ipfw будет пытаться отослать сообщение и обнаружив, что соединения нет — прекратит выполнение скрипта. Старые правила сбросятся, а новые не установятся.

3. а команда ipfw -f flush выполняет предварительный сброс настроек файервола, иначе вы можете получить непредсказуемые результаты при изменении скрипта =)

 

Мануал:

По сути файерволл управляется списком правил, если вы не настраивали его до этого, то введя команду

ipfw show

вы должны увидеть

65536 allow ip from any to any

Это правило, создаваемое системой по умолчанию и зависящее от опции, с которой скомпилировано ядро(FIREWALL_DEFAULT_TO_DENY или FIREWALL_DEFAULT_TO_ALLOW), оно имеет последний порядковый номер в системе и не может быть удалено или перезаписано.

Всего правил, как вы поняли, может быть 65535, начиная с нуля, также правила могут иметь один и тот же порядковый номер, если (!) они не мешают друг другу (исключают друг друга, т.е. например одно правило касается только протокола tcp, а другое только udp). При обработке поступившего пакета файерволл проверяет пакет на соответствие правилам по порядку, начиная с нулевогого правила и заканчивая 65535м. По умолчанию, если пакет соответствует правилу, то к нему применяется действие, описанное там и пакет покидает файерволл. Также важно помнить, что пакет, проходя каждый раз через интерфейс, в случае попадания на интерфейс извне обрабатывается файерволлом сразу по прибытию, в случае отправки пакета обрабатывается заранее, до попадания на интерфейс, т.е. если задача компьютера состоит в шлюзовании(т.е. пропускании пакетов через себя), то пакет проходит файерволл дважды: при принятии пакета и до отправки пакета.

 

Чтобы добавлять правила необходимо использовать команду

ipfw add [порядковый номер правила] [действие] [протокол] from [отправитель] to [получатель]

а команда ipfw delete [порядковый номер правила] удаляет правило с указанным номером

Рассмотрим по порядку

1. порядковый номер правила — число от 1 до 65535, обозначающее, каким по счету будет рассматриваться правило(зачастую порядок следования правил имеет ключевое значение);

2. действие — действие, которое нужно применить к пакету, основные варианты:

— allow — разрешить пакету пройти, т.е. пакет сразу покидает файерволл и отправляется дальше;

— deny — разрешить пакету пройти, т.е. пакет отбрасывается и дальше не проходит;

— [divert название программы] — передать пакет на обработку сторонней программе(например natd), пакет передается и покидает файерволл, далее программа в соответствии с собственной конфигурацией обработает пакет, на выходе пакет вновь будет обработан;

— [nat название НАТа] — совершить преобразование адресов с пакетом;

— [pipe название пайпа] — поместить пакет в пайп(по-русски трубу);

— [queue название очереди] — поместить пакет в очередь;

3. протокол — основные варианты:

— all — пакеты любых типов протоколов соответствуют;

— tcp — соответствуют пакеты tcp;

— udp — соответствуют пакеты udp;

4. отправитель и получатель — это адреса, возможные варианты:

— any — любой адрес соответствует этому значению;

— xxx.xxx.xxx.xxx/yy — где xxx.xxx.xxx.xxx — ip-адрес, yy — маска подсети;

Некоторые действия, такие как nat, pipe и queue необходимо еще и сконфигурировать до или после добавления правил в файерволл, делается это следующим образом:

ipfw [действие] [название действия] config [опции]

Конфигурируемыми действиями являются nat, pipe, queue и другие (менее интересные нам), единственным нераскрытым пунктом в данном случае являются опции, и для разных действий они, естественно, разные. Рассмотрим самое распространенное:

— для nat:

ipfw nat [название НАТа] config if [внешний интерфейс] log redirect_port [protocol] [ip]:[ports]  [ports]

Распространенный случай: поднятие НАТа на внешнем интерфейсе — опция if задает этот интерфейс, log — включает логирование, опция redirect_port делает возможным перенаправление пакетов с портов внешнего интерфейса на порты внутреннего локального адреса(вопреки распространенному мнению, будто бы этим занимается функция port_forward). На тот момент, что я настраивал эту функцию, для протоколов были возможны только 2 варианта: tcp и udp, т.е. приходилось отдельно указывать перенаправление для этих 2х протоколов. [ports] в конце обозначает порт или диапазон портов на внешнем интерфейсе, где НАТ будет ждать пакеты для перенаправления(диапазон указывается через знак минуса xxxxx-yyyyy). Надо сказать, что диапазоны могут быть разными =), но должны быть одинаковыми по размеру, т.е. допустимы записи

xxx.xxx.xxx.xxx:aaaaa-bbbbb ccccc-ddddd, но при этом при вычитании должно выполняться: bbbbb — aaaaa = ddddd — ccccc

т.е. при поступлении пакета на внешний интерфейс на порт ccccc файервол передаст его на порт aaaaa локального айпи xxx.xxx.xxx.xxx и сделает так же соответственно с каждым пакетом из диапазона.

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

— для pipe:

ipfw pipe [название ПАЙПа] config bw [пропускная способность] queue [количество пакетов]

опция bw (bandwidth, англ. — пропускная способность) указывает в различных единицах измерения ширину создаваемого канала, приемлимые единицы bit/s, Kbit/s, Mbit/s. queue (англ. — очереди) — указывает число пакетов, помещаемых в очередь, в большинстве случаев достаточно 10-20 пакетов. Нужно заметить, что действие pipe не обладает никакими особыми свойствами и кроме ограничения канала заданными границами ничего не умеет. Для расширения возможностей контроля трафика как раз нужно действие queue.

— для queue:

ipfw queue [название очереди] config [название ПАЙПа] weight [приоритет пакета] queue [размер очереди] mask [маска (я кэп ;)] Очереди создаются внутри канала (ПАЙПа), пакеты в этих очередях могут иметь различный приоритет (число от 0 до 100, чем меньше число, тем выше приоритет) и приоритет присваивается этим пакетам в соответствии с заданной маской. Я использую эту возможность для того, чтобы создавать много одинаковых очередей с одинаковым приоритетом, но для разных айпи, это позволяет равномерно делить канал между всеми хостами. Очереди динамически создаются и удаляются, что позволяет не бояться того, что часть канала будет неиспользована, занята хостом, который уже отключился. Приоритет задается числом от 1 до 100, размер очереди определяется числом пакетов, которые туда могут поместиться. Самый интересный параметр mask, я использовал 2 типа маски: src-ip и dst-ip со значением 0xffffffff, которое означает, что для любого нового айпи в этом пайпе будет создаваться новая очередь (синтаксис: mask src-ip 0xffffffff), с маской src-ip будет проверяться адрес отправителя, а с маской dst-ip — соответственно адрес получателя.