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);

struct cmsghdr {
    socklen_t cmsg_len;    /* счетчик байтов данных с заголовком */
    int       cmsg_level;  /* создаваемый протокол передачи */
    int       cmsg_type;   /* тип, зависящий от протокола */
    /* далее unsigned char cmsg_data[]; */
};

ОПИСАНИЕ
Эти макросы используются для создания сообщений контроля доступа (также называемых вспомогательными данными), которые не являются частью полезной нагрузки сокета. Эта управляющая информация может содержать интерфейс, с которого получен пакет, различные редко используемые поля заголовков и расширенное описание ошибок, наборы файловых дескрипторов или параметров доступа (credentials) UNIX. Например, управляющие сообщения могут быть использованы для отправки дополнительных полей заголовков, таких, как параметры IP. Вспомогательные данные посылаются с помощью вызова sendmsg(2) и принимаются посредством вызова recvmsg(2). Более полная информация приведена в их справочных страницах.

Вспомогательные данные представляют собой последовательность структур struct cmsghdr с присоединёнными к ней данными. Доступ к этой последовательности может быть осуществлён только с помощью макросов, описанных в данной справочной странице. Возможные типы управляющих сообщений смотрите в соответствующих страницах протоколов. Максимально возможный размер буфера вспомогательных данных одного сокета можно установить через /proc/sys/net/core/optmem_max; смотрите socket(7).

Макрос CMSG_FIRSTHDR() возвращает указатель на первый cmsghdr в буфере вспомогательных данных, связанных с переданным msghdr.

Макрос CMSG_NXTHDR() возвращает следующее значение cmsghdr после переданного cmsghdr. Он возвращает NULL, если в буфере недостаточно места.

Макрос CMSG_ALIGN() возвращает длину, учитывая необходимое выравнивание. Это постоянное выражение.

Макрос CMSG_SPACE() возвращает количество байт, которое занимает вспомогательный элемент с полезной нагрузкой переданных данных. Это постоянное выражение.

Макрос CMSG_DATA() возвращает указатель на часть с данными в cmsghdr.

Макрос CMSG_LEN возвращает значение, хранящееся в поле cmsg_len структуры cmsghdr с учётом необходимого выравнивания. Он принимает значение объёма данных в качестве аргумента. Это постоянное выражение.

Чтобы создать вспомогательные данные, сначала инициализируйте элемент msg_controllen структуры msghdr, указав длину буфера управляющего сообщения. Запустите CMSG_FIRSTHDR() со структурой msghdr для получения первого управляющего сообщения и CMSG_NEXTHDR() для получения последующих. Для каждого управляющего сообщения инициализируйте cmsg_len (с помощью CMSG_LEN()) и другие поля заголовка cmsghdr, а также часть для данных (с помощью CMSG_DATA()). В результате значение поля msg_controllen структуры msghdr будет равно сумме CMSG_SPACE() для длин всех управляющих сообщений в буфере. Дополнительная информация о msghdr находится в recvmsg(2).

Если буфер управляющих сообщений слишком мал для хранения всех сообщений, то флагу MSG_CTRUNC в поле msg_flags структуры msghdr присваивается значение единицы.

СООТВЕТСТВИЕ СТАНДАРТАМ
Эта модель вспомогательных данных соответствует черновому варианту 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]; /* содержит дескрипторы файлов для передачи */
union {
    /* буфер вспомогательных данных, обёрнутый в union, чтобы точно
       получить корректное выравнивание */
    char buf[CMSG_SPACE(sizeof myfds)];
    struct cmsghdr align;
} u;
int *fdptr;
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);
/* инициализация полезной нагрузки: */
fdptr = (int *) CMSG_DATA(cmsg);
memcpy(fdptr, myfds, NUM_FD * sizeof(int));

СМОТРИТЕ ТАКЖЕ
recvmsg(2), sendmsg(2)

RFC 2292