Сокет packet (тип PF_PACKET) используется для приема и передачи необработанных (raw) пакетов на канальном уровне (OSI Layer 2). Интерфейс сокета позволяет реализовать в пользовательском пространстве модули протоколов, использующих только сервис канального уровня.
В качестве типа сокета может указываться значение SOCK_RAW (необработанные пакеты, включающие заголовок канального уровня) или SOCK_DGRAM (обработанные пакеты с удаленным заголовком канального уровня). Заголовки канального уровня представляются в виде структуры sockaddr_ll, описанной ниже.
Поле sll_protocol содержит номер протокола IEEE 802.3 (значение ETH_P_ALL соответствует всем протоколам Ethernet). Все входящие пакеты заданного в адресной структуре протокола, будут передаваться пакетному сокету до их передачи протокольным модулям ядра.
Для того, чтобы открыть пакетный сокет приложение должно иметь флаг возможности CAP_NET_RAW или исполняться от имени пользователя с эффективным идентификатором UID=0.
Пакеты SOCK_RAW передаются и принимаются от драйвера устройства без каких-либо изменений в данных. При получении пакета адресная информация разбирается и передается в структуру sockaddr_ll. При передаче пакета полученный от пользователя буфер должен содержать заголовок канального уровня. Такой пакет передается без изменений сетевому драйверу интерфейса, указанного в поле адреса получателя.
Обработка пакетов SOCK_DGRAM происходит на более высоком уровне. Заголовок канального уровня удаляется из пакета до передачи этого пакета пользователю. Передаваемые через пакетный сокет пакеты SOCK_DGRAM получают подходящий заголовок канального уровня в структуре sockaddr_ll до их размещения в очереди.
По умолчанию все пакеты заданного типа протокола передаются пакетному сокету. Для того, чтобы получать только пакеты от интересующего интерфейса, следует использовать функцию bind, передав ей в качестве параметра структуру sockaddr_ll для соответствующего интерфейса, чтобы связать сокет с этим интерфейсом. В этом случае из адресной структуры используются лишь поля sll_protocol и sll_ifindex.
Операции подключения (connect) не поддерживаются для пакетного сокета.
Если при вызове функций recvmsg, recv, recvfrom был установлен флаг MSG_TRUNC, функции всегда будут возвращать реальный размер кадра в среде, даже если он отличается от размера кадра в буфере (заполнение для выравнивания).
Для переносимых программ разумно использовать сокеты PF_PACKET через библиотеку pcap, хотя эта библиотека не перекрывает всех возможностей пакетных сокетов.
Пакетные сокеты SOCK_DGRAM не пытаются создавать или разбирать заголовки IEEE 802.2 LLC для кадров IEEE 802.3. При выборе для передачи в качестве протокола ETH_P_802_3 ядро создает кадр 802.3 и заполняет поле размера. Пользовательская программа должна предоставить заголовок LLC для создания законченного пакета. Входящие пакеты 802.3 не демультиплексируются по полям протокола DSAP/SSAP – они просто передаются пользовательскому приложению как пакеты протокола ETH_P_802_2 с готовым заголовком LLC. Таким образом, привязка к протоколу ETH_P_802_3 невозможна и приложению следует использовать привязку к протоколу ETH_P_802_2 и самостоятельно выполнять демультиплексирование. По умолчанию для передаваемых пакетов используется инкапсуляция Ethernet DIX.
Пакетные сокеты никак не связаны с цепочками правил межсетевых экранов.
Типы адресов
Для адресации пакетный сокет использует независимую от адресов физического уровня структуру sockaddr_ll.
struct sockaddr_ll {
unsigned short sll_family; /* AF_PACKET */
unsigned short sll_protocol; /* протокол канального уровня */
int sll_ifindex; /* номер интерфейса */
unsigned short sll_hatype; /* тип заголовка */
unsigned char sll_pkttype; /* тип пакета */
unsigned char sll_halen; /* размер адреса */
unsigned char sll_addr[8]; /* аппаратный адрес */
};
Поле sll_protocol содержит идентификатор стандартного типа протокола Ethernet (см. файл linux/if_ether.h). Поле sll_ifindex содержит индекс интерфейса; sll_hatype задает тип ARP. Поле sll_pkttype указывает тип адресации пакета; допустимыми типами являются PACKET_HOST (пакеты, адресованные локальному хосту), PACKET_BROADCAST (широковещательные пакеты канального уровня), PACKET_MULTICAST (групповая адресация на канальном уровне), PACKET_OTHERHOST (пакет для другого хоста, который может быть захвачен интерфейсом, работающим в режиме promiscuous), и PACKET_OUTGOING (для пакетов от локального хоста, возвращенных пакетному сокету). Перечисленные типы адресации имеют смысл только для принимаемых пакетов. Поля sll_addr и sll_halen содержат аппаратный адрес (например, IEEE 802.3) и его размер. Интерпретация этих полей зависит от конкретного устройства.
При передаче пакетов достаточно задать значения sll_family=AF_PACKET, sll_addr, sll_halen и sll_ifindex. Остальные поля должны иметь значение 0. Поля sll_hatype и sll_pkttype устанавливаются в принимаемых пакетах только для информации. Для связывания сокетов используются только поля sll_protocol и sll_ifindex.
Опции сокета
Пакетные сокеты могут использоваться для групповой рассылки на канальном уровне и работы интерфейсов в режиме захвата. Опции сокета задаются с помощью функции setsockopt для SOL_PACKET. Опция PACKET_ADD_MEMBERSHIP добавляет привязку, а опция PACKET_DROP_MEMBERSHIP удаляет ее. В обоих случаях в качестве аргумента передается структура:
struct packet_mreq
{
int mr_ifindex; /* индекс интерфейса */
unsigned short mr_type; /* действие */
unsigned short mr_alen; /* размер адреса */
unsigned char mr_address[8]; /* аппаратный адрес */
};
Поле mr_ifindex содержит индекс интерфейса, для которого нужно изменить состояние, mr_type задает выполняемое действие (PACKET_MR_PROMISC разрешает прием из среды всех пакетов – режим захвата, PACKET_MR_MULTICAST привязывает сокет к multicast-группе канального уровня, а PACKET_MR_ALLMULTI разрешает сокету принимать все пакеты с групповыми адресами, поступающие в данный интерфейс).
Для решения этих задач можно использовать также стандартные операции IOCTL SIOCSIFFLAGS, SIOCADDMULTI, SIOCDELMULTI.
Операции IOCTL
Для получения временной метки последнего доставленного пакета может использоваться SIOCGSTAMP со структурой timeval в качестве аргумента.
Кроме того для пакетных сокетов поддерживаются все операции IOCTL, определенные для netdevice и socket.
Обработка ошибок
Пакетные сокеты не выполняют обработки каких-либо ошибок за исключением тех, которые происходят при передаче пакетов драйверу устройства.
Коды ошибок
Таблица 1 Коды ошибок для пакетных сокетов
Код
|
Описание
|
ENETDOWN
|
Интерфейс неактивен.
|
ENOTCONN
|
Функции не передан адрес интерфейса.
|
ENODEV
|
Задано неизвестное имя устройства или индекс интерфейса.
|
EMSGSIZE
|
Размер пакета превышает значение MTU для интерфейса.
|
ENOBUFS
|
Недостаточно памяти для размещения пакета.
|
EFAULT
|
Пользователь передал некорректный адрес памяти.
|
EINVAL
|
Некорректный аргумент
|
ENXIO
|
В адресе содержится некорректный индекс интерфейса.
|
EPERM
|
Пользователь не имеет прав на выполнение операции.
|
EADDRNOTAVAIL
|
Передан неизвестный адрес multicast-группы.
|
ENOENT
|
Пакет не был получен
|
Драйверы устройств могут генерировать дополнительные коды ошибок.
Известные проблемы
-
Обработка IEEE 802.2/803.3 LLC может трактоваться как ошибка.
-
Отсутствует документация для фильтров сокета.
-
Расширение MSG_TRUNC для функции recvmsg является слишком опасным – вместо него следует использовать управляющее сообщение.
-
Отсутствует способ определения исходного адреса получателя пакетов SOCK_DGRAM.
|