ADAM'S WEB PRESENCE

5 September 2006

Enumerating Network Interfaces on Linux

Filed under: Nerd Notes — adam @ 1:34 pm

Here is some C code which will list the network interfaces on your Linux box and also the IP and MAC address associated with each interface. I put it together because I couldn’t find an example on the net of exactly what I wanted to do – so now here it is.

/*
  Example code to obtain IP and MAC for all available interfaces on Linux.
  by Adam Pierce <adam@doctort.org>

http://www.doctort.org/adam/

*/

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

int main(void)
{
	char          buf[1024];
	struct ifconf ifc;
	struct ifreq *ifr;
	int           sck;
	int           nInterfaces;
	int           i;

/* Get a socket handle. */
	sck = socket(AF_INET, SOCK_DGRAM, 0);
	if(sck < 0)
	{
		perror("socket");
		return 1;
	}

/* Query available interfaces. */
	ifc.ifc_len = sizeof(buf);
	ifc.ifc_buf = buf;
	if(ioctl(sck, SIOCGIFCONF, &ifc) < 0)
	{
		perror("ioctl(SIOCGIFCONF)");
		return 1;
	}

/* Iterate through the list of interfaces. */
	ifr         = ifc.ifc_req;
	nInterfaces = ifc.ifc_len / sizeof(struct ifreq);
	for(i = 0; i < nInterfaces; i++)
	{
		struct ifreq *item = &ifr[i];

	/* Show the device name and IP address */
		printf("%s: IP %s",
		       item->ifr_name,
		       inet_ntoa(((struct sockaddr_in *)&item->ifr_addr)->sin_addr));

	/* Get the MAC address */
		if(ioctl(sck, SIOCGIFHWADDR, item) < 0)
		{
			perror("ioctl(SIOCGIFHWADDR)");
			return 1;
		}

	/* Get the broadcast address (added by Eric) */
		if(ioctl(sck, SIOCGIFBRDADDR, item) >= 0)
			printf(", BROADCAST %s", inet_ntoa(((struct sockaddr_in *)&item->ifr_broadaddr)->sin_addr));
		printf("\n");
	}

        return 0;
}

UPDATE: Adam Risi has been kind enough to publish an updated version to handle IPv6 on his website.

UPDATE: Eric supplied some extra code to also list the broadcast address for each interface

