Rate this page

Flattr this

Get the MAC address of an Ethernet interface in C using SIOCGIFHWADDR

Tested on

Debian (Lenny, Squeeze)
Ubuntu (Lucid, Precise)

Objective

To get the MAC address of an Ethernet interface in C using the ioctl command SIOCGIFHWADDR

Scenario

Suppose you wish to display the MAC address of an Ethernet 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 the MAC address of an interface can be obtained using the ioctl command SIOCGIFHWADDR. The method described here has five steps:

  1. Create an ifreq structure for passing data in and out of ioctl.
  2. Provide an open socket descriptor.
  3. Invoke ioctl.
  4. Check the type of the returned hardware address.
  5. Extract the hardware address from the ifreq structure.

The following header files will be needed:

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_arp.h>

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

The socket descriptor is merely an artefact of the way in which ioctl commands are invoked generally, and is not used for any particular purpose by SIOCGIFHWADDR. It must be open and must refer to a socket (as opposed to, for example, a regular file). 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 UNIX domain socket will be used:

int fd=socket(AF_UNIX,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,SIOCGIFHWADDR,&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_hwaddr in the form of a struct sockaddr.

Check the type of the returned hardware address

The length and format of the hardware address will depend on the type of network interface it belongs to, so you should not assume that it is an Ethernet MAC address. You can check the address type by inspecting the sa_family field of the sockaddr. For an Ethernet interface this should be equal to ARPHRD_ETHER:

if (ifr.ifr_hwaddr.sa_family!=ARPHRD_ETHER) {
    die("not an Ethernet interface");
}

Other possible values of sa_family for different types of network interface can be found in the header file <net/if_arp.h>, each beginning with the prefix ARPHRD_. Note that for some of these (such as ARPHRD_LOOPBACK) there is no hardware address as such.

Extract the hardware address from the ifreq structure

Having checked its type, the address can now be safely extracted from req.ifr_hwaddr.sa_data. It is presented by an array of char, which could be a signed type, so if you wish to interpret it in any way then it should first be converted to an unsigned representation. A crude but straightforward way to achieve this is to cast the whole array to an unsigned char*:

const unsigned char* mac=(unsigned char*)ifr.ifr_hwaddr.sa_data;
printf("%02X:%02X:%02X:%02X:%02X:%02X\n",
    mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);

See also

Further reading

Tags: c