Салимóненко Дмитрий Александрович
ЗАДАНИЕ 2: Получение информации о сетевых интерфейсах LINUX
СОДЕРЖАНИЕ
I. Введение (теория)
↑ К содержанию ↑Информация о сетевых интерфейсах в любой операционной системе (в том числе и в Linux) бывает необходим для того, чтобы, например:
- узнать имя сетевого интерфейса,
- обратиться к сетевому интерфейсу в ситуации, когда неизвестно его имя и/или IP-адрес,
- узнать IP-адрес (публичный или приватный).
Для работы с сетями, в том числе и в ОС Linux, на самом деле, существует множество различных средств, в частности – структур. Это, в первую очередь:
struct sockaddr,
struct sockaddr_in,
struct ifreq,
struct if_nameindex,
struct ifparam,
struct in_addr,
struct if_nameindex
и т.д.
Надо сразу оговориться, что информацию о сетевых интерфейсах мы будем получать при помощи использования интерфейса сокетов (socket).
Примечание. Помимо интерфейса сокетов существует немало и других интерфейсов. Например, транспортный интерфейс TLI, вызов удаленной процедуры RPC и др.
Интерфейс сокетов основан на использовании, как правило, транспортных протоколов ТСР/UDP. Тогда как программа, основанная на TLI, может быть перенесена и на другие виды транспортных протоколов.
Вместе с тем, интерфейс сокетов является на данный момент прочно устоявшейся а, главное, удобной концепцией и технологией.
Интерфейс сокетов впервые появился в BSD Unix. Программный интерфейс сокетов описан в стандарте POSIX.1 и в той или иной мере поддерживается всеми современными операционными системами. В настоящее время – это настолько универсальное и мощное средство, что целесообразно использовать именно его (хотя бы с учетом легкости переносимости программ из одной ОС в другую) в противовес иным, быть может, с какой-то точки зрения более удобным и/или эффективным интерфейсам.
Что такое сокет, как он создается, какие они бывают и т.д. – можно прочитать в наших Методических указаниях по предмету Вычислительные Системы, Сети и Телекоммуникации (ВССТ), а также здесь. На данный момент будем считать, что читатель знаком с сокетами. Если нет, то, пожалуйста, ознакомьтесь - прежде, чем продолжить дальнейшее чтение этого материала.
Для создания программ, считывающих информацию о сетевом интерфейсе, нам понадобится ряд концепций и инструментов.
Примечание. Все три листинга, скорее всего, потребуют запуска в привилегированном режиме, т.е. с правами администратора (root). Например, в Ubuntu это осуществляется следующим образом (пример):
sudo ./a.out
затем указывается пароль.
Поэтому, если нет возможности запускать листинги на Ваших личных ноутбуках и т.д., при работе в компьютерных классах целесообразно работать с ними в виртуальной машине с установленным в ней Linux.
2. Структура ifreq
↑ К содержанию ↑Это – системная структура, содержащая параметры функции ioctl для работы с устройствами (о самой функции – см. ниже):
struct ifreq {
char ifr_name[IFNAMSIZ]; /* имя интерфейса */
union {
struct sockaddr ifr_addr;
struct sockaddr ifr_dstaddr;
struct sockaddr ifr_broadaddr;
struct sockaddr ifr_netmask;
struct sockaddr ifr_hwaddr;
short ifr_flags;
int ifr_ifindex;
int ifr_metric;
int ifr_mtu;
struct ifmap ifr_map;
char ifr_slave[IFNAMSIZ];
char ifr_newname[IFNAMSIZ];
char *ifr_data;
};
;
ifr_addr – IP адрес интерфейса
ifr_dstaddr – адрес сервера (для Point-to-Point соединения)
ifr_broadaddr – широковещательный адрес интерфейса
ifr_netmask – маска подсети
ifr_hwaddr – mac адрес
ifr_ifindex – индекс интерфейса (внутри ядра сетевые интерфейсы имеют уникальные индексы, для упращения работы сетевой подсистемы)
ifr_flags – различные флаги (интерфейс поднят или опущен, интерфейс активен или неактивен и др.)
ifr_metric – метрика интерфейса
ifr_mtu – mtu интерфейса
ifr_map – структура, содержащая в себе техническую информацию (номер прерывания, память устройства и т.д.)
ifr_slave – подчиненное устройство
ifr_newname – новое имя интерфейса (для переименования)
Примечание. Перед любым использованием структуры следует ее обязательно обнулять с помощью функции memset, а затем задавать имя интересуемого интерфейса ifr_name.
Обычно, программист указывает имя нужного устройства в ifr_name. Все остальные поля структуры могут занимать единое пространство в памяти.
О параметре IFNAMSIZ можно прочитать в здесь.
3. Функция ioctl
<sys/ioctl.h>
nt ioctl(int d, int request, …);
d – это открытый файловый дескриптор устройства (например, дескриптор сокета).
request – это тип запроса, для различных устройств запросы различные.
Пример применения функции:
ioctl(fd, SIOCGIFADDR, &ifr)
fd – дескриптор сокета,
SIOCGIFADDR – тип запроса «Назначение адреса устройства» (см. ниже),
&ifr – ссылка на структуру struct ifreq ifr; .
Это – функция для работы с устройствами (низкоуровневая). Точнее, конечно, для работы – с драйверами устройств. Вызов ioctl считается привилегированным, то для его использования необходимо иметь эффективный идентификатор пользователя 0 или мандат CAP_NET_ADMIN. В противном случае будет возвращено значение EPERM.
SIOCGIFNAME
Возвращает в ifr_name имя интерфейса для заданного индекса ifr_ifindex. Это единственный вызов ioctl, возвращающий результат в ifr_name.
SIOCGIFINDEX
Возвращает индекс интерфейса в ifr_ifindex.
SIOCGIFFLAGS, SIOCSIFFLAGS
Считывает или устанавливает слово флагов устройства. В ifr_flags содержится битовая маска из следующих значений:
Флаги устройства | Значения |
IFF_UP | Интерфейс активен. |
IFF_BROADCAST | Установлен правильный широковещательный адрес. |
IFF_DEBUG | Флаг внутренней отладки. |
IFF_LOOPBACK | Интерфейс является устройством обратной петли. |
IFF_POINTOPOINT | Интерфейс является соединением точка-точка. |
IFF_RUNNING | Ресурсы выделены. |
IFF_NOARP | Нет протокола arp, адрес назначения 2-ого уровня (L2) не установлен. |
IFF_PROMISC | Интерфейс в режиме захвата (promiscuous). |
IFF_NOTRAILERS | Избегать использования концевиков (trailers). |
IFF_ALLMULTI | Принимать все многоадресные пакеты. |
IFF_MASTER | Мастер в связке балансирования нагрузки. |
IFF_SLAVE | Подчинённый в связке балансирования нагрузки. |
IFF_MULTICAST | Поддержка многоадресной передачи. |
IFF_PORTSEL | Может выбирать тип среды с помощью ifmap. |
IFF_AUTOMEDIA | Запущен автоматический выбор среды. |
IFF_DYNAMIC | Адреса теряются, если интерфейс становится неактивным. |
IFF_LOWER_UP | Драйвер L1 указывает на включение интерфейса (начиная с Linux 2.6.17) |
IFF_DORMANT | Драйвер L1 указывает на неактивность интерфейса (начиная с Linux 2.6.17) |
IFF_ECHO | Посылать пакеты echo (начиная с Linux 2.6.25) |
Установка флага в слове флагов является привилегированной операцией, но считывать его может любой процесс.
SIOCGIFPFLAGS, SIOCSIFPFLAGS
Считает или устанавливает (индивидуальные) флаги устройства. Значение ifr_flags представляет собой битовую маску следующих значений:
Индивидуальные флаги | Значения |
IFF_802_1Q_VLAN | Интерфейс является устройством 802.1Q VLAN. |
IFF_EBRIDGE | Интерфейс является устройством моста Ethernet. |
IFF_SLAVE_INACTIVE | Интерфейс является неактивным подчинённым в связке балансирования нагрузки. |
IFF_MASTER_8023AD | Интерфейс является мастером в связке балансирования нагрузки 802.3ad. |
IFF_MASTER_ALB | Интерфейс является мастером связки балансирования нагрузки в режиме balanced-alb. |
IFF_BONDING | Интерфейс является мастером или подчинённым в связке балансирования нагрузки. |
IFF_SLAVE_NEEDARP | Интерфейсу требуется ARP для проверки. |
IFF_ISATAP | Интерфейс является интерфейсом RFC4214 ISATAP. |
Установка расширенных (индивидуальных) флагов интерфейса является привилегированной операцией.
SIOCGIFADDR, SIOCSIFADDR
Назначает или получает адрес устройства с помощью ifr_addr. Назначение адреса интерфейса является привилегированной операцией. Для совместимости, принимаются или возвращаются только адреса семейства AF_INET.
SIOCGIFDSTADDR, SIOCSIFDSTADDR
Назначает или получает адрес назначения устройства точка-точка с помощью ifr_dstaddr. Для совместимости, принимаются или возвращаются только адреса семейства AF_INET. Присвоение адреса назначения является привилегированной операцией.
SIOCGIFBRDADDR, SIOCSIFBRDADDR
Назначает или получает широковещательный адрес устройства с помощью ifr_brdaddr. Для совместимости, принимаются или возвращаются только адреса семейства AF_INET. Присвоение широковещательного адреса является привилегированной операцией.
SIOCGIFNETMASK, SIOCSIFNETMASK
Назначает или получает маску сети устройства с помощью ifr_netmask. Для совместимости, принимаются или возвращаются только адреса семейства AF_INET. Назначение маски сети адреса является привилегированной операцией.
SIOCGIFMETRIC, SIOCSIFMETRIC
Считывает или устанавливает метрику устройства с помощью ifr_metric. В данный момент возможность не реализована; при считывании ifr_metric присваивается значение, равное нулю, а при попытке установки возвращается значение EOPNOTSUPP.
SIOCGIFMTU, SIOCSIFMTU
Считывает или устанавливает MTU (Maximum Transfer Unit — максимальную порцию данных) с помощью ifr_mtu. Установка MTU является привилегированной операцией. Установка слишком маленьких значений может привести к авариям в ядре.
SIOCGIFHWADDR, SIOCSIFHWADDR
Считывает или устанавливает аппаратный адрес устройства с помощью ifr_hwaddr. Аппаратный адрес задаётся в структуре sockaddr. В sa_family содержится тип устройства ARPHRD_*, в sa_data содержится аппаратный адрес L2, начиная с байта 0. Установка аппаратного адреса является привилегированной операцией.
SIOCSIFHWBROADCAST
Устанавливает аппаратный широковещательный адрес устройства с помощью ifr_hwaddr. Это привилегированная операция.
SIOCGIFMAP, SIOCSIFMAP
Считывает или устанавливает аппаратные параметры интерфейса с помощью ifr_map. Установка параметров является привилегированной операцией.
struct ifmap {
unsigned long mem_start;
unsigned long mem_end;
unsigned short base_addr;
unsigned char irq;
unsigned char dma;
unsigned char port;
};
Назначение структуры ifmap зависит от драйвера устройства и архитектуры системы.
SIOCADDMULTI, SIOCDELMULTI
Добавляет или удаляет адрес из фильтров многоадресной передачи уровня связи устройства с помощью ifr_hwaddr. Это привилегированная операция. Альтернативный способ приведен в packet(7).
SIOCGIFTXQLEN, SIOCSIFTXQLEN
Считывает или устанавливает размер очереди передачи устройства с помощью ifr_qlen. Установка размера очереди передачи является привилегированной операцией.
SIOCSIFNAME
Изменяет имя интерфейса, указанное в ifr_name, на ifr_newname. Это привилегированная операция. Она разрешена только тогда, когда интерфейс не активен.
SIOCGIFCONF
Возвращает список адресов (транспортного уровня) интерфейсов. В настоящее время, сюда входят только адреса семейства AF_INET (IPv4), для совместимости. В отличии от других, данный ioctl передаёт структуру ifconf:
struct ifconf {
int ifc_len; /* размер буфера */
union {
char *ifc_buf; /* адрес буфера */
struct ifreq *ifc_req; /* массив структур */
};
};
Если ifc_req равно NULL, то SIOCGIFCONF возвращает необходимый размер буфера в байтах для приёма всех доступных адресов в ifc_len. В противном случае ifc_req содержит указатель на массив структур ifreq, который будет заполнен адресами всех активных интерфейсов L3. В ifc_len содержится размер массива в байтах. Внутри каждой структуры ifreq в ifr_name будет записано имя интерфейса, а в ifr_addr — адрес. Реальное количество переданных байт возвращается в ifc_len.
Если размера, указанного в ifc_len, недостаточно для сохранения всех адресов, то ядро не запишет не поместившиеся и сообщит об успешном выполнении. Не существует надёжного способа обнаружения возникновения такой ситуации. Поэтому рекомендуется или сначала определить необходимый размер буфера вызовом SIOCGIFCONF с значением ifc_req равным NULL, или повторить вызов с большим буфером и проверить не отличается ли ifc_len на менее чем sizeof(struct ifreq) от первого значения.
Если произошла ошибка доступа к структуре ifconf или ifreq, то возвращается EFAULT.
Большинство протоколов поддерживают свои собственные вызовы ioctl для настройки предназначенных только для протокола параметров интерфейса. Подробности приведены в справочных страницах протоколов. Настройка адресов IP описывается в ip(7).
В дополнение ко всему, некоторые устройства поддерживают индивидуальные (private) вызовы ioctl. Здесь они не описаны.
4. Функция inet_ntoa
↑ К содержанию ↑#include <arpa/inet.h>
char *inet_ntoa(struct in_addr in);
Функция преобразует 32-разрядный двоичный адрес IPv4, хранящийся в сетевом порядке байтов в struct in_addr (которая принадлежит struct sockaddr_in) в десятичную запись с точками (например, в запись вида "192.168.5.10").
Примечание. Вообще, эта функция немного устарела. Вместо нее можно применять более новую функциу inet_ntop, которая способна работать не только с адресами IPv4 (как inet_ntoa), и с адресами IPv6. Для этого потребуется заменить вызовы вида
ptr = inet_ntoa(foo.sin_addr);
на
char str[INET_ADDRSTRLEN];
ptr = inet_ntop(AF_INET, &foo.sin_addr, str, sizeof(str));
5. Функция if_nameindex
↑ К содержанию ↑#include <net/if.h>
struct if_nameindex *if_nameindex(void);
void if_freenameindex(struct if_nameindex *ptr);
Функция if_nameindex() возвращает массив структур if_nameindex, каждая из которых содержит информацию об одном из сетевых интерфейсов системы. Структура if_nameindex содержит по крайней мере следующие данные:
unsigned int if_index; /* индекс интерфейса (1, 2, …) */
char *if_name; /* имя с Null в конце («eth0», и т. д.) */
В поле if_index содержится индекс интерфейса. Значение поля ifa_name указывает на имя интерфейса (завершается null). Конец массива определяется по записи if_index с нулевым значением и по записи ifa_name со значением NULL.
Память под структуру данных, возвращаемая if_nameindex(), выделяется динамически и должна освобождаться с помощью if_freenameindex(), когда больше не нужна.
6. Интерфейсы
↑ К содержанию ↑У Вас на компьютере могут быть, вообще говоря, интерфейсы с разными именами. Например, локальный интерфейс (которому соответствует IP-адрес 127.0.0.1) имеет имя lo.
Локальные (приватные) интерфейсы имеют, по умолчанию, имена eth0, eth1 и т.д. Например, интерфейс с именем eth0 может иметь IP_адрес 192.168.145.130.
Однако, имя интерфейса может быть изменено как программным путем, так и через команды консоли (bash). Кроме того, это возможно путем правки конфигурационных файлов.
В данном задании Вы научитесь считывать основные характеристики интерфейсов (IP-адрес и имя).
7. II. Практика
↑ К содержанию ↑Ниже рассмотрим несколько программных листингов, выполненных на языке С (под Linux), которые позволяют узнать такие характеристики интерфейсов, как имя и IP-адрес.
Примечание. Скорее всего, для запуска программ потребуются права администратора (root).
8. Листинг 1
↑ К содержанию ↑/* ip1.c */
#include <sys/ioctl.h>
#include <net/if.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
struct in_addr addr;
int get_addr(const char *intf, struct in_addr *addr)
{
int fd;
if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -1;
struct ifreq ifr;
strncpy(ifr.ifr_name, intf, IFNAMSIZ);
if(ioctl(fd, SIOCGIFADDR, &ifr) < 0)
{
perror("ioctl SIOCGIFADDR");
return -1;
}
*addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
return 0;
}
int main()
{
if (get_addr("eth0", &addr) < 0)
{
perror("get_addr");
return -1;
}
printf("Приватный IP-адрес %s\n", inet_ntoa(addr));
return 0;
}
Результатом должно быть что-то вроде:
Приватный IP-адрес 192.168.145.130
Как видим, листинг использует три системные структуры:
in_addr,
ifreq,
sockaddr_in.
Пользовательская функция get_addr обращается к сетевому интерфейсу с конкретным именем "eth0" и также ссылается на структуру addr (которая является наследницей структуры in_addr).
Пользовательская функция создает дэйтаграмный сокет и затем записывает в поле ifr_name структуры ifr заданное в программе имя сетевого интерфейса (т.е. eth0).
Затем, путем вызова функции ioctl, осуществляется обращение к сетевому интерфейсу, который создан сокетом, для целей получения IP-адреса этого интерфейса. Так как информация об этом содержится в структуре ifreq, вызов функции ioctl содержит ссылку на потомка этой структуры, т.е. на ifr. После чего из структуры ifreq в структуру ifr заносится информация об IP-адресе.
Затем происходит преобразование из массива байт в структуру sockaddr_in и считывание IP-адреса из нее путем обращения к полю sin_addr. Так как структура addr является глобальной для данного листинга, то из функции main можно получить к ней доступ.
Функция inet_ntoa применяется для преобразования сетевого порядка байт в обычный IP-адрес вида ХХХ.ХХХ.ХХХ.ХХХ (Network TO Ascii). Отметим, что аргумент функции inet_ntoa должен иметь тип sockaddr_in (или in_addr),
иначе она не выдаст результат вообще (программа завершится) или результат будет некорректным – «странным». Если это не так, то следует привести аргумент к этому типу, для чего перед ним следует указать
*(struct in_addr*)
Кстати, очистка полей структуры-потомка перед их заполнением – в этом листинге не используется. Что – несколько некорректно. Неплохо бы исправить этот недостаток.
9. Листинг 2
↑ К содержанию ↑/* ip2.c */
// Этот листинг выдает локальный (127.0.0.1) и локальный-приватный (например, 192.168.ххх.ххх) IP-адреса компьютера, на котором он будет запущен.
#include <net/if.h>
#include <stdio.h>
#include <string.h>
#include <linux/socket.h>
//#include <linux/ioctl.h>
#include <linux/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <inttypes.h>
#include <netdb.h>
struct ifparam {
__u32 ip; // IP адрес
__u32 mask; // маска подсети
} ifp;
int getifconf(__u8 *intf, struct ifparam *ifp)
{
int fd;
struct sockaddr_in s;
struct ifreq ifr; // см. <linux/if.h>
memset((void *)&ifr, 0, sizeof(struct ifreq));
if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return (-1);
sprintf(ifr.ifr_name, "%s", intf);
/*
* Определяем IP адрес сетевого интерфейса
*/
if(ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
perror("ioctl SIOCGIFADDR");
return -1;
}
memset((void *)&s, 0, sizeof(struct sockaddr_in));
memcpy((void *)&s, (void *)&ifr.ifr_addr, sizeof(struct sockaddr));
memcpy((void *)&ifp->ip, (void *)&s.sin_addr.s_addr, sizeof(__u32));
return 0;
}
int main()
{
/*
* Получаем параметры сетевого интерфейса eth0
*/
if(getifconf("eth0", &ifp) < 0) {
perror("getifconf");
return -1;
}
// Получаем локальный IP-адрес (127.0.0.1 или loopback)
char *ac = "localhost";
struct hostent *hosten= gethostbyname(ac);
int cnt;
u_long IpAddr;
for( cnt=0 ; hosten->h_addr_list[cnt] != NULL ; cnt++ ) {
int Ip1,Ip2,Ip3,Ip4;
IpAddr=ntohl(*((u_long *)(hosten -> h_addr_list[cnt])));
// 0xff - это в 16-ричном формате число 255
Ip1=(int)( IpAddr>>24 ) & 0xff;
Ip2=(int)( IpAddr>>16 ) & 0xff;
Ip3=(int)( IpAddr>>8 ) & 0xff;
Ip4=(int)( IpAddr&0xff );
printf("Локальный IP-адрес (loopback) \%d = \%d.\%d.\%d.\%d\n",cnt,Ip1,Ip2,Ip3,Ip4);
}
// Получаем приватный (он тоже называется локальным) IP-адрес
int IpAddr_my;
// Исправьте следующую строчку в целях корректности
for( cnt=0 ; hosten->h_addr_list[cnt] != NULL ; cnt++ ) {
int Ip1,Ip2,Ip3,Ip4;
IpAddr_my = ifp.ip;
Ip1=(int)( IpAddr_my>>24 ) & 0xff;
Ip2=(int)( IpAddr_my>>16 ) & 0xff;
Ip3=(int)( IpAddr_my>>8 ) & 0xff;
Ip4=(int)( IpAddr_my & 0xff);
// Почему IP-адрес выводится в обратном порядке, но результат получается правильный?
printf("Приватный IP-адрес \%d = \%d.\%d.\%d.\%d\n",cnt,Ip4,Ip3,Ip2,Ip1);
}
/*
* Отобразим полученные параметры сетевого интерфейса
*/
printf("IpAddr: %lu\n", IpAddr);
printf("IP адрес (__U32): %"PRIi32"\n",ifp.ip);
return 0;
}
Эта программа аналогична предыдущей, но в ней используется еще одна структура:
struct ifparam;
Обратите внимание на функцию sprintf, которая в некотором смысле является аналогом функций memset, strncpy, но, является, вообще говоря, более высокоуровневой.
В функции main обратите внимание на строчку
char *ac = "localhost";
Если в кавычках задать иное доменное имя, например, имя какого-нибудь сайта (скажем, 4846d.ru), то программа выдаст уже не 127.0.0.1, а IP-адрес этого сайта.
Также обратите внимание на конструкции вида
Ip1=(int)( IpAddr>>24 ) & 0xff;
Самостоятельно изучите – КАК это работает. Имейте в виду, что IP-адрес:
- в версии протокола IPv4 IP-адрес имеет длину 4 байта, (т.е. 32 бита), пространство адресов AF_INET)
- в версии протокола IPv6 IP-адрес имеет длину 16 байт (пространство адресов AF_INET6).
Мы с Вами работаем с версией протокола IPv4.
Результат работы листинга 2 будет иметь примерно следующий вид:Локальный IP-адрес 0 = 127.0.0.1
Приватный IP-адрес 0 = 192.168.145.130
IpAddr: 2130706433
IP адрес (__U32): -2104383296
Обратите внимание на последние две странные строчки. Что это за непонятные числа?
10. Листинг 3
↑ К содержанию ↑Этот листинг взят с сайта: programmersclub.ru/Взаимодействие-с-сетевыми-интерфейс/
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <string.h>
#include <net/if.h>
#include <errno.h>
#include <stdio.h>
int main()
{
int sock; // дескриптор сокета
struct sockaddr_in *in_addr; // структура интернет адреса (поля)
struct ifreq ifr; // структура - параметр
struct if_nameindex *ifNameIndex; // структура интерфейсов и их индексов
sock = socket(AF_INET, SOCK_DGRAM, 0); // открываем дескриптор сокета
if (sock < 0) {
printf("Не удалось открыть сокет, ошибка: %s\n", strerror(errno));
return 1;
}
ifNameIndex = if_nameindex();
if (ifNameIndex) { // если удалось получить данные
while (ifNameIndex->if_index) { // пока имеются данные
memset(&ifr, 0, sizeof(ifr)); // очищаем структуру
strncpy(ifr.ifr_name, ifNameIndex->if_name, IFNAMSIZ); // получаем имя следующего интерфейса
// получаем IP адрес с помощью SIOCGIFADDR, одновременно проверяя результат
if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
printf("Не получить IP адрес для %s, ошибка: %s\n", ifr.ifr_name, strerror(errno));
close(sock);
return 1;
}
// преобразовываем из массива байт в структуру sockaddr_in
in_addr = (struct sockaddr_in *) &ifr.ifr_addr;
printf("Интерфейс %s индекс %i IP адрес: %s\n", ifr.ifr_name, ifNameIndex->if_index, inet_ntoa (in_addr-> sin_addr));
++ifNameIndex; // переходим к следующему интерфейсу
}
}
close(sock);
return 0;
}
Этот листинг должен выдавать что-то вроде:
Интерфейс lo индекс 1 IP адрес: 127.0.0.1
Интерфейс eth0 индекс 2 IP адрес: 192.168.145.130
Примечание 1. Этот листинг может показать «интересный» результат в случае, когда не работает сеть по вине провайдера (который не пускает своих абонентов дальше своего IP-адреса). Если в такой ситуации зайти браузером на сайт провайдера, то, естественно, он может быть недоступным (не говоря уже о других сайтах). Однако, в списке ethX вполне может появиться ТРЕТИЙ интерфейс… IP_адрес которого будет являться IP-адресом провайдера… Это может означать, что последний «зачем-то» переключил Вас на локальное сетевое пространство без возможности выхода в сеть интернет, не давая ответа ВЫСОКОуровневым протоколам браузера – типа http, https – потому-то браузер и выдает ошибку при открытии страницы. Поэтому браузером может не открываться ни один сайт. А вот на низком уровне этот интерфейс вполне можно определить.
Понятно, что нередко бывает недоступен и сам провайдер – тогда в момент исчезновения сети дополнительного интерфейса не появится.
Примечание 2. Естественно, всеми данными листингами можно определить только(!) локальные адреса. Публичные IP-адреса возможно определить ТОЛЬКО путем обращения к сети интернет. Ибо назначает адрес не владелец компьютера, а сеть (например, провайдер). Например, в консоли можно набрать команду вида
wget -O - -q icanhazip.com
Если сеть доступна – в консоли появится Ваш (точнее, Вашего сетевого устройства) ПУБЛИЧНЫЙ IP-адрес.
Примечание 3. Нередко (при работе без виртуальной машины, в частности) сеть провайдера может являться локальной для подключенного к ней сетевого устройства (модема, телефона и т.п.).
Итак, вернемся к листингу 3.
Он перебирает ВСЕ сетевые интерфейсы, доступные функции if_nameindex();
А далее – все как обычно (см. Листинги 1 , 2 ).
11. III. Самостоятельная разработка
↑ К содержанию ↑Доработайте листинг 3 так, чтобы он:
- Работал резидентно (т.е. после запуска постоянно находился бы в памяти до тех пор, пока не будет нажат какой-нибудь символ на клавиатуре),
- Отслеживал резидентно (например, с интервалом в 1 секунду) все сетевые интерфейсы. И, если появляется новый интерфейс, или, наоборот, исчезает ранее функционировавший, программа должна выдавать в консоль соответствующее сообщение. Проверить это можно путем подключения / отключения сети интернет на компьютере (или в виртуальной машине, в которой установлен Linux Ubuntu)
После чего в Вашем распоряжении появится простая утилита, написанная Вами самостоятельно, которая сможет контролировать все подключение или отключение любого сетевого интерфейса и выдавать такие характеристики всех подключенных интерфейсов, как имя и IP-адрес.
С уважением, Салимоненко Д.А.