Операционная система ЗОСРВ «Нейтрино» > Руководство разработчика > Интерфейсы различных подсистем > Сетевая подсистема > Общие сведения > Фильтрация пакетов



Фильтрация пакетов

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

В этой главе:

Фильтры пакетов
Интерфейс фильтра пакетов (Packet Filter, PF)
Модуль фильтров пакетов (pf): брандмауэры и NAT
Фильтр пакетов Беркли (Berkeley Packet Filter, BPF)

Фильтры пакетов

В фильтрации пакетов участвуют следующие псевдоустройства:

Псевдоустройство pf реализовано с использованием pfil хуков. bpf представляет собой средство прослушивания всех сетевых драйверов. Мы кратко рассмотрим их подключение к остальной части стека.

Интерфейс фильтра пакетов (Packet Filter, PF)

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

Интерфейс pfil является одним из множества уровней, в которых пользовательское приложение может регистрироваться для выполнения в контексте стека. В результате компиляции этих уровней формируются загружаемые совместно используемые модули (Loadable Shared Modules, lsm) или загружаемые модули ядра (Loadable Kernel Modules, lkm) в терминологии BSD.

Существуют два обязательных этапа регистрации в стеке io-pkt-*:

Совместно используемые модули динамически загружаются в стек. Их можно указывать в командной строке с помощью ключа -p при запуске менеджера io-pkt-*, либо подключать к существующему процессу io-pkt-* командой mount.

Модуль приложения должен включать в себя точку входа, которая определяется следующим образом:

#include "sys/io-pkt.h"
#include "nw_datastruct.h"
int mod_entry( void *dll_hdl, struct _iopkt_self *iopkt, char *options )
{
...
}

Входная функция имеет следующие параметры:

void *dll_hdl
Непрозрачный указатель, который идентифицирует совместно используемый модуль в стеке io-pkt-*.
struct _iopkt_self *iopkt
Структура, с помощью которой стек работает с собственными внутренними данными.
char *options
Строка пользовательских параметров, которую разбирает модуль.

Далее следует структура регистрации, которую ищет стек для получения точки входа с помощью функции dlopen() после загрузки модуля:

struct _iopkt_lsm_entry IOPKT_LSM_ENTRY_SYM( mod ) = IOPKT_LSM_ENTRY_SYM_INIT( mod_entry );

Регистрация точки входа осуществляется всеми совместно используемыми модулями независимо от уровня, к которому присоединяется остальная часть кода. Для регистрации на уровне pfil используются следующие функции:

#include <sys/param.h>
#include <sys/mbuf.h>
#include <net/if.h>
#include <net/pfil.h>
struct pfil_head * pfil_head_get( int af, u_long dlt );
struct packet_filter_hook * pfil_hook_get( int dir, struct pfil_head *ph );
int pfil_add_hook( int (*func)(), void *arg, int flags, struct pfil_head *ph );
int pfil_remove_hook( int (*func)(), void *arg, int flags, struct pfil_head *ph );
int (*func)( void *arg, struct mbuf **mp, struct ifnet *, int dir );

Функция head_get() возвращает заголовок соответствующего списка функций-хуков pfil. Аргумент af может иметь значение PFIL_TYPE_AF (хук семейства адресов) или PFIL_TYPE_IFNET (хук интерфейса) для «стандартных» интерфейсов.

Если указано значение PFIL_TYPE_AF, аргумент dlt (Data Link Type, тип канала передачи), содержит семейство протоколов. В настоящее время поддерживаются точки фильтрации только для AF_INET (IPv4) или AF_INET6 (IPv6).

При использовании хука интерфейса (PFIL_TYPE_IFNET) аргумент dlt содержит указатель на структуру сетевого интерфейса. В этом случае все присоединяемые хуки относятся к указанному сетевому интерфейсу.

После получения заголовка соответствующего списка можно добавить хук в список фильтра с помощью функции pfil_add_hook(). Эта функция принимает в качестве аргументов функцию-хук фильтра, непрозрачный указатель, который передается в пользовательскую функцию фильтра как arg, описанные ниже флаги (параметр flags) и заголовок соответствующего списка, возвращаемый функцией pfil_head_get().

Параметр flags определяет, для каких пакетов вызывается функция-хук:

