ИМЯ
packet -
пакетный
интерфейс
на уровне
устройства
ОБЗОР
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h> /* протоколы L2 */
packet_socket = socket(AF_PACKET, int socket_type, int protocol);
ОПИСАНИЕ
Пакетные
сокеты
используются
для приёма
и передачи
неструктурированных
пакетов на
уровне
драйвера
устройства
(второй
уровень OSI).
Они
позволяют
пользователю
реализовывать
модули
протоколов
в
пользовательском
пространстве
поверх
физического
уровня.
Значением
socket_type может
быть SOCK_RAW —
для
неструктурированных
пакетов,
содержащих
заголовок
уровня
связи, или
SOCK_DGRAM — для
подготовленных
(cooked) пакетов
без
заголовка
уровня
связи.
Информация
заголовка
уровня
связи
имеет
общий
формат и
предоставляется
структурой
sockaddr_ll. В protocol
содержится
номер
протокола
согласно IEEE 802.3
в сетевом
порядке
байт.
Список
допустимых
протоколов
можно
найти в
заголовочном
файле <linux/if_ether.h>.
Если
значение
протокола
равно htons(ETH_P_ALL),
то
принимаются
все
протоколы.
Все
входящие
пакеты с
этим типом
протокола
будут
переданы в
пакетный
сокет до их
передачи
протоколам,
реализуемым
в ядре.
Для
создания
пакетного
сокета
процесс
должен
иметь
мандат CAP_NET_RAW в
пользовательском
пространстве
имён,
определяемом
по его
сетевому
пространству
имён.
Пакеты SOCK_RAW
передаются
в и из
драйвера
устройства
без
каких-либо
изменений
в данных
пакета. При
получении
пакета,
адрес
по-прежнему
анализируется
и
передаётся
в
стандартной
адресной
структуре
sockaddr_ll. При
отправке
пакета,
выделенный
пользователем
буфер
должен
содержать
заголовок
физического
уровня.
Этот пакет
затем
ставится
без
изменений
в очередь
сетевого
драйвера
интерфейса,
определяемого
адресом
назначения.
Некоторые
драйверы
устройств
всегда
добавляют
другой
заголовок.
Пакеты SOCK_RAW
похожи, но
не
совместимы
с
устаревшими
AF_INET/SOCK_PACKET из Linux 2.0.
При типе
SOCK_DGRAM
обработка
происходит
на чуть
более
высоком
уровне.
Физический
заголовок
удаляется
перед
передачей
пакета
пользователю.
Пакеты,
посылаемые
через
пакетный
сокет SOCK_DGRAM,
перед
постановкой
в очередь
получают
подходящий
заголовок
физического
уровня на
основе
информации
из адреса
назначения
в sockaddr_ll.
По
умолчанию,
все пакеты
заданного
типа
протокола
передаются
в пакетный
сокет. Для
получения
пакетов
только из
определённого
интерфейса
используйте
bind(2), задав
адрес в struct sockaddr_ll
для
привязки
пакетного
сокета к
интерфейсу.
Поля,
используемые
для
привязывания:
sll_family (должно
быть равно
AF_PACKET), sll_protocol и sll_ifindex.
Операция
connect(2) не
поддерживается
для
пакетных
сокетов.
Если в recvmsg(2),
recv(2) или recvfrom(2)
передаётся
флаг MSG_TRUNC, то
возвращается
реальная
длина
пакета в
канале,
даже если
значение
длиннее
буфера.
Типы
адресов
Структура
sockaddr_ll
описывает
независимый
от
устройства
адрес на
физическом
уровне.
struct sockaddr_ll {
unsigned short sll_family; /* всегда равно AF_PACKET */
unsigned short sll_protocol; /* протокол физического уровня */
int sll_ifindex; /* номер интерфейса */
unsigned short sll_hatype; /* тип аппаратного ARP */
unsigned char sll_pkttype; /* тип пакета */
unsigned char sll_halen; /* длина адреса */
unsigned char sll_addr[8]; /* адрес на физическом уровне */
};
Поля этой
структуры
имеют
следующее
назначение:
- Поле sll_protocol
содержит
стандартные
типы
протокола
ethernet в сетевом
порядке
байт;
значения
определены
в
заголовочном
файле <linux/if_ether.h>.
Это
значение
по
умолчанию
для
протокола
сокета.
- Поле sll_ifindex
содержит
индекс
интерфейса
(смотрите
netdevice(7)); 0
означает
любой
интерфейс
(допустимо
только для
привязывания).
Поле sll_hatype
содержит
тип ARP;
значения
хранятся в
заголовочном
файле
<linux/if_arp.h>.
- В поле sll_pkttype
содержится
тип пакета.
Допустимые
типы: PACKET_HOST —
пакет
предназначен
локальному
узлу, PACKET_BROADCAST —
широковещательный
пакет
физического
уровня, PACKET_MULTICAST
— пакет,
посланный
на
групповой
(multicast) адрес
физического
уровня, PACKET_OTHERHOST
— пакет
предназначен
не тому
узлу, где
он пойман
драйвером
устройства
в
неразборчивом
режиме, PACKET_OUTGOING
— пакет,
поступивший
от
локального
узла и
завёрнутый
обратно в
пакетный
сокет. Эти
типы имеют
смысл
только для
принятых
пакетов.
- В полях sll_addr и
sll_halen
содержится
адрес
физического
уровня
(например, IEEE
802.3) и его
длина.
Конкретный
смысл
зависит от
устройства.
Когда вы
посылаете
пакеты,
достаточно
указать sll_family,
sll_addr, sll_halen, sll_ifindex и
sll_protocol.
Остальные
поля
должны
равняться 0.
Поля sll_hatype и
sll_pkttype
заполняются
в
получаемых
пакетах
для вашей
информированности.
Параметры
сокета
Параметры
пакетных
сокетов
настраиваются
вызовом
setsockopt(2) с
уровнем
SOL_PACKET.
- PACKET_ADD_MEMBERSHIP
- PACKET_DROP_MEMBERSHIP
- Пакетные
сокеты
можно
использовать
для
настройки
неразборчивого
режима и
групповой
рассылки
на
физическом
уровне.
Параметр
PACKET_ADD_MEMBERSHIP
добавляет
привязку,
PACKET_DROP_MEMBERSHIP
отменяет
её. Для
обоих в
качестве
аргумента
передаётся
структура
packet_mreq:
-
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
привязывает
сокет к
групповой
рассылке
физического
уровня,
задаваемой
в mr_address и mr_alen, а
PACKET_MR_ALLMULTI
заставляет
сокет
принимать
все пакеты
групповых
рассылок,
поступающих
на
интерфейс.
- Также, для
тех же
целей
можно
использовать
обычные ioctl
SIOCSIFFLAGS, SIOCADDMULTI, SIOCDELMULTI.
- PACKET_AUXDATA
(начиная с Linux
2.6.21)
- Если
включён
этот
двоичный
параметр,
то
пакетный
сокет
передаёт
структуру
метаданных
вместе с
каждым
пакетом в
управляющем
поле recvmsg(2).
Структуру
можно
прочитать
с помощью
cmsg(3). Она
определена
как:
-
struct tpacket_auxdata {
__u32 tp_status;
__u32 tp_len; /* длина пакета */
__u32 tp_snaplen; /* захваченная длина */
__u16 tp_mac;
__u16 tp_net;
__u16 tp_vlan_tci;
__u16 tp_padding;
};
- PACKET_FANOUT
(начиная с Linux
3.1)
- Для
масштабирования
обработки
на
несколько
нитей,
пакетные
сокеты
можно
объединять
в
разветвлённую
группу (fanout group). В
этом
режиме
каждый
подходящий
пакет
ставится в
очередь
только
одного
сокета в
группе.
Сокет
добавляется
в
разветвлённую
группу
вызовом
setsockopt(2) с
уровнем SOL_PACKET
и
параметром
PACKET_FANOUT. Каждое
сетевое
пространство
имён может
включать
до 65536
независимых
групп.
Сокет
выбирает
группу по
закодированному
ID в первых 16
битах
целочисленного
значения
параметра.
Первый
пакетный
сокет,
подключаемый
к группе
неявно её
создаёт.
Для
успешного
подключения
к
существующей
группе все
дальнейшие
пакетные
сокеты
должны
иметь тот
же
протокол,
настройки
устройства,
режим
разветвления
и флаги
(смотрите
далее).
Пакетные
сокеты
могут
покинуть
группу
только при
закрытия
сокета.
Группа
удаляется
после
закрытия
последнего
сокета.
- Для
разветвления
поддерживается
несколько
алгоритмов
распределения
трафика по
сокетам:
- Режим по
умолчанию
PACKET_FANOUT_HASH
посылает
пакеты из
одного
потока в
один и тот
же сокет
для
обеспечения
упорядочивания
по потоку.
Для
каждого
пакета
выбирается
сокет,
получаемый
из хэша
потока
пакетов,
взятого по
модулю
количества
сокетов в
группе, где
хэш потока
— это хэш
адреса
сетевого
уровня и
необязательных
полей
портов
транспортного
уровня.
- Режим
балансировки
нагрузки
PACKET_FANOUT_LB
реализует
карусельный
алгоритм.
- В режиме
PACKET_FANOUT_CPU
выбираются
сокеты
исходя из
ЦП, на
который
поступил
пакет.
- В режиме
PACKET_FANOUT_ROLLOVER все
данные
обрабатываются
одним
сокетом,
следующий
задействуется,
если
текущий
занят (backlogged).
- В режиме
PACKET_FANOUT_RND сокет
выбирается
согласно
генератору
псевдослучайных
чисел.
- В режиме
PACKET_FANOUT_QM
(доступен,
начиная с Linux
3.14) сокет
выбирается
с помощью
записанного
queue_mapping из
полученной
skb.
- Режимы
разветвления
могут
учитывать
дополнительные
параметры.
Фрагментация
IP приводит
к тому, что
пакеты
одного
потока
имеют
разные
хэши
потоков.
Если
установлен
флаг PACKET_FANOUT_FLAG_DEFRAG,
то пакеты
будут
дефрагментироваться
перед
применением
разветвления,
что
позволит
сохранить
порядок
даже в этом
случае.
Параметры
режима
разветвления
задаются
во вторых 16
битах
целочисленного
значения
параметра.
Флаг PACKET_FANOUT_FLAG_ROLLOVER
включает
механизм
перекатывания
в качестве
запасного:
если
первоначальный
алгоритм
разветвления
выбрал
занятый
сокет, то
пакет
переходит
на
следующий
доступный.
- PACKET_LOSS
(с PACKET_TX_RING)
- Когда в
кольце
передачи
обнаруживается
некорректный
пакет, то
по
умолчанию
его
состояние
в tp_status
сбрасывается
в TP_STATUS_WRONG_FORMAT и
происходит
немедленная
отмена
передачи.
Некорректный
пакет
блокирует
как свою
отправку,
так и всех
следующих
пакетов в
очереди.
Ошибка в
формате
должна
быть
исправлена,
соответствующий
tp_status сброшен
в значение
TP_STATUS_SEND_REQUEST, а
передача
перезапущена
с помощью
send(2). Однако,
если задан
параметр
PACKET_LOSS, то все
некорректные
пакеты
будут
пропускаться,
их tp_status
сбрасываться
в TP_STATUS_AVAILABLE и
процесс
передачи
продолжаться.
- PACKET_RESERVE
(с PACKET_RX_RING)
- По
умолчанию,
в кольцо
приёма
пакетов
сразу за
пакетом
записывается
структура
метаданных
и
заполнитель
для
выравнивания.
Этот
целочисленный
параметр
резервирует
дополнительное
свободное
место.
- PACKET_RX_RING
- Включает
создание
отображаемого
в памяти
кольцевого
буфера
асинхронного
приёма
пакетов.
Пакетный
сокет
резервирует
непрерывную
область в
адресном
пространстве
приложения,
размечает
её как
массив
пакетных
слотов и
последовательно
копирует
пакеты (не
более tp_snaplen) в
слоты. В
начале
каждого
пакета
помещается
структура
метаданных,
похожая на
tpacket_auxdata. В поле
протокола
кодируется
смещение
данных от
начала
заголовка
метаданных.
В tp_net
хранится
смещение
сетевого
уровня.
Если тип
пакетного
сокета —
SOCK_DGRAM, то это
делается и
для tp_mac. Если
тип — SOCK_RAW, то
в этом поле
хранится
смещение
на кадр
канального
уровня.
Пакетный
сокет и
приложение
обмениваются
началом и
концом
кольца
через поле
tp_status.
Пакетному
сокету
принадлежат
все слоты
со
значением
tp_status равным
TP_STATUS_KERNEL. После
заполнения
слота,
изменяется
состояние
слота и
права на
него
передаются
приложению.
При
нормальной
работе в
новом
значении
tp_status, как
минимум,
установлен
бит TP_STATUS_USER, что
показывает,
что
принятый
пакет был
сохранён.
Когда
приложение
заканчивает
обработку
пакета, оно
передаёт
права на
слот
обратно
сокету
посредством
установки
tp_status в
значение
TP_STATUS_KERNEL.
- Для
пакетных
сокетов
реализовано
несколько
вариантов
пакетных
колец.
Информацию
о
реализации
можно
найти в
файле
Documentation/networking/packet_mmap.txt из
дерева
исходного
кода ядра
Linux.
- PACKET_STATISTICS
- Возвращает
статистику
по
пакетному
сокету в
виде
структуры
-
struct tpacket_stats {
unsigned int tp_packets; /* общее количество пакетов */
unsigned int tp_drops; /* кол-во отброшенных пакетов */
};
- При
получении
статистики
сбрасываются
внутренние
счётчики.
Если
используется
вариант
кольца TPACKET_V3,
то
статистика
имеет
другую
структуру.
- PACKET_TIMESTAMP
(с PACKET_RX_RING;
начиная с Linux
2.6.36)
- В кольце
приёма
пакетов
всегда
сохраняется
метка
времени в
заголовке
метаданных.
По
умолчанию,
это метка
генерируется
ПО при
копировании
пакета в
кольцо.
Данный
целочисленный
параметр
задаёт тип
метки
времени.
Кроме
значения
по
умолчанию,
поддерживается
два
аппаратных
формата,
описанных
в файле
Documentation/networking/timestamping.txt из
дерева
исходного
кода ядра
Linux.
- PACKET_TX_RING
(начиная с Linux
2.6.31)
- Включает
создание
отображаемого
в памяти
кольцевого
буфера
передачи
пакетов.
Этот
параметр
подобен
PACKET_RX_RING и имеет
те же
аргументы.
Приложение
записывает
пакеты в
слоты со
значением
tp_status равным
TP_STATUS_AVAILABLE и
планирует
их для
передачи
делая
значение
tp_status равным
TP_STATUS_SEND_REQUEST. Когда
пакеты
готовы к
передаче,
приложение
вызывает
send(2) или его
вариант.
Поля buf и len в
этом
вызове
игнорируются.
Если
передаётся
адрес с
помощью sendto(2)
или sendmsg(2), то
он
заменяет
сокетное
значение
по
умолчанию.
При
успешной
передаче
сокет
сбрасывает
значение
tp_status в TP_STATUS_AVAILABLE.
При ошибке
передача
немедленно
прерывается,
если не
задан PACKET_LOSS.
- PACKET_VERSION
(с PACKET_RX_RING;
начиная с Linux
2.6.27)
- По
умолчанию,
PACKET_RX_RING
создаёт
кольцо
приёма
пакетов по
варианту
TPACKET_V1. Для
создания
другого
варианта,
задайте
желаемый,
указав
целочисленное
значение в
этом
параметре
перед
созданием
кольца.
- PACKET_QDISC_BYPASS
(начиная с Linux
3.14)
- По
умолчанию,
пакеты,
посылаемые
через
пакетные
сокеты,
проходят
через
уровень
ядра qdisc
(управление
трафиком),
что
правильно
в
подавляющем
большинстве
случаев.
Для
программно-аппаратных
комплексов,
использующих
пакетные
фильтры
для
затопления
сети —
например,
для
тестирования
устройств
под
нагрузкой,
подобно
тому, как
это делает
pktgen — этот
уровень
можно не
задействовать,
установив
целочисленной
параметр в
1. Побочным
эффектом
будет
отмена
пакетной
буферизации
на уровне qdisc,
что
приведёт к
увеличению
отброшенных
пакетов
при
занятости
передающих
очередей
сетевого
устройства;
поэтому,
пользуйтесь
с
осторожностью.
Вызовы ioctl
Вызов SIOCGSTAMP
можно
использовать
для
получения
метки
времени
последнего
полученного
пакета.
Аргументом
является struct
timeval.
Также, для
пакетных
сокетов
работают
все
стандартные
ioctl,
определённые
в netdevice(7) и socket(7).
Обработка
ошибок
Пакетные
сокеты не
выполняют
обработку
ошибок,
кроме
ошибок,
которые
возникают
при
передаче
пакета
драйверу
устройства.
В них не
заложен
принцип
ожидания
ошибки.
ОШИБКИ
- EADDRNOTAVAIL
- Передан
неизвестный
адрес
групповой
рассылки.
- EFAULT
- Пользователь
передал
неправильный
адрес
памяти.
- EINVAL
- Неверный
аргумент.
- EMSGSIZE
- Пакет
больше, чем
интерфейс
MTU.
- ENETDOWN
- Интерфейс
не поднят.
- ENOBUFS
- Недостаточно
памяти для
размещения
пакета.
- ENODEV
- В адресе
интерфейса
указано
неизвестное
имя
устройства
или индекс
интерфейса.
- ENOENT
- Пакет не
принят.
- ENOTCONN
- Не передан
адрес
интерфейса.
- ENXIO
- В адресе
интерфейса
содержится
некорректный
индекс
интерфейса.
- EPERM
- У
пользователя
недостаточно
прав для
выполнения
этой
операции.
Также,
драйвером
низкого
уровня
могут
генерироваться
другие
ошибки.
ВЕРСИИ
AF_PACKET
появился в
Linux 2.2. В ранних
версиях Linux
поддерживался
только SOCK_PACKET.
ЗАМЕЧАНИЯ
Для
переносимых
программ
предлагается
использовать
AF_PACKET в pcap(3), хотя
это
покрывает
не весь
набор
возможностей
AF_PACKET.
Пакетные
сокеты 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 с
заполненным
полем
протокола.
Пакетные
сокеты
недоступны
(not subject) во
входной и
выходной
цепочках
межсетевого
экрана.
Совместимость
В Linux 2.0
единственным
способом
получить
пакетный
сокет
является
вызов:
socket(AF_INET, SOCK_PACKET,
протокол)
Он всё ещё
поддерживается,
но устарел
и
настоятельно
не
рекомендуется.
Основным
отличием
между
методами —
для
указания
интерфейса
через SOCK_PACKET
используется
старая struct sockaddr_pkt,
которая не
предоставляет
независимого
физического
уровня.
struct sockaddr_pkt {
unsigned short spkt_family;
unsigned char spkt_device[14];
unsigned short spkt_protocol;
};
В spkt_family
содержится
тип
устройства,
в spkt_protocol — тип
протокола
IEEE 802.3,
определённый
в <sys/if_ether.h>, а в spkt_device
— имя
устройства
в виде
строки с null в
конце,
например, eth0.
Эта
структура
устарела и
не должна
использоваться
в новом
коде.
ДЕФЕКТЫ
Способ
обработки
IEEE 802.2/803.3 LLC не
считается
за
дефектный.
Не
описаны
сокетные
фильтры.
Расширение
MSG_TRUNC recvmsg(2)
является
неудачным
решением и
должно
быть
заменено
на
управляющее
сообщение.
Пока нет
способа
получить
первоначальный
адрес
назначения
пакетов
через SOCK_DGRAM.
СМОТРИТЕ
ТАКЖЕ
socket(2), pcap(3), capabilities(7),
ip(7), raw(7), socket(7)
В RFC 894
описана
упаковка
стандартного
IP Ethernet. В RFC 1700
описана
упаковка IP IEEE
802.3.
Заголовочный
файл <linux/if_ether.h>
содержит
протоколы
физического
уровня.
Дерево
исходного
кода ядра Linux.
В /Documentation/networking/filter.txt
описано
как к
пакетным
сокетам
применять
Berkeley Packet Filters. В
/tools/testing/selftests/net/psock_tpacket.c
содержится
пример
исходного
кода для
всех
доступных
версий PACKET_RX_RING
и PACKET_TX_RING.