17

Let's say I'm running a program called IpAddresses.c. I want that program to get all IP addresses this device has according to each interface. Just like ifconfig. How can I do that?

I don't know much about ioctl, but I read it might help me.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
gvalero87
  • 855
  • 3
  • 15
  • 32

7 Answers7

43

Just use getifaddrs(). Here's an example:

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

And here's the output I get on my machine:

Interface: lo   Address: 127.0.0.1
Interface: eth0 Address: 69.72.234.7
Interface: eth0:1       Address: 10.207.9.3
chrisaycock
  • 36,470
  • 14
  • 88
  • 125
  • why do you have 2 eth0? btw, thnks for your answer – gvalero87 Nov 10 '10 at 00:32
  • @gvalero87 The first eth0 tells the network card to communicate over the Internet. That second eth0 communicates over a private connection (optical line) to a third party. It's a setting in the routing table that the network admin put together. – chrisaycock Nov 10 '10 at 01:33
  • See Luiz Normando's reply below: your code may crash on some machines, because ifa->ifa_addr may be NULL for some of the interfaces. – Lanzelot Nov 14 '19 at 11:54
  • @Lanzelot I have updated my answer with Luiz Normando's patch. – chrisaycock Nov 14 '19 at 18:43
  • i have added error-checking code : added a new header line "`#include `" below the `stdio.h` line, and added this line "`if (getifaddrs(&ifap) == -1) { perror("getifaddrs"); exit(1); }`" by replacing this line "`getifaddrs (&ifap);`" . Without previous (2-lines) changes, if the "ifa_addr" has a NULL pointer then it creates segmentation fault . Mentioned changes are inspired by Michael-Hampton's code shown here: https://stackoverflow.com/a/27433320/3553808 . This code worked fine on macOS (10.15.x/Catalina). – atErik Sep 09 '20 at 05:03
9

Here's some Linux sample code that might help you out.

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

jschmier
  • 15,458
  • 6
  • 54
  • 72
  • Thanks for an ioctl alternative. However i never know, when i should use `ioctl` and when `getifaddrs`. – Youda008 Dec 27 '15 at 09:25
  • I believe `ioctl` may be somewhat more portable even if it does not conform to any single standard. It is supported on most Unix and Unix-like systems and the `ioctl` function call first appeared in Version 7 AT&T UNIX. I believe `getifaddrs` is supported in BSD and Linux and first appeared in glibc 2.3. – jschmier Jan 04 '16 at 15:40
  • This also works on Android which doesn't have the `ifaddrs` thing for some reason but doesn't on OS X. [Here's an example](https://gist.github.com/OrangeTide/909204) that works on both. – Grishka Oct 30 '16 at 03:22
  • 1
    Use standard `htonl()`/`ntohl()` instead of custom `INT_TO_ADDR` macro. – Matthieu Jul 10 '18 at 16:29
  • Hi a bit confused how does ioctl work? the first time we call on a socket that helps us get all network interfaces on the machine, the next calls for address how does it work correctly for each interface? &ifr[i] just stores the information from ioctl or is information from it used as well? sorry for dumb question but ioctl has very little info related to it when i search... – AAB Nov 03 '22 at 21:49
6

The solution using getifaddrs() is great. I would suggest only one improve:

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

Just because I myself got a Segmentation Fault.

Luiz Normando
  • 61
  • 1
  • 2
  • We also got a segfault with the original code. According to the man page of getifaddrs, the ifa_addr member may be NULL for some of the interfaces... – Lanzelot Nov 14 '19 at 11:50
2

See this other Stack Overflow question, Enumerating each IP address assigned to network interfaces.

In summary, you can use:

  • ioctl(SIOCGIFCONF) -> the traditional ioctl
  • getifaddrs() -> from BSDi, now also on Linux and the BSD's.
  • RTNETLINK (Linux)
Community
  • 1
  • 1
ninjalj
  • 42,493
  • 9
  • 106
  • 148
1

You could try something like that:

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;
}
adamkonrad
  • 6,794
  • 1
  • 34
  • 41
macgarden
  • 361
  • 2
  • 2
  • 4
    Note that this code will fail on BSD where the size of the ifreq struct is variable. You can't just assume `ifc.ifc_len/sizeof(struct ifreq)` will give you the interface count. Instead you need to iterate through the elements in the list like this: `struct ifreq *ifr_iterator = ifc.ireq; size_t len; while (i < ifc.ifc_len) { /* DO STUFF */ len = IFNAMSIZ + ifr_iterator->ifr_addr.sa_len; ifr_iterator = (struct ifreq *)((char *)ifr_iterator + len); i += len; }` – Joakim Sep 24 '12 at 09:20
  • @Joakim so that means that ``#define ifc_req ifc_ifcu.ifcu_req /* array of structures ret'd */ `` technically isnt an array because size varies? It seems 5.9 had added more stuff. Also ifconfig.c code didnt seem to have SIOCGIFCONF – GorillaApe Jul 15 '16 at 20:34
  • 1
    @GorillaApe On one system it might be an array, on another not. There are different implementations is my point. On Linux this is an array with each item being of a fixed size, but on BSD the size of each item can vary, so the correct way of doing it is to always use the length field. If you want portable code that is. – Joakim Aug 27 '16 at 11:19
0

Check out the (Windows specific) IP Helper API - fortunately you don't need ioctl for this on Windows.

Steve Townsend
  • 53,498
  • 9
  • 91
  • 140
0

In case you also need IP6 addresses, you can extend the accepted answer like this:

#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;
}
Kim Nyholm
  • 1,055
  • 1
  • 10
  • 19