2009-07-21 66 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; 
} 

工程就像一個魅力!