PACKET(7) | Руководство программиста Linux | PACKET(7) |
ИМЯ¶
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.
ЗАМЕЧАНИЯ¶
Эта страница является частью проекта Linux man-pages версии 4.16. Описание проекта, информацию об ошибках и последнюю версию этой страницы можно найти по адресу https://www.kernel.org/doc/man-pages/.
ПЕРЕВОД¶
Русский перевод этой страницы руководства был сделан Alexey, Azamat Hackimov <azamat.hackimov@gmail.com>, kogamatranslator49 <r.podarov@yandex.ru>, Kogan, Max Is <ismax799@gmail.com>, Yuri Kozlov <yuray@komyakino.ru> и Иван Павлов <pavia00@gmail.com>
Этот перевод является бесплатной документацией; прочитайте Стандартную общественную лицензию GNU версии 3 или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.
Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на man-pages-ru-talks@lists.sourceforge.net.
15 сентября 2017 г. | Linux |