UNIX: разработка сетевых приложений
29.3. DLPI: интерфейс поставщика канального уровня
SVR4 обеспечивает доступ к канальному уровню через DLPI (Data Link Provider Interface — интерфейс поставщика канального уровня). DLPI — это не зависящий от протокола интерфейс, разработанный в AT&T и служащий средством связи с сервисами, обеспечиваемыми канальным уровнем [124]. Доступ к DLPI осуществляется посредством отправки и получения сообщений через потоки STREAMS.
Для подсоединения к канальному уровню приложение просто открывает устройство (например,
) с помощью команды open и использует запросle0. Но для эффективной работы используются два дополнительных модуля:DL_ATTACH_REQ, который осуществляет фильтрацию внутри ядра, иpfmod, буферизующий данные, предназначенные для приложения. Это показано на рис. 29.2.bufmodРис. 29.2. Захват пакета с использованием DLPI, pfmod и bufmod
Концептуально DLPI аналогичен BPF.
поддерживает фильтрацию внутри ядра, используя псевдопроцессор, apfmodсокращает количество данных и системных вызовов, поддерживая длину захвата и время ожидания для считывания.bufmodОдно интересное различие, тем не менее, заключается в том, что для BPF и фильтров
используются разные типы псевдопроцессоров. Фильтр BPF — это ориентированный ациклический граф управления потоком (acyclic control flow graph, CFG), в то время какpfmodиспользует дерево булевых выражений. В первом случае естественным является отображение в код для вычислительной машины с регистровой организацией, а во втором — в код для машины со стековой организацией [72]. В статье [72] показано, что реализация CFG, используемая в BPF, обычно работает быстрее, чем дерево булевых выражений, в 3-20 раз в зависимости от сложности фильтра.pfmodЕще одно отличие состоит в том, что BPF всегда выполняет фильтрацию перед копированием пакета, чтобы не копировать те пакеты, которые будут сброшены фильтром. В некоторых реализациях DLPI пакеты сначала копируются в модуль
, который затем может сбрасывать их.pfmod29.4. Linux: SOCK_PACKET и PF_PACKET
Существует два метода получения пакетов канального уровня в Linux. Первоначальный метод получил более широкое распространение, но является менее гибким. Он состоит в создании сокета типа
. Новый метод, предоставляющий больше возможностей для настройки фильтров и оптимизации производительности, состоит в создании сокета семействаSOCK_PACKET. В любом случае мы должны обладать правами привилегированного пользователя (аналогичные необходимым для создания символьного сокета), а третий аргумент функции socket должен быть ненулевым значением, задающим тип кадра Ethernet. При использовании сокетовPF_PACKETвторой аргументPF_PACKETможет быть константойsocket(для получения обработанных пакетов без заголовка канального уровня) илиSOCK_DGRAM(для получения пакетов целиком). СокетыSOCK_RAWпередают пакеты только целиком. Например, для получения всех кадров канального уровня мы пишем:SOCK_PACKETfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); /* в новых системах */или
fd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL)); /* в старых системах */В результате этого будут возвращены кадры для всех протоколов, получаемые канальным уровнем. Если нам нужны кадры IPv4, то вызов будет таким:
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); /* в новых системах */fd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP)); /* в старых системах */Другие константы, которые могут использоваться в качестве последнего аргумента, — это, например,
иETH_P_ARP.ETH_P_IPV6Указывая протокол
, мы тем самым сообщаем канальному уровню, какой тип из получаемых канальным уровнем кадров передавать сокету. Если канальный уровень поддерживает смешанный режим (например, Ehternet), то устройство тоже должно работать в смешанном режиме. Это осуществляется при помощи параметра сокетаETH_P_хххс использованием структурыPACKET_ADD_MEMBERSHIP. При этом необходимо указать конкретный интерфейс и задать тип действияpacket_mreq. В старых системах для этого нужно вызвать функциюPACKET_MR_PROMISCс запросомioctlдля получения флагов, установить флагSIOCGIFFLAGSи далее сохранить флаги с помощьюIFF_PROMISC. К сожалению, при использовании этого метода программы, работающие в смешанном режиме, могут мешать друг другу, а если в одной из них содержатся ошибки, то она может и не отключить смешанный режим по завершении.SIOCSIFFLAGSСравнивая это средство Linux с BPF и DLPI, мы можем отметить некоторые различия.
1. В Linux не обеспечивается буферизация. Фильтрация на уровне ядра доступна только в новых системах (при помощи параметра
). Существует обычный буфер приема сокета, но отсутствует возможность буферизации и отправки приложению нескольких кадров с помощью одной операции считывания. Это увеличивает накладные расходы, связанные с копированием потенциально возможных больших объемов данных из ядра в приложение.SO_ATTACH_FILTER2. В Linux не предусмотрена фильтрация на уровне устройства. Сокеты
могут быть связаны с устройством функциейPF_PACKET. Если в вызове функцииbindуказан аргументsocket, то все пакеты IPv4 со всех устройств (например, Ethernet, каналы PPP, каналы SLIP и закольцовка) будут переданы на сокет. ФункцияETH_P_IPвозвращает общую структуру адреса сокета, а элементrecvfromсодержит имя устройства (например,sa_data). Тогда приложение само должно игнорировать данные с тех устройств, которые не представляют для него интереса. Здесь мы сталкиваемся фактически с той же проблемой: возможно, что приложение будет получать слишком много данных, особенно в случае наблюдения за высокоскоростной сетью.eth029.5. Libcap: библиотека для захвата пакетов
Библиотека захвата пакетов
обеспечивает не зависящий от реализации доступ к средствам операционной системы, с помощью которых осуществляется этот захват. В настоящее время поддерживается только чтение пакетов (хотя добавление нескольких строк кода в библиотеку позволяет также записывать пакеты в некоторых системах). В следующем разделе приводится описание альтернативной библиотеки, которая не только дает возможность записывать пакеты на канальный уровень, но и позволяет конструировать пакеты произвольного типа.libcap
