Пример: функция mcast_join

We use cookies. Read the Privacy and Cookie Policy

Пример: функция mcast_join

В листинге 21.1[1] показана первая часть функции mcast_join. Эта часть демонстрирует простоту интерфейса программирования, не зависящего от протокола.

Листинг 21.1. Присоединение к группе: сокет IPv4

//lib/mcast_join.c

 1 #include "unp.h"

 2 #include <net/if.h>

 3 int

 4 mcast_join(int sockfd, const SA *grp, socklen_t grplen,

 5 const char *ifname, u_int ifindex)

 6 {

 7 #ifdef MCAST_JOIN_GROUP

 8  struct group_req req;

 9  if (ifindex > 0) {

10   req.gr_interface = ifindex;

11  } else if (ifname != NULL) {

12   if ((req.gr_interface = if_nametoindex(ifname)) == 0) {

13    errno = ENXIO; /* интерфейс не найден */

14    return(-1);

15   }

16  } else

17  req.gr_interface = 0;

18  if (grplen > sizeof(req.gr_group)) {

19   errno = EINVAL;

20   return -1;

21  }

22  memcpy(&req.gr_group, grp, grplen);

23  return (setsockopt(sockfd, family_to_level(grp->sa_family),

24  MCAST_JOIN_GROUP, &req, sizeof(req)));

25 #else

Обработка индекса

9-17 Если при вызове был указан индекс интерфейса, функция использует его непосредственно. В противном случае (при указании имени интерфейса), имя преобразуется в индекс вызовом if_nametoindex. Если ни имя, ни индекс не заданы, интерфейс выбирается ядром.

Копирование адреса и вызов setsockopt

18-22 Адрес сокета копируется непосредственно в поле группы. Вспомните, что поле это имеет тип sockaddr_storage, а потому достаточно велико для хранения адреса любого типа, поддерживаемого системой. Для предотвращения переполнения буфера (при ошибках в программе) мы проверяем размер sockaddr и возвращаем EINVAL, если он слишком велик.

23-24 Присоединение к группе выполняется вызовом setsockopt. Аргумент level определяется на основании семейства группового адреса вызовом нашей собственной функции family_to_level. Некоторые системы допускают несоответствие аргумента level семейству адреса сокета, например использование IPPROTO_IP с MCAST_JOIN_GROUP, даже если сокет относится к семейству AF_INET6, но это верно не для всех систем, поэтому мы и должны выполнить преобразование семейства к нужному значению level. Листинг этой тривиальной функции в книге мы не приводим, но исходный код этой функции вы можете скачать вместе со всеми остальными программами.

В листинге 21.2 представлена вторая часть функции mcast_join, обрабатывающая сокеты IPv4.

Листинг 21.2. Присоединение к группе: обработка сокета IPv4

26  switch (grp->sa_family) {

27  case AF_INET: {

28   struct ip_mreq mreq;

29   struct ifreq ifreq;

30   memcpy(&mreq.imr_multiaddr,

31    &((const struct sockaddr_in*)grp)->sin_addr,

32    sizeof(struct in_addr));

33    if (ifindex > 0) {

34     if (if_indextoname(ifindex, ifreq.ifr_name) == NULL) {

35      errno = ENXIO; /* i/f index not found */

36      return(-1);

37     }

38     goto doioctl;

39    } else if (ifname != NULL) {

40     strncpy(ifreq.ifr_name, ifname, IFNAMSIZ);

41 doioctl:

42     if (ioctl(sockfd, SIOCGIFADDR, &ifreq) < 0)

43      return(-1);

44     memcpy(&mreq.imr_interface,

45      &((struct sockaddr_in*)&ifreq.ifr_addr)->sin_addr,

46      sizeof(struct in_addr));

47    } else

48     mreq.imr_interface.s_addr = htonl(INADDR_ANY);

49    return(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,

50     &mreq, sizeof(mreq)));

51   }

Обработка индекса

33-38 Адрес многоадресной передачи IPv4 в структуре адреса сокета копируется в структуру ip_mreq. Если индекс был задан, вызывается функция if_indextoname, сохраняющая имя в нашей структуре ip_mreq. Если это выполняется успешно, мы переходим на точку вызова ioctl.

Обработка имени

39-46 Имя вызывающего процесса копируется в структуру ip_mreq, а вызов SIOCGIFADDR функции ioctl возвращает адрес многоадресной передачи, связанный с этим именем. При успешном выполнении адрес IPv4 копируется в элемент imr_interface структуры ip_mreq.

Значения по умолчанию

47-48 Если ни индекс, ни имя не заданы, используется универсальный адрес, что указывает ядру на необходимость выбрать интерфейс.

49-50 Функция setsockopt выполняет присоединение к группе.

Третья, и последняя, часть функции, обрабатывающая сокеты IPv6, приведена в листинге 21.3.

Листинг 21.3. Присоединение к группе: обработка сокета IPv6

52 #ifdef IPV6

53  case AF_INET6: {

54   struct ipv6_mreq mreq6;

55   memcpy(&mreq6.ipv6mr_multiaddr,

56    &((const struct sockaddr_in6*) grp)->sin6_addr,

57    sizeof(struct in6_addr));

58    if (ifindex > 0) {

59     mreq6.ipv6mr_interface = ifindex;

60    } else if (ifname != NULL) {

61     if ((mreq6.ipv6mr_interface = if_nametoindex(ifname)) == 0) {

62      errno = ENXIO; /* интерфейс не найден */

63      return(-1);

64     }

65    } else

66     mreq6.ipv6mr_interface = 0;

67    return(setsockopt(sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,

68     &mreq6, sizeof(mreq6)));

69   }

70 #endif

71  default:

72   errno = EAFNOSUPPORT;

73   return(-1);

74  }

75 #endif

76 }

Копирование адреса

55-57 Сначала адрес IPv6 копируется из структуры адреса сокета в структуру ipv6_mreq.

Обработка индекса или имени интерфейса или выбор интерфейса по умолчанию

58-66 Если был задан индекс, он записывается в элемент ipv6mr_interface. Если индекс не задан, но задано имя, то для получения индекса вызывается функция if_nametoindex. В противном случае для функции setsockopt индекс устанавливается в 0, что указывает ядру на необходимость выбрать интерфейс.

67-68 Выполняется присоединение к группе.

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