Displaying MAC Address as String Does Not Print Leading Zeroes

On many occasions it is beneficial to convert a MAC Address from its network byte order to a user readable string in the standard hex-digit and colon notation. There is a specific function that provides this functionality from the C library, known as “ether_ntoa” or “ether_ntoa_r”. Depending on the usage of this string it may be advantageous to print those leading zeroes. The manual page of this function, luckily, does inform the programmer of this functionality:

The ether_ntoa() function converts the Ethernet host address addr given in
network byte order to a string in standard hex-digits-and-colons notation,
omitting leading zeros.  The string is returned in a statically allocated
buffer, which subsequent calls will overwrite.

So now that this functionality is known – what if we actually wanted to see those zeroes. Well there are two options to fix this problem:

  1. Patch the libc function to print leading zeroes.
  2. Write your own conversion function to execute the same result.

A drawback to patching the libc function is that you may not always be compiling your code on the same computer – i.e. it is not very portable. Thus we will go with option two (2). Write our own function!

Lets first have a look at the current implementation of “ether_ntoa”.

char *ether_ntoa (const struct ether_addr *addr)
{
  static char asc[18];
 
  return ether_ntoa_r (addr, asc);
}

Hrmm, well look at this – “ether_ntoa” calls the re-entrant implementation of “ether_ntoa” which is the “ether_ntoa_r” function. This is a GNU extension and is thread-safe and also does not utilize a static buffer, which means you will not have to worry about overwriting your buffer. Okay, so let us examine the “ether_ntoa_r” function – note these functions can be found in the source code of libc in the glibc-2.17/inet directory.

char *ether_ntoa_r (const struct ether_addr *addr, char *buf)
{
  sprintf (buf, "%x:%x:%x:%x:%x:%x",
           addr->ether_addr_octet[0], addr->ether_addr_octet[1],
           addr->ether_addr_octet[2], addr->ether_addr_octet[3],
           addr->ether_addr_octet[4], addr->ether_addr_octet[5]);
  return buf;
}

Here is the bones of the function that requires minimal tweaking to display the leading zeroes. Its a basic sprintf function that stores the contents in buf. So lets tweak it to our own liking:

char *ether_ntoa_erik (const struct ether_addr *addr, char *buf)
{
  sprintf (buf, "%02x:%02x:%02x:%02x:%02x:%02x",
           addr->ether_addr_octet[0], addr->ether_addr_octet[1],
           addr->ether_addr_octet[2], addr->ether_addr_octet[3],
           addr->ether_addr_octet[4], addr->ether_addr_octet[5]);
  return buf;
}

This will now print the leading zeroes and all we had to do was modify the format. If you wanted to have your values all in capital letters just swap the lowercase “x” with an uppercase “X”.

Test Code for ether_ntoa_erik

Nothing is complete until it is tested, so here is a little test program I wrote that takes a basic MAC address with leading zeroes in it and then calls the “ether_ntoa_r” function then the function I made that will print leading zeroes. Lets check the code and output.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netinet/ether.h>
#include <netinet/if_ether.h>
 
char *ether_ntoa_erik (const struct ether_addr *addr, char *buf)
{
  sprintf (buf, "%02x:%02x:%02x:%02x:%02x:%02x",
           addr->ether_addr_octet[0], addr->ether_addr_octet[1],
           addr->ether_addr_octet[2], addr->ether_addr_octet[3],
           addr->ether_addr_octet[4], addr->ether_addr_octet[5]);
  return buf;
}
 
int main()
{
    char str_buf[ETH_ALEN];
    struct ether_addr erik;
 
    erik.ether_addr_octet[0] = 0x0a;
    erik.ether_addr_octet[1] = 0xbb;
    erik.ether_addr_octet[2] = 0x0c;
    erik.ether_addr_octet[3] = 0x12;
    erik.ether_addr_octet[4] = 0x04;
    erik.ether_addr_octet[5] = 0x56;
 
    ether_ntoa_r(&erik, str_buf);
    fprintf(stdout, "ether_ntoa_r:    -%s-\n", str_buf);
 
    ether_ntoa_erik(&erik, str_buf);
    fprintf(stdout, "ether_ntoa_erik: -%s-\n", str_buf);
 
    return 0;
}

Voila…now the output:

$ ./main 
ether_ntoa_r:    -a:bb:c:12:4:56-
ether_ntoa_erik: -0a:bb:0c:12:04:56

As you can see from the output of the modified function there are now the leading zeroes. Try it out yourself! I hope this helps anyone needing zeros.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *