Rate this page

Get the IP address of a network interface in C using SIOCGIFADDR

Tested on

Debian (Lenny)

Objective

To get the IPv4 address of a network interface in C using the ioctl command SIOCGIFADDR

Scenario

Suppose that you wish to display the IPv4 address of a network interface. The variable if_name points to a null-terminated string containing the name of the interface (for example, eth0).

Method

Overview

On Linux-based systems, one way to obtain the IPv4 address of an interface is to use the ioctl command SIOCGIFADDR. The method described here has four steps:

  1. Create an ifreq structure for passing data in and out of ioctl.
  2. Provide an open socket descriptor with the address family AF_INET.
  3. Invoke ioctl.
  4. Extract the IP address from the ifreq structure.

The following header files are needed when using this method:

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

In addition, this particular implementation makes use of:

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <arpa/inet.h>

Please note that whilst this method can be used with some network protocols other than IPv4, the Linux implementation does not support IPv6. Furthermore it is only able to return a single result for any given network protocol, so will only return one of the addresses of an interface that has several. It is not necessarily portable to other POSIX-compatible systems, and is no longer the preferred method on Linux.

Create an ifreq structure for passing data in and out of ioctl

The ifreq structure should initially contain the name of the interface to be queried, which should be copied into the ifr_name field. Since this is a fixed-length buffer you should take care to ensure that the name does not cause an overrun:

struct ifreq ifr;
size_t if_name_len=strlen(if_name);
if (if_name_len<sizeof(ifr.ifr_name)) {
    memcpy(ifr.ifr_name,if_name,if_name_len);
    ifr.ifr_name[if_name_len]=0;
} else {
    die("interface name is too long");
}

Provide an open socket descriptor with the address family AF_INET

All ioctl calls need a file descriptor to act on. In the case of SIOCGIFADDR this must refer to a socket (as opposed to, for example, a regular file) and must be of the address family that you wish to obtain (AF_INET in this instance). Otherwise any type of socket would suffice, but it should preferably not be one that requires any obscure kernel modules to be loaded. For this example a UDP socket will be used:

int fd=socket(AF_INET,SOCK_DGRAM,0);
if (fd==-1) {
    die("%s",strerror(errno));
}

Invoke ioctl

Once you have the ifreq structure and socket descriptor then you are ready to invoke ioctl:

if (ioctl(fd,SIOCGIFADDR,&ifr)==-1) {
    int temp_errno=errno;
    close(fd);
    die("%s",strerror(temp_errno));
}
close(fd);

If this completes without error then the hardware address of the interface should have been returned in ifr.ifr_addr in the form of a struct sockaddr_in.

Extract the IP address from the ifreq structure

If an address was returned at all then it ought to be an IPv4 address, because that was the address family of the socket. To obtain the numerical value of the address you should:

  1. Cast the returned address to a struct sockaddr_in.
  2. Extract the sin_addr field of this structure to obtain a struct in_addr.
  3. Extract the s_addr field of the in_addr structure to obtain an in_addr_t (equivalent to a uint32_t).
  4. Finally, convert the s_addr field (which is in network byte order) into whatever representation you require.
struct sockaddr_in* ipaddr = (struct sockaddr_in*)&ifr.ifr_addr;
printf("IP address: %s\n",inet_ntoa(ipaddr->sin_addr));

See also

Further reading

(Note that SIOCGIFADDR was not documented in netdevice(7) until version 3.40 of the Linux man-pages project, which was released in April 2012, so at the time of writing it had not been incorporated into the stable releases of most GNU/Linux distributions. The ioctl itself has been present in Linux since 1993.)

Tags: c