nuclight: (Default)
[personal profile] nuclight
Нарисовал тут давеча в RU.UNIX.BSD схему прохождения пакета через ядро и ipfw, с объяснениями, как это все стыкуется с divert, dummynet, keep-state и т.д., теория и примеры. Народу понравилось, решил опубликовать и здесь, чтоб не потерялось (ибо объяснений, как оно там все внутри, в сети не встречал — только howto-шки на что-то простое или конкретику "вот у меня наконец получилось", не дающие возможности понять и составить что-то сложное другое самому).

Newsgroups: fido7.ru.unix.bsd
From: Vadim Goncharov <vadimnuclight@tpu.ru>
Subject: ipfw: порядок прохождения пакетов, сложные случаи (was: ipfw fwd и natd)
Date: Tue, 20 May 2008 13:48:53 +0000 (UTC)
Message-ID: <1187235430@server.racoon.ru>
Organization: Nuclear Lightning @ Tomsk, TPU AVTF Hostel
X-Comment-To: Victor Sudakov
X-FTN-Sender: Vadim Goncharov <vadim.goncharov@f400.n5020.z2.fidonet.org>
X-FTN-REPLYTO: 2:5020/400 UUCP
X-FTN-Tearline: slrn/0.9.8.1 on FreeBSD 6.2/i386

Hi Victor Sudakov! 

On Mon, 12 May 2008 09:48:57 +0000 (UTC); Victor Sudakov wrote about 'Re: ipfw fwd и natd':

 VS>>>>>>> Пакет, отправленный по ipfw fwd, уже больше через ipfw не проходит?
 VS>>>>>>> Как бы мне ухитриться сделать policy routing в некий интерфейс и
 VS>>>>>>> одновременно на этом интерфейсе поднять static NAT ?
 VS>>>>>>      См. на тему ipfw one pass. Я сначала кладу пакет в divert, затем все
 VS>>>>>> что попало на следующее правило отправляю по fwd.
 VS>>>>> А я никак не могу грокнуть такие конфигурации ipfw, в которых правила
 VS>>>>> не привязаны к конкретным интерфейсам на in и out. Также как
 VS>>>>> конфигурации со skipto.
 VG>>>> А в чем конкретно затруднение?
 VS>>> Грокнуть не получается.
 VG>> Что в них конкретно вкурить не получается? 
 VS> Пожалуй, порядок прохождения пакетов через правила и моменты, когда
 VS> пакет попадает на повторную обработку (divert etc). 

Хм... ну попробую ниже.

 VS> Hа иную конфигурацию смотришь, как на программу на бейсике с
 VS> бесконечными goto, пытаясь распутать клубок.

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

 VG>> А то так в общем объяснять — скорее
 VG>> всего снова не вкурится.
 VS> Объяснять на пальцах бесполезно — на уровне мана оно и так понятно.
 VS> А вот именно интуитивно понять, почувствовать красоту этого дела и с
 VS> удовольствием начать пользоваться новым знанием — не выходит.
 VS> Просветление надобно.

Нууу.. на такие вещи надо видео показывать. Или при живом общении за кружкой
пива в реальном времени на бумажке чертить. В письме это сложно сделать, но я
попробую донести рисунками и аналогиями :)

Поскольку ipfw — довольно низкоуровневая штука, надобно объяснять
с привлечением сведений для программиста, о вызовах конкретных функций.
Пусть у нас ядро (и часть юзерленда) будет некоей местностью (аналогия на
географию), по которой перемещается пакет. Пешком ли, поезд на сортировочной
станции — неважно... Когда пакет прилетает в какую-нибудь функцию — он попадает
в определенное место. Потом он по ней двигается (у нее есть протяженность)
и уходит в какую-нибудь другую.

                                                  ____            
                                             .-<-|natd|-<-.       
                                            /     ----     \       
                                            |              |       
                                            v              ^       
                                             \->------.    |       
                                                      |   /        
out xmit int0         ________________                v  /
--<--4---3---2---1-<-|                |---<-----4---3---2---1---<---
                     |   роутер с     |                 in recv ext0
внутренняя           |    FreeBSD     | внешняя
сетевуха int0        |                | сетевуха ext0
                     |                |                out xmit ext0
-->--1--2--3--4--->--|________________|--->-----1---2---3---4--->---
in recv int0                                       /  ^              
                                                  /   |
                                            .--<-'     `-<-.
                                           /                |
                                           |      ____      |     
                                            \.->-|natd|->-./      
                                                  ----             

Это классическая картинка, тут вся машина в целом, видны оба прохода по ipfw.
Допустим, набор правил у нас такой:

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 для сетевух.

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

                приложения нашей машины                  natd
                                                        |    |
             ^                          V (5)           ^    v  вместо divert
             |   (4)  ____________      |               |    |    сюда можно
             |   .->-|ip_forward()|->---+     (8)     .--------.  подставить
         (3) |   |   `------------'     |        .->--| DIVERT |  netgraph и
             |   |                      |        |    `--------'  dummynet,
             |   |      .->--------------------+-'       (9) |    механизм и
             |   |      |               |      |             v  пути пакетов
             |   |      |               |      |             |  точно такие же
             |   |      |               +--<---|-----------<-+
             |   |      |               |      |             |
  ip_input() |  не нам  |  ip_output()  v (6)  ^             v
 .-----------|---|-.   /  .-------------|----. |             |
 |          нам /  |  |   | определить шлюз  | |             |
 |           | /   |  ^   | и сетевуху  |    | |             |
 |   кому пакет?   |  |   |             |    | |             |
 |           |     | /    |      ipfw_chk()->--'             |
 |      ipfw_chk()--'     |             |    |               |
 |___________|_____|      |_____________|____|               |
             |                          |                    |
         (2) ^                          V (7)                |
             |                          |                    |
             +---<---------------------------------------<---'
             |                          |
         (1) ^                          V
             |                          |
             |                          |
        ether_demux()           ether_output_frame() это для ipfw layer2, тут
             |                          |            оно нас не интересует
             ^                          V
             |   сетевые интерфейсы     |
             (железки), уровень драйверов            BPF работает здесь

То есть, пакет на входе передается драйвером в 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). Таблица просматривается сверху вниз, пакет
сравнивается с условиями. На первом совпавшем условии смотрим в столбец
действий, выполняем действие, прекращаем просмотр.

Это же можно, возвращаясь к географическим аналогиям, представить как
беговую дорожку или коридор с инструкциями, типа "Если ваш вес больше
50 кг, поверните направо, иначе следуйте дальше". Соответственно, на
первой же инструкции, которая подойдет, пакет свернет с прямой в
нужную дверь. Можно заметить, что инструкции могут быть и вида "если вы
болели в детстве ветрянкой, идите дальше и читайте следующую инструкцию
на двери номер 150, а все, что встретится до нее — пропустите не читая".
Это, очевидно, полный аналог действия skipto в ipfw.

Таким образом, работу ipfw_chk() можно упрощенно предствить в виде следующей
блок-схемы:

         |
         v  вход в ipfw_chk()
   ======|===================
         |
    на пакете есть divert-тег?
         |     \
        нет    да-->----перейти по номеру--->-.
         |             за правилом с divert   |
         v                                    v
         |                                    |
   включен one_pass и пакет                   |
   вернулся из другой подсистемы?             |
         |     \                              |
        нет    да-->--------------------------|-----.
         |                                    |     |
         v                                    |     v
         |                                    |     |
   правило 1: это tcp с любым отправителем    |     |
   и получателем на порты 135 или 445 ?       |     |      DENY: уничтожить
         |     \                              |     |      пакет (если он от
        нет    да-->----------------->--------|-----|-->-- локального прило-
         |                                    |     |      жения, вернуть ему
         v                                    v     v      Permission denied)
         |                                    |     |              ^
   правило 2: пакет проходит через            |     |              '---------.
   интерфейс ext0, в любом направлении ?      |     |                        |
         |     \                              |     |      отправить пакет в |
        нет    да-->----------------->--------|-----|-->-- DIVERT с номером  |
         | ___________________________________|     |      правила 2 в теге  |
         |/                                  .      |                        |
         v                                     .    v                        ^
         |                                       .  |                        |
   правило 3: это icmp-пакет ?                     .|                        |
         |     \                                    |.   следует обратить    |
        нет    да-->- обновить счетчики на правиле  |  . внимание на стрелку |
         |            3 и больше ничего не делать   |    перехода от тега    |
         | ______________________/                  |    divert к правилу 3  |
         |/                                         |    — если бы где-то    |
         v                                          v    в начале было       ^
         |                                          |    правило с действием |
   правило 4: вообще любой пакет?                   |    "skipto 3", его     |
         |     \                                    |    стрелка на ответ    |
        нет    да-->----------------->--------------+    "да" вела бы в то   |
         |  (конкретно в _нашем_ 4 правиле варианта |    же самое место (в   |
         v   "нет", конечно, быть не может)         v    наших правилах      ^
         |                                          |    такого нет, но у    |
   правило 65535: любой пакет, правило по умолчанию |    кого-нибудь —       |
         |                                          |    вполне может быть)  |
   файрвол (ядро) скомпилирован с опцией            |                        |
   IPFIREWALL_DEFAULT_TO_ACCEPT ?                   |                        |
         |      \                                   |                        |
         да      \                                  |                        |
         |        '- нет ----------------------->---|------------>-----------'
         |                                          |
         +--<------------------------<--------------'
         |
   ВЫХОД из ipfw_chk(): действие allow, пакет
   беспрепятственно пропущен через файрвол дальше
         |
         v

Из схемы, пояснений и мана уже должно быть понятно, как это всё работает, и
что skipto полностью аналогичен goto, и как он выглядел бы на схеме. Может
возникнуть вопрос, зачем нужен skipto, если он нередко запутывает правила?
Введен он, как это ни странно может показаться, как раз для возможности
упрощения правил и увеличения производительности файрвола (а также позволяет
делать интересные трюки с динамическими правилами, но об этом ниже). Для
того, чтобы это понять, надо рассмотреть, как устроена часть правила ipfw,
отвечающая за проверку соответствия пакета условию.

Ман говорит, что синтаксис "тела" правила (rule body) в ipfw2 есть
[ протокол from набор_адресов1 to набор_адресов2 ] [опция1 [опция2 ...]]
То есть, привычная часть "tcp from any to me" вообще говоря, необязательна,
а в списке опций допустимы OR-блоки, то есть он, по сути своей, представляет
то, что в математике называется конъюнктивной нормальной формой (КНФ) булева
выражения. А сами опции — это предикаты, они могут быть истинны или ложны.
Список опций в мане — и есть список таких предикатов для пакета. Все, что
можно сделать в привычной части в старом синтаксисе, можно сделать и опциями
(во внутреннем представлении в ядре оно так и есть). Таким образом, следующие
формы записи эквивалентны:

ipfw add allow tcp from 1.1.1.1,2.2.2.2 to not me in
ipfw add allow proto tcp { src-ip 1.1.1.1 or src-ip 2.2.2.2 } not dst-ip me in

и соответствуют логическому выражению:

(протокол = tcp ?) И ((src-адрес = 1.1.1.1 ?) ИЛИ (src-адрес = 2.2.2.2 ?)) И
(НЕ (dst-адрес = любой мой адрес ?)) И (пакет проверятся на входном проходе ?)

То есть, здесь для каждого предиката проверяется его истинность, и из них из
всех вычисляется истинность или ложность всего логического выражения. Следует
отметить, что, поскольку это КНФ, "НЕ" (not) может быть применен только к
самому предикату, а не их группе, то есть, "to not me 445" будет означать
"(НЕ (dst-адрес = любой мой адрес ?)) И (порт назначения = 445 ?)", но не
"НЕ ((dst-адрес = любой мой адрес ?) И (порт назначения = 445 ?))", а форма
вида "not { ... or ...}" вообще недопустима. К слову, реально вычисление
OR-блока идет слева направо, и при первом же истинном предикате (или его
отрицании) все остальные внутри OR-блока не вычисляются (может быть полезно
для оптимизации).

Однако вычисление предиката для пакета — операция, занимающая какое-то время,
и при большом числе правил и пакетов оно может стать существенным. Здесь-то
как раз и может придти на помощь skipto. Пример из жизни — в исследовательских
целях было написано несколько сот правил вида:

add 120 count log ip from 1.1.1.1 to any in via int0 ipttl 63,65-127,129-255
add 120 count log ip from 1.1.1.2 to any in via int0 ipttl 63,65-127,129-255
add 120 count log ip from 1.1.1.3 to any in via int0 ipttl 63,65-127,129-255
add 120 count log ip from 1.1.1.4 to any in via int0 ipttl 63,65-127,129-255
...

