FreeBSD. Путь сетевого пакета внутри ядра +11


AliExpress RU&CIS

  1. Файрвол PF в ОС FreeBSD
  2. FreeBSD. Фильтрация трафика PF
  3. FreeBSD. трансляции, тэги и якоря в PF
  4. FreeBSD. Условная маршрутизация средствами PF
  5. FreeBSD. Путь сетевого пакета внутри ядра. <- Вы здесь

В прошлых статьях мы разобрали PF, его основные возможности, и попробовали применить этот файрвол в различных ситуациях. Однако, простого знания, какие правила и в какой последовательности надо поставить в конфигурации, для достижения цели не всегда достаточно. Многое становится понятно только если взглянуть чуть глубже: на уровень ядра ОС, и того, как файрволы с ним взаимодействуют.



Мы не будем разбирать уровни драйвера сетевой карты (2 уровень модели OSI), а сразу поднимемся на 3 уровень, где работает tcp/ip стек ядра.


Для каждого входящего пакета первым делом создаётся объект mbuf. Это базовая структура данных ядра, в которой хранится как сам пакет, так и все метаданные относительно него: aдрес источника и назначения, маршрут (next hop), ttl, метки, и прочие атрибуты. В таких же объектах хранятся буферы локальных сокетов. Состоит он из заголовков и небольшого внутреннего буфера данных. Затем этот mbuf начинает путешествие по стэку ядра.


Вот упрощенная диаграмма пути пакета:

ip_input()


На выходе со второго уровня пакет попадает в функцию ip_input().


  • Проверяется ttl и прочие параметры.
  • Затем проходит по условиям ALTQ (система приоритезации трафика), если такие назначены.
  • IPSEC_CAPS(), если назначен перехватчик пакетов ipsec, то пакет будет передан ему, и, если он не вернет пакет, обработка завершится. На выходе ipsec положит расшифрованный пакет обратно в начало ip_input(), так что обработка заканчивается.
  • pfil. Здесь пакет передаётся файрволам в порядке регистрации. То есть, кто первый был загружен, того и тапки.
  • Проверяется назначение пакета. Если назначение пакета — не локальный IP, то он передаётся функции ip_forward(). Либо обработка продолжается.
  • Проходит попытка сборки (reassemble), если пакет фрагментирован.
  • Локальный пакет отправляется в IPSEC_INPUT().
  • Отправка в локальный сокет, если это возможно. Если невозможно — пакет отбрасывается с отправкой обратно icmp error через функцию ip_output().

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


ip_forward()


Здесь происходят различные проверки на возможность отправки.


  • Происходит вызов функции обработки ipsec — IPSEC_FORWARD().
  • Различные проверки. Например, установлен ли параметр net.inet.ip.forwarding в значение 1. Если что-то пошло не так, то отправляется icmp error в функцию ip_output().
  • Если пакет должен выйти с того же интерфейса, с которого пришел, источнику будет отправлен icmp_redirect, если источник в той же сети.
  • Декрементится ttl.
  • Пакет отправляется в ip_output().

ip_output()


Функция подготовки и отправки пакета через сетевой интерфейс.


  • Поиск маршрута. Если пакет принадлежит потоку, то маршрут будет взят из кэша маршрутов.
  • Передача пакета обработчику ipsec — IPSEC_OUTPUT().
  • pfil — второй вызов файрволов в порядке регистрации.
  • Проверка, изменилось ли назначение (dst), или таблица маршрутизации (fib) пакета после выхода из файрволов. Если изменилось, то возврат в начало. Однако, второй раз для пакета уже не будут вызваны обработчики файрволов.
  • При необходимости, происходит попытка фрагментации пакетов.
  • Пакет передаётся на второй уровень для отправки.

Выводы


Таким образом, на третьем уровне пакет отправляется на обработку файрволлам дважды. Один раз на входе, один раз на выходе. При этом файрвол может как пропустить пакет, так и отбросить его. Или создать новый, например, поменяв источник, и передать его соответствующей функции ядра. Файрволы вообще могут менять любые поля mbuf.


К примеру, в предыдущих статьях мы использовали правила pf с ключевыми словами in и out.


pass in on re0 inet...

Такие правила будут обрабатываться только при вызове ip_input. Соответственно правила с out — в ip_output.


pass out quick on $ExtIf2 ...

В сочетании с anchors это позволяет добиться впечатляющей оптимизации даже при большом количестве правил.


Ключевые слова route-to или reply-to поменяют next hop для соответствующих пакетов:


pass in quick on $ExtIf2 reply-to ($ExtIf2 $ExtIf2Gw ) tagged DSTNAT
pass in quick on $IntIf1 route-to ( $Tun1 $Tun1Gw ) from ($IntIf1:network)

Или укажет необходимость обработки пакета в контексте таблицы маршрутизации опцией rtable:


pass in quick on $IntIf2 from { $IntIf2Net } rtable 1 tag PASS

Заключение


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


Описанные схемы мы применяем для создания нашего ИКСа — универсального шлюза безопасности. Защита сети, прокси, встроенный антивирус, фильтрация контента по спискам Минюста, контроль доступа и учет трафика, VPN для удаленной работы, почта, ftp-, web- и jabber-сервер, IP-телефония… Тестируйте все и сразу, скачав ИКС с официального сайта. Триал 35 дней, есть бесплатная версия на 9 пользователей.




Комментарии (3):

  1. kernelconf
    /#22963474 / +1

    Если в rc.conf первым указан PF, то в него пакет попадёт в первую очередь.

    Думаю, что это не так. Скорее всего, надо смотреть в rcorder, в каком порядке запускаются демоны.

    • M14xa
      /#22963536

      Да, Вы абсолютно правы, уберу это из статьи. По поводу порядка загрузки файрволов и настройки — хоть еще одну статью пиши. Спасибо!

    • skeletor
      /#22969838

      Не вариант. К примеру, на работающем ipfw вы подгрузили pf или ipfilter, то rcorder покажет неверную информацию. Если кратко, то так — www.opennet.ru/tips/1431_ipfw_firwall_pf_freebsd.shtml

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