简体   繁体   English

获取Linux上接口的IP地址

[英]Get IP address of an interface on Linux

How can I get the IPv4 address of an interface on Linux from C code?如何从 C 代码中获取 Linux 上接口的IPv4地址?

For example, I'd like to get the IP address (if any) assigned to eth0.例如,我想获取分配给 eth0 的 IP 地址(如果有)。

Try this:尝试这个:

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

The code sample is taken from here .代码示例取自此处

In addition to the ioctl() method Filip demonstrated you can use getifaddrs() .除了 ioctl() Filip 演示的方法之外,您还可以使用getifaddrs() There is an example program at the bottom of the man page.手册页底部有一个示例程序。

If you're looking for an address (IPv4) of the specific interface say wlan0 then try this code which uses getifaddrs() :如果您正在寻找特定接口的地址(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);
}

You can replace wlan0 with eth0 for ethernet and lo for local loopback.您可以将wlan0替换为eth0用于以太网和lo用于本地环回。

The structure and detailed explanations of the data structures used could be found here .可以在此处找到所用数据结构的结构和详细说明。

To know more about linked list in C this page will be a good starting point.要了解有关 C 中链表的更多信息,此页面将是一个很好的起点。

My 2 cents: the same code works even if iOS:我的 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

I simply removed the test against wlan0 to see data.我只是删除了针对 wlan0 的测试以查看数据。 ps You can remove "family" ps 您可以删除“家庭”

If you don't mind the binary size, you can use iproute2 as library.如果你不介意二进制大小,你可以使用 iproute2 作为库。

iproute2-as-lib iproute2-as-lib

Pros:优点:

  • No need to write the socket layer code.无需编写套接字层代码。
  • More or even more information about network interfaces can be got.可以获得更多甚至更多关于网络接口的信息。 Same functionality with the iproute2 tools.与 iproute2 工具的功能相同。
  • Simple API interface.简单的API接口。

Cons:缺点:

  • iproute2-as-lib library size is big. iproute2-as-lib 库的大小很大。 ~500kb. ~500kb。

I have been in the same issue recently, and this is the code I made up and it works.我最近遇到了同样的问题,这是我编写的代码并且它有效。 Make sure to use the name of the network interface, exactly as you have it (could be "eth0" or else).确保使用网络接口的名称,与您拥有的名称完全一样(可以是“eth0”或其他)。

gotta check if ifconfig command beforehand to get the interface name and use it in C.必须事先检查ifconfig命令以获取接口名称并在 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));

    }

I found a quite easy way to get ip, by take advantage of using bash command:我找到了一种非常简单的方法来获取 ip,通过使用 bash 命令:

hostname -I

but use "hostname -I" natively will print the result on screen, we need to use "popen()" to read result out and save it into a string, here is c code:但是使用“hostname -I”本机将在屏幕上打印结果,我们需要使用“popen()”读取结果并将其保存为字符串,这里是 c 代码:

#include <stdio.h> // popen
#define BUFFER 17

char * get_ip()
{
    // Read out "hostname -I" command output
    FILE *fd = popen("hostname -I", "r");
    if(fd == NULL) {
    fprintf(stderr, "Could not open pipe.\n");
    return NULL;
    }

    // Put output into a string (static memory)
    static char buffer[BUFFER];
    fgets(buffer, BUFFER, fd);

    // Only keep the first ip.
    for (int i = 0; i < BUFFER; ++i)
    {
        if (buffer[i] == ' ')
        {
            buffer[i] = '\0';
            break;
        }
    }
    printf("%s\n", buffer);
    return buffer;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM