22.2. Получение флагов, IP-адреса получателя и индекса интерфейса

22.2. Получение флагов, IP-адреса получателя и индекса интерфейса

Исторически функции sendmsg и recvmsg использовались только для передачи дескрипторов через доменные сокеты Unix (см. раздел 15.7), но даже это происходило сравнительно редко. Однако в настоящее время популярность этих двух функций растет по двум причинам:

1. Элемент msg_flags, добавленный в структуру msghdr в реализации 4.3BSD Reno, возвращает приложению флаги сообщения. Эти флаги мы перечислили в табл. 14.2.

2. Вспомогательные данные используются для передачи все большего количества информации между приложением и ядром. В главе 27 мы увидим, что IPv6 продолжает эту тенденцию.

В качестве примера использования функции recvmsg мы напишем функцию recvfrom_flags, аналогичную функции recvfrom, но дополнительно позволяющую получить:

? возвращаемое значение msg_flags;

? адрес получателя полученной дейтаграммы (из параметра сокета IP_RECVDSTADDR);

? индекс интерфейса, на котором была получена дейтаграмма (параметр сокета IP_RECVIF).

Чтобы можно было получить два последних элемента, мы определяем в нашем заголовке unp.h следующую структуру:

struct in_pktinfo {

 struct in_addr ipi_addr;    /* IPv4-адрес получателя */

 int            ipi_ifindex; /* индекс интерфейса, на котором была

                                получена дейтаграмма */

};

Мы выбрали имена структуры и ее элементов так, чтобы получить определенное сходство со структурой IPv6 in6_pktinfo, возвращающей те же два элемента для сокета IPv6 (см. раздел 22.8). Наша функция recvfrom_flags будет получать в качестве аргумента указатель на структуру in_pktinfo, и если этот указатель не нулевой, возвращать структуру через указатель.

Проблема построения этой структуры состоит в том, что неясно, что возвращать, если недоступна информация, которая должна быть получена из параметра сокета IP_RECVDSTADDR (то есть реализация не поддерживает данный параметр сокета). Обработать индекс интерфейса легко, поскольку нулевое значение может использоваться как указание на то, что индекс неизвестен. Но для IP-адреса все 32-разрядные значения являются действительными. Мы выбрали такое решение: адрес получателя 0.0.0.0 возвращается в том случае, когда действительное значение недоступно. Хотя это реальный IP-адрес, использовать его в качестве IP-адреса получателя не разрешается (RFC 1122 [10]). Он будет действителен только в качестве IP-адреса отправителя во время начальной загрузки узла, когда узел еще не знает своего IP-адреса.

ПРИМЕЧАНИЕ

К сожалению, Беркли-ядра принимают дейтаграммы, предназначенные для адреса 0.0.0.0 [128, с. 218-219]. Это устаревшие адреса широковещательной передачи, генерируемые ядрами 4.2BSD.

Первая часть нашей функции recvfrom_flags представлена в листинге 22.1[1]. Эта функция предназначена для использования с сокетом UDP.

Листинг 22.1. Функция recvfrom_flags: вызов функции recvmsg

//adviо/recvfromflags.c

 1 #include "unp.h"

 2 #include <sys/param.h> /* макрос ALIGN для макроса CMSG_NXTHDR() */

 3 ssize_t

 4 recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,

 5  SA *sa, socklen_t *salenptr, struct unp_in_pktinfo *pktp)

 6 {

 7  struct msghdr msg;

 8  struct iovec iov[1];

 9  ssize_t n;

10 #ifdef HAVE_MSGHDR_MSG_CONTROL

11  struct cmsghdr *cmptr;

12  union {

13   struct cmsghdr cm;

14   char control[CMSG_SPACE(sizeof(struct in_addr)) +

15    CMSG_SPACE(sizeof(struct unp_in_pktinfo))];

16  } control_un;

17  msg.msg_control = control_un.control;

18  msg.msg_controllen = sizeof(control_un.control);

19  msg.msg_flags = 0;

20 #else

21  bzero(&msg, sizeof(msg)); /* обнуление msg_accrightslen = 0 */

22 #endif

23  msg.msg_name = sa;

24  msg.msg_namelen = *salenptr;

25  iov[0].iov_base = ptr;

26  iov[0].iov_len = nbytes;

27  msg.msg_iov = iov;

28  msg.msg_iovlen = 1;

29  if ((n = recvmsg(fd, &msg, *flagsp)) < 0)

30   return(n);

31  *salenptr = msg.msg_namelen; /* возвращение результатов */

32  if (pktp)

33   bzero(pktp, sizeof(struct unp_in_pktinfo)); /* 0.0.0.0. интерфейс = 0 */

Подключаемые файлы

1-2 Использование макроопределения CMSG_NXTHDR требует подключения заголовочного файла <sys/param.h>.

Аргументы функции

3-5 Аргументы функции аналогичны аргументам функции recvfrom за исключением того, что четвертый аргумент является указателем на целочисленный флаг (так что мы можем возвратить флаги, возвращаемые функцией recvmsg), а седьмой аргумент новый: это указатель на структуру unp_in_pktinfo, содержащую IPv4-адрес получателя пришедшей дейтаграммы и индекс интерфейса, на котором дейтаграмма была получена.

Различия реализаций

10-22 При работе со структурой msghdr и различными константами MSG_XXX мы встречаемся со множеством различий в реализациях. Одним из вариантов обработки таких различий может быть использование имеющейся в языке С возможности условного подключения (директива #ifdef). Если реализация поддерживает элемент msg_control, то выделяется пространство для хранения значений, возвращаемых параметрами сокета IP_RECVDSTADDR и IP_RECVIF, и соответствующие элементы инициализируются.

Заполнение структуры msghdr и вызов функции recvmsg

23-33 Заполняется структура msghdr и вызывается функция recvmsg. Значения элементов msg_namelen и msg_flags должны быть переданы обратно вызывающему процессу. Они являются аргументами типа «значение-результат». Мы также инициализируем структуру вызывающего процесса unp_in_pktinfo, устанавливая IP-адрес 0.0.0.0 и индекс интерфейса 0.

В листинге 22.2 показана вторая часть нашей функции.

Листинг 22.2. Функция recvfrom_flags: возвращаемые флаги и адрес получателя

//advio/recvfromflags.c

34 #ifndef HAVE_MSGHDR_MSG_CONTROL

35  *flagsp = 0; /* возвращение результатов */

36  return(n);

37 #else

38  *flagsp = msg.msg_flags; /* возвращение результатов */

39  if (msg.msg_controllen < sizeof(struct cmsghdr) ||

40   (msg.msg_flags & MSG_CTRUNC) || pktp == NULL)

41    return(n);

42   for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;

43    cmptr = CMSG_NXTHDR(&msg, cmptr)) {

44 #ifdef IP_RECVDSTADDR

45    if (cmptr->cmsg_level == IPPROTO_IP &&

46     cmptr->cmsg_type == IP_RECVDSTADDR) {

47     memcpy(&pktp->ipi_addr, CMSG_DATA(cmptr),

48      sizeof(struct in_addr));

49     continue;

50    }

51 #endif

52 #ifdef IP_RECVIF

53    if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) {

54     struct sockaddr_dl *sdl;

55    sdl = (struct sockaddr_dl*)CMSG_DATA(cmptr);

56    pktp->ipi_ifindex = sdl->sdl_index;

57    continue;

58   }

59 #endif

60   err_quit("unknown ancillary data, len = %d, level = %d, type = %d",

61    cmptr->cmsg_len, cmptr->cmsg_level, cmptr->cmsg_type);

62  }

63  return(n);

64 #endif /* HAVE_MSGHDR_MSG_CONTROL */

65 }

34-37 Если реализация не поддерживает элемента msg_control, мы просто обнуляем возвращаемые флаги и завершаем функцию. Оставшаяся часть функции обрабатывает информацию, содержащуюся в структуре msg_control.

Возвращение при отсутствии управляющей информации

38-41 Мы возвращаем значение msg_flags и передаем управление вызывающей функции в том случае, если нет никакой управляющей информации, управляющая информация была обрезана или вызывающий процесс не требует возвращения структуры unp_in_pktinfo.

Обработка вспомогательных данных

42-43 Мы обрабатываем произвольное количество объектов вспомогательных данных с помощью макросов CMSG_FIRSTHDR и CMSG_NEXTHDR.

Обработка параметра сокета IP_RECVDSTADDR

47-54 Если в составе управляющей информации был возвращен IP-адрес получателя (см. рис. 14.2), он возвращается вызывающему процессу.

Обработка параметра сокета IP_RECVIF

55-63 Если в составе управляющей информации был возвращен индекс интерфейса, он возвращается вызывающему процессу. На рис. 22.1 показано содержимое возвращенного объекта вспомогательных данных.

Рис. 22.1. Объект вспомогательных данных, возвращаемый для параметра IP_RECVIF

Вспомните структуру адреса сокета канального уровня (см. листинг 18.1). Данные, возвращаемые в объекте вспомогательных данных, представлены в одной из этих структур, но длины трех элементов являются нулевыми (длина имени, адреса и селектора). Следовательно, нет никакой необходимости указывать эти значения, и таким образом структура имеет размер 8 байт, а не 20, как было в листинге 18.1. Возвращаемая нами информация — это индекс интерфейса.

Данный текст является ознакомительным фрагментом.