23 Comments »

  1. Comment by Salman Ahmed — 31 August 2007 @ 5:11 am

    A code snippet that actually does what it advertises and compiles and works in one attempt!
    Thanks – this is very useful.

    Cheers,

  2. Comment by Yiannis Tsiouris — 26 November 2007 @ 8:19 am

    My friend when i try to compile it i get a segmentation fault. Nothing else.. What do you think?

  3. Comment by adam — 26 November 2007 @ 10:01 am

    Here’s how I compile it:

    1. Cut & paste the code into a text file called ifenum.c
    2. gcc -o ifenum ifenum.c
    3. ./ifenum

    That works for me, I just tried it now. I am on Debian Linux with gcc version 4.1.2. What compiler and OS are you using ?

  4. Comment by Yiannis Tsiouris — 27 November 2007 @ 1:15 am

    I have Arch Linux and the compiler i use is gcc 4.2.2. And this is the output:

    [yiannis@main Programs]$ gcc -o interface interface.c
    [yiannis@main Programs]$ ./interface
    Segmentation fault

    A straight segmentation fault! (btw, a very ignoring error don’t u think? :P)
    Thank u for the quick answer! You have done an excellent work here! ;)

  5. Comment by adam — 27 November 2007 @ 8:16 am

    Can’t tell much from a segmentation fault. At least now I know it is failing when you run the utility. I kind of assumed from your last post that it was segfaulting during compilation. So here are some suggestions:

    1. Segfaults are usually caused by a bad pointer or a buffer overflow. There is only one buffer in this code, so try changing buf[1024] to a much larger number (eg. buf[16384]) and see if that helps.

    2. Compile the code with the debug option (gcc -g -o ifenum ifenum.c) and then step through it with a debugger such as kdbg. Then you can see what line is causing the problem.

  6. Comment by Gautam — 5 February 2008 @ 3:32 pm

    Thanks !!! it is very useful.

  7. Comment by Gautam Raut — 5 February 2008 @ 3:39 pm

    Can we fetch routing table from a router?
    If yes then how to do it using C. How would u detect router in a subnet?
    I have a code that detects router if that router has any virtual interface or a proxy ARP.
    My email id is gautam.raut.here@gmail.com

  8. Comment by adam — 5 February 2008 @ 7:43 pm

    Sorry Gautam, that’s not really my area of expertise. Try doing a little research into routing information protocols such as RIP and OSPF.

  9. Comment by marc — 1 March 2008 @ 9:58 am

    Thanks Adam! Worked right out of the box, unlike some of the other i tried.
    Be proud that your code is a part of our firmware. :)

  10. Comment by Michel Onoff — 31 March 2008 @ 6:39 pm

    This code shows only the *running* interfaces.

    To get infos about *all* interfaces, I know of no other way than to read the file /proc/net/dev which, unfortunately, is not quite parse-friendly.

  11. Comment by adam — 31 March 2008 @ 9:29 pm

    Yes it does only list the interfaces that are up.

    Unfortunately, I don’t think you can find every interface via the ioctl method so /proc/net/dev is probably your best bet for what you want.

    /proc/net/dev is not so hard to parse. Here’s some code which can do it – at least on Debian. I don’t know if the format of the file is different on other distros:

    /* listif.c - List all known interfaces on a Linux System */
    
    #include <stdio.h>
    #include <string.h>
    
    main()
    {
    	char line[512];
    	char *colon;
    	char *name;
    	FILE *fp;
    
    	if(0 == (fp = fopen("/proc/net/dev", "r")))
    		return 1;
    
    	while(0 != (name = fgets(line, 512, fp)))
    	{
    		while(isspace(name[0])) /* Trim leading whitespace */
    			name++;
    
    		colon = strchr (ame, ':');
    		if(colon)
    		{
    			*colon = 0;
    			printf("%s:\n", name);
    		}
    	}
    
    	fclose(fp);
    	return 0;
    }
    
  12. Comment by ccspro — 29 June 2008 @ 3:44 pm

    the above example has a typo:
    colon = strchr (ame, ‘:’);
    should be:
    colon = strchr (name, ‘:’);

    It will compile.

  13. Comment by adam — 29 June 2008 @ 9:43 pm

    Thanks for spotting that ccspro!

  14. Comment by Elessedil — 26 July 2008 @ 1:49 am

    Hello Adam,

    i tried your code but unfortunately only the loopback interface is found, though the other two are up.
    If you got an idea what could be the problem, please let me know.

    thank you

  15. Comment by Elessedil — 26 July 2008 @ 1:56 am

    just figured that out. if no ip is assigned the interfaces are not found.
    is there a possibility to get the list of interfaces even if they are not connected?

  16. Comment by adam — 26 July 2008 @ 10:08 pm

    Try the code mentioned in comment #11 above.

  17. Comment by Erb — 28 August 2008 @ 2:54 am

    to fix the seg fault, try #include

    also, that first printf should have a \n

  18. Comment by Erb — 28 August 2008 @ 2:55 am

    Hmm that comment got messed up. Should be
    #include <arpa/inet.h>

  19. Comment by Prameeth Sreesha — 5 November 2008 @ 4:42 pm

    In response to:
    ===========================================
    Comment by Yiannis Tsiouris — 27 November 2007 @ 1:15 am

    I have Arch Linux and the compiler i use is gcc 4.2.2. And this is the output:

    [yiannis@main Programs]$ gcc -o interface interface.c
    [yiannis@main Programs]$ ./interface
    Segmentation fault
    ============================================

    I came across the same problem too.. In my case, it was the query for the MAC Address field for a loopback device, that was the culprit. In the code, the lines below:

    printf(“%s: IP %s\n”,
    item->ifr_name,
    inet_ntoa(((struct sockaddr_in *)&item->ifr_addr)->sin_addr));

    printf(“, MAC %s\\n”, ether_ntoa((struct ether_addr *)item->ifr_hwaddr.sa_data));

    were the culprits in causing the segfaults. Since loopback device does not have a MAC value associated, it might have led to ifr_hwaddr being NULL. So the access to the sa_data might have led to the Segfault. I still need to go through the 2 structures though.

    Commenting the above 2 printf’s and just printing item->ifr_name works fine.

    Regards,
    Prameeth

  20. Comment by Adam Risi — 17 February 2009 @ 4:05 pm

    Hi all,
    I have written a fix for this code to allow it to use IPv6 interfaces, and to eliminate the segfault bug some users were experiencing. The new source can be found at my website.

    HTH,
    Adam Risi

  21. Comment by Lieven — 12 May 2009 @ 5:09 am

    Hi,

    Is there a quick way, with these or similar methods to obtain the subnet mask of these interfaces ?

    Thanks.
    Lieven

  22. Comment by Eric — 16 January 2010 @ 2:56 am

    Hi,
    Great sample, thanks! Thought some of you might be interested in how to get broadcast address:
    if (ioctl(sock, SIOCGIFBRDADDR, &ifr[nInterface]) >= 0) // rectify with ‘item’ as above, as this chunk of code comes from my application
    strcpy(szBroadcast, inet_ntoa(((struct sockaddr_in *)&ifr[nInterface].ifr_broadaddr)->sin_addr));
    So, to reply the previous question: to get the subnet mask, I guess you’d use the same ioctl call, passing SIOCGIFNETMASK as 2nd argument.
    One note: some data returned by ioctl in the buffer (3rd argument) are part of a union, so one ioctl call might overwrite data of a previous call. So, make sure you retrieve the data you need, straight after each ioctl call
    Cheers,
    Eric

  23. Comment by adam — 18 January 2010 @ 8:05 am

    Thanks Eric, it just so happens I was thinking about broadcast addresses today. I’ve added your code to the example in the article.

RSS feed for comments on this post. TrackBack URI

Leave a comment


Powered by WordPress