如何枚举连接到计算机的所有IP地址,在POSIX C 2(How to enumerate all

2019-06-26 11:07发布

背景:

我正在写一个守护进程,使传出TCP / IP连接。 它将与多个(非环回)的IP地址的机器上运行。 我希望用户能够指定,在守护程序的配置文件,IP地址(ES)来使用这对于传出连接,或*使用所有。

该地址将在一个轮次使用,每个连接从IP地址走出去最近最少使用。 这种行为是很重要的,因为是*是对“所有”等多台机器上运行的守护进程可以指向一个文件共享相同的配置文件的替代者,并让每个使用它自己的一套IP地址。

问题:

如何获得的所有IP地址的机器可拨打列表(即任何其它计算机)上的连接? 鉴于所有IP地址的列表,我怎么会过滤掉回送地址?

我在C,如果可能的话,我想只使用POSIX,但后台程序将在Linux系统可能永远只能跑,所以我会接受一个以Linux为中心的答案。

每个IP地址将可以在只有一个(可能是虚拟的)网络设备,反之亦然,这样的方式来枚举网络设备,并得到相关的IP地址也就够了,虽然我不会真的是会高兴的。 (方的问题:它甚至可以将多个IP地址与单个设备怎么回合在多个设备相同的IP不重要关联?)。

没有足够的解决方案:

  • gethostname() / gethostbyname()作为这个问题 )。 使用这种方法,我只得到127.0.0.1回(或Debian的2.1.1)。 我怀疑这是因为机器的主机名是在hosts文件,那就是尽可能gethostbyname()检查。 (我相信这就是为什么在Debian中我总是得到127.0.1.1:Debian的默认添加localhost作为127.0.0.1和机器的主机127.0.1.1的hosts文件,对不对?)我想忽略的解决方案hosts ,并给出我的一切居然有。
  • 我已经没有更多的运气getaddrinfo()gethostname() / gethostbyname() 这似乎是由同一个问题的约束。 我测试这使机器的主机名和一个NULL服务(端口)进入它; 文档说传递一个NULL主机名和一个NULL的服务是非法的,这是由测试备份。 不知道怎么回事,要问它的机器上的一切,但是我愿意在这方面的建议。
  • 编辑: 这样的回答表明如何从一个设备名称的IP地址,但没有说明如何枚举的设备名称。 有任何想法吗?

最后编辑:我已经接受卡斯基的回答给他的功劳指着我的如何需要做的方向。 我已为我自己的答案上市究竟如何做到这一点的情况下,任何人都需要它的源代码。

Answer 1:

这只能在一个操作系统相关的方式来完成。 你可以尝试解析“iptables的”的输出,但为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;
};

正如你所看到的,它包含了你想要的地址信息。



Answer 2:

这是我使用的概念证明代码卡斯基的接受的答案 ,留给后人的缘故:

#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;
}

奇迹般有效!



Answer 3:

有些答案侧的问题:

  • 添加多个IP地址的设备可以用别名来完成。 当你做这个0:Linux下创建一个名为像eth0的设备。

    ifconfig eth0:0 10.0.0.1

  • 具有下多个设备相同的IP可与信道绑定/链路聚合来完成。



Answer 4:

你可以得到的信息界面所需的几个方面,包括调用的ioctl()与SIOCGIFCONF选项,并通过返回的结构循环,从而获取接口的地址信息。

鉴于所有IP地址的列表,我怎么会过滤掉回送地址?

见卡斯基的答案的ifreq结构。 您可以确定回环(正确)具有:

if (ifru_flags & IFF_LOOPBACK) 

常量是在if.h中



Answer 5:

你确定你正在使用的gethostname()/的gethostbyname()是否正确? 看看这里 ,我这样做看到的唯一问题是,它可能是一个域名具有映射到它的多个IP地址。 如果是这样的话,那么有没有办法知道属于本地计算机的IP地址是什么方式



Answer 6:

如何获得的所有IP地址的机器可拨打列表(即任何其它计算机)上的连接? 鉴于所有IP地址的列表,我怎么会过滤掉回送地址?

看看lsof的netstat的源代码。 你会看到它涉及遍历内核内存结构,不只是让系统调用。



文章来源: How to enumerate all IP addresses attached to a machine, in POSIX C?