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). Они позволяют пользователю реализовывать модули протоколов в пользовательском пространстве поверх физического уровня.

The socket_type is either SOCK_RAW for raw packets including the link-level header or SOCK_DGRAM for cooked packets with the link-level header removed. The link-level header information is available in a common format in a sockaddr_ll structure. protocol is the IEEE 802.3 protocol number in network byte order. See the <linux/if_ether.h> include file for a list of allowed protocols. When protocol is set to htons(ETH_P_ALL), then all protocols are received. All incoming packets of that protocol type will be passed to the packet socket before they are passed to the protocols implemented in the kernel. If protocol is set to zero, no packets are received. bind(2) can optionally be called with a nonzero sll_protocol to start receiving packets for the protocols specified.

Для создания пакетного сокета процесс должен иметь мандат 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
    is the standard ethernet protocol type in network byte order as defined in the <linux/if_ether.h> include file. It defaults to the socket's protocol.

sll_ifindex
    is the interface index of the interface (see netdevice(7)); 0 matches any interface (only permitted for binding). sll_hatype is an ARP type as defined in the <linux/if_arp.h> include file.

sll_pkttype
    contains the packet type. Valid types are PACKET_HOST for a packet addressed to the local host, PACKET_BROADCAST for a physical-layer broadcast packet, PACKET_MULTICAST for a packet sent to a physical-layer multicast address, PACKET_OTHERHOST for a packet to some other host that has been caught by a device driver in promiscuous mode, and PACKET_OUTGOING for a packet originating from the local host that is looped back to a packet socket. These types make sense only for receiving.

sll_addr
sll_halen
    contain the physical-layer (e.g., IEEE 802.3) address and its length. The exact interpretation depends on the device.

Когда вы посылаете пакеты, достаточно указать 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;      /* packet length */
            __u32 tp_snaplen;  /* captured length */
            __u16 tp_mac;
            __u16 tp_net;
            __u16 tp_vlan_tci;
            __u16 tp_vlan_tpid; /* Since Linux 3.14; earlier, these
                                   were unused padding bytes */
        };

PACKET_FANOUT (начиная с Linux 3.1)
    To scale processing across threads, packet sockets can form a fanout group. In this mode, each matching packet is enqueued onto only one socket in the group. A socket joins a fanout group by calling setsockopt(2) with level SOL_PACKET and option PACKET_FANOUT. Each network namespace can have up to 65536 independent groups. A socket selects a group by encoding the ID in the first 16 bits of the integer option value. The first packet socket to join a group implicitly creates it. To successfully join an existing group, subsequent packet sockets must have the same protocol, device settings, fanout mode, and flags (see below). Packet sockets can leave a fanout group only by closing the socket. The group is deleted when the last socket is closed.

    Для разветвления поддерживается несколько алгоритмов распределения трафика по сокетам:

    •   Режим по умолчанию 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.

    Packet sockets implement multiple variants of the packet ring. The implementation details are described in Documentation/networking/packet_mmap.rst in the Linux kernel source tree.

PACKET_STATISTICS
    Возвращает статистику по пакетному сокету в виде структуры

        struct tpacket_stats {
            unsigned int tp_packets;  /* общее количество пакетов */
            unsigned int tp_drops;    /* кол-во отброшенных пакетов */
        };

    При получении статистики сбрасываются внутренние счётчики. Если используется вариант кольца TPACKET_V3, то статистика имеет другую структуру.

PACKET_TIMESTAMP (с PACKET_RX_RING; начиная с Linux 2.6.36)
    The packet receive ring always stores a timestamp in the metadata header. By default, this is a software generated timestamp generated when the packet is copied into the ring. This integer option selects the type of timestamp. Besides the default, it support the two hardware formats described in Documentation/networking/timestamping.rst in the Linux kernel source tree.

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)
    By default, packets sent through packet sockets pass through the kernel's qdisc (traffic control) layer, which is fine for the vast majority of use cases. For traffic generator appliances using packet sockets that intend to brute-force flood the network—for example, to test devices under load in a similar fashion to pktgen—this layer can be bypassed by setting this integer option to 1. A side effect is that packet buffering in the qdisc layer is avoided, which will lead to increased drops when network device transmit queues are busy; therefore, use at your own risk.

  Вызовы 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, protocol)

Он всё ещё поддерживается, но устарел и настоятельно не рекомендуется. Основным отличием между методами — для указания интерфейса через 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.

Эта структура устарела и не должна использоваться в новом коде.

ОШИБКИ

  LLC header handling
Способ обработки IEEE 802.2/803.3 LLC не считается за дефектный.

  MSG_TRUNC issues
Расширение MSG_TRUNC recvmsg(2) является неудачным решением и должно быть заменено на управляющее сообщение. Пока нет способа получить первоначальный адрес назначения пакетов через SOCK_DGRAM.

  spkt_device device name truncation
The spkt_device field of sockaddr_pkt has a size of 14 bytes, which is less than the constant IFNAMSIZ defined in <net/if.h> which is 16 bytes and describes the system limit for a network interface name. This means the names of network devices longer than 14 bytes will be truncated to fit into spkt_device. All these lengths include the terminating null byte ('\0')).

Issues from this with old code typically show up with very long interface names used by the Predictable Network Interface Names feature enabled by default in many modern Linux distributions.

The preferred solution is to rewrite code to avoid SOCK_PACKET. Possible user solutions are to disable Predictable Network Interface Names or to rename the interface to a name of at most 13 bytes, for example using the ip(8) tool.

  Documentation issues
Не описаны сокетные фильтры.

СМОТРИТЕ ТАКЖЕ
socket(2), pcap(3), capabilities(7), ip(7), raw(7), socket(7), ip(8),

В RFC 894 описана упаковка стандартного IP Ethernet. В RFC 1700 описана упаковка IP IEEE 802.3.

Заголовочный файл <linux/if_ether.h> содержит протоколы физического уровня.

The Linux kernel source tree. Documentation/networking/filter.rst describes how to apply Berkeley Packet Filters to packet sockets. tools/testing/selftests/net/psock_tpacket.c contains example source code for all available versions of PACKET_RX_RING and PACKET_TX_RING.

ПЕРЕВОД
Русский перевод этой страницы руководства разработал(и) Alexey, Azamat Hackimov <azamat.hackimov@gmail.com>, kogamatranslator49 <r.podarov@yandex.ru>, Darima Kogan <silverdk99@gmail.com>, Max Is <ismax799@gmail.com>, Yuri Kozlov <yuray@komyakino.ru>, Иван Павлов <pavia00@gmail.com> и Kirill Rekhov <krekhov.dev@gmail.com>

Этот перевод является свободной программной документацией; он распространяется на условиях общедоступной лицензии GNU (GNU General Public License - GPL, https://www.gnu.org/licenses/gpl-3.0.html версии 3 или более поздней) в отношении авторского права, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ.

Если вы обнаружите какие-либо ошибки в переводе этой страницы руководства, пожалуйста, сообщите об этом разработчику(ам) по его(их) адресу(ам) электронной почты или по адресу списка рассылки русских переводчиков (<debian-l10n-russian@lists.debian.org>).