【问题标题】:Get IP address of an interface on Linux在 Linux 上获取接口的 IP 地址
【发布时间】:2011-01-18 00:23:47
【问题描述】:

如何从 C 代码中获取 Linux 上接口的IPv4 地址?

例如,我想获取分配给 eth0 的 IP 地址(如果有)。

【问题讨论】:

    标签: c linux sockets


    【解决方案1】:

    试试这个:

    #include <stdio.h>
    #include <unistd.h>
    #include <string.h> /* for strncpy */
    
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/ioctl.h>
    #include <netinet/in.h>
    #include <net/if.h>
    #include <arpa/inet.h>
    
    int
    main()
    {
     int fd;
     struct ifreq ifr;
    
     fd = socket(AF_INET, SOCK_DGRAM, 0);
    
     /* I want to get an IPv4 IP address */
     ifr.ifr_addr.sa_family = AF_INET;
    
     /* I want IP address attached to "eth0" */
     strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1);
    
     ioctl(fd, SIOCGIFADDR, &ifr);
    
     close(fd);
    
     /* display result */
     printf("%s\n", inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
    
     return 0;
    }
    

    代码示例取自here

    【讨论】:

    • 为什么会出现分段错误?
    • 如果把SIOCGIFADDR改成SIOCGIFNETMASK,就可以得到接口的网络掩码。
    • ioctl()的返回值值得检查,如果不为零,检查errno的值。
    • 不需要IFNAMSIZ-1,只需IFNAMSIZ 就足够了。 strncpy 将使用最后一个字节来终止字符串以防溢出。
    • @zapstar 我不认为这是正确的。来自strncpy 手册页:警告:如果 src 的前 n 个字节中没有空字节,则放在 dest 中的字符串不会以空值结尾。
    【解决方案2】:

    除了 Filip 演示的 ioctl() 方法之外,您还可以使用 getifaddrs()。手册页底部有一个示例程序。

    【讨论】:

    • getifaddrs 似乎很全面。其他方法只会给出每个接口的主地址或首地址。
    • 太棒了,从来不知道这个!
    • 我在 eth0 上没有任何连接,如果我使用其他方法它输出 128.226.115.183 这是错误的。但是,此方法显示 eth0 上没有提供可靠输出的连接
    【解决方案3】:

    如果您要查找特定接口的地址 (IPv4),请说 wlan0 然后 试试这个使用getifaddrs()的代码:

    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <ifaddrs.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    int main(int argc, char *argv[])
    {
        struct ifaddrs *ifaddr, *ifa;
        int family, s;
        char host[NI_MAXHOST];
    
        if (getifaddrs(&ifaddr) == -1) 
        {
            perror("getifaddrs");
            exit(EXIT_FAILURE);
        }
    
    
        for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) 
        {
            if (ifa->ifa_addr == NULL)
                continue;  
    
            s=getnameinfo(ifa->ifa_addr,sizeof(struct sockaddr_in),host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
    
            if((strcmp(ifa->ifa_name,"wlan0")==0)&&(ifa->ifa_addr->sa_family==AF_INET))
            {
                if (s != 0)
                {
                    printf("getnameinfo() failed: %s\n", gai_strerror(s));
                    exit(EXIT_FAILURE);
                }
                printf("\tInterface : <%s>\n",ifa->ifa_name );
                printf("\t  Address : <%s>\n", host); 
            }
        }
    
        freeifaddrs(ifaddr);
        exit(EXIT_SUCCESS);
    }
    

    您可以将 wlan0 替换为 eth0 用于以太网,lo 用于本地环回。

    数据结构的结构及详解 使用的可以找到here

    要了解更多关于 C 中链表的信息,page 将是一个很好的起点。

    【讨论】:

    • 为什么我们需要getnameinfo
    • @abhiarora 来自文档 - 它以独立于协议的方式将套接字地址转换为相应的主机和服务。请注意,稍后打印的 host 参数的值是在 getnameinfo 函数内部提供的。
    • 为什么ifa-&gt;ifa_addr == NULL不够用?我不确定为什么我们需要getnameinfo,因为 ifa_addr 是内部地址。
    【解决方案4】:

    我的 2 美分:即使 iOS 也可以使用相同的代码:

    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <ifaddrs.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    
    
    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        showIP();
    }
    
    
    
    void showIP()
    {
        struct ifaddrs *ifaddr, *ifa;
        int family, s;
        char host[NI_MAXHOST];
    
        if (getifaddrs(&ifaddr) == -1)
        {
            perror("getifaddrs");
            exit(EXIT_FAILURE);
        }
    
    
        for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
        {
            if (ifa->ifa_addr == NULL)
                continue;
    
            s=getnameinfo(ifa->ifa_addr,sizeof(struct sockaddr_in),host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
    
            if( /*(strcmp(ifa->ifa_name,"wlan0")==0)&&( */ ifa->ifa_addr->sa_family==AF_INET) // )
            {
                if (s != 0)
                {
                    printf("getnameinfo() failed: %s\n", gai_strerror(s));
                    exit(EXIT_FAILURE);
                }
                printf("\tInterface : <%s>\n",ifa->ifa_name );
                printf("\t  Address : <%s>\n", host);
            }
        }
    
        freeifaddrs(ifaddr);
    }
    
    
    @end
    

    我只是删除了针对 wlan0 的测试以查看数据。 ps 你可以去掉“family”

    【讨论】:

    • 可爱。这绝对是纯 C。
    【解决方案5】:

    我最近遇到了同样的问题,这是我编写的代码,它可以工作。确保使用网络接口的名称,与您的名称完全相同(可以是“eth0”或其他)。

    必须事先检查ifconfigcommand 以获取接口名称并在C 中使用它。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/ioctl.h>
    #include <net/if.h>
    #include <unistd.h>
    #include <linux/if.h>
    #include <errno.h>
    #include <ifaddrs.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
        
    
    void extract_ipaddress()
        {
            //create an ifreq struct for passing data in and out of ioctl
            struct ifreq my_struct;
         
            //declare and define the variable containing the name of the interface
            char *interface_name="enp0s3";   //a very frequent interface name is "eth0";
         
            //the ifreq structure should initially contains the name of the interface to be queried. Which should be copied into the ifr_name field.
            //Since this is a fixed length buffer, one should ensure that the name does not cause an overrun
            size_t interface_name_len=strlen(interface_name);
         
            if(interface_name_len<sizeof(my_struct.ifr_name))
            {
                memcpy(my_struct.ifr_name,interface_name,interface_name_len);
                my_struct.ifr_name[interface_name_len]=0;
            }
            else
            {
                perror("Copy name of interface to ifreq struct");
                printf("The name you provided for the interface is too long...\n");
            }
         
            //provide an open socket descriptor with the address family AF_INET
            /* ***************************************************************
             * All ioctl call needs a file descriptor to act on. In the case of SIOCGIFADDR this must refer to a socket file descriptor. This socket must be in the address family that you wish to obtain (AF_INET for IPv4)
             * ***************************************************************
             */
         
            int file_descriptor=socket(AF_INET, SOCK_DGRAM,0);
         
            if(file_descriptor==-1)
            {
                perror("Socket file descriptor");
                printf("The construction of the socket file descriptor was unsuccessful.\n");
                return -1;
            }
         
            //invoke ioctl() because the socket file descriptor exists and also the struct 'ifreq' exists
            int myioctl_call=ioctl(file_descriptor,SIOCGIFADDR,&my_struct);
         
            if (myioctl_call==-1)
            {
                perror("ioctl");
                printf("Ooops, error when invoking ioctl() system call.\n");
                close(file_descriptor);
                return -1;
            }
         
            close(file_descriptor);
         
            /* **********************************************************************
             * If this completes without error , then the hardware address of the interface should have been returned in the  'my_struct.ifr_addr' which is types as struct sockaddr_in.
             * ***********************************************************************/
         
          //extract the IP Address (IPv4) from the my_struct.ifr_addr which has the type 'ifreq'
         
            /* *** Cast the returned address to a struct 'sockaddr_in' *** */
            struct sockaddr_in * ipaddress= (struct sockaddr_in *)&my_struct.ifr_addr;
           /* *** Extract the 'sin_addr' field from the data type (struct) to obtain a struct 'in_addr' *** */
          printf("IP Address is %s.\n", inet_ntoa(ipaddress->sin_addr));
    
        }
    

    【讨论】:

      【解决方案6】:

      如果您不介意二进制大小,可以使用 iproute2 作为库。

      iproute2-as-lib

      优点:

      • 无需编写套接字层代码。
      • 可以获得更多甚至更多关于网络接口的信息。与 iproute2 工具功能相同。
      • 简单的 API 接口。

      缺点:

      • iproute2-as-lib 库很大。 ~500kb。

      【讨论】:

        猜你喜欢
        • 2013-07-28
        • 2012-03-15
        • 2010-12-26
        • 2011-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-28
        • 2014-05-26
        相关资源
        最近更新 更多