Видно, что правила указаны вполне точно, направление, адрес, интерфейс — но
получается много повторений, и проверяться будет каждый пакет, в то время как
подпадающих под условие пакетов — не так много. Простое добавление 119 правилом
skipto 121 с условием, не совпадающим с повторящимися частями, привело к
вполне заметному невооруженным глазом снижению нагрузки на процессор процентов
на 5-10 (точные замеры не проводились)! А всю конструкцию можно было таким
манером оптимизировать еще больше, убрав повторяющиеся части:

add 119 skipto 121 { not in or not recv int0 or not ipttl 63,65-127,129-255 }
add 120 count log ip from 1.1.1.1 to any
add 120 count log ip from 1.1.1.2 to any
add 120 count log ip from 1.1.1.3 to any
add 120 count log ip from 1.1.1.4 to any
...

Разумеется, такой частный случай оптимизации по скорости — не единственное
применение skipto. Многие, к примеру, рекомендуют разделять пакет по
направлению и интерфейсу наподобие вот такого:

ipfw add 10 deny tcp from any to any 135,445 // блокируем всегда
ipfw add 20 allow tcp from any to any 22 // доступ к роутеру на всякий случай
ipfw add 100 skipto 1000 all from any to any in recv int0
ipfw add 200 skipto 2000 all from any to any out xmit int0
ipfw add 300 skipto 3000 all from any to any in recv ext0
ipfw add 400 skipto 4000 all from any to any out xmit ext0
ipfw add 1000 ... // все пакеты в этой точке и далее будут для in recv int0
...               // поэтому к правилам здесь это можно не приписывать
ipfw add 1999 allow ip from any to any // дефолтная политика для входящих int0
ipfw add 2000 ... // здесь будут пакеты, уходящие с интерфейса int0
...
и т.д.

Использование такого набора правил позволит всегда четко знать, в какой части
рулесета с какими характеристиками проходит пакет. Ман-страница ipfw, кроме
того, настоятельно рекомендует выполнить такое разделение для пакетов на
layer2 (уровень фреймов Ethernet) — когда включен соответствующий sysctl,
пакет, проходящий через роутер, попадает в ipfw_chk() уже не два раза, а
ЧЕТЫРЕ (два на входе и два на выходе), из соответствующих ether_* функций
на первой схеме. Причем предикаты для второго уровня будут проверяться и на
обычных проходах в ip_input()/ip_output() — просто они всегда будут ложными.
Но вот отрицания их всегда будут истинными, и здесь очень легко ошибиться в
правилах с чем-нибудь вроде not MAC 10:20:30:40:50:60 any — так что проверки
второго уровня лучше выделить в отдельные правила, не смешивая их с проверками
на более высоких уровнях, и завершить allow all from any to any для L2-прохода.
Таким образом увеличится и производительность — пакеты на L2 не будут лишний
раз прогоняться по всем IP-правилам, обычно для L2 пишут совсем небольшое
количество правил, они быстро выполнятся, и пакет продолжит путь дальше.

ДИНАМИЧЕСКИЕ ПРАВИЛА и STATEFUL FIREWALL.

И в заключение следует рассмотреть еще одну сложную тему. Как известно, одним
из принципов при проектировании протоколов Internet являлся "состояние должно
храниться во взаимодействующих машинах, а не в самой сети", что является
гарантией того, что сбои где-то в сети между хостами будут иметь на них
минимально возможное влияние. Разнообразные NATы и файрволы с отслеживанием
состояния нарушают этот принцип. Кроме того, отслеживание соединений ведет к
увеличению нагрузки (в том числе по памяти) на маршрутизатор в зависимости от
количества активных соединений, и его, в отличие от аналогичного без хранения
состояния, нередко можно "зафлудить" запросами на соединение. Однако за все
надо платить, и некоторые вещи невозможно сделать без нормального отслеживания
состояния. Скажем, в ipfw есть опции setup и established для tcp-соединений,
которые просто смотрят на соответствующие флаги в tcp-пакете — просто
и быстро, правило с established в начале списка правил может весьма ускорить
работу файрвола ввиду отсутствия необходимости проверять дальнейшие правила
для основной массы пакетов. Но таким образом нельзя организовать отслеживание
для других протоколов (не tcp), да и хакеру никто не мешает передавать данные
в пакетах без флагов с помощью специальных программ — пакеты беспрепятственно
пройдут через такое правило с established, и до остальных запрещающих просто
не дойдут.

Поэтому в ipfw была добавлена поддержка отслеживания состояний (stateful
firewall), называемая динамическими правилами. В соответствии с озвученным
выше принципом, она была именно добавлена, то есть администратор может
использовать обычную попакетную фильтрацию, и в строго определенных, нужных
ему точках, добавлять проверку состояния (отступление: это называется
сохранением состояния, потому что файрвол сохраняет данные о соединениях и
"помнит" их между пакетами, тогда как в обычном режиме, показанном на схемах
выше, вычисление для каждого пакета начинается заново, вне зависимости от
других пакетов).

Реализуется эта поддержка ключевыми словами check-state, keep-state и limit.
Дальше мы мы будет рассматривать только keep-state, потому что правила с
limit отличаются только тем, что налагают ограничение на число записей в
таблице динамических правил, одновременно подпадающих под указанное
ограничение — текущая реализация в ipfw2 при попытке создать новую запись
просто молча уничтожает пакет (применяет deny).

Отдельно от обычных правил, называемых теперь статическими, в ядре заводится
таблица динамических правил (ее текущее содержимое можно посмотреть по
ipfw -d show), над которой возможны две операции: создание записи
(динамического правило) на основе информации в пакете и проверка пакета на
соответствие таблице — есть ли подходящие ему записи. Запись имеет вид:
"протокол адрес1:порт1 <-> адрес2:порт2 ссылка_на_родительское_правило".
Чтобы пакеты соединения в обоих направлениях подпадали под одно и то же
динамическое правило, направление в нем не учитывается — то есть, должен
совпасть протокол и обе пары адресов и портов, но пары можно менять местами:
udp-пакеты с 1.2.3.4:5678 на 6.7.8.9:1234 и с 6.7.8.9:1234 на 1.2.3.4:5678 -
оба подпадут под одно и то же правило.

