当前位置:  开发笔记 > 运维 > 正文

如何在POSIX C中枚举连接到机器的所有IP地址?

如何解决《如何在POSIXC中枚举连接到机器的所有IP地址?》经验,为你挑选了2个好方法。

背景:

我正在编写一个进行TCP/IP连接的守护进程.它将在具有多个(非环回)IP地址的计算机上运行.我希望用户能够在守护程序的配置文件中指定用于传出连接的IP地址,或者*使用all.

地址将用于轮换,每个连接从最近最少使用的IP地址发出.这种行为很重要,因为它*是"all"的替代品,因此在多台机器上运行的守护程序可以指向文件共享上的相同配置文件,并且每个守护程序都使用自己的一组IP地址.

问题:

如何获取机器可以传出(即连接到任何其他计算机)连接的所有IP地址列表?给定所有IP地址的列表,我将如何过滤掉环回地址?

我在C中,如果可能的话我只想使用POSIX,但守护进程可能只能在Linux机器上运行,所以我接受一个以Linux为中心的答案.

每个IP地址都可以在一个(可能是虚拟的)网络设备上使用,反之亦然,因此枚举网络设备和获取相关IP地址的方法也足够了,尽管我对此并不满意.(附带问题:甚至可以将多个IP地址与单个设备关联起来吗?多个设备下的相同IP怎么样?不重要.)

解决方案不足:

gethostname()/ gethostbyname()(作为这个问题).使用该方法,我只返回127.0.0.1(或Debian中的.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地址,但不显示如何枚举设备名称.有任何想法吗?

最后的编辑:我已经接受了卡斯基的答案给了他一个功劳,因为我指的是如何做到这一点的方向.我已经发布了自己的答案,列出了如果有其他人需要它的确切方法的源代码.



1> chazomaticus..:

这是我使用caskey接受的答案的概念代码证明,为了后人的缘故:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


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

奇迹般有效!



2> caskey..:

这只能以依赖于操作系统的方式完成.您可以尝试解析'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 [].

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

如您所见,它包含您想要的地址信息.

推荐阅读
牛尾巴2010
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有