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:
- Create an
ifreq
structure for passing data in and out ofioctl
. - Provide an open socket descriptor.
- Invoke
ioctl
. - Check the type of the returned hardware address.
- 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
- netdevice(7) (Linux manpage)
Tags: c