如何知道 C 中接口的 IP 地址?

Posted

技术标签:

【中文标题】如何知道 C 中接口的 IP 地址?【英文标题】:How can I get to know the IP address for interfaces in C? 【发布时间】:2011-05-07 13:36:13 【问题描述】:

假设我正在运行一个名为 IpAddresses.c 的程序。我希望该程序根据每个接口获取此设备具有的所有 IP 地址。就像ifconfig。我该怎么做?

我不太了解ioctl,但我读过它可能对我有帮助。

【问题讨论】:

【参考方案1】:

只需使用getifaddrs()。这是一个例子:

#include <arpa/inet.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <stdio.h>

int main ()

    struct ifaddrs *ifap, *ifa;
    struct sockaddr_in *sa;
    char *addr;

    getifaddrs (&ifap);
    for (ifa = ifap; ifa; ifa = ifa->ifa_next) 
        if (ifa->ifa_addr && ifa->ifa_addr->sa_family==AF_INET) 
            sa = (struct sockaddr_in *) ifa->ifa_addr;
            addr = inet_ntoa(sa->sin_addr);
            printf("Interface: %s\tAddress: %s\n", ifa->ifa_name, addr);
        
    

    freeifaddrs(ifap);
    return 0;

这是我在我的机器上得到的输出:

Interface: lo   Address: 127.0.0.1
Interface: eth0 Address: 69.72.234.7
Interface: eth0:1       Address: 10.207.9.3

【讨论】:

为什么你有 2 个 eth0?顺便说一句,谢谢你的回答 @gvalero87 第一个 eth0 告诉网卡通过 Internet 进行通信。第二个 eth0 通过专用连接(光纤线路)与第三方通信。这是网络管理员放在路由表中的设置。 见下面 Luiz Normando 的回复:你的代码在某些机器上可能会崩溃,因为 ifa->ifa_addr 对于某些接口可能为 NULL。 @Lanzelot 我已经用 Luiz Normando 的补丁更新了我的答案。 我添加了错误检查代码:在stdio.h 行下方添加了一个新标题行“#include &lt;stdlib.h&gt;”,并通过替换此行“getifaddrs (&amp;ifap);”添加了此行“if (getifaddrs(&amp;ifap) == -1) perror("getifaddrs"); exit(1); ” ” 。如果没有先前的(2 行)更改,如果“ifa_addr”有一个 NULL 指针,那么它会创建分段错误。提及的更改受到 Michael-Hampton 代码的启发,这里显示的是:***.com/a/27433320/3553808。此代码在 macOS (10.15.x/Catalina) 上运行良好。【参考方案2】:

这里有一些 Linux 示例代码可能会对您有所帮助。

#include <stdio.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>

#define INT_TO_ADDR(_addr) \
(_addr & 0xFF), \
(_addr >> 8 & 0xFF), \
(_addr >> 16 & 0xFF), \
(_addr >> 24 & 0xFF)

