Книга: UNIX: разработка сетевых приложений

18.5. Функция get_ifi_info (повтор)

18.5. Функция get_ifi_info (повтор)

Вернемся к примеру из раздела 17.6 — возвращение всех активных интерфейсов в виде связного списка структур ifi_info (см. листинг 17.2). Программа prifinfo остается без изменений (см. листинг 17.3), но теперь мы покажем версию функции get_ifi_info, использующую функцию sysctl вместо вызова SIOCGIFCONF функции ioctl в листинге 17.4.

Сначала в листинге 18.8 мы представим функцию net_rt_iflist. Эта функция вызывает функцию sysctl с командой NET_RT_IFLIST, чтобы возвратить список интерфейсов для заданного семейства адресов.

Листинг 18.8. Вызов функции sysctl для возвращения списка интерфейсов

//libroute/net_rt_iflist.c
 1 #include "unproute.h"
 2 char*
 3 net_rt_iflist(int family, int flags, size_t *lenp)
 4 {
 5  int mib[6];
 6  char *buf;
 7  mib[0] = CTL_NET;
 8  mib[1] = AF_ROUTE;
 9  mib[2] = 0;
10  mib[3] = family; /* только адреса этого семейства */
11  mib[4] = NET_RT_IFLIST;
12  mib[5] = flags; /* индекс интерфейса или 0.*/
13  if (sysctl(mib, 6, NULL, lenp, NULL, 0) < 0)
14   return (NULL);
15  if ((buf = malloc(*lenp)) == NULL)
16   return (NULL);
17  if (sysctl(mib, 6, buf, lenp, NULL, 0) < 0) {
18   free(buf);
19   return (NULL);
20  }
21  return (buf);
22 }
7-14
 Инициализируется массив mib, как показано в табл. 18.3, для возвращения списка интерфейсов и всех сконфигурированных адресов заданного семейства. Затем функция sysctl вызывается дважды. В первом вызове функции третий аргумент нулевой, в результате чего в переменной, на которую указывает lenp, возвращается размер буфера, требуемый для хранения всей информации об интерфейсе.

15-21 Затем в памяти выделяется место для буфера, и функция sysctl вызывается снова, на этот раз с ненулевым третьим аргументом. При этом переменная, на которую указывает lenp, содержит при завершении функции число, равное количеству информации, хранимой в буфере, и эта переменная размещается в памяти вызывающим процессом. Указатель на буфер также возвращается вызывающему процессу.

ПРИМЕЧАНИЕ

Поскольку размер таблицы маршрутизации или число интерфейсов может изменяться между двумя вызовами функции sysctl, значение, возвращаемое при первом вызове, содержит поправочный множитель 10% [128, с. 639-640].

В листинге 18.9 показана первая половина функции get_ifi_info.

Листинг 18.9. Функция get_ifi_info, первая половина

//route/get_ifi_info.c
 3 struct ifi_info *
 4 get_ifi_info(int family, int doaliases)
 5 {
 6  int flags;
 7  char *buf, *next, *lim;
 8  size_t len;
 9  struct if_msghdr *ifm;
10  struct ifa_msghdr *ifam;
11  struct sockaddr *sa, *rti_info[RTAX_MAX];
12  struct sockaddr_dl *sdl;
13  struct ifi_info *ifi, *ifisave, *ifihead, **ifipnext;
14  buf = Net_rt_iflist(family, 0, &len);
15  ifihead = NULL;
16  ifipnext = &ifihead;
17  lim = buf + len;
18  for (next = buf; next < lim; next += ifm->ifm_msglen) {
19   ifm = (struct if_msghdr*)next;
20   if (ifm->ifm_type = RTM_IFINFO) {
21    if (((flags = ifm->ifm_flags) & IFF_UP) == 0)
22     continue; /* игнорируем, если интерфейс не активен */
23    sa = (struct sockaddr*)(ifm + 1);
24    get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
25    if ((sa = rti_info[RTAX_IFP]) != NULL) {
26     ifi = Calloc(1, sizeof(struct ifi_info));
27     *ifipnext = ifi; /* предыдущий указатель указывал на эту
                           структуру */
28     ifipnext = &ifi->ifi_next; /* указатель на следующую структуру */
29     ifi->ifi_flags = flags;
30     if (sa->sa_family == AF_LINK) {
31      sdl = (struct sockaddr_dl*)sa;
32      ifi->ifi_index = sdl->sdl_index;
33      if (sdl->sdl_nlen > 0)
34       snprintf(ifi->ifi_name, IFI_NAME, "%*s",
35        sdl->sdl_nlen, &sdl->sdl_data[0]);
36      else
37       snprintf(ifi->ifi_name, IFI_NAME, "index %d",
38        sdl->sdl_index);
39      if ((ifi->ifi_hlen = sdl->sdl_alen) > 0)
40       memcpy(ifi->ifi_haddr, LLADDR(sdl),
41      min(IFI_HADDR, sdl->sdl_alen));
42     }
43    }
6-14
 Мы объявляем локальные переменные и затем вызываем нашу функцию net_rt_iflist.

17-19 Цикл for — это цикл по всем сообщениям маршрутизации, попадающим в буфер в результате выполнения функции sysctl. Мы предполагаем, что сообщение — это структура if_msghdr, и рассматриваем поле ifm_type (вспомните, что первые три элемента трех структур идентичны, поэтому все равно, какую из трех структур мы используем для просмотра типа элемента).

Проверка, включен ли интерфейс

20-22 Для каждого интерфейса возвращается структура RTM_IFINFO. Если интерфейс не активен, он игнорируется.

Определение, какие структуры адреса сокета присутствуют

23-24 sa указывает на первую структуру адреса сокета, следующую за структурой if_msghdr. Наша функция get_rtaddrs инициализирует массив rti_info в зависимости от того, какие структуры адреса сокета присутствуют.

Обработка имени интерфейса

25-42 Если присутствует структура адреса сокета с именем интерфейса, в памяти размещается структура ifi_info и хранятся флаги интерфейса. Предполагаемым семейством этой структуры адреса сокета является AF_LINK, что означает структуру адреса сокета канального уровня. Если элемент sdl_nlen ненулевой, имя интерфейса копируется в структуру ifi_info. В противном случае в качестве имени хранится строка, содержащая индекс интерфейса. Если элемент sdl_alen ненулевой, аппаратный адрес (например, адрес Ethernet) копируется в структуру ifi_info, а его длина также возвращается как ifi_hlen.

В листинге 18.10 показана вторая часть нашей функции get_ifi_info, которая возвращает IP-адреса для интерфейса.

Листинг 18.10. Функция get_ifi_info, вторая часть

//route/get_ifi_info.c
44   } else if (ifm->ifm_type == RTM_NEWADDR) {
45    if (ifi->ifi_addr) { /* уже имеется IP-адрес для интерфейса */
46     if (doaliases == 0)
47      continue;
48     /* у нас имеется новый IP-адрес для существующего интерфейса */
49     ifisave = ifi;
50     ifi = Calloc(1, sizeof(struct ifi_info));
51     *ifipnext = ifi; /* предыдущий указатель указывал на эту
                           структуру */
52     ifipnext = &ifi->ifi_next; /* указатель на следующую структуру */
53     ifi->ifi_flags = ifi_save->ifi_flags;
54     ifi->ifi_index = ifisave->ifi_index;
55     ifi->ifi_hlen = ifisave->ifi_hlen;
56     memcpy(ifi->ifi_name, ifisave->ifi_name, IFI_NAME);
57     memcpy(ifi->ifi_haddr, ifisave->ifi_haddr, IFI_HADDR);
58    }
59    ifam = (struct ifa_msghdr*)next;
60    sa = (struct sockaddr*)(ifam + 1);
61    get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
62    if ((sa = rti_infо[RTAX_IFA]) != NULL) {
63     ifi->ifi_addr = Calloc(1, sa->sa_len);
64     memcpy(ifi->ifi_addr, sa, sa->sa_len);
65    }
66    if ((flags & IFF_BROADCAST) && (sa = rti_infо[RTAX_BRD]) |= NULL) {
67     ifi->ifi_brdaddr = Calloc(1, sa->sa_len);
68     memcpy(ifi->ifi_brdaddr, sa, sa->sa_len);
69    }
70    if ((flags & IFF_POINTOPOINT) &&
71     (sa = rti_infо[RTAX_BRD]) != NULL) {
72     ifi->ifi_dstaddr = Calloc(1, sa->sa_len);
73     memcpy(ifi->ifi_dstaddr, sa, sa->sa_len);
74    }
75   } else
76    err_quit("unexpected message type %d", ifm->ifm_type);
77  }
78  /* "ifihead" указывает на первую структуру в связном списке */
79  return (ifihead); /* указатель на первую структуру в связном списке */
80 }

Возвращение IP-адресов

44-65 Сообщение RTM_NEWADDR возвращается функцией sysctl для каждого адреса, связанного с интерфейсом: для первичного адреса и для всех альтернативных имен (псевдонимов). Если мы уже заполнили IP-адрес для этого интерфейса, то мы имеем дело с альтернативным именем. Поэтому если вызывающему процессу нужен адрес псевдонима, мы должны выделить память для другой структуры ifi_info, скопировать заполненные поля и затем заполнить возвращенный адрес.

Возвращение широковещательного адреса и адреса получателя

66-75 Если интерфейс поддерживает широковещательную передачу, возвращается широковещательный адрес, а если интерфейс является интерфейсом типа «точка-точка», возвращается адрес получателя.

Оглавление книги


Генерация: 1.309. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз