How can I get to know the IP address for interfaces in C?
Asked Answered
S

7

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.

Surmount answered 9/11, 2010 at 22:35 Comment(0)
A
45

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
Abbreviation answered 9/11, 2010 at 23:43 Comment(5)
why do you have 2 eth0? btw, thnks for your answerSurmount
@Surmount 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.Abbreviation
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.Bremer
@Bremer I have updated my answer with Luiz Normando's patch.Abbreviation
i have added error-checking code : added a new header line "#include <stdlib.h>" 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://mcmap.net/q/742779/-how-to-know-the-ip-address-for-interfaces-in-c-using-ipv6 . This code worked fine on macOS (10.15.x/Catalina).Sukkoth
E
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;
}

Elk answered 9/11, 2010 at 23:31 Comment(5)
Thanks for an ioctl alternative. However i never know, when i should use ioctl and when getifaddrs.Cleodal
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.Elk
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 that works on both.Pulmonary
Use standard htonl()/ntohl() instead of custom INT_TO_ADDR macro.Veliavelick
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...Roseline
I
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.

Island answered 29/9, 2014 at 20:50 Comment(1)
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...Bremer
D
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)
Dialogism answered 9/11, 2010 at 23:5 Comment(0)
E
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;
}
Endermic answered 9/11, 2010 at 22:54 Comment(3)
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; }Stratocracy
@Stratocracy 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 SIOCGIFCONFTritanopia
@Tritanopia 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.Stratocracy
L
0

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

Lanilaniard answered 9/11, 2010 at 22:37 Comment(0)
B
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;
}
Boart answered 8/9, 2020 at 7:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.