Этим двум операциям и соответствуют ключевые слова check-state и keep-state.
Причем, поскольку администратору необязательно указывать в правилах явный
check-state (либо он может быть "перепрыгнут" каким либо правилом skipto до
него), то в _каждое_ правило с keep-state неявно добавляется "невидимый"
check-state — это сделано затем, чтоб избежать попыток добавить в таблицу
динамических правил такое правило, которое там уже есть.

         |
         v  фрагмент ipfw_chk(): начало правила с keep-state
         |                                             ______________________
         |                                            | ТАБЛИЦА ДИНАМИЧЕСКИХ |
    проверить таблицу (неявный check-state): <=======>|        ПРАВИЛ        |
    есть ли соответствующее пакету правило?           | (с таймером удаления |
         |     \                            .-------->| для каждого правила) |
        нет    да-->--перейти к действию в /          '----------------------'   
         |          найденном "родительском"                     ^
         v          правиле и сбросить таймер                    |
         |                в таблице         \                    |
         |                                   \                   |
   правило N: пакет соответствует указанному  |                  |
   условию в статическом правиле номер N ?    |         указано keep-state:
         |     \                              |         создать в таблице
        нет    да-->----------------->--------|-------> новое динамическое
         |                                    |         правило  |
         v                                    v                  |
   перейти к проверке следующего       выполнить параметры   <---'
   по списку статического правила      действия статического
         |                             правила N (log, tag, ...)
         v                                    |
         |    выполнить действие,             |
         |    указанное в статическом <-------'
         |    правиле N и обновить на
         |    правиле N счетчики
         v

Как можно видеть из схемы, каждая запись в таблице динамических правил
содержит ссылку на так называемое родительское правило — то, которое его
сгенерировало по keep-state. И при поиске соответствия полей пакета записям
в таблице динамических правил производится переход на часть действия
указанного родительского правила — с этого момента динамические правила
как бы перестают существовать, пакет привычным образом продолжает движение
по статическому набору правил, просто был сделан своеобразный skipto (хоть
и не на само правило, а на его кусок).

Что из этого следует? А то, что если действие в правиле каким-либо образом
предполагает дальнейшую обработку в файрволе, например это divert или pipe,
то пакет продолжит свое движение по правилам! Но наиболее интересен случай,
когда действием является skipto. В этом случае для пакетов в оба направления
(принадлежащих соединению) можно организовывать своего рода "подпрограммы" в
любом месте набора правил, применять к ним несколько действий — например,
отправить в pipe/queue, а потом часть пакетов запретить, другую разрешить (по
критериям, отличным от изначального условия создания динамического правила).

Тот факт, что на самом деле "перепрыгивание" выполняется на параметры
действия, позволяет использовать это для интересных вещей. В частности, с
использованием появившегося во FreeBSD 6.2 параметра tag на каждый пакет можно
навешивать внутриядерный тег, что в применении со skipto позволяет сделать, к
примеру, запоминание, с какого шлюза пришел входящий пакет на машине с каналами
к двум разным провайдерам, и ответные пакеты отправлять в тот канал, откуда они
пришли (допустим, у вашей машины только один IP-адрес, и сделать fwd на базе
внешнего адреса не получится), т.е. реализовать аналог reply-to из pf:

ipfw add 100 skipto 300 tag 1 in recv $ext_if1 keep-state
ipfw add 200 skipto 300 tag 2 in recv $ext_if2 keep-state
ipfw add 300 allow { recv $ext_if1 or recv $ext_if2 }  # входящие снаружи
ipfw add 400 allow in recv $int_if   # разрешить ответы на внутреннем проходе
ipfw add 500 fwd $gw1 tagged 1      # остались ответы на внешнем интерфейсе,
ipfw add 600 fwd $gw2 tagged 2      # зарулим их куда надо

Следующий пример, взятый из реального up-script'а mpd, хоть и несколько
запутан, но показывает, каким образом можно организовать на внешнем интерфейсе
одновременно NAT для внутренней сети (выпуская только тех пользователей,
которым это разрешено), ограничение полосы пропускания для каждого, лимит
одновременных соединений для каждого пользвателя (динамические правила),
причем с разными лимитами для HTTP-трафика и всего остального, и всё это — в
минимальном количестве правил:

# mpd up-script args vars
eif=$1
our_ip=$3
fw="/sbin/ipfw -q add"

# first split traffic to "incoming" and "outgoing from allowed hosts"
$fw 161 skipto  166 src-ip table\($allowed_int_hosts\) out xmit $eif
$fw 161 divert natd all from any to $our_ip in recv $eif
$fw 163 queue     1 ip from any to any in via $eif
$fw 164 allow       ip from any to any in via $eif
# deny world inet by default, both incoming and outgoing
$fw 165 deny        not src-ip $our_ip not dst-ip $our_ip via $eif
# don't allow users to open more than 9 WWW connects and 6 for other protocols
$fw 166 skipto 167 tcp from any to any 80 out xmit $eif recv int0 limit src-addr 9
$fw 166 skipto 167 all from any to any out xmit $eif recv int0 limit src-addr 6
# we are using here (undocumented) that 'limit' will drop overlimit packets,
# not go to next rule
$fw 167 divert natd ip from any to any out xmit $eif
$fw 168 allow       ip from $our_ip to any via $eif

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


UPDATE:
По многочисленным просьбам трудящихся, опишу некоторые туманные вопросы, не затронутые в посте.

1. Отличия предикатов via/xmit/recv.
Newsgroups: fido7.ru.unix.bsd
From: Vadim Goncharov <vadimnuclight@tpu.ru>
Subject: Re: ipfw dummynet на много пользователей.
X-Comment-To: Mike Yurlov
Date: Wed, 11 Mar 2009 17:10:28 +0600

Hi Mike Yurlov!

On Mon, 23 Feb 2009 13:48:25 +0000 (UTC); Mike Yurlov wrote about 'Re: ipfw dummynet на много пользователей.':

 MY> Пользуясь случаем прошу ликбеза по in/out via и recv/xmit. Если я правильно понял, то
 MY> in/out это направление прохождения пакета в данный момент, recv/xmit это
 MY> часть метаинформации пакета пока он путешествует по ядру и модулям. recv — через какой
 MY> интерфейс он был получен,  xmit — через какой  интерфейс будет передан на основе
 MY> таблицы маршрутизации (что может быть переиначено например через fwd).

