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

17.6. Функция get_ifi_info

17.6. Функция get_ifi_info

Поскольку многим программам нужно знать обо всех интерфейсах системы, мы разработаем нашу собственную функцию get_ifi_info, возвращающую связный список структур — по одной для каждого активного в настоящий момент интерфейса. В этом разделе мы покажем, как эта функция реализуется с помощью вызова SIOCGIFCONF функции ioctl, а в главе 18 мы создадим ее другую версию, использующую маршрутизирующие сокеты.

ПРИМЕЧАНИЕ

BSD/OS предоставляет функцию getifaddrs, имеющую аналогичную функциональность.

Поиск по всему дереву исходного кода BSD/OS 2.1 показывает, что 12 программ выполняют вызов SIOCGIFCONF функции ioctl для определения присутствующих интерфейсов.

Сначала мы определяем структуру ifi_info в новом заголовочном файле, который называется unpifi.h, показанном в листинге 17.2.

Листинг 17.2. Заголовочный файл unpifi.h

//ioctl/unpifi.h
 1 /* Наш собственный заголовочный файл для программ, которым требуется
 2 информация о конфигурации интерфейса. Включаем его вместо "unp.h". */
 3 #ifndef __unp_ifi_h
 4 #define __unp_ifi_h
 5 #include "unp.h"
 6 #include <net/if.h>
 7 #define IFI_NAME 16 /* то же, что и IFNAMSIZ в заголовке <net/if.h> */
 8 #define IFI_HADDR 8 /* с учетом 64-битового интерфейса EUI-64 в будущем */
 9 struct ifi_info {
10  char ifi_name[IFI_NAME];     /* имя интерфейса, заканчивается
                                    символом конца строки */
11  short ifi_index;             /* индекс интерфейса */
12  short ifi_mtu;               /* MTU для интерфейса */
13  u_char ifi_haddr[IFI_HADDR]; /* аппаратный адрес */
14  u_short ifi_hlen; /* количество байтов в аппаратном адресе: 0, 6, 8 */
15  short ifi_flags;  /* константы IFF_xxx из <net/if.h> */
16  short if_myflags; /* наши флаги IFI_xxx */
17  struct sockaddr *ifi_addr;    /* первичный адрес */
18  struct sockaddr *ifi_brdaddr; /* широковещательный адрес */
19  struct sockaddr *ifi_dstaddr; /* адрес получателя */
20 s truct ifi_info *ifi_next;    /* следующая из этих структур */
21 };
22 #define IFI_ALIAS 1 /* ifi_addr - это псевдоним */
23 /* прототипы функций */
24 struct ifi_info *get_ifi_info((int, int);
25 struct ifi_info *Get_ifi_info(int, int);
26 void free_ifi_info(struct ifi_info*);
27 #endif /* _unp_ifi_h */
9-21
 Связный список этих структур возвращается нашей функцией. Элемент ifi_next каждой структуры указывает на следующую структуру. Мы возвращаем в этой структуре информацию, которая может быть востребована в типичном приложении: имя интерфейса, индекс интерфейса, MTU, аппаратный адрес (например, адрес Ethernet), флаги интерфейса (чтобы позволить приложению определить, поддерживает ли приложение широковещательную или многоадресную передачу и относится ли этот интерфейс к типу «точка-точка»), адрес интерфейса, широковещательный адрес, адрес получателя для связи «точка-точка». Вся память, используемая для хранения структур ifi_info вместе со структурами адреса сокета, содержащимися в них, выделяется динамически. Следовательно, мы также предоставляем функцию free_ifi_info для освобождения всей этой памяти.

Перед тем как представить реализацию нашей функции ifi_info, мы покажем простую программу, которая вызывает эту функцию и затем выводит информацию. Эта программа, представленная в листинге 17.3, является уменьшенной версией программы ifconfig.

Листинг 17.3. Программа prifinfo, вызывающая нашу функцию ifi_info

//ioctl/prifinfo.c
 1 #include "unpifi.h"
 2 int
 3 main(int argc, char **argv)
 4 {
 5  struct ifi_info *ifi, *ifihead;
 6  struct sockaddr *sa;
 7  u_char *ptr;
 8  int i, family, doaliases;
 9  if (argc != 3)
10   err_quit("usage: prifinfo <inet4|inet6> <doaliases>");
11  if (strcmp(argv[1], "inet4") == 0)
12   family = AF_INET;
13  else if (strcmp(argv[1], "inet6") == 0)
14   family = AF_INET6;
15  else
16   err_quit("invalid <address-family>");
17  doaliases = atoi(argv[2]);
18  for (ifihead = ifi = Get_ifi_info(family, doaliases);
19  ifi ! = NULL; ifi = ifi->ifi_next) {
20   printf("%s: <", ifi->ifi_name);
21   if (ifi->ifi_index != 0)
22    printf("%d) ", ifi->ifi_index);
23   printf("<");
24   if (ifi->ifi_flags & IFF_UP) printf ("UP ");
25   if (ifi->ifi_flags & IFF_BROADCAST) printf("BCAST ");
26   if (ifi->ifi_flags & IFF_MULTICAST) printf("MCAST ");
27   if (ifi->ifi_flags & IFF_LOOPBACK) printf("LOOP ");
28   if (ifi->ifi_flags & IFF_POINTOPOINT) printf("P2P ");
29   printf(">n");
30   if ((i = ifi->ifi_hlen) > 0) {
31    ptr = ifi->ifi_haddr;
32    do {
33     printf("%s%x", (i == ifi->ifi_hlen) ? " " : ":", *ptr++);
34    } while (--i > 0);
35    printf("n");
36   }
37   if (ifi->ifi_mtu != 0)
38    printf(" MTU: %dn". ifi->ifi_mtu);
39   if ((sa = ifi->ifi_addr) != NULL)
40    printf(" IP addr: %sn", Sock_ntop_host(sa, sizeof(*sa)));
41   if ((sa = ifi->ifi_brdaddr) != NULL)
42    printf(" broadcast addr, %sn",
43     Sock_ntop_host(sa, sizeof(*sa)));
44   if ((sa = ifi->ifi_dstaddr) != NULL)
45    printf(" destination addr %sn",
46     Sock_ntop_host(sa, sizeof(*sa)));
47   }
48  free_ifi_info(ifihead);
49  exit(0);
59 }
18-47
 Программа представляет собой цикл for, в котором один раз вызывается функция get_ifi_info, а затем последовательно перебираются все возвращаемые структуры ifi_info.

20-36 Выводятся все имена интерфейсов и флаги. Если длина аппаратного адреса больше нуля, он выводится в виде шестнадцатеричного числа (наша функция get_ifi_info возвращает нулевую длину ifi_hlen, если адрес недоступен).

37-46 Выводится MTU и те IP-адреса, которые были возвращены.

Если мы запустим эту программу на нашем узле macosx (см. рис. 1.7), то получим следующий результат:

macosx % prifinfo inet4 0
lo0: <UP MCAST LOOP >
 MTU: 16384
 IP addr: 127.0.0.1
en1: <UP BCAST MCAST >
 MTU: 1500
 IP addr: 172.24.37.78
 broadcast addr: 172.24.37.95

Первый аргумент командной строки inet4 задает адрес IPv4, а второй, нулевой аргумент указывает, что не должно возвращаться никаких псевдонимов, или альтернативных имен (альтернативные имена IP-адресов мы описываем в разделе А.4). Обратите внимание, что в MacOS X аппаратный адрес интерфейса Ethernet недоступен.

Если мы добавим к интерфейсу Ethernet (en1) три альтернативных имени адреса с идентификаторами узла 79, 80 и 81 и изменим второй аргумент командной строки на 1, то получим:

macosx % prifinfo inet4 1
lo0: <UP MCAST LOOP >
 MTU: 16384
 IP addr: 127.0.0.1
en1: <UP BCAST MCAST >
 MTU: 1500
 IP addr: 172.24.37.78 первичный IP-адрес
 broadcast addr: 172.24.37.95
en1: <UP BCAST MCAST >
 MTU: 1500
 IP addr: 172.24.37.79 первый псевдоним
 broadcast addr: 172.24.37.95
en1: <UP BCAST MCAST >
 MTU: 1500
 IP addr: 172 24.37.80 второй псевдоним
 broadcast addr: 172.24 37.95
en1: <UP BCAST MCAST >
 MTU: 1500
 IP addr: 172 24.37.81 третий псевдоним
 broadcast addr: 172.24.37 95

Если мы запустим ту же программу под FreeBSD, используя реализацию функции get_ifi_info, приведенную в листинге 18.9 (которая может легко получить аппаратный адрес), то получим:

freebsd4 % prifinfo inet4 1
de0: <UP BCAST MCAST >
 0:80:c8:2b:d9:28
 IP addr: 135.197.17.100
 broadcast addr: 135.197.17.255
de1: <UP BCAST MCAST >
 0:40:5:42:d6:de
 IP addr: 172.24.37.94 основной IP-адрес
 broadcast addr: 172.24.37.95
ef0: <UP BCAST MCAST >
 0:40:5:42:d6:de
 IP addr: 172.24.37.93 псевдоним
 broadcast addr: 172.24.37.93
lo0: <UP MCAST LOOP >
 IP addr: 127.0.0.1

В этом примере мы указали программе выводить псевдонимы, и мы видим, что один из псевдонимов определен для второго интерфейса Ethernet (de1) с идентификатором узла 93.

Теперь мы покажем нашу реализацию функции get_ifi_info, использующую вызов SIOCGIFCONF функции ioctl. В листинге 17.4 показана первая часть этой функции, получающая от ядра конфигурацию интерфейса.

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

//lib/get_if_info.c
 1 #include "unpifi.h"
 2 struct ifi_info*
 3 get_ifi_info(int family, int doaliases)
 4 {
 5  struct ifi_info *ifi, *ifihead, **ifipnext;
 6  int sockfd, len, lastlen, flags, myflags, idx = 0, hlen = 0;
 7  char *ptr, *buf, lastname[IFNAMSIZ], *cptr, *haddr, *sdlname;
 8  struct ifconf ifc;
 9  struct ifreq *ifr, ifrcopy;
10  struct sockaddr_in *sinptr;
11  struct sockaddr_in6 *sin6ptr;
12  sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
13  lastlen = 0;
14  len = 100 * sizeof(struct ifreq); /* начальное приближение к нужному размеру буфера */
15  for (;;) {
16   buf = Mallос(len);
17   ifc.ifc_len = len;
18   ifc.ifc_buf = buf;
19   if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
20    if (errno != EINVAL || lastlen != 0)
21     err_sys("ioctl error");
22   } else {
23    if (ifc.ifc_len == lastlen)
24    break; /* успех, значение len не изменилось */
25    lastlen = ifc.ifc_len;
26   }
27   len += 10 * sizeof(struct ifreq); /* приращение */
28   free(buf);
29  }
30  ifihead = NULL;
31  ifipnext = &ifihead;
32  lastname[0] = 0;
33  sdlname = NULL;

Создание сокета Интернета

11 Мы создаем сокет UDP, который будет использоваться с функциями ioctl. Может применяться как сокет TCP, так и сокет UDP [128, с. 163].

Выполнение вызова SIOCGIFCONF в цикле

12-28 Фундаментальной проблемой, связанной с вызовом SIOCGIFCONF, является то, что некоторые реализации не возвращают ошибку, если буфер слишком мал для хранения полученного результата [128, с. 118–119]. В этом случае результат просто обрезается так, чтобы поместиться в буфер, и функция ioctl возвращает нулевое значение, что соответствует успешному выполнению. Это означает, что единственный способ узнать, достаточно ли велик наш буфер, — сделать вызов, сохранить возвращенную длину, снова сделать вызов с большим размером буфера и сравнить полученную длину со значением, сохраненным из предыдущего вызова. Только если эти две длины одинаковы, наш буфер можно считать достаточно большим.

ПРИМЕЧАНИЕ

Беркли-реализации не возвращают ошибку, если буфер слишком мал [128, с. 118-199], и результат просто обрезается так, чтобы поместиться в существующий буфер. Solaris 2.5 возвращает ошибку EINVAL, если возвращаемая длина больше или равна длине буфера. Но мы не можем считать вызов успешным, если возвращаемая длина меньше размера буфера, поскольку Беркли-реализации могут возвращать значение, меньшее размера буфера, если часть структуры в него не помещается.

В некоторых реализациях предоставляется вызов SIOCGIFNUM, который возвращает число интерфейсов. Это позволяет приложению перед выполнением вызова SIOCGIFCONF выделить в памяти место для буфера достаточного размера, но такой подход не является широко распространенным.

Выделение в памяти места под буфер фиксированного размера для результата вызова SIOCGIFCONF стало проблемой с ростом Сети, поскольку большие веб-серверы используют много альтернативных адресов для одного интерфейса. Например, в Solaris 2.5 был предел в 256 альтернативных адресов для интерфейса, но в версии 2.6 этот предел вырос до 8192. Обнаружилось, что на сайтах с большим числом альтернативных адресов перестают работать программы с буферами фиксированного размера для размещения информации об интерфейсе. Хотя Solaris возвращает ошибку, если буфер слишком мал, эти программы размещают в памяти буфер фиксированного размера, запускают функцию ioctl, но затем перестают работать при возвращении ошибки.

12-15 Мы динамически размещаем в памяти буфер начиная с размера, достаточного для 100 структур ifreq. Мы также отслеживаем длину, возвращаемую последним вызовом SIOCGIFCONF в lastlen, и инициализируем ее нулем.

19-20 Если функция ioctl возвращает ошибку EINVAL и функция еще не возвращалась успешно (то есть lastlen все еще равно нулю), значит, мы еще не выделили буфер достаточного размера, поэтому мы продолжаем выполнять цикл.

22-23 Если функция ioctl завершается успешно и возвращаемая длина равна lastlen, значит, длина не изменилась (наш буфер имеет достаточный размер), и мы с помощью функции break выходим из цикла, так как у нас имеется вся информация.

26-27 В каждом проходе цикла мы увеличиваем размер буфера для хранения еще 10 структур ifreq.

Инициализация указателей связного списка

29-31 Поскольку мы будем возвращать указатель на начало связного списка структур ifi_info, мы используем две переменные ifihead и ifipnext для хранения указателей на список по мере его создания.

Следующая часть нашей функции get_ifi_info, содержащая начало основного цикла, показана в листинге 17.5.

Листинг 17.5. Конфигурация интерфейса процесса

//lib/get_ifi_info.c
34 for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
35  ifr = (struct ifreq*)ptr;
36 #ifdef HAVE_SOCKADDR_SA_LEN
37  len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len);
38 #else
39  switch (ifr->ifr_addr.sa_family) {
40 #ifdef IPV6
41  case AF_INET6:
42   len = sizeof(struct sockaddr_in6);
43   break;
44 #endif
45  case AF_INET:
46  default:
47   len = sizeof(struct sockaddr);
48   break;
49  }
50 #endif /* HAVE_SOCKADDR_SA_LEN */
51  ptr += sizeof(ifr->ifr_name) + len; /* для следующей строки */
52 #ifdef HAVE_SOCKADDR_DL_STRUCT
53  /* предполагается, что AF_LINK идет перед AF_INET и AF_INET6 */
54  if (ifr->ifr_addr.sa_family == AF_LINK) {
55   struct sockaddr_dl *sdl = (struct sockaddr_dl*)&ifr->ifr_addr;
56   sdlname = ifr->ifr_name;
57   idx = sdl->sdl_index;
58   haddr = sdl->sdl_data + sdl->sdl_nlen;
59   hlen = sdl->sdl_alen;
60  }
61 #endif
62  if (ifr->ifr_addr.sa_family != family)
63   continue; /* игнорируется, если семейство адреса не то */
64  myflags = 0;
65  if ((cptr = strchr(ifr->ifr_name, ':')) != NULL)
66   *cptr = 0; /* замена двоеточия нулем */
67  if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
68   if (doaliases == 0)
69    continue; /* этот интерфейс уже обработан */
70   myflags = IFI_ALIAS;
71  }
72  memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
73  ifrcopy = *ifr;
74  Ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
75  flags = ifrcopy.ifr_flags;
76  if ((flags & IFF_UP) == 0)
77   continue; /* игнорируется, если интерфейс не используется */

Переход к следующей структуре адреса сокета

35-51 При последовательном просмотре всех структур ifreq ifr указывает на текущую структуру, а мы увеличиваем ptr на единицу, чтобы он указывал на следующую. Необходимо предусмотреть особенность более новых систем, предоставляющих поле длины для структур адреса сокета, и вместе с тем учесть, что более старые системы этого поля не предоставляют. Хотя в листинге 17.1 структура адреса сокета, содержащаяся в структуре ifreq, объявляется как общая структура адреса сокета, в новых системах она может относиться к произвольному типу. Действительно, в 4.4BSD структура адреса сокета канального уровня также возвращается для каждого интерфейса [128, с. 118]. Следовательно, если поддерживается элемент длины, то мы должны использовать его значение для переустановки нашего указателя на следующую структуру адреса сокета. В противном случае мы определяем длину, исходя из семейства адресов, используя размер общей структуры адреса сокета (16 байт) в качестве значения по умолчанию.

ПРИМЕЧАНИЕ

В системах, поддерживающих IPv6, не оговаривается, возвращается ли адрес IPv6 вызовом SIOCGIFCONF. Для более новых систем мы вводим оператор case, в котором предусмотрена возможность возвращения адресов IPv6. Проблема состоит в том, что объединение в структуре ifreq определяет возвращаемые адреса как общие 16-байтовые структуры sockaddr, подходящие для 16-байтовых структур sockaddr_in IPv4, но для 24-байтовых структур sockaddr_in6 IPv6 они слишком малы. В случае возвращения адресов IPv6 возможно некорректное поведение существующего кода, созданного в предположении, что в каждой структуре ifreq содержится структура sockaddr фиксированного размера. В системах, где структура sockaddr имеет поле sa_len, никаких проблем не возникает, потому что такие системы легко могут указывать размер структур sockaddr.

52-60 Если система возвращает структуры sockaddr семейства AF_LINK в SIOCGIFCONF, мы копируем индекс интерфейса и данные об аппаратном адресе из таких структур.

62-63 Мы игнорируем все адреса из семейств, отличных от указанного вызывающим процессом в аргументе функции get_ini_info.

Обработка альтернативных имен

64-72 Нам нужно обнаружить все альтернативные имена (псевдонимы), которые могут существовать для интерфейса, то есть присвоенные этому интерфейсу дополнительные адреса. Обратите внимание в наших примерах, следующих за листингом 17.3, что в Solaris псевдоним содержит двоеточие, в то время как в 4.4BSD имя интерфейса в псевдониме не изменяется. Чтобы обработать оба случая, мы сохраняем последнее имя интерфейса в lastname и сравниваем его только до двоеточия, если оно присутствует. Если двоеточия нет, мы игнорируем этот интерфейс в том случае, когда имя эквивалентно последнему обработанному интерфейсу.

Получение флагов интерфейса

73-77 Мы выполняем вызов SIOCGIFFLAGS функции ioctl (см. раздел 16.5), чтобы получить флаги интерфейса. Третий аргумент функции ioctl — это указатель на структуру ifreq, содержащую имя интерфейса, для которого мы хотим получить флаги. Мы создаем копию структуры ifreq, перед тем как запустить функцию ioctl, поскольку в противном случае этот вызов перезаписал бы IP-адрес интерфейса, потому что оба они являются элементами одного и того же объединения из листинга 17.1. Если интерфейс не активен, мы игнорируем его.

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

Листинг 17.6. Получение и возвращение адресов интерфейса

//ioctl/get_ifi_infо.c
78   ifi = Calloc(1, sizeof(struct ifi_info));
79   *ifipnext = ifi; /* prev указывает на новую структуру */
80   ifipnext = &ifi->ifi_next; /* сюда указывает указатель на
                                   следующую структуру */
81   ifi->ifi_flags = flags; /* значения IFF_xxx */
82   ifi->ifi_myflags = myflags; /* значения IFI_xxx */
83 #if defined(SIOCGIFMTU) && defined(HAVE_STRUCT_IFREQ_IFR_MTU)
84   Ioctl(sockfd, SIOCGIFMTU, &ifrcopy);
85   ifi->ifi_mtu = ifrcopy.ifr_mtu;
86 #else
87   ifi->ifi_mtu = 0;
88 #endif
89   memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
90   ifi->ifi_name[IFI_NAME-1] = '';
91   /* если sockaddr_dl относится к другому интерфейсу, он игнорируется */
92   if (sdlname == NULL || strcmp(sdlname, ifr->ifr_name) != 0)
93    idx = hlen = 0;
94   ifi->ifi_index = idx;
95   ifi->ifi_hlen = hlen;
96   if (ifi->ifi_hlen > IFI_HADDR)
97    ifi->ifi_hlen = IFI_HADDR;
98   if (hlen)
99    memcpy(ifi->ifi_haddr, haddr, ifi->ifi_hlen);

Выделение памяти и инициализация структуры ifi_info

78-99 На этом этапе мы знаем, что возвратим данный интерфейс вызывающему процессу. Мы выделяем память для нашей структуры ifi_info и добавляем ее в конец связного списка, который мы создаем. Мы копируем флаги и имя интерфейса в эту структуру. Далее мы проверяем, заканчивается ли имя интерфейса нулем, и поскольку функция callос инициализирует выделенную в памяти область нулями, мы знаем, что ifi_hlen инициализируется нулем, a ifi_next — пустым указателем.

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

Листинг 17.7. Получение и возврат адреса интерфейса

100   switch (ifr->ifr_addr.sa_family) {
101   case AF_INET:
102    sinptr = (struct sockaddr_in*)&ifr->ifr_addr;
103    ifi->ifi_addr = Calloc(1, sizeof(struct sockaddr_in));
104    memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
105 #ifdef SIOCGIFBRDADDR
106    if (flags & IFF_BROADCAST) {
107     Ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
108     sinptr = (struct sockaddr_in*) &ifrcopy.ifr_broadaddr;
109     ifi->ifi_brdaddr = Calloc(1, sizeof(struct sockaddr_in));
110     memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
111    }
112 #endif
113 #ifdef SIOCGIFDSTADDR
114    if (flags & IFF_POINTOPOINT) {
115     Ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
116     sinptr = (struct sockaddr_in*) &ifrcopy.ifr_dstaddr;
117     ifi->ifi_dstaddr = Calloc(1, sizeof(struct sockaddr_in));
118     memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
119    }
120 #endif
121    break;
122   case AF_INET6:
123    sin6ptr = (struct sockaddr_in6*)&ifr->ifr_addr;
124    ifi->ifi_addr = Calloc(1, sizeof(struct sockaddr_in6));
125    memcpy(ifi->ifi_addr, sin6ptr, sizeof(struct sockaddr_in6));
126 #ifdef SIOCGIFDSTADDR
127    if (flags & IFF_POINTOPOINT) {
128     Ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
129     sin6ptr = (struct sockaddr_in6*)&ifrcopy.ifr_dstaddf;
130     ifi->ifi_dstaddr = Calloc(1, sizeof(struct sockaddr_in6));
131     memcpy(ifi->ifi_dstaddr, sin6ptr,
132     sizeof(struct sockaddr_in6));
133    }
134 #endif
135    break;
136   default:
137    break;
138   }
139  }
140  free(buf);
141  return(ifihead); /* указатель на первую структуру в связной списке */
142 }
102-104
 Мы копируем IP-адрес, возвращенный из нашего начального вызова SIOCGIFCONF функции ioctl, в структуру, которую мы создаем.