PFIL_IN
для входящих пакетов
PFIL_OUT
для исходящих пакетов
PFIL_ALL
для входящих и исходящих пакетов

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

В хуках интерфейсов аргумент flags может иметь следующие значения:

PFIL_IFADDR
Вызов при перенастройке интерфейса (mbuf ** содержит номер ioctl()).
PFIL_IFNET
Вызов при присоединении или отсоединении интерфейса (mbuf ** содержит значение PFIL_IFNET_ATTACH или PFIL_IFNET_DETACH).

Ниже приведен пример простого хука pfil, который отображает информацию о присоединении и отсоединении интерфейса. При отсоединении интерфейса с помощью ifconfig iface destroy, фильтр выгружается.

#include <sys/types.h>
#include <errno.h>
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <net/if.h>
#include <net/pfil.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include "sys/io-pkt.h"
#include "nw_datastruct.h"
static int in_bytes = 0;
static int out_bytes = 0;
static int input_hook( void *arg, struct mbuf **m, struct ifnet *ifp, int dir )
{
in_bytes += (*m)->m_len;
return (0);
}
static int output_hook( void *arg, struct mbuf **m, struct ifnet *ifp, int dir )
{
out_bytes += (*m)->m_len;
return (0);
}
static int deinit_module( void );
static int iface_hook( void *arg, struct mbuf **m, struct ifnet *ifp, int dir )
{
printf( "Iface hook called ... " );
if ( (int)m == PFIL_IFNET_ATTACH )
{
printf( "Interface attached\n" );
printf( "%d bytes in, %d bytes out\n", in_bytes, out_bytes );
} else
if ( (int)m == PFIL_IFNET_DETACH )
{
printf( "Interface detached\n" );
printf( "%d bytes in, %d bytes out\n", in_bytes, out_bytes );
deinit_module();
}
return (0);
}
static int ifacecfg_hook( void *arg, struct mbuf **m, struct ifnet *ifp, int dir )
{
printf( "Iface cfg hook called with 0x%08X\n", (int)(m) );
return (0);
}
static int deinit_module( void )
{
struct pfil_head *pfh_inet;
pfh_inet = pfil_head_get( PFIL_TYPE_AF, AF_INET );
if ( pfh_inet == NULL )
return (ESRCH);
pfil_remove_hook( input_hook, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet );
pfil_remove_hook( output_hook, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet );
pfh_inet = pfil_head_get( PFIL_TYPE_IFNET, 0 );
if ( pfh_inet == NULL )
return (ESRCH);
pfil_remove_hook( ifacecfg_hook, NULL, PFIL_IFNET, pfh_inet );
pfil_remove_hook( iface_hook, NULL, PFIL_IFNET | PFIL_WAITOK, pfh_inet );
printf( "Unloaded pfil hook\n" );
return (0);
}
int pfil_entry( void *dll_hdl, struct _iopkt_self *iopkt, char *options )
{
struct pfil_head *pfh_inet;
pfh_inet = pfil_head_get( PFIL_TYPE_AF, AF_INET );
if ( pfh_inet == NULL )
return (ESRCH);
pfil_add_hook( input_hook, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet );
pfil_add_hook( output_hook, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet );
pfh_inet = pfil_head_get( PFIL_TYPE_IFNET, 0 );
if ( pfh_inet == NULL )
return (ESRCH);
pfil_add_hook( iface_hook, NULL, PFIL_IFNET, pfh_inet );
pfil_add_hook( ifacecfg_hook, NULL, PFIL_IFADDR, pfh_inet );
printf( "Loaded pfil hook\n" );
return (0);
}
struct _iopkt_lsm_entry IOPKT_LSM_ENTRY_SYM( pfil ) = IOPKT_LSM_ENTRY_SYM_INIT( pfil_entry );

Модуль фильтров пакетов (pf): брандмауэры и NAT

С помощью интерфейса pfil фильтр пакетов (Packet Filter, pf) подключается к потоку пакетов для реализации брандмауэров и трансляции сетевых адресов (NAT). Фильтр пакетов представляет собой загружаемый модуль для стеков v4 и v6 ( lsm-pf-v4.so или lsm-pf-v6.so соответственно). После загрузки (например, командой mount -T io-pkt /lib/dll/lsm-pf-v4.so) этот модуль создает псевдоустройство pf.

Псевдоустройство pf обеспечивает приблизительно те же функции, что и комплект ipfilter для фильтрации и трансляции сетевых адресов (NAT), в котором также используются хуки pfil.

Более подробная информация указана на страницах:

pf
Псевдоустройство фильтра пакетов
pf.conf
Конфигурационный файл для pf
pfctl
Управление устройством фильтра пакетов и трансляции сетевых адресов (NAT)

Для запуска устройства pf используется утилита pfctl, которая выполняет команду DIOCSTART функции ioctl(). В результате pf вызывает функцию pf_pfil_attach(), которая выполняет необходимые процедуры присоединения pfil. Затем выполняются две важные функции – pf_test() и pf_test6(), которые определяют, какие пакеты IPv4 и IPv6 следует отправлять, принимать и отбрасывать. Хуки фильтра пакетов, а вместе с ними и все устройство pf, отключаются с помощью команды DIOCSTOP функции ioctl(), которая обычно выполняется в виде pfctl -d.

Дополнительную информацию об использовании фильтров пакетов PF см. в документации OpenBSD по адресу http://www.obsd.si/pub/OpenBSD/doc/pf-faq.pdf. Некоторые фрагменты этого документа (касающиеся очередей пакетов, CARP и др.) неприменимы к стеку io-pkt-*, однако общая информация о конфигурировании является актуальной. В указанном документе рассматриваются конфигурации брандмауэров и NAT, которые можно применять с помощью фильтра пакетов PF.

Фильтр пакетов Беркли (Berkeley Packet Filter, BPF)

Фильтр пакетов Berkeley (Berkeley Packet Filter, BPF) обеспечивает доступ к сетевым данным канального уровня с помощью интерфейсов, присоединенных к системе. Для использования BPF следует сначал открыть узел устройства /dev/bpf, а затем передавать устройству команды с помощью функции ioctl(). Популярным инструментом, который использует BPF, является утилита tcpdump.

Поскольку устройство /dev/bpf поддерживает клонирование, его можно открывать несколько раз. В целом BPF похож на интерфейс клонирования, но предоставляет не сетевой интерфейс, а лишь метод для многократного открытия одного и того же устройства.

Для захвата сетевого трафика необходимо присоединить устройство BPF к интерфейсу. После этого трафик интерфейса передается BPF для анализа. Присоединение интерфейса к открытому устройству BPF осуществляется командой BIOCSETIF функции ioctl(). Для идентификации интерфейса используется параметр struct ifreq, в котором указано имя интерфейса в кодировке ASCII. Это имя используется для поиска интерфейса в таблицах ядра. BPF регистрируется в поле if_bpfstruct ifnet), чтобы уведомить систему о намерении пользоваться доступом к трафику этого интерфейса. Слушатель также может указывать набор правил фильтрации, чтобы захватывать только определенные пакеты (например, с заданным сочетанием узла и порта).

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

#if NBPFILTER > 0
if ( ifp->if_bpf )
bpf_mtap( ifp->if_bpf, m0 );
#endif

Этот код передает пакет mbuf фильтру BPF для проверки. BPF анализирует данные и определяет, какие слушатели этого интерфейса заинтересованы в них. Фильтр, который проверяет данные, тщательно оптимизирован и максимально быстро проверяет каждый пакет. Если пакет удовлетворяет условиям фильтра, он копируется и ожидает считывания устройством.

Функция прослушивания BPF и интерфейсы pfil имеют схожее назначение, однако работают по-разному. BPF mtap пользуется непосредственным доступом к пакетам на физическом уровне и может копировать их для дальнейшего использования. Клиенты pfil могут изменять и отбрасывать пакеты.

BPF имеет богатый многофункциональный синтаксис и является стандартным интерфейсом, который используется многими сетевыми программами. При необходимости перехвата/передачи пакетов применение BPF следует рассматривать в первую очередь. Интерфейс BPF не обладает максимальной производительностью, поскольку копирует отфильтрованные пакеты перед тем, как передать их приложению, находящемуся вне стека. Утилита tcpdump и библиотека libpcap используют интерфейс BPF для перехвата пакетов и отображения статистики по ним.




Предыдущий раздел: перейти