int main()

    struct ifconf ifc;
    struct ifreq ifr[10];
    int sd, ifc_num, addr, bcast, mask, network, i;

    /* Create a socket so we can use ioctl on the file 
     * descriptor to retrieve the interface info. 
     */

    sd = socket(PF_INET, SOCK_DGRAM, 0);
    if (sd > 0)
    
        ifc.ifc_len = sizeof(ifr);
        ifc.ifc_ifcu.ifcu_buf = (caddr_t)ifr;

        if (ioctl(sd, SIOCGIFCONF, &ifc) == 0)
        
            ifc_num = ifc.ifc_len / sizeof(struct ifreq);
            printf("%d interfaces found\n", ifc_num);

            for (i = 0; i < ifc_num; ++i)
            
                if (ifr[i].ifr_addr.sa_family != AF_INET)
                
                    continue;
                

                /* display the interface name */
                printf("%d) interface: %s\n", i+1, ifr[i].ifr_name);

                /* Retrieve the IP address, broadcast address, and subnet mask. */
                if (ioctl(sd, SIOCGIFADDR, &ifr[i]) == 0)
                
                    addr = ((struct sockaddr_in *)(&ifr[i].ifr_addr))->sin_addr.s_addr;
                    printf("%d) address: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(addr));
                
                if (ioctl(sd, SIOCGIFBRDADDR, &ifr[i]) == 0)
                
                    bcast = ((struct sockaddr_in *)(&ifr[i].ifr_broadaddr))->sin_addr.s_addr;
                    printf("%d) broadcast: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(bcast));
                
                if (ioctl(sd, SIOCGIFNETMASK, &ifr[i]) == 0)
                
                    mask = ((struct sockaddr_in *)(&ifr[i].ifr_netmask))->sin_addr.s_addr;
                    printf("%d) netmask: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(mask));
                                

                /* Compute the current network value from the address and netmask. */
                network = addr & mask;
                printf("%d) network: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(network));
                                  
        

        close(sd);
    

    return 0;


【讨论】:

感谢您提供 ioctl 替代方案。但是我永远不知道,什么时候应该使用ioctl,什么时候应该使用getifaddrs 我相信ioctl 可能更便携,即使它不符合任何单一标准。大多数 Unix 和类 Unix 系统都支持它,ioctl 函数调用首次出现在版本 7 AT&T UNIX 中。我相信 getifaddrs 在 BSD 和 Linux 中得到支持,并且首次出现在 glibc 2.3 中。 这也适用于由于某种原因没有ifaddrsandroid,但不适用于OS X。Here's an example两者都适用。 使用标准 htonl()/ntohl() 而不是自定义 INT_TO_ADDR 宏。【参考方案3】:

使用getifaddrs() 的解决方案很棒。我建议只改进一项:

--- chrisaycock
+++ normando
@@ -11,7 +11,7 @@

     getifaddrs (&ifap);
     for (ifa = ifap; ifa; ifa = ifa->ifa_next) 
-        if (ifa->ifa_addr->sa_family==AF_INET) 
+        if (ifa->ifa_addr && ifa->ifa_addr->sa_family==AF_INET) 
             sa = (struct sockaddr_in *) ifa->ifa_addr;
             addr = inet_ntoa(sa->sin_addr);
             printf("Interface: %s\tAddress: %s\n", ifa->ifa_name, addr);

只是因为我自己遇到了分段错误。

【讨论】:

我们还遇到了原始代码的段错误。根据 getifaddrs 的手册页,某些接口的 ifa_addr 成员可能为 NULL...【参考方案4】:

请参阅其他 Stack Overflow 问题,Enumerating each IP address assigned to network interfaces

总而言之,您可以使用:

ioctl(SIOCGIFCONF) -> 传统的ioctl getifaddrs() -> 来自 BSDi,现在也在 Linux 和 BSD 上。 RTNETLINK (Linux)

【讨论】:

【参考方案5】:

你可以试试这样的:

struct ifreq ifr[MAX_INTERFACES];
struct ifconf ifc;
memset(ifr, 0, sizeof(ifr));
ifc.ifc_len = sizeof(ifr);
ifc.ifc_req = ifr;

// Get the list of interfaces
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) 
    fprintf(stderr, "ioctl SIOCGIFCONF failed: %d", errno);


struct ifreq *ifr_iterator = ifc.ireq;
int i = 0;
size_t len;
while (i < ifc.ifc_len) 
   /* DO STUFF */
   // Maybe some more filtering based on SIOCGIFFLAGS 
   // Your code
   // Use ifr_iterator-> ...

   len = IFNAMSIZ + ifr_iterator->ifr_addr.sa_len;
   ifr_iterator = (struct ifreq *)((char *)ifr_iterator + len);
   i += len;

【讨论】:

请注意,此代码在 ifreq 结构的大小可变的 BSD 上会失败。你不能只假设ifc.ifc_len/sizeof(struct ifreq) 会给你接口数。相反,您需要像这样遍历列表中的元素:struct ifreq *ifr_iterator = ifc.ireq; size_t len; while (i &lt; ifc.ifc_len) /* DO STUFF */ len = IFNAMSIZ + ifr_iterator-&gt;ifr_addr.sa_len; ifr_iterator = (struct ifreq *)((char *)ifr_iterator + len); i += len; @Joakim 所以这意味着#define ifc_req ifc_ifcu.ifcu_req /* array of structures ret'd */ 在技术上不是一个数组,因为大小不同?似乎 5.9 增加了更多的东西。另外 ifconfig.c 代码似乎没有 SIOCGIFCONF @GorillaApe 在一个系统上它可能是一个数组,而在另一个系统上则不是。我的观点是有不同的实现。在 Linux 上,这是一个数组,每个项目的大小都是固定的,但在 BSD 上,每个项目的大小可以变化,因此正确的做法是始终使用长度字段。如果你想要可移植的代码。【参考方案6】:

查看(Windows 特定)IP Helper API - 幸运的是,在 Windows 上您不需要 ioctl

【讨论】:

【参考方案7】:

如果您还需要 IP6 地址,您可以像这样扩展接受的答案:

#include <arpa/inet.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <netdb.h>

int main() 
    struct ifaddrs *ifap;
    getifaddrs(&ifap);
    for (struct ifaddrs *ifa = ifap; ifa; ifa = ifa->ifa_next) 
        if (ifa->ifa_addr && (ifa->ifa_addr->sa_family == AF_INET6)) 
            char addr[INET6_ADDRSTRLEN];
            getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), addr, sizeof(addr), NULL, 0, NI_NUMERICHOST);
            printf("Interface: %s\tAddress: %s\n", ifa->ifa_name, addr);
         else if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) 
            struct sockaddr_in *sa = (struct sockaddr_in *)ifa->ifa_addr;
            char *addr = inet_ntoa(sa->sin_addr);
            printf("Interface: %s\tAddress: %s\n", ifa->ifa_name, addr);
        
    
    freeifaddrs(ifap);
    return 0;

【讨论】:

以上是关于如何知道 C 中接口的 IP 地址?的主要内容,如果未能解决你的问题,请参考以下文章

C语言如何实现IP分类

IP地址的分类

在 Linux 上获取接口的 IP 地址

Socket 如何知道断开连接的客户端ip地址

如何获取web客户端IP地址

在C语言中,如何将字符串“192.168.1.1”转换成MAC地址或IP地址?