Вопрос по c, ip-address, posix, sockets – Как перечислить все IP-адреса, подключенные к машине, в POSIX C?

5

Background:

Я пишу демон, который устанавливает исходящие соединения TCP / IP. Он будет работать на компьютерах с несколькими (не зацикленными) IP-адресами. Я хотел бы, чтобы пользователи могли указать в файле конфигурации демона, какой IP-адрес (а) использовать для исходящих соединений, или* использовать все.

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

Problem:

Как получить список всех IP-адресов, на которых машина может устанавливать исходящие (то есть на любой другой компьютер) соединения? Учитывая список всех IP-адресов, как бы я отфильтровал петлевые адреса?

Я нахожусь в Си, и, если возможно, я хотел бы использовать только POSIX, но демон, вероятно, будет работать только на Linux-блоках, поэтому я принимаю Linux-ориентированный ответ.

Каждый IP-адрес будет доступен только на одном (возможно, виртуальном) сетевом устройстве, и наоборот, поэтому достаточно было бы перечислить сетевые устройства и получить связанные IP-адреса, хотя мне бы это не очень понравилось. (Дополнительные вопросы: возможно ли даже связать несколько IP-адресов с одним устройством? Как «один и тот же IP-адрес на нескольких устройствах? Не важно.)

Insufficient Solutions:

gethostname()/gethostbyname() (as this question). Using that method, I only ever get 127.0.0.1 back (or .1.1 in Debian). I suspect this is because the hostname of the machine is in the hosts file, and that's as far as gethostbyname() checks. (I believe that's why in Debian I always get 127.0.1.1: Debian defaults to adding localhost as 127.0.0.1 and the machine's hostname as 127.0.1.1 to the hosts file, right?) I'd like a solution that ignores hosts and gives me everything actually there. I've had no more luck with getaddrinfo() than gethostname()/gethostbyname(). It seems to be bound by the same problem. I tested this passing the machine's hostname and a NULL service (port) into it; the docs say passing a NULL hostname AND a NULL service is illegal, and this is backed up by testing. Not sure how else to ask it for everything on the machine, but I'm open to suggestions in this vein. EDIT: this answer shows how to get the IP address from a device name, but doesn't show how to enumerate the device names. Any ideas?

ОКОНЧАТЕЛЬНОЕ РЕДАКТИРОВАНИЕ: Я принятответ Кэски чтобы отдать ему должное за указание на то, как это нужно сделать. Я разместил свойсобственный ответ перечисление исходного кода того, как именно это сделать, если кому-то еще это нужно.

я имел в видуman netdevice но потом я понял, что это не POSIX, так что это спорный вопрос. Sean Bright

Ваш Ответ

6   ответов
1

Adding multiple IPs to a device can be done with aliasing. Linux creates devices named like eth0:0 when you do this.

ifconfig eth0:0 10.0.0.1

Having the same IP under multiple devices can be done with channel bonding/link aggregation.

Это невозможно в Linuxifconfig, но Linuxip Команда может сделать это. Например,ip addr add dev lo 127.0.0.2/8 даст второй адресlo, который работает, хотяifconfig не могу справиться с этим.
Слово. Мне было известно о виртуальных устройствах (то, что я всегда слышал от устройств типа eth0: 1) - возможно ли иметь несколько IP-адресов на логическое устройство, например, без разделения устройства на псевдонимы? chazomaticus
Не то, чтобы я мог понять, по крайней мере, на Linux. Однако во FreeBSD суффикс не создается, что приводит к устройствам, которые могут выглядеть следующим образом: bge0: flags = 8843 & lt; UP, BROADCAST, RUNNING, SIMPLEX, MULTICAST & gt; метрика 0 mtu 1500 вариантов = 9b & lt; RXCSUM, TXCSUM, VLAN_MTU, VLAN_HWTAGGING, VLAN_HWCSUM & gt; ether 00: 11: 22: 33: 44: 55 inet 10.0.0.2 netmask 0xffffff00 широковещательная 10.0.0.255 inet 10.0.0.3 netmask 0xffffff00 широковещательная 10.0.0.255 ... Обратите внимание, это все еще называется псевдонимами.
0

How do I get a list of all the IP addresses a machine can make outgoing (i.e. to any other computer) connections on? Given a list of all IP addresses, how would I filter out loopback addresses?

Посмотрите на исходный кодlsof а такжеnetstat, Вы увидите, что это включает в себя обход структур памяти ядра, а не просто выполнение системных вызовов.

0