При получении пакета на него прилепляется m_pkthdr.rcvif, который сохраняется
на всё время жизни пакета, его и проверяет recv, то есть оно действует и
на out-проходе.

Перед запуском файрвола на out-проходе выполняется просмотр таблицы
маршрутизации, интерфейс сообщается файрволу в oif, который проверяет xmit.
В текущей реализации fwd его не меняет, более того, fwd тут же делает allow
(немедленный возврат из файрвола), чтобы обойти проблему изменения xmit для
остальных правил. Причем, fwd на in-проходе его также НЕ изменит, см. мою
переписку с Гросбейном по поводу патча на это самое дело.

Ну а via просто на in работает как recv, а на out как xmit, позволяя записывать
сокращенно одним правилом два разных (одно с in recv, другое с out xmit), что
бывает удобно, например, для divert natd.

        case O_RECV:
                match = iface_match(m->m_pkthdr.rcvif, (ipfw_insn_if *)cmd);
                break;

        case O_XMIT:
                match = iface_match(oif, (ipfw_insn_if *)cmd);
                break;

        case O_VIA:
                match = iface_match(oif ? oif : m->m_pkthdr.rcvif, (ipfw_insn_if *)cmd);
                break;

Еще раз, другими словами: метаинформация о интерфейсе входа в машину записывается драйвером по получению и доступна для проверки recv всё время жизни пакета в ядре, информация же об интерфейсе xmit становится доступна только на выходе. Поэтому out recv int0 — не бессмысленное выражение, в отличие от бессмысленого in xmit ext0.

Для чего это может быть полезно? Например, чтобы запретить нашей машине быть "одноруким роутером", когда полученный на интерфейсе пакет должен по таблице маршрутизации уйти в ту же сеть (случается обычно из-за ошибки в настройках того, кто прислал нам этот пакет, и по умолчанию ему вместе с таким пакетом будет послан и icmp-редирект, сообщающий об этом):

ipfw add deny ip from any to any out recv fxp0 xmit fxp0

Ну а via просто выбирает наиболее подходящую сейчас проверку, если проход входящий, сработает как recv, если исходящий — то как xmit. Другими словами, если обратиться к самой первой схеме вверху поста: там 4 возможные комбинации in/out и recv/xmit, на каждой из двух сетевух. Если вам нужно точно указать, скажем, левый нижний на схеме проход, вы напишете in recv int0. А если нужно, чтобы правило срабатывало сразу на обоих проходах справа и только на них (или на обоих слева), то есть на одной сетевухе вне зависимости от направления, вы можете написать два одинаковых правила, одно с in recv ext0, другое с out xmit ext0. А можете одно — с via. То есть, можно написать:

ipfw add 2 divert 8668 all from any to any in recv ext0
ipfw add 2 divert 8668 all from any to any out xmit ext0

а можно:

ipfw add 2 divert 8668 all from any to any via ext0

и эти конструкции будут абсолютно эквивалентны по эффекту. Просто в ситуации, когда у вас простой набор правил, и они идут вот так вот подряд, второй вариант короче и насколько-то-долей-процента эффективнее в обработке пакетов.

А еще (это есть в мане, но я всё равно разжую), из серии "знаете ли вы, что..." — проверка функции iface_match() в ядре, которая обрабатывает recv/xmit/via (кусок исходника был выше), принимает не только имя интерфейса, но и IP-адреса, и даже шаблон имени в том же виде, в котором в шелле задаются имена файлов! То есть, можно написать:

ipfw add 2 pipe 3 all from any to any in recv ng[23]?*

и это будет ловить интерфейсы с именами с ng200 по ng299 и c ng300 по 399, а также с ng20 по ng29 и c ng30 по 39 (на самом деле с любыми символами в названии после, но мы же знаем, что mpd сделает названия только с циферками).

2. Динамические правила и ipfw fwd; теги.

Приведенный в посте пример того, как на динамических правилах сделать аналог reply-to в pf, на самом деле, не работает. В исходниках был обнаружен запрет специально именно этого случая в мохнатом 2000 году из-за какой-то паники ядра на тех версиях. Что давно уже не актуально, но это, конечно же, поправить забыли. Патчится одной строчкой — открываете /sys/netinet/ip_fw2.c, находите слова case O_FORWARD_IP: (конкретный патч не приведу, зависит от версии системы). И вот там чуть ниже в строчке кусок

if (!q || dyn_dir == MATCH_FORWARD)

надо заменить на

if (sa->sin_port && (!q || dyn_dir == MATCH_FORWARD))

[UPD: 06.07.11 патч закоммичен в 7.x и 8.x, патч более неактуален, теперь всё работает так, как и описано в посте]

Еще, кстати, не стоит забывать, что все имеющиеся на пакетах теги, будь то метаинформация самой системы (от того же IPSEC и много чего еще) или явно навешенные теги ipfw/pf — существуют только внутри ядра. То есть, если вывести пакет из ядра через divert, они потеряются.

3. Порядок вызова файрволов и IPSEC.

Вообще говоря, то, что нарисовано на второй схеме — это упрощенное изложение вызова ipfw. На самом деле, вызывается не непосредственно ipfw_chk(), а фреймворк запуска файрволов pfil(9), который уже вызовет включенные файрволы, то есть в конечном счете и ipfw_chk(). Но это же может быть и другой файрвол — например, pf, а может быть, и оба сразу, или даже три (ipfilter), а может и четыре (а кто знает, что вы там еще в систему установили?). Происходит это так: при загрузке системы (или модуля) файрвол регистрирует себя в pfil, тот дописывает его в список хуков входного прохода и в список выходного, причем в одном случае в конец, а в другом в начало, дабы всё было симметрично. Ну и потом в работе над каждым пакетом ip_input() вызывает pfil_run_hooks(), который все файрволы по списку и вызывает для пакета. И ip_output() поступает аналогично. Поскольку файрволу может быть приказано свои хуки и убрать из списка обработки (pf это делает по pfctl -e/-d, ipfw в 6.х только при загрузке-выгрузке модуля, но с 7.0 ведет себя как pf, по enable/disable), можно добиться такого порядка прохождения пакетов, какой вам нужен Подробнее об этом процессе можно прочитать на http://paix.org.ua/freebsd/fwpackets.html (или здесь, если не открывается), там же можно прочитать о методике экспериментального определения порядка при двух файрволах. А еще я надеюсь, вы не забыли, что при обработке пакета на layer2, с Ethernet-заголовками, ipfw вызывается уже не два, а ЧЕТЫРЕ раза? То есть, на 6.2, вот так:

