Книга: 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-21ifi_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-47for
, в котором один раз вызывается функция 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) {
Мы копируем IP-адрес, возвращенный из нашего начального вызова
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-104SIOCGIFCONF
функции 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 }
- 15.6. Функция door_info
- 18.5. Функция get_ifi_info (повтор)
- 2.1.3. Функция getopt_long()
- Группировка по встроенным функциям и UDF
- 19.1.1. Функция jQuery()
- Chapter 11. Iptables targets and jumps
- Information request
- Where to get iptables
- User specified chains
- Функция strcmp( )
- ACCEPT target
- CLASSIFY target