75

How can I get the IPv4 address of an interface on Linux from C code?

For example, I'd like to get the IP address (if any) assigned to eth0.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
leeeroy
  • 11,216
  • 17
  • 52
  • 54

7 Answers7

100

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.

Christian Ternus
  • 8,406
  • 24
  • 39
Filip Ekberg
  • 36,033
  • 20
  • 126
  • 183
  • 3
    why there is a segmentation fault? – user138126 Mar 26 '13 at 11:13
  • 10
    If you change `SIOCGIFADDR` to `SIOCGIFNETMASK`, you can get the interface's netmask. – Craig McQueen Apr 04 '14 at 02:23
  • 8
    It's worth checking the return value of `ioctl()`, and if it's non-zero, check the value of `errno`. – Craig McQueen Apr 04 '14 at 02:49
  • No need for `IFNAMSIZ-1`, instead just `IFNAMSIZ` will suffice. strncpy will use the last byte to terminate the string incase of overflow. – zapstar Nov 16 '16 at 09:01
  • 5
    @zapstar I don't think that's correct. From the `strncpy` manpage: _Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated._ – Mark Smith Apr 04 '17 at 10:15
  • @MarkSmith Yes you are correct, I was under the wrong impression that `strncpy` does terminate the string with NULL byte. Thanks for pointing this out for me. – zapstar Apr 12 '17 at 09:57
  • @Filip Ekberg, will it return all IP attached to the same nic? – Alex Mathew Dec 22 '19 at 14:50
48

In addition to the ioctl() method Filip demonstrated you can use getifaddrs(). There is an example program at the bottom of the man page.

Duck
  • 26,924
  • 5
  • 64
  • 92
  • 1
    getifaddrs seems very comprehensive. Other methods will only give the primary or first address per interface. – MarkR Feb 17 '10 at 22:00
  • 1
    I don't have any connection on eth0, if I use the other method it outputs 128.226.115.183 which is wrong. However, this method shows that there is no connection on eth0 which provides a reliable output – Angs Jul 14 '13 at 10:47
31

If you're looking for an address (IPv4) of the specific interface say wlan0 then try this code which uses 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.

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.

Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
sjsam
  • 21,411
  • 5
  • 55
  • 102
  • why we need `getnameinfo`? – abhiarora Jul 14 '20 at 15:00
  • @abhiarora From the doc - it converts a socket address to a corresponding host and service, in a protocol-independent manner. Note that the value of the `host` parameter printed later is supplied inside the `getnameinfo` function. – sjsam Jul 14 '20 at 17:58
  • Why `ifa->ifa_addr == NULL` is not enough? I am not sure why we need `getnameinfo` as ifa_addr is internal address. – abhiarora Jul 15 '20 at 04:08
4

My 2 cents: the same code works even if 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. ps You can remove "family"

ingconti
  • 10,876
  • 3
  • 61
  • 48
3

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).

gotta check if ifconfigcommand beforehand to get the interface name and use it in 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));

    }
George
  • 653
  • 6
  • 18
2

If you don't mind the binary size, you can use iproute2 as library.

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.
  • Simple API interface.

Cons:

  • iproute2-as-lib library size is big. ~500kb.
eshenhu
  • 31
  • 3
0

I found a quite easy way to get ip, by take advantage of using bash command:

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:

#include <stdio.h> // popen
#include "ip_common_def.h"

const 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[IP_BUFFER_LEN];
    fgets(buffer, IP_BUFFER_LEN, fd);

    // Only keep the first ip.
    for (int i = 0; i < IP_BUFFER_LEN; ++i)
    {
        if (buffer[i] == ' ')
        {
            buffer[i] = '\0';
            break;
        }
    }

    char *ret = malloc(strlen(buffer) + 1);
    memcpy(ret, buffer, strlen(buffer));
    ret[strlen(buffer)] = '\0';
    printf("%s\n", ret);
    return ret;
}
Hogan Chou
  • 11
  • 2