nuclight: (Default)
[personal profile] nuclight
К посту http://nuclight.livejournal.com/124348.html в свое время не всё влезло, из-за ограничений на объем поста в ЖЖ (судя по всему 65535 в байтах, причем UTF-8, так как для разной пропорции русского текста длина переменная и непредсказуемая). Ниже идет недостающий кусок, с того места, где там прервалось. Может быть, со временем, что-нибудь еще добавится :)
Итак, продолжение UPD2 про IPSEC:

...Хотя я не знаю, насколько в нем большой смысл и понятность — в IPSEC я не эксперт совсем, случая хорошо его пощупать мне не представилось, черный ящик такой. Потому и не выкладывал, собственно — это выдержка из письма попросившему человеку... Да и версия системы уже старовата. Но пусть будет. Лучше, чем ничего.

...Всё, однако, становится несколько геморройнее при вкомпилированном в ядре IPSEC. Оная вещь вообще доставляет админам хлопоты много в чем и на разных платформах, не обошлось без этого и в ядре FreeBSD — IPSEC нормально файрволы не учитывает. По исходникам 6.2, картинка выглядит так:
 
         |
         v  вход в ip_input()
   ======|===================
         |
    основные проверки заголовка
         |
#if вкомпилирован IPSEC (или FAST_IPSEC) без IPSEC_FILTERGIF
         |
     есть ли на пакете история ipsec?
         |     \
        нет    да-->----можно доверять,----->-.
         |             пропустим файрвол      |
#endif
         |                                    |
         v                                    v
         |                                    |
   прогнать пакет через pfil_run_hooks()      |
   был ли пакет разрешен?                     |
         |    \                               |         DENY: уничтожить
         да    нет->--------------------------|----->-- пакет молча (некому
         |                                    |         возвращать ошибку)
         | ___________________________________|
         |/
         v
         |
   проверка адреса назначения пакета — нам или не нам?
         |        \
        нам        не нам, будем готовить к ip_forward()
         |                             |
         v                             v
         |                             |
#if вкомпилирован IPSEC или FAST_IPSEC
         |                             |
   выполнение входной       выполнение входной обработи
    обработки IPSEC            IPSEC для форвардинга
         |                             |
#endif
         v                             v
         |                             |
   передача пакета на L4,     проверка sysctl net.inet.ip.forwarding
   локальным приложениям           и вызов ip_forward()
         |                             |
         v                             v
   =========== ВЫХОД из ip_input() ===============

Тут следует обратить внимание на проверку истории перед вызовом файрволов — это рассчитано на туннельные интерфейсы. То есть, поскольку ip_input() вызывается драйвером интерфейса, для туннельного режима IPSEC реальная последовательность будет выглядеть так: реальный сетевой интерфейс -> ip_input() -> дешифровка IPSEC, пакет передан на туннельный интерфейс, в метаинформации пакета помечено, что он прошел через IPSEC -> ip_input() расшифрованного пакета на туннельном интерфейсе.
         |
         v  вход в ip_output()
   ======|===================
         |
    просмотр таблицы маршрутизации и т.п. подготовка <----.
         |                                                |
#if вкомпилирован IPSEC или FAST_IPSEC                    |
         |                                                |
    применение политик IPSEC                              |
         |                                                |
#endif                                                    |
         |                                           поставить флаг
         |                                           пропуска ipfw
    вызов pfil_run_hooks(), что сказали файрволы?         |
         |              |            \                    ^
       всё OK      DENY пакета       ipfw fwd             |
         |              |                \________________'
         v         вернуть EPERM
         |         вызвавшему нас верхнему уровню
         |
    ВЫХОД из ip_output(): поставить
    пакет в очередь выходного интерфейса
         |
         v

Что из этого видно? А то, что IPSEC не учитывает схему взаимодействия ipfw с внешними сущностями. Например, ipfw divert выведет пакет из ip_output(), после обработки пакет будет запущен в ip_output() вновь, и попадет в IPSEC до ipfw. В некоторых конфигурациях это людям действительно мешает.

В ip_input() также можно заметить проверку истории IPSEC. Ровно это же самое действие доступно позже и в ipfw, с помощью одноименного предиката — ipsec.

UPD3: По состоянию на 17.09.10 выше только описание порядка IPSEC для 6.x (в других версиях уже другое), да и то, как выяснилось, неточное. Кроме того:

1. В первом апдейте выше упоминались динамические правила и fwd, для работы которых необходим патч. Тов. Dmitriy Demidov сделал баг-репорт kern/147720, в котором приводится этот патч. Делать именно так пока не хотят, мол, когда-нибудь в будущем будет придумано правильное решение на базе setfib, и всё такое. Но официальных комментариев к этому PR на текущий момент нет. Кому нужна эта функциональность — не поленитесь, отметьтесь там, пусть пошевелятся раньше наступления светлого будущего.

2. Порядок обработки внутри одного правила был затронут лишь вскользь, где говорилось про оптимизацию OR-блоков. В общем-то, точно такой же и во всём правиле, но есть нюансы. Каждое правило ipfw представляет аналог BPF-программы, где каждая опция один-в-один транслируется в опкод ipfw2 (подробнее см. пост о BPF). При этом сначала по порядку слева направо записываются опкоды опций из тела правила, затем идут опкоды модификаторов действий, наконец, последним — сам action, и эта программа просто исполняется (в отличие от BPF, в ней нет условных переходов, кроме OR-блоков). Что в этом знании полезного для практики, кроме вылизывания при оптимизации? Предположим, есть правило:

add pipe tablearg log all from table(1) to table(2)

Тогда при проверке сначала будет исполнен опкод O_IP_SRC_LOOKUP для таблицы 1, если адрес подходит, исполнение продолжается и переменная tablearg получается значение из таблицы, затем аналогично выполняется O_IP_DST_LOOKUP для адреса назначения, который ищется в таблице 2, и если нашелся, то переменная tablearg переписывается значением из таблицы 2, после чего O_LOG всегда "совпадает", отправляя данные о пакете в syslog. Каждый опкод возвращает true/false, если false, то считается, что всё правило несовпало, и машина переходит к следующему правилу (см. схемы ранее); только OR-блок внутри себя проверяет опкоды далее при false. Так что до O_PIPE, непосредственно отправляющего пакет в трубу, очередь дойдет в случае совпадения всех перед ним.

Как можно видеть, здесь tablearg получит значение из той таблицы, которая была позже (правее в правиле), т.е. table(2). Но что, если нужно значение из table(1)? Тогда придется переписать правило так, чтобы table(1) была последней:

add pipe tablearg log dst-ip table(2) src-ip table(1)

Хоть синтаксис без from ... to многим и покажется непривычен, иначе в такой ситуации никак.

UPD4: По состоянию на 07.07.11, ситуация из апдейта №3 выше теперь такая:

1. Ситуация с динамическими правилами и fwd, для работы которых необходим патч, исправлена, PR kern/147720 теперь закрыт. Теперь всё работает так, как описано в посте, исправление закоммичено в 8-STABLE (r223819) и 7-STABLE (r223820). Заодно закрыли несколько других PR с той же причиной (кому-то это и прозрачный сквид ломало). Исправили без всякого светлого будущего, именно в этом месте.

2. Для выстановки tablearg из нужной таблицы начиная с 8.1R можно применять новую опцию ipfw:

lookup {dst-ip | dst-port | src-ip | src-port | uid | jail} N

Она точно так же выполняет просмотр таблицы с номером N и выставляет tablearg, но позволяет делать это не только для IP-адресов, но так же и помещать в таблицу номера портов, uid и jailid, вместо IP-адресов. Однако порядок просмотра правила и выстановки tablearg слева направо, описанный в апдейте №3 выше, сохраняется — что следует учитывать на случай, если таблицы опять-таки используются в правиле несколько раз.

3. В готовящийся релиз 9.0 (статус MFC пока неясен) добавлены новые действия для правил ipfw: call и return. В соответствии с духом ipfw как своеобразного ассемблера, делают они именно это — вызывают процедуру и возвращаются из неё (причем, как и в ассемблере, границы условны, можно в одном месте прыгать в начало процедуры, в другом — в середину). В отличие от skipto, вызывать можно правило с любым номером, т.е. делать и прыжки назад — skipto разрешен только вперед во избежание зацикливания пакета при ошибке пользователя. Использовано это может быть для упрощения организации сложных рулесетов, поскольку "процедуры" являются практически тем же, что и цепочки в iptables, просто не выделены в отдельный объект. Следует иметь в виду подводные камни использования этого дела в сложных случаях, особенно при ошибках пользователя (общность стека для in и out-проходов, вывод пакета из ядра, ошибки выделения памяти и т.д.), подробнее всё это описано в мане.

Date: 2010-03-03 05:43 pm (UTC)
From: [identity profile] dadv.livejournal.com
Текст действительно устарел, да и изначально содержал неточность. Я говорю о судьбе пакета после успешной расшифровки его реализацией KAME IPSEC в FreeBSD 6.x и ранее - пакет, действительно, попадает на L4 для локальной доставки и это может быть плохо - если на машине работает NAT, то правильней будет пропустить пакет через NAT и после этого может оказаться, что он предназначен вовсе не для локальной доставки, а для дальнейшего форвардинга по таблице роутинга, обычным путем.

Именно так делает FAST_IPSEC в шестерке - не направляет расшифрованный пакет сразу в L4, а снова пропускает по ip_input и pfil, в случае natd срабатывает divert и т.п. Подробное изложение, со схемой сети и с патчем для FreeBSD6, вводящим sysctl для включения такой обработки пакета для KAME IPSEC есть тут:

http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/123793

Патч работает у меня в продакшне скоро два года как, без проблем. bz@ отказался его коммитить в шестерку. В семерке и новее это поведение единственно возможное.

Date: 2010-09-24 02:56 pm (UTC)
From: [identity profile] http://users.livejournal.com/_slw/
а можно еще добавить про ipfw nat?

и из man ipfw не понятен переход от длинного правила ipfw nat 123 config к пачке строчек ipfw nat 1 config .. ipfw nat 5 config.

там что, по инстансам будет бежать в порядке возрастания номеров? или надо будет кучу add nat добавлять?

Date: 2010-09-24 03:09 pm (UTC)
From: [identity profile] nuclight.livejournal.com
Его следует рассматривать как еще одну сущность за пределами, полностью аналогичную divert/dummynet/netgraph и т.д. Если упрощенно, то здесь полная аналогия с natd: ipfw nat - это конфиг инстанса ната в ядре, навроде процесса natd, только в ядре. Т.е. само по себе создание инстанса еще ничего не делает, хоть сотню их сделайте, на номера всем наплевать. Соответственно, реально трафик в него отправляется через ipfw add nat - и эти правила будут полностью аналогичны ipfw add divert, и пишутся по ровно тем же законам.

Вот только обвязка ipfw nat сделана довольно криво - когда у вас много редиректов или инстансов, оно начинает глючить. Причем это проблема именно конфигурялки. Некоторые для этого патчат ядро... ну в общем поищите в базе PR.

Date: 2010-09-24 03:17 pm (UTC)
From: [identity profile] http://users.livejournal.com/_slw/
ну про недостаточность NAT_BUF_LEN я уже в курсе.

а непонятно следующее -- если по add nat отправлю трафик, который не попадает под редирект, например, он стухнет или вернется и на следующее правило пойдет? т.е. надо ли заморачиваться на check_state и всем остальным?

и опять же, к ману. вроде как из их примера следует эквивалентность предложенного разбиения на правила. из твоих слов следует противоположное.

Date: 2010-09-24 03:26 pm (UTC)
From: [identity profile] nuclight.livejournal.com
Я же сказал - абсолютно те же принципы, что и с divert в natd (если natd не модифицировал пакет, то и ipfw nat это не сделает). Абсолютно. Пишется точно так же. Ну, за исключением возврата на точно следующее, а не следующий номер (как в divert), и добавленной возможности one_pass. А что там конкретно в Вашем полном наборе правил происходит - это уж Вам на месте виднее.

>вроде как из их примера следует эквивалентность предложенного разбиения на правила. из твоих слов следует противоположное.

О чем конкретно речь? Вопрос мне был задан другой, на него я и ответил.

Date: 2010-09-24 03:30 pm (UTC)
From: [identity profile] http://users.livejournal.com/_slw/
в примерах man ipfw написанно

===
Or a redirect rule with mixed modes could looks like:

ipfw nat 123 config redirect_addr 10.0.0.1 10.0.0.66
redirect_port tcp 192.168.0.1:80 500
redirect_proto udp 192.168.1.43 192.168.1.1
redirect_addr 192.168.0.10,192.168.0.11
10.0.0.100 # LSNAT
redirect_port tcp 192.168.0.1:80,192.168.0.10:22
500 # LSNAT

or it could be split in:

ipfw nat 1 config redirect_addr 10.0.0.1 10.0.0.66
ipfw nat 2 config redirect_port tcp 192.168.0.1:80 500
ipfw nat 3 config redirect_proto udp 192.168.1.43 192.168.1.1
ipfw nat 4 config redirect_addr
192.168.0.10,192.168.0.11,192.168.0.12
10.0.0.100
ipfw nat 5 config redirect_port tcp
192.168.0.1:80,192.168.0.10:22,192.168.0.20:25 500
===
From: [identity profile] nuclight.livejournal.com
Явно ошибка в документации, send-pr(1).
From: [identity profile] http://users.livejournal.com/_slw/
не-не-не-дэвид-блэйн
если всем один номер, то останется только один -- последний.

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. 12th, 2025 06:08 pm
Powered by Dreamwidth Studios