2009-07-21 31 views
5

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

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

地址将在循环中使用,每个连接将从最近使用的IP地址出去。这种行为很重要,*也是“all”的替代品,所以在多台机器上运行的守护程序可以指向文件共享上的同一个配置文件,并且每个都使用自己的一组IP地址。

问题:

我如何获得的所有IP地址的机器可拨打(即以任何其它计算机)上连接一个列表?给定所有IP地址的列表,我将如何筛选出环回地址?

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

每个IP地址只能在一个(可能是虚拟的)网络设备上使用,反之亦然,因此枚举网络设备并获取关联IP地址的方式也足够了,尽管我不会为此感到高兴。 (方的问题:它甚至可以将多个IP地址与单个设备关联怎么回合在多个设备相同的IP不重要?)

不足解决方案:

  • gethostname()/gethostbyname() (如this question)。使用这种方法,我只能得到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服务是非法的,并且通过测试进行备份。不知道还有什么要问它的机器上的所有内容,但我接受这方面的建议。
  • 编辑:this answer显示如何从设备名称获取IP地址,但不显示如何枚举设备名称。有任何想法吗?

最终编辑:我已经接受caskey's answer给他信贷指导我如何做到这一点。我已经发布了我的own answer,列出了在其他人需要的情况下如何完成它的源代码。

+0

我的意思是`人netdevice`,但后来我意识到它不是POSIX,所以它没有任何意义。 – 2009-07-21 18:46:17

回答

6

这只能以依赖于操作系统的方式完成。你可以尝试解析'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; 
}; 

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

0

你确定你正在使用gethostname()/ gethostbyname()吗?检查出here,我看到这样做的唯一问题是有可能一个域名有多个IP地址映射到它。如果是这种情况,那么有没有办法知道属于本地机器的IP地址是什么

+0

是的,我确定.. – chazomaticus 2009-07-21 18:54:51

0

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

看的lsof的netstat的的源代码。你会发现它涉及遍历内核内存结构,而不仅仅是进行系统调用。

1

一些答案到另一边的问题:

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

    ifconfig eth0:0 10.0.0.1

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

+0

单词。我知道虚拟设备(这是我一直听到的设备,如eth0:1所称的设备) - 是否有可能为每个逻辑设备配备多个IP,如同在不将设备分离为别名的情况下一样? – chazomaticus 2009-07-21 19:02:10

+0

不是我能弄清楚,至少在Linux上。 然而,在FreeBSD上,没有创建后缀,导致设备看上去如下: bge0:flags = 8843 metric 0 mtu 1500 options = 9b 醚00:11:22:33:44:55 INET 10.0.0.2掩码0xffffff00广播10.0.0.255 INET 10.0.0.3掩码0xffffff00广播10.0.0.255 ... 这些仍然称为作为别名,介意你。 – 2009-07-21 19:15:56

1

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

给定一个所有IP地址的列表,我将如何筛选出环回地址?

请在caskey的答案中查看ifreq结构。您可以确定回环(正确)具有:

if (ifru_flags & IFF_LOOPBACK) 

常量在if.h中

7

这是我使用caskey's accepted answer的概念证明代码,为后人的缘故:

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

工程就像一个魅力!