in: железо -> bpf(4)/tcpdump -> ng_ether -> ipfw layer2 -> pfil(9)
out: pfil(9) -> ng_ether -> ipfw layer2 -> bpf(4)/tcpdump -> железо

Всё, однако, становится несколько геморройнее при вкомпилированном в ядре IPSEC. Оная вещь вообще доставляет админам хлопоты много в чем и на разных платформах, не обошлось без этого и в ядре FreeBSD — IPSEC нормально файрволы не учитывает. По исходникам 6.2, картинка выглядит так:

...а вот на этом месте ЖЖ не дал обновить пост, поругавшись на размер, хотя всего-то еще 5 килобайт...
UPD2: недостающие 5 Кб (а может, со временем и больше будет) теперь здесь: http://nuclight.livejournal.com/125453.html
Более поздние апдейты: по ссылке выше теперь

  • уточнение, что схема про IPSEC только для 6.x и неточна

  • ситуация с kern/147720 — патчем про динамические правила и fwd

  • пояснение про опкоды и выбор нужного tablearg, если в правиле несколько таблиц

Date: 2008-05-23 10:15 am (UTC)
From: [identity profile] iskatel.livejournal.com
Классно написал.. очень полезная статья.

Date: 2008-05-23 04:10 pm (UTC)
From: [identity profile] techquisitor.livejournal.com
Чудно. Пока что бегло пробежался по ней, на выходных, как будет время, уделю статье пристальное внимание. :)

Date: 2008-05-23 11:28 pm (UTC)
From: [identity profile] historic-truth.livejournal.com
Замечательно (нарисовано) ;)) -- чем вы пользуетесь для ASCII-art'а?

Date: 2008-05-23 11:32 pm (UTC)
From: [identity profile] historic-truth.livejournal.com
.. кстати, замените "роутер" на "раутер" (ударение на "а") - это так же неверно, как поразивший меня "перл" вместо правильного пёрл'а
(поверьте, я с штатах на работе каждый день слушаю, как надо ;)))))) )

Date: 2008-05-24 10:27 am (UTC)
From: [identity profile] nuclight.livejournal.com
ASCII-арт рисую редко, поэтому руками.

По поводу произношения - это проблемы американцев :) Я стараюсь писать наиболее близко к латинскому варианту - отражая правописание оригинала, оно у нас тут, не в америках, как-то важнее. Кроме того, по-английски всё-таки "пёрл" без "р" произносится, и "раута" вместо "раутер", так что эти варианты все равно произношения не передают :-Р

Date: 2008-05-25 11:46 am (UTC)
From: [identity profile] aster-ta.livejournal.com
ай спасибо, Айболит)

Date: 2008-05-26 11:23 am (UTC)
From: [identity profile] black-angel-by.livejournal.com
таки да, спасибо %)

Date: 2008-07-02 09:40 am (UTC)
From: [identity profile] pashustik.livejournal.com
в мемориз!
спасибо!

Date: 2008-08-27 06:27 am (UTC)
From: [identity profile] http://users.livejournal.com/_tartar_/
Хорошо отписал, спасибо.
Пытаюсь вникнуть в суть ipfw уже давно и сея "разжёванная морковка" очень кстати. ))
Но у меня есть вопрос относительно последнего примера конфига.
Разве строчки
$fw 161 skipto  166 src-ip table\($allowed_int_hosts\) out xmit $eif
$fw 161 divert natd all from any to $our_ip in recv $eif

и
$fw 166 skipto 167 tcp from any to any 80 out xmit $eif recv int0 limit src-addr 9
$fw 166 skipto 167 all from any to any out xmit $eif recv int0 limit src-addr 6

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

Date: 2008-08-31 02:15 pm (UTC)
From: [identity profile] nuclight.livejournal.com
Как видим из мана ipfw(8) (http://www.freebsd.org/cgi/man.cgi?query=ipfw&sektion=8):
rule_number
        Each rule is associated with a rule_number in the range 1..65535,
        with the latter reserved for the default rule.  Rules are checked
        sequentially by rule number.  Multiple rules can have the same
        number, in which case they are checked (and listed) according to
        the order in which they have been added.

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

Date: 2008-09-01 07:12 am (UTC)
From: [identity profile] http://users.livejournal.com/_tartar_/
О! Ещё один интересный упущенный момент для таких как я. Благодарю!

Date: 2008-09-02 02:27 pm (UTC)
From: [identity profile] nuclight.livejournal.com
Какой же он упущенный, когда он в мане в самых основах? Не дело это - разжевывать базовые вещи.

Date: 2008-10-08 02:33 am (UTC)
From: [identity profile] a-zaytcev.livejournal.com
Владимир, а сможете подсказать почему происходит вот такая штука:
http://www.opennet.ru/openforum/vsluhforumID1/82284.html
Методом тыка выяснил, что это связано с правилом keep-state. Но не могу понять почему оно так влияет на count.

Вообще-то меня зовут Вадим

Date: 2008-10-08 05:55 am (UTC)
From: [identity profile] nuclight.livejournal.com
Какая именно штука? Без полного набора говорить довольно бессмысленно, только общие соображения:

1. Между добавлением правил существуют временнЫе промежутки, в которые могут пролетать пакеты. Когда правила 100-101 уже были добавлены, а 108-109 еще не успели, это небольшое количество как раз могло пролететь.
2. Пакеты, подпадающие под keep-state и все дальнейшие в соединении, будут проверены на ЛЮБОМ первом попавшемся keep-state или check-state, до дальнейших правил дело не дойдет.

Почему и как всё это происходит, в посте описано, надо внимательно прочитать и подумать. Потом подумать над своим конкретным полным рулесетом.
From: [identity profile] a-zaytcev.livejournal.com
Вадим, прошу прощения за оплошность.
Насчёт 1-го, это так, но в данном случае количество пакетов, которое считалось в правилах 140 и 141 отличалось от нужного (правило 50) в разы...
Насчёт второго, кажется, я сейчас понял. Если у меня дальше в правилах идёт правило вроде allow tcp from <мой шлюз> to any keep-state, то все пакеты, которые будут попадать под динамические правила, обработаются на правиле 130, так что ли?! И при этом счетчик этого правила не увеличивается? (он всегда был 0). Если это так, то это всё объясняет.
From: [identity profile] nuclight.livejournal.com
"Если у меня дальше в правилах" - да, если там правило, которое за пределами показанного фрагмента, то обрабытываться они будут на первом попавшемся, допустим, 130, при этом они будут увеличивать счетчик на том правиле, которому принадлежит соединение, но не будут на 130. За этим и существует check-state, чтоб указать место проверки динамических правил явно, а на непонятно сходу каком keep-state. Что там у вас реально в динамике бегает и каким правилам принадлежат динамические правила (со своими счетчиками), расскажет ipfw -d show
From: [identity profile] a-zaytcev.livejournal.com
Но check-state у меня там далее в правилах тоже присутствует...
From: [identity profile] nuclight.livejournal.com
При отсутствии в правилах skipto располагать check-state не до всех keep-state/limit, а где-то после или в середине, смысла не имеет никакого.

Date: 2008-10-13 02:36 pm (UTC)
From: [identity profile] drtr0jan.livejournal.com
Очень полезная статья, пригодилось в грамотной настройке роутера :)

Возник небольшой вопросик, правда немного оффтопный (т.к. больше касается dummynet(4)):
Положим у нас one_pass=0 и есть две трубки (или очереди, что не суть важно): pipe 1 (queue 1) и pipe 2 (queue 2)
теперь есть два правила:
pipe 1 ip from any to me 22
pipe 2 ip from any to me

Как в данном случае будет работать шейпер? Мне надо, чтобы SSH траффик шёл в одну трубу (очередь), а весь остальной траффик шёл в другую трубу (очередь).
Заранее благодарен
From: [identity profile] nuclight.livejournal.com
pipe 1 ip from any to me 22
pipe 2 ip from any to me not dst-port 22

Date: 2008-10-31 06:08 pm (UTC)
From: [identity profile] blacklion.livejournal.com
вот тут, с комментариями:
http://community.livejournal.com/ru_freebsd/169127.html

Не могли бы объяснить — что происходит? Почему добавление совершенно необязательного out у правила divert что-то меняет в ситуации?

Date: 2008-10-31 06:28 pm (UTC)
From: [identity profile] nuclight.livejournal.com
Потому что, как верно заметили в комментах, туда попадают пакеты и с in, и с out. Динамической правило срабатывает в обоих направлениях, и применяет действие из родительского правила (skipto). В описании это нарисовано на схеме "фрагмент ipfw_chk(): начало правила с keep-state".

Date: 2008-10-31 06:32 pm (UTC)
From: [identity profile] blacklion.livejournal.com
Да, я уже врубился. Это чуть-чуть убивает чистоту разделения фарйволла на куски по направлениям и адаптерам.

Теперь вот думаю, как туда воткнуть ответы с того же IP, на который запрос… А то сейчас ping не-дефолтового IP снаружи вызывает ответы через default-канал. Смешно :)

Date: 2008-10-31 06:46 pm (UTC)
From: [identity profile] nuclight.livejournal.com
Смотря каковы потребности (что делает рулесет целиком). Возможно, вполне достаточно будет natd multiple instances (недокументировано присутствует во всех версиях с 5.4, документирован в мане в 6.3-PRERELEASE и 7.1-PRERELEASE), без всяких динамических правил самого ipfw (ибо natd свои стэйты отслеживает же). Я так дома два канала разруливаю (внутренняя сеть натится в тот или иной белый адрес), входящие соединения продолжают идти через тот интерфейс, через который пришли, исходящие главным образом согласно дефолтному роутингу идут в основной канал, но часть (некоторые подсети, исходящие) заворачивается в другой.

Date: 2008-10-31 06:52 pm (UTC)
From: [identity profile] blacklion.livejournal.com
natd у меня только на локалке провайдера, которая самая неинтересная. А два интересных канала — это два VPN'а у двух провайдеров, оба силами mpd5, и nat делает он же для упрощения собственно правил ipfw.

Т.е. тоже дома два канала с двумя белыми адресами — один PPPoE (ADSL) другой L2TP (ethernet-подключение). Ну и локалка провайдера (того, что интернет даёт по L2TP, адреса 10/8, DHCP) и две локалки МОИ — проводная и безпроводная + кросс в ADSL-модем :)

Я могу показать комментированный рулесет с макросами, который получился достаточно понятным, благо обращение к natd там всего одно, а не три. Я себе представляю, какой винегрет получился бы стремя natd :)

Т.е. задачи ровно те же самые :)

(no subject)

From: [identity profile] nuclight.livejournal.com - Date: 2008-10-31 07:08 pm (UTC) - Expand

(no subject)

From: [identity profile] blacklion.livejournal.com - Date: 2008-10-31 07:49 pm (UTC) - Expand

(no subject)

From: [identity profile] nuclight.livejournal.com - Date: 2008-11-01 12:29 pm (UTC) - Expand

Date: 2008-11-11 01:35 pm (UTC)
From: [identity profile] 66zombie66.livejournal.com
а можно увидеть конфигурацию файрвола реализующего подобное поведение?

Вопрос автору статьи!

Date: 2009-01-12 08:35 am (UTC)
From: [identity profile] rasskazovbsd.livejournal.com
Каким материалом вы пользовались? Статья показывает не только работу изнутри, но и логику к которой не так уже легко прийти изучая механизм ЛЮБОГО пакетного фильтр.

Re: Вопрос автору статьи!

Date: 2009-01-12 10:43 am (UTC)
From: [identity profile] nuclight.livejournal.com
Ман-страницей ipfw, исходными текстами (главным образом той части, что в ядре) и в небольшой степени опыт общения с теми старшими товарищами из разработчиков, кто в этой части уже разбирался. А что вы имеете ввиду насчет "прийти", я не понял. Вообще с логикой у меня всё хорошо с детства. Зачем любой пакетный фильтр, непонятно - в общем виде у них всех похожая логика, так как те же самые сети TCP/IP, ну а конкретную логику надо по конкретному фильтру смотреть, обладая обширным опытом, это не сложно.

Date: 2009-05-06 06:37 am (UTC)
From: [identity profile] pahanivo.livejournal.com
Отличная статья. Многое для себя наконец прояснил.
Например почему в манах написано что после дайверт правила пакет покидает фаервол, а судя по правилам с log он после дайверта продолжает движения со следующего правила.
(deleted comment)

Date: 2009-08-19 04:57 am (UTC)
From: [identity profile] nuclight.livejournal.com
При такой каше в голове статья, конечно же, не поможет. Могу посоветовать найти в своем городе профессионала и заплатить ему денег за настройку.
(deleted comment)

Date: 2009-08-19 08:35 am (UTC)
From: [identity profile] nuclight.livejournal.com
Даже и не знаю, что посоветовать. Большинство админов как-то вот научилось же? Значит, видимо, это где-то у вас пробелы. А я документацией для совсем начинающих не занимаюсь, увы.
(deleted comment)

(no subject)

From: [identity profile] nuclight.livejournal.com - Date: 2009-08-19 09:56 am (UTC) - Expand

Date: 2009-08-19 09:21 am (UTC)
From: [identity profile] andrewkochetkov.livejournal.com
"Посмотрел - и правда, даже при не запущенном natd пакеты перекладываются из int0 в ext0 и обратно. То есть, по умолчанию, получается двухсторонний nat"
Что значит "перекладываются"? Сдаётся мне, вы имеете в виду обычный форвардинг. Нат включается через ipfw nat (man ipfw).
P.S. Читать лиссяру - очень плохо. Это унылое говно. Могу привести аналогию: там написано, как пройти в данном конкретном случае по натянутой струне на высоте двух метров, но для трёх метров это уже не работает; вы ёбнетесь и разобьёте себе голову.
(deleted comment)

Date: 2009-08-20 09:39 am (UTC)
From: [identity profile] nuclight.livejournal.com
Видите ли, админу платят деньги за то, что он умеет - знать, как сделать, и может это сделать. Админ обучается, в частности, читая документацию. Если человек не понимает, что написано в документации - какой из него админ? Значит ему не хватает базы для того, чтобы её понимать. А это уже за просто так, бесплатно, не лечится. Это раз. С другой стороны, по фразам типа "намутить фаер" заметно, что и общий уровень развития не велик, не только база по сетям. Специалисту же в таком случае выгодно, чтобы существовали лохи, с которых можно стричь бабки за то, что они не хотят разбираться и понимать. Выводы делайте сами - ибо для тех, кто хочет, пишется документация, и, как видно по комментам выше, большинство статью поняло.

bridge и fwd

Date: 2009-09-25 07:45 pm (UTC)
From: [identity profile] dio1740.livejournal.com
давно хотел решить такой вопрос: как можно реализовать bridge с функцией pbr? по дефолту (что четко написано в мане ipfw) правила fwd не срабатывают на layer-2 пакетах. в нете можно найти маны, которые предлагают внести изменения в исходники что бы обойти этот запрет, но, к сожалению, после этих изменению ядро не пересобираеться. если это возможно, то подскажите что точно для этого надо сделать. на данный момент интересует версия 7.2

Date: 2009-12-01 06:57 am (UTC)
From: [identity profile] yuriiav.livejournal.com
очень интересная статья, спасибо, а про ipsec нельзя опубликовать? тоже актуально очень

Date: 2010-02-19 06:12 pm (UTC)
From: [identity profile] volodint.livejournal.com
Здравствуйте,
Растолкуйте, как в freebsd, корректно составить правила для FTP.
Пробовал уже просто так
${FwCMD} add allow tcp from any 21 to any
${FwCMD} add allow tcp from any to any 21
${FwCMD} add allow tcp from any 20 to any
${FwCMD} add allow tcp from any to any 20
#пассив
${FwCMD} add allow tcp from any to any 49152-65535

Порты ставятся не со всех серверов именно по ftp. Даже ftp.freebsd.org , как-то через раз срабатывает.
(deleted comment)

Re: теория и практика

Date: 2010-03-03 02:27 pm (UTC)
From: [identity profile] nuclight.livejournal.com
Есть еще хорошая цитата, автор, кажется, Больцман:
"Нет ничего практичнее хорошей теории".
Выделение в цитате расставить по вкусу :)
(deleted comment)

Re: теория и практика

Date: 2010-03-03 05:28 pm (UTC)
From: [identity profile] nuclight.livejournal.com
При полностью нулевых масках оно вообще динамических очередей не создаст - будут все в одной. Посмотреть, чего оно реально напложило, можно по ipfw pipe show (правда, айпишники в динамических будут те, которые туда первые попали, а не все хором).
(deleted comment)

Re: теория и практика

From: [identity profile] nuclight.livejournal.com - Date: 2010-03-04 02:30 pm (UTC) - Expand

Date: 2010-03-04 07:06 pm (UTC)
From: [identity profile] sam-sobaka.livejournal.com
Спасибо
Все удалил. Оно тут дествительно не к месту

Date: 2010-03-04 07:10 pm (UTC)
From: [identity profile] nuclight.livejournal.com
Первый в ветке-то не требовалось тереть, вполне по теме было.

Date: 2010-03-04 09:02 pm (UTC)
From: [identity profile] volodint.livejournal.com
Нашел, что искал. Получилось так для шлюза.
${FwCMD} add allow tcp from me to any 20,21 via rl0 setup
# для пассива
${FwCMD} add allow tcp from me to any 1024-65535 via rl0 setup
# для установившихся
${FwCMD} add allow tcp from any to any established

Спасибо, Вадим, за статью. Очень помогла в осознании работы файрволла

Date: 2010-05-04 08:05 am (UTC)
From: [identity profile] zoobok.livejournal.com
благодарю за статью. многое черпнул и нашел некоторые ответы на свои вопросы, которые ищу уже определенное время.

Спасибо!

Date: 2010-12-01 01:45 pm (UTC)
From: [identity profile] daemony (from livejournal.com)
Вадим, спасибо большое.
Ваше изложение - это колосальный труд.
Сам люблю что-то писать, разъяснять, но в таком объеме, как у Вас я бы, пожалуй, изложить что-то не решился.
Прочитал все от корки до корки. Многое знал, но что-то нашел нового для себя.
В своем блоге я изложил пример настройки маршрутизатора с двумя каналами в Интернет - http://daemony.org/freebsd/freebsd-server-and-two-internet-connections.html Материал основан на реальной практике и во многом опирается на ваш труд.
Еще раз спасибо.

February 2017

S M T W T F S
   1 234
567891011
12131415161718
19202122232425
262728    

Style Credit

Expand Cut Tags

No cut tags
Page generated Jul. 15th, 2025 12:22 am
Powered by Dreamwidth Studios