CMSG(3) | Руководство программиста Linux | CMSG(3) |
CMSG_ALIGN, CMSG_SPACE, CMSG_NXTHDR, CMSG_FIRSTHDR - доступ к вспомогательным данным
#include <sys/socket.h> struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh); struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh , struct cmsghdr *cmsg); size_t CMSG_ALIGN(size_t length); size_t CMSG_SPACE(size_t length); size_t CMSG_LEN(size_t length); unsigned char *CMSG_DATA(struct cmsghdr *cmsg);
Эти макросы используются для создания сообщений контроля доступа (также называемых вспомогательными данными), которые не являются частью полезной нагрузки сокета. Эта управляющая информация может содержать интерфейс, с которого получен пакет, различные редко используемые поля заголовков и расширенное описание ошибок, наборы файловых дескрипторов или параметров доступа (credentials) UNIX. Например, управляющие сообщения могут быть использованы для отправки дополнительных полей заголовков, таких, как параметры IP. Вспомогательные данные посылаются с помощью вызова sendmsg(2) и принимаются посредством вызова recvmsg(2). Более полная информация приведена в их справочных страницах.
Вспомогательные данные представляют собой последовательность структур cmsghdr с присоединёнными к ней данными. Возможные типы управляющих сообщений смотрите в соответствующих страницах протоколов. Максимально возможный размер буфера вспомогательных данных одного сокета можно установить через /proc/sys/net/core/optmem_max; смотрите socket(7).
Структура cmsghdr определена следующим образом:
struct cmsghdr { size_t cmsg_len; /* счетчик байтов данных с заголовком (тип — socklen_t в POSIX) */ int cmsg_level; /* начальный протокол */ int cmsg_type; /* тип, зависящий от протокола */ /* после unsigned char cmsg_data[]; */ };
К последовательности структур cmsghdr нельзя обращаться напрямую. Для этого используйте следующие макросы:
Чтобы создать вспомогательные данные, сначала инициализируйте элемент msg_controllen структуры msghdr, указав длину буфера управляющего сообщения. Запустите CMSG_FIRSTHDR() со структурой msghdr для получения первого управляющего сообщения и CMSG_NEXTHDR() для получения последующих. Для каждого управляющего сообщения инициализируйте cmsg_len (с помощью CMSG_LEN()) и другие поля заголовка cmsghdr, а также часть для данных (с помощью CMSG_DATA()). В результате значение поля msg_controllen структуры msghdr будет равно сумме CMSG_SPACE() для длин всех управляющих сообщений в буфере. Дополнительная информация о msghdr находится в recvmsg(2).
Эта модель вспомогательных данных соответствует черновому варианту POSIX.1g, 4.4BSD-Lite, расширению IPv6 API, описанному в RFC 2292, и SUSv2. CMSG_ALIGN() — это расширение Linux.
В целях переносимости вспомогательные данные могут быть доступны только с помощью макросов, описанных здесь. CMSG_ALIGN() — это расширение Linux, поэтому оно не должно быть использовано в переносимых программах.
В Linux CMSG_LEN(), CMSG_DATA() и CMSG_ALIGN() — это постоянные выражения (вне зависимости от переданных аргументов), то есть эти значения можно использовать для объявления размера глобальных переменных. Но это также сделает программу непереносимой на другие платформы.
Данный код ищет параметр IP_TTL в буфере с полученными вспомогательными данными:
struct msghdr msgh; struct cmsghdr *cmsg; int *ttlptr; int received_ttl; /* приём вспомогательных данных в msgh */ for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) { ttlptr = (int *) CMSG_DATA(cmsg); received_ttl = *ttlptr; break; } } if (cmsg == NULL) { /* Ошибка: не включён IP_TTL, мал буфер или ошибка ввода-вывода */ }
Данный код передаёт массив файловых дескрипторов через доменный сокет UNIX, используя SCM_RIGHTS:
struct msghdr msg = { 0 }; struct cmsghdr *cmsg; int myfds[NUM_FD]; /* содержит дескрипторы файлов для передачи */ char iobuf[1]; struct iovec io = { .iov_base = iobuf, .iov_len = sizeof(iobuf) }; union { /* буфер вспомогательных данных, обёрнутый в union, чтобы точно получить нужное выравнивание */ char buf[CMSG_SPACE(sizeof(myfds))]; struct cmsghdr align; } u; msg.msg_iov = &io; msg.msg_iovlen = 1; msg.msg_control = u.buf; msg.msg_controllen = sizeof(u.buf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int) * NUM_FD); memcpy(CMSG_DATA(cmsg), myfds, NUM_FD * sizeof(int));
RFC 2292
2019-03-06 | Linux |