Чтение онлайн

ЖАНРЫ

UNIX: разработка сетевых приложений
Шрифт:

fd = 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
.

Указывая протокол

ETH_P_ххх
, мы тем самым сообщаем канальному уровню, какой тип
из получаемых канальным уровнем кадров передавать сокету. Если канальный уровень поддерживает смешанный режим (например, Ehternet), то устройство тоже должно работать в смешанном режиме. Это осуществляется при помощи параметра сокета
PACKET_ADD_MEMBERSHIP
с использованием структуры
packet_mreq
. При этом необходимо указать конкретный интерфейс и задать тип действия
PACKET_MR_PROMISC
. В старых системах для этого нужно вызвать функцию
ioctl
с запросом
SIOCGIFFLAGS
для получения флагов, установить флаг
IFF_PROMISC
и далее сохранить флаги с помощью
SIOCSIFFLAGS
. К сожалению, при использовании этого метода программы, работающие в смешанном режиме, могут мешать друг другу, а если в одной из них содержатся ошибки, то она может и не отключить смешанный режим по завершении.

Сравнивая это средство Linux с BPF и DLPI, мы можем отметить некоторые различия.

1. В Linux не обеспечивается буферизация. Фильтрация на уровне ядра доступна только в новых системах (при помощи параметра

SO_ATTACH_FILTER
). Существует обычный буфер приема сокета, но отсутствует возможность буферизации и отправки приложению нескольких кадров с помощью одной операции считывания. Это увеличивает накладные расходы, связанные с копированием потенциально возможных больших объемов данных из ядра в приложение.

2. В Linux не предусмотрена фильтрация на уровне устройства. Сокеты

PF_PACKET
могут быть связаны с устройством функцией
bind
. Если в вызове функции
socket
указан аргумент
ETH_P_IP
, то все пакеты IPv4 со всех устройств (например, Ethernet, каналы PPP, каналы SLIP и закольцовка) будут переданы на сокет. Функция
recvfrom
возвращает общую структуру адреса сокета, а элемент
sa_data
содержит имя устройства (например,
eth0
). Тогда приложение само должно игнорировать данные с тех устройств, которые не представляют для него интереса. Здесь мы сталкиваемся фактически с той же проблемой: возможно, что приложение будет получать слишком много данных, особенно в случае наблюдения за высокоскоростной сетью.

29.5. Libcap: библиотека для захвата пакетов

Библиотека захвата пакетов

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

Сейчас осуществляется поддержка BPF для Беркли-ядер, DLPI для Solaris 2.x, NIT для SunOS 4.1.x, пакетных сокетов (

SOCK_PACKET
,
PF_PACKET
) в Linux и нескольких других операционных системах. Библиотека
libcap
используется программой
tcpdump
. Всего в библиотеке насчитывается порядка 25 функций, но вместо того чтобы просто описывать их, мы продемонстрируем их фактическое использование на примере, рассматриваемом в следующем разделе. Названия всех функций начинаются с
pcap_
. Они описаны более подробно на странице руководства, которая называется
pcap
.

ПРИМЕЧАНИЕ

Библиотека libcap находится в свободном доступе по адресу http://www.tcpdump.org/.

29.6. Libnet: библиотека создания и отправки пакетов

Библиотека

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

Библиотека скрывает большую часть деталей формирования заголовков IP, UDP и TCP и обеспечивает приложению простой и переносимый интерфейс для отправки пакетов канального уровня и IP-пакетов через символьные сокеты. Как и

libcap
, библиотека
libnet
содержит достаточно много функций. Мы приведем пример использования небольшой их части, предназначенной для работы с символьными сокетами, но в следующем разделе. Для сравнения там же будет приведен код, непосредственно работающий с символьными сокетами. Все функции библиотеки начинаются с префикса
libnet_
. За более подробным их описанием вы можете обратиться к странице руководства
libnet
или к доступной в Сети документации.

ПРИМЕЧАНИЕ

Библиотека libnet свободно доступна по адресуРуководство находится по адресуНа момент написания этой книги в Сети имелось руководство только по устаревшей версии 1.0. Актуальная версия 1.1 имеет значительно отличающийся интерфейс. В нашем примере используется API версии 1.1.

29.7. Анализ поля контрольной суммы UDP

Теперь мы приступаем к рассмотрению примера, в котором отсылается дейтаграмма UDP, содержащая запрос UDP к серверу имен, а затем считывается ответ с помощью библиотеки захвата пакетов. Цель данного примера — установить, вычисляется на сервере имен контрольная сумма UDP или нет. В случае IPv4 вычисление контрольной суммы не является обязательным. В большинстве систем в настоящее время вычисление контрольных сумм по умолчанию включено, но, к сожалению, в более старых системах, в частности SunOS 4.1.x, оно по умолчанию отключено. В настоящее время все системы, а особенно система, в которой работает сервер имен, всегдадолжны работать с включенными контрольными суммами UDP, поскольку поврежденные (содержащие ошибки) дейтаграммы могут повредить базу данных сервера.

ПРИМЕЧАНИЕ

Включение и выключение контрольных сумм обычно осуществляется сразу для всей системы, как показано в приложении Е [111].

Мы формируем дейтаграмму UDP (запрос DNS) и записываем ее в символьный сокет. Параллельно мы проделаем то же самое с помощью

libnet
. Для отправки запроса мы могли бы использовать обычный сокет UDP, но мы хотим показать, как использовать параметр сокета
IP_HDRINCL
для создания полной дейтаграммы IP.

Нет возможности получить контрольную сумму UDP при чтении из обычного сокета UDP, а также считывать пакеты UDP или TCP, используя символьный сокет (см. раздел 28.4). Следовательно, путем захвата пакетов нам нужно получить целую дейтаграмму UDP, содержащую ответ сервера имен.

Затем мы исследуем поле контрольной суммы UDP в заголовке UDP, и если оно равно нулю, это означает, что на сервере отключено вычисление контрольной суммы.

Действие нашей программы иллюстрирует рис. 29.3. Мы записываем наши собственные дейтаграммы UDP в символьный сокет и считываем ответы, используя библиотеку

libcap
. Обратите внимание, что UDP также получает ответ сервера имен и отвечает сообщением о недоступности порта ICMP, так как ничего не знает о номере порта, выбранном нашим приложением. Сервер имен игнорирует эту ошибку ICMP. Также можно отметить, что написать подобную тестовую программу, использующую TCP, было бы сложнее, даже несмотря на то, что мы с легкостью можем записывать свои собственные сегменты TCP. Дело в том, что любой ответ на сегмент TCP, который мы генерируем, обычно инициирует отправку протоколом TCP ответного сегмента RST туда, куда был послан первый сегмент.

Рис. 29.3. Приложение, определяющее, включено ли на сервере вычисление контрольных сумм UDP

ПРИМЕЧАНИЕ

Указанную проблему можно обойти. Для этого нужно посылать сегменты TCP с IP- адресом отправителя, который принадлежит присоединенной подсети, но в настоящий момент не присвоен никакому другому узлу. Нужно также добавить данные ARP на посылающем узле для этого нового IP-адреса, чтобы узел отвечал на запросы ARP для него. В результате стек IP на посылающем узле будет игнорировать пакеты, приходящие на этот IP-адрес, в предположении, что посылающий узел не является маршрутизатором.

Поделиться с друзьями: