UNIX: разработка сетевых приложений - Уильям Стивенс
Шрифт:
Интервал:
Закладка:
Функция if_nameindex возвращает указатель на массив структур if_nameindex:
struct if_nameindex {
unsigned int if_index; /* 1, 2. ... */
char *if_name; /* имя, завершаемое нулем: "le0", ... */
};
Последняя запись в этом массиве содержит структуру с нулевым индексом if_index и с пустым указателем ifname. Память для этого массива, а также для имен, на которые указывают элементы массива, выделяется динамически и освобождается при вызове функции if_freenameindex.
Теперь мы представим реализацию этих четырех функций с использованием маршрутизирующих сокетов.
Функция if_nametoindex
В листинге 18.11 показана функция if_nametoindex.
Листинг 18.11. Возвращение индекса интерфейса по его имени
//libroute/if_nametoindex.c
1 #include "unpifi.h"
2 #include "unproute.h"
3 unsigned int
4 if_nametoindex(const char *name)
5 {
6 unsigned int idx, namelen;
7 char *buf, *next, *lim;
8 size_t len;
9 struct if_msghdr *ifm;
10 struct sockadd *sa, *rti_info[RTAX_MAX];
11 struct sockaddr_dl *sdl;
12 if ((buf = net_rt_iflist(0, 0, &len)) == NULL)
13 return(0);
14 namelen = strlen(name);
15 lim = buf + len;
16 for (next = buf; next < lim; next += ifm->ifm_msglen) {
17 ifm = (struct if_msghdr*)next;
18 if (ifm->ifm_type == RTM_IFINFO) {
19 sa = (struct sockaddr*)(ifm + 1);
20 get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
21 if ((sa = rti_infо[RTAX_IFP]) != NULL) {
22 if (sa->sa_family == AF_LINK) {
23 sdl = (struct sockaddr_dl*)sa;
24 if (sdl->sdl_nlen == namelen
25 && strncmp(&sdl->sdl_data[0], name,
26 sdl->sdl_nlen) == 0) {
27 idx = sdl->sdl_index; /* сохранение перед
вызовом free */
28 free(buf);
29 return(idx);
30 }
31 }
32 }
33 }
34 }
35 free(buf);
36 return(0); /* индекс для имени не найден */
37 }
Получение списка интерфейсов12-13 Наша функция net_rt_iflist возвращает список интерфейсов.
Обработка только сообщений RTM_IFINFO17-30 Мы обрабатываем сообщения в буфере (см. рис. 18.4) в поисках сообщений типа RTM_IFINFO. Найдя такое сообщение, мы вызываем нашу функцию get_rtaddrs, чтобы установить указатели на структуры адреса сокета, а если присутствует структура имени интерфейса (элемент RTAX_IFP массива rti_info), то имя интерфейса сравнивается с аргументом.
Функция if_indextoname
Следующая функция, if_indextoname, показана в листинге 18.12.
Листинг 18.12. Возвращение имени интерфейса по его индексу
libroute/if_indextoname.c
1 #include "unpifi.h"
2 #include "unproute.h"
3 char*
4 if_indextoname(unsigned int index, char *name)
5 {
6 char *buf, *next, *lim;
7 size_t len;
8 struct if_msghdr *ifm;
9 struct sockaddr *sa, *rti_info[RTAX_MAX];
10 struct sockaddr_dl *sdl;
11 if ((buf = net_rt_iflist(0, index, &len)) == NULL)
12 return (NULL);
13 lim = buf + len;
14 for (next = buf; next < lim; next += ifm->ifm_msglen) {
15 ifm = (struct if_msghdr*)next;
16 if (ifm->ifm_type == RTM_IFINFO) {
17 sa = (struct sockaddr*)(ifm + 1);
18 get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
19 if ((sa = rti_info[RTAX_IFP]) != NULL) {
20 if (sa->sa_family == AF_LINK) {
21 sdl = (struct sockaddr_dl*)sa;
22 if (sdl->sdl_index == index) {
23 int slen = min(IFNAMSIZ - 1, sdl->sdl_nlen);
24 strncpy(name, sdl->sdl_data, slen);
25 name[slen] = 0; /* завершающий нуль */
26 free(buf);
27 return (name);
28 }
29 }
30 }
31 }
32 }
33 free(buf);
34 return (NULL); /* нет соответствия индексу */
35 }
Эта функция практически идентична предыдущей, но вместо поиска имени интерфейса мы сравниваем индекс интерфейса с аргументом вызывающего процесса. Кроме того, второй аргумент нашей функции net_rt_iflist — это заданный индекс, поэтому результат должен содержать информацию только для определенного интерфейса. Когда обнаруживается совпадение, возвращается имя интерфейса, к которому добавляется завершающий нуль.
Функция if_nameindex
Следующая функция, if_nameindex, возвращает массив структур if_nameindex, содержащих все имена интерфейсов и индексы. Она показана в листинге 18.13.
Листинг 18.13. Возвращение всех имен и индексов интерфейсов
//libroute/if_nameindex.c
1 #include "unpifi.h"
2 #include "unproute.h"
3 struct if_nameindex*
4 if_nameindex(void)
5 {
6 char *buf, *next, *lim;
7 size_t len;
8 struct if_msghdr *ifm;
9 struct sockaddr *sa, *rti_info[RTAX_MAX];
10 struct sockaddr_dl *sdl;
11 struct if_nameindex *result, *ifptr;
12 char *namptr;
13 if ((buf = net_it_iflist(0, 0, &len)) == NULL)
14 return (NULL);
15 if ((result = malloc(len)) == NULL) /* завышенная оценка */
16 return (NULL);
17 ifptr = result;
18 namptr = (char*)result + len; /* имена начинаются с конца буфера */
19 lim = buf + len;
20 for (next = buf; next < lim; next += ifm->ifm_msglen) {
21 ifm = (struct if_msghdr*)next;
22 if (ifm->ifm_type == RTM_IFINFO) {
23 sa = (struct sockaddr*)(ifm + 1);
24 get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
25 if ((sa = rti_infо[RTAX_IFP]) != NULL) {
26 if (sa->sa_family == AF_LINK) {
27 sdl = (struct sockaddr_in*)sa;
28 namptr -= sdl->sdl_nlen + 1;
29 strncpy(namptr, &sdl->sdl_data[0], sdl->sdl_nlen);
30 namptr[sdl->sdl_nlen] = 0; /* завершающий нуль */
31 ifptr->if_name = namptr;
32 ifptr->if_index = sdl->sdl_index;
33 ifptr++;
34 }
35 }
36 }
37 }
38 ifptr->if_name = NULL; /* отмечаем конец массива структур */
39 ifptr->if_index = 0;
40 free(buf);
41 return (result); /* вызывающий процесс должен освободить память
с помощью free(), когда все сделано */
43 }
Получение списка интерфейсов, выделение места для результата13-18 Мы вызываем нашу функцию net_rt_iflist для возвращения списка интерфейсов. Мы также используем возвращаемый размер в качестве размера буфера, который мы размещаем в памяти для записи массива возвращаемых структур if_nameindex. Оценка необходимого размера буфера несколько завышена, но это проще, чем проходить список интерфейсов дважды: один раз для подсчета числа интерфейсов и общего размера имен, а второй — для записи этой информации. Мы создаем массив if_nameindex в начале этого буфера и записываем имена интерфейсов, начиная с конца буфера.
Обработка только сообщений RTM_IFINFO22-36 Мы обрабатываем все сообщения, ища сообщения RTM_IFINFO и следующие за ними структуры адреса сокета. Имя и индекс интерфейса записываются в создаваемый нами массив.
Завершение массива38-39 Последняя запись в массиве имеет пустой указатель if_name и нулевой индекс.
Функция if_freenameindex
Последняя функция, показанная в листинге 18.13, освобождает память, которая была выделена для массива структур if_nameindex и хранящихся в нем имен.
Листинг 18.14. Освобождение памяти, выделенной функцией if_nameindex
43 void
44 if_freenameindex(struct if_nameindex *ptr)
45 {
46 free(ptr);
47 }
Эта функция тривиальна, поскольку мы хранили и массив структур, и имена в одном и том же буфере. Если бы мы каждый раз вызывали функцию malloc, то для освобождения памяти нам бы пришлось проходить через весь массив, освобождать память, выделенную для каждого имени, а затем удалять сам массив (используя функцию free).
18.7. Резюме
Последняя из структур адреса сокета, с которой мы встретились в книге, это sockaddr_dl — структура адреса сокета канального уровня, имеющая переменную длину. Ядра Беркли-реализаций связывают их с интерфейсами, возвращая в одной из этих структур индекс интерфейса, его имя и аппаратный адрес.
В маршрутизирующий сокет процессом могут быть записаны 5 типов сообщений, и 12 различных сообщений могут быть асинхронно возвращены ядром через маршрутизирующий сокет. Мы привели пример, когда процесс запрашивает у ядра информацию о записи в таблице маршрутизации и ядро отвечает со всеми подробностями. Ответы ядра могут содержать до восьми структур адреса сокета, поэтому нам приходится анализировать сообщение, чтобы получить все фрагменты информации.