что используете gethostname () / gethostbyname () правильно? проверять, выписыватьсяВотединственная проблема, которую я вижу при этом, состоит в том, что возможно, что доменное имя имеет несколько сопоставленных IP-адресов. Если это так, то нет никакого способа узнать, какой IP-адрес принадлежит локальной машине.

Да, я уверен ... chazomaticus
6

ы можете попытаться проанализировать вывод «iptables», ноright Ответ для Linux заключается в использовании ioctl.

SIOCGIFCONF  takes  a  struct  ifconf *.  The ifc_buf field points to a
      buffer of length ifc_len bytes, into which the kernel writes a list of
      type struct ifreq [].

Структура ifreq описана в linux / if.h:

struct ifreq 
{
#define IFHWADDRLEN     6
        union
        {
                char    ifrn_name[IFNAMSIZ];            /* if name, e.g. "en0" */
        } ifr_ifrn;

        union {
                struct  sockaddr ifru_addr;
                struct  sockaddr ifru_dstaddr;
                struct  sockaddr ifru_broadaddr;
                struct  sockaddr ifru_netmask;
                struct  sockaddr ifru_hwaddr;
                short   ifru_flags;
                int     ifru_ivalue;
                int     ifru_mtu;
                struct  ifmap ifru_map;
                char    ifru_slave[IFNAMSIZ];   /* Just fits the size */
                char    ifru_newname[IFNAMSIZ];
                void *  ifru_data;
                struct  if_settings ifru_settings;
        } ifr_ifru;
};

Как видите, он содержит информацию об адресе, которую вы хотите.

7

принятый ответ Caskeyради потомков:

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>


static const char * flags(int sd, const char * name)
{
    static char buf[1024];

    static struct ifreq ifreq;
    strcpy(ifreq.ifr_name, name);

    int r = ioctl(sd, SIOCGIFFLAGS, (char *)&ifreq);
    assert(r == 0);

    int l = 0;
#define FLAG(b) if(ifreq.ifr_flags & b) l += snprintf(buf + l, sizeof(buf) - l, #b " ")
    FLAG(IFF_UP);
    FLAG(IFF_BROADCAST);
    FLAG(IFF_DEBUG);
    FLAG(IFF_LOOPBACK);
    FLAG(IFF_POINTOPOINT);
    FLAG(IFF_RUNNING);
    FLAG(IFF_NOARP);
    FLAG(IFF_PROMISC);
    FLAG(IFF_NOTRAILERS);
    FLAG(IFF_ALLMULTI);
    FLAG(IFF_MASTER);
    FLAG(IFF_SLAVE);
    FLAG(IFF_MULTICAST);
    FLAG(IFF_PORTSEL);
    FLAG(IFF_AUTOMEDIA);
    FLAG(IFF_DYNAMIC);
#undef FLAG

    return buf;
}

int main(void)
{
    static struct ifreq ifreqs[32];
    struct ifconf ifconf;
    memset(&ifconf, 0, sizeof(ifconf));
    ifconf.ifc_req = ifreqs;
    ifconf.ifc_len = sizeof(ifreqs);

    int sd = socket(PF_INET, SOCK_STREAM, 0);
    assert(sd >= 0);

    int r = ioctl(sd, SIOCGIFCONF, (char *)&ifconf);
    assert(r == 0);

    for(int i = 0; i < ifconf.ifc_len/sizeof(struct ifreq); ++i)
    {
        printf("%s: %s\n", ifreqs[i].ifr_name, inet_ntoa(((struct sockaddr_in *)&ifreqs[i].ifr_addr)->sin_addr));
        printf(" flags: %s\n", flags(sd, ifreqs[i].ifr_name));
    }

    close(sd);

    return 0;
}

Работает как шарм!

Это неприменимо для вашей конкретной проблемы, но одно замечание, которое может быть полезно для всех, кто просматривает этот код, заключается в том, что SIOCGICONF будет сообщать только о интерфейсах, которые имеют назначенный адрес. Если вы ищете всеinterfaces вместо всехaddressesВы должны (в Linux) разобрать / proc / net / dev.
1

требуемую несколькими способами, включая вызов ioctl () с опцией SIOCGIFCONF и цикл по возвращаемым структурам для получения информации об адресе интерфейса.

Given a list of all IP addresses, how would I filter out loopback addresses?

См. Структуру ifreq в ответе caskey. Вы можете определить петлю (правильно) с помощью:

if (ifru_flags & IFF_LOOPBACK) 

Константы в if.h

Похожие вопросы