Фильтры сокета
Фильтр сокета является фильтром, связанным с определенным сокетом, как показано на рисунке 4-1. Эти расширения могут отфильтровать входящий или исходящий трафик на сокете. Они также могут отфильтровать внеполосную коммуникацию, включая вызовы к setsockopt
, getsockopt
, ioctl
, connect
, listen
, и bind
.
Фильтры сокета могут работать в одном из двух режимов: программируемый или глобальный. Глобальный фильтр автоматически включен для новых сокетов типа, указанного для фильтра. Программируемый фильтр включен только под программным управлением при помощи setsockopt
на определенном сокете. (В самом коде единственная разница между глобальной переменной и программируемыми фильтрами то, ли флаг SFLT_GLOBAL
или SFLT_PROG
был установлен в фильтре sf_flags
поле.)
Когда KEXT вызывает sock_socket
или приложение вызывает socket
для создания сокета любые глобальные фильтры, связанные с соответствующим протоколом, присоединены к структуре сокета. В зависимости от того, фильтрует ли фильтр входящие или исходящие данные, он изменит данные или непосредственно перед тем, как входящие данные хранятся в буфер сокета или сразу после того, как исходящие данные получены от того буфера ядром.
Поочередно, приложение может вызвать setsockopt
использование опции сокета SO_NKE
вставить программируемый фильтр в цепочку фильтра того сокета, следующим образом:
setsockopt(s, SOL_SOCKET, SO_NKE, &so_nke, sizeof (struct so_nke); |
so_nke
структура определяется следующим образом:
struct so_nke { |
unsigned int nke_handle; |
unsigned int nke_where; |
int nke_flags; |
}; |
Значения nke_where
и nke_flags
проигнорированы. Эти поля сохраняются только для совместимости.
nke_handle
указывает фильтр, который будет соединен с сокетом. Это - задача программиста определить местоположение KEXT, содержащего надлежащий фильтр и удостовериться, что это загружается.
nke_handle
значения присваиваются Apple Computer от того же пространства имен как тип и коды создателя, используемые в Mac OS 8 и Mac OS 9 и использование того же регистрационного механизма.
Однако можно также использовать механизм выделения идентификатора события ядра для получения значения уникального дескриптора для фильтра сокета. Приложение пространства пользователя может тогда использовать SIOCGKEVVENDOR
ioctl на событии ядра снабжают сокетом для определения динамического значения дескриптора для данного фильтра сокета. Этот механизм описан в Использовании kern_event API для Уведомлений Ядра.
Создание фильтра сокета
Жизненному циклу фильтра сокета можно подвести итог следующим образом:
Фильтры сокета установлены в ядре путем вызова
sflt_register
, обычно от подпрограммы инициализации фильтра.Позже, когда фильтр инстанцируют на сокете, протокол вызывает фильтр
sf_attach_func
обратный вызов. Этот обратный вызов может возвратить уникальный cookie через свой первый параметр, который может использоваться для отслеживания хранения, определенного для приведенного примера фильтра (присоединенный к определенному сокету).Когда фильтр отсоединяется (ли через фильтр, являющийся незарегистрированным, сокет, закрываемый, или фильтр, явно отсоединяемый от сокета), фильтр
sf_detach_func
обратный вызов вызывают. В этой точке фильтр должен освободить любые специфичные для сокета ресурсы, которые это выделило (обычно вsf_attach_func
).Фильтр сокета может, в некоторый момент, решить, что хочет быть разгруженным. Если так, это должно вызвать
sflt_unregister
. Это будет препятствовать тому, чтобы фильтр был присоединен к новым сокетам в будущем, и начнет процесс отсоединения фильтра от существующих сокетов.
Как часть вызова к sflt_register
, Ваш KEXT передает в a struct
sflt_filter
объект. Эта структура содержит много полей, содержащих различные обратные вызовы и флаги, связанные с Вашим фильтром.
Каждый фильтр сокета содержит много обратных вызовов (указатели функции). Эти обратные вызовы вызывают автоматически когда соответствие socket
функции вызваны. Обратные вызовы разрешают фильтру выборочно прерывать операции сокета.
Например, прототип для sf_bind_func
похож на это:
int (*sf_bind_func)(void *cookie, socket_t so, const struct sockaddr *to); |
Ядро sobind
вызовы функции фильтр sf_bind_func
обратный вызов со значением cookie, что фильтр sf_attach_func
обратный вызов возвратился, когда фильтр был сначала присоединен, вместе с экземпляром сокета (so
) и имя локальной связываемой конечной точки (to
).
Большинство этих обратных вызовов может возвратить целочисленное значение (за исключением detach
и notify
, которые, как предполагается, всегда успешно выполняются). Возвращаемое значение нуля интерпретируется, чтобы означать, что вызывающая сторона должна продолжать обрабатывать, как обычно. Ненулевое возвращаемое значение интерпретируется как ошибка (как определено в <sys/errno.h>
) это заставляет обработку пакета или работы сокета останавливаться; ошибка тогда распространяет через штабель.
Одно исключение является возвращаемым значением EJUSTRETURN
. Если Вы возвращаете это значение, функция вызова (например, sobind
) возвраты в той точке со значением нуля (никакая ошибка). Таким образом фильтр может «глотать» пакет или работу. Фильтр может повторно ввести данные или работу в более позднее время. Для других ненулевых возвращаемых значений функция вызова возвращает ненулевой код ошибки.
Когда любой фильтр будет глотать и повторно вводит пакет или работу, он должен ожидать, что соответствующая функция фильтра будет вызвана снова на введенных данных или работе. Это может произойти многократно — каждый раз, когда пакет глотают и повторно вводят.
Много фильтров (инкапсуляция, например) естественно предоставляют себя обнаружению повторно введенных пакетов. В других ситуациях можно использовать функциональность тега mbuf, чтобы упростить определять повторно введенный трафик.
Для использования тегирования mbuf необходимо сначала установить идентификатор тега для KEXT в его подпрограмме запуска использование mbuf_tag_id_find
функция Затем при записи в Ваш sf_data_in_func
обратный вызов, используйте mbuf_tag_find
функция, чтобы видеть, тегировал ли Ваш фильтр уже этот пакет. В противном случае это должно обработать пакет. Иначе, Ваша функция фильтра должна возвратиться 0 сразу.
Как только Вы закончили обрабатывать пакет, необходимо вызвать mbuf_tag_allocate
на заголовке пакета mbuf для тегирования пакета, указывая, что Вы уже обработали его. Когда mbuf будет позже освобожден, любые ссылки тега будут также освобождены.
tcplognke
выборка обеспечивает пример того, как должным образом глотать и повторно ввести пакеты.
Пример Фильтра сокета: tcplognke
tcplognke
фильтр является фильтром сокета, вызывающимся для каждого сокета TCP. Это записывает подробную информацию о каждом соединении, включая число байтов, отправленных в и от системы, время, на которое соединение возросло, и удаленный IP-адрес.
tcplog утилита демонстрирует использование PF_SYSTEM
сокет, чтобы позволить/запретить войти в систему tcplognke
, считать информации журнала из фильтра и указать различные критерии журналирования.
Когда tcplognke
загружается и инициализируется, это устанавливает себя как глобальный фильтр для протокола TCP и регистрирует управление ядром. tcplognke
фильтр тогда сохраняет буфер записей соединения. Если никакая управляющая программа не присоединяет к нему, буфер постоянно перезаписывается, поскольку соединения установлены и завершены. Сохранить или просмотреть информацию что tcplognke
фильтр собирается, используйте вложенное tcplog
утилита командной строки. Инструмент конфигурирует tcplognke
фильтр для отправки записей журнала в tcplog
программа. tcplog
инструмент тогда циклы, выводя на экран и пишущий записи журнала как tcplognke
фильтр создает их.
Исходный код для tcplognke
фильтр и для tcplog
утилита командной строки доступна от веб-сайта примера кода ADC. Посмотрите Чтение Меня файл с tcplognke примером кода для большего количества инструкций на проекте и использовании демонстрационного KEXT.