106-119 Если интерфейс поддерживает широковещательную передачу, мы получаем широковещательный адрес с помощью вызова SIOCGIFBRDADDR функции ioctl. Мы выделяем память для структуры адреса сокета, содержащей этот адрес, и добавляем ее к структуре ifi_info, которую мы создаем. Аналогично, если интерфейс является интерфейсом типа «точка-точка», вызов SIOCGIFBRDADDR возвращает IP-адрес другого конца связи.

123-133 Обработка случая IPv6 — полная аналогия IPv4 за тем исключением, что вызов SIOCGIFBRDADDR не делается, потому что IPv6 не поддерживает широковещательную передачу.

В листинге 17.8 показана функция free_ifi_info, которой передается указатель, возвращенный функцией get_ifi_info. Эта функция освобождает всю динамически выделенную память.

Листинг 17.8. Функция free_ifi_info: освобождение памяти, которая была динамически выделена функцией get_ifi_info

//iосtl/get_ifi_info.c
143 void
144 free_ifi_info(struct ifi_info *ifihead)
145 {
146  struct ifi_info *ifi, *ifinext;
147  for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
148   if (ifi->ifi_addr != NULL)
149    free(ifi->ifi_addr);
150   if (ifi->ifi_brdaddr != NULL)
151    free(ifi->ifi_brdaddr);
152   if (ifi->ifi_dstaddr != NULL)
153    free(ifi->ifi_dstaddr);
154   ifinext = ifi->ifi_next; /* невозможно получить ifi_next
                                   после вызова freed */
155   free(ifi);
156  }
157 }

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


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