У меня есть вопрос об использовании PF_ROUTE в macOS для обнаружения изменений IP-адреса.В принципе, мне кажется, что он не работает для IPv4.Я собрал пример программы, которая просто создает сокет PF_ROUTE и затем распечатывает при получении RTM_NEWADDR, RTM_DELADDR и RTM_IFINFO.
Что я замечаю, так это то, что при использовании одного интерфейса (кабель Wi-Fi или Ethernet) иотключить сетевой адаптер (отключить вайфай или отключить кабель) вообще ничего не получаю.Если я затем заново подключаюсь (включаю Wi-Fi или подключаю кабель), я получаю RTM_NEWADDR, но не RTM_IFINFO.
Если у меня одновременно подключены и Wi-Fi, и кабель, оба отключаются, а затем снова подключаются один из интерфейсов(например, отключить Wi-Fi, а затем повторно включить Wi-Fi) вообще не генерирует никаких событий.
IPv6 , кажется, работает.Если я тестирую IPv6 таким же образом, я получаю RTM_NEWADDR при соединении и RTM_DELADDR при разъединении (адрес - это локальный адрес канала IPv6 - мой DHCP-сервер не обслуживает адреса IPv6).
Несколько другихпримечания: если я пытаюсь сделать if_indextoname (), это не всегда работает.Мне нужно вставить спящий режим, чтобы иметь возможность последовательно возвращать имя (я выбрал 500 миллисекунд, я не тратил время, пытаясь найти другие значения, чтобы посмотреть, будет ли работать меньшее значение).
Кроме того, еслиЯ вызываю getifaddrs () в цикле (с небольшим перерывом между вызовами) после получения события IPv6 RTM_NEWADDR, чтобы попытаться найти отсутствующий адрес IPv4, может потребоваться много времени для его обнаружения в возвращенных данных.Я видел, что это занимает до 8 секунд в моей системе.Обратите внимание, что IP-адрес работает и может использоваться задолго до этого, так как непрерывный пинг на внешний адрес легко подтверждается.
Я тестировал эту программу на MacBook Pro с 10.13, iMac с 10.14 и виртуальной машиной с 10.12- все ведут себя одинаково.
Итак, мой вопрос: это ошибка в ОС, или у меня есть принципиальное недопонимание того, как сокет PF_ROUTE должен работать?
Спасибо, Кевин
#include <SystemConfiguration/SystemConfiguration.h>
#include <net/route.h>
#include <errno.h>
struct cmn_msghdr
{
u_short msglen;
u_char version;
u_char type;
};
int main(int argc, const char * argv[])
{
char buf[1024];
size_t len;
int skt, family = AF_UNSPEC;
if ( argv[1] && argv[1][0] == '4' )
family = AF_INET;
else if ( argv[1] && argv[1][0] == '6' )
family = AF_INET6;
// Create a PF_ROUTE socket over which we will receive change messages
skt = socket( PF_ROUTE, SOCK_RAW, family );
if ( skt == -1 )
{
printf( "ERR: Failed to create PF_ROUTE socket. error %d\n", errno );
return -1;
}
printf( "Watching for %s address changes. Press Ctrl-C to exit\n",
family == AF_UNSPEC ? "IP" : ( family == AF_INET6 ? "IPv6" : "IPv4" ) );
// Loop forever waiting for messages
for (;;)
{
len = recv( skt, buf, sizeof(buf), 0 );
if ( len < 0 )
{
switch (errno)
{
case EINTR:
case EAGAIN:
printf( "ERR: EINTR or EAGAIN on PF_ROUTE socket\n" );
continue;
default:
printf( "ERR: Failed to receive on PF_ROUTE socket. error %d\n", errno );
continue;
}
}
if ( len < sizeof( cmn_msghdr ) )
{
printf( "ERR: Data received on PF_ROUTE socket too small: %ld bytes\n", len );
continue;
}
struct cmn_msghdr *hdr = (struct cmn_msghdr *)buf;
if ( hdr->version != RTM_VERSION )
{
printf( "ERR: RTM version %d is not supported\n", hdr->version );
continue;
}
switch( hdr->type )
{
case RTM_NEWADDR:
printf( "RTM_NEWADDR\n" );
break;
case RTM_DELADDR:
printf( "RTM_DELADDR\n" );
break;
case RTM_IFINFO:
printf( "RTM_IFINFO\n" );
break;
default:
// Don't care
continue;
}
}
return 0;
}