tcpdump mailing list archives

Re: Obtaining MAC on OSX using AF_LINK


From: Guy Harris <guy () alum mit edu>
Date: Sun, 2 Jan 2011 14:33:35 -0800


On Dec 30, 2010, at 5:00 PM, Mathew Rowley wrote:

I am trying to understand how to get the MAC address when a pcap_addr family is of type AF_LINK.

...on OS X, which is relevant here.  AF_LINK is a BSDism, and only OSes that inherit AF_LINK from whatever flavor of 
BSD introduced it support it - i.e., you can only do that on *BSD and OS X.  Forget about it on other OSes.

It seems that the pacap_addr.sa_data should be of type (struct sockaddr_dl*)

Yes.  A sockaddr_dl is defined in OS X 10.6 as

struct sockaddr_dl {
        u_char  sdl_len;        /* Total length of sockaddr */
        u_char  sdl_family;     /* AF_LINK */
        u_short sdl_index;      /* if != 0, system given index for interface */
        u_char  sdl_type;       /* interface type */
        u_char  sdl_nlen;       /* interface name length, no trailing 0 reqd. */
        u_char  sdl_alen;       /* link level address length */
        u_char  sdl_slen;       /* link layer selector length */
        char    sdl_data[12];   /* minimum work area, can be larger;
                                   contains both if name and ll address */
#ifndef __APPLE__
        /* For TokenRing */
        u_short sdl_rcf;        /* source routing control */
        u_short sdl_route[16];  /* source routing information */
#endif
};

but then the sockaddr_dl sdl_alen is of variable size (I was expecting),

You were expecting a fixed-length structure?  As the above indicates, there's no reason to expect it to be 
fixed-length; as it says, sdl_data "contains both if name and ll address", with no guarantee that the interface name is 
fixed-length.  Furthermore, there is no guarantee that the link-layer address is a 6-octet MAC address - a PPP 
interface would not have any link-layer address, and a FireWire interface would have an 8-octet link-layer address, not 
a 6-octet one.

and LLADDR doesnt help much.  Any insight? Here is the sample code and output:

OUTPUT:
link sdl_alen: 101
mac : 64:ffffffb9:ffffffe8:ffffffb7:ffffffb8:06

OK, that's 64:b9:e8:b7:b8:06.  "char" in "char sdl_data[12]" really means "byte", and, for the link-layer address, 
"byte" generally means "unsigned byte", and "char" is not an unsigned byte on most platforms - it's a *signed* byte on 
most platforms (and you're not using a Gould/SEL minicomputer, so you're on a platform where it's signed; maybe there 
are others where char is signed, but no modern UN*X platform has char as an unsigned type).  I would treat the bytes 
pointed to by LLADDR(link) as "unsigned char".

                      if(a->addr->sa_family == AF_LINK && a->addr->sa_data != NULL){

a->addr->sa_data will never be null - sa_data is an array member of "struct sockaddr", not a pointer, and the address 
of that array member can never be null.

                              // MAC ADDRESS
                              //struct sockaddr_dl *sdl = (struct sockaddr_dl *) a->addr->sa_data;
                              link = (struct sockaddr_dl*)a->addr->sa_data;

No, you want to do

                link = (struct sockaddr_dl *)(a->addr);

a->addr points to a sockaddr structure of some sort.  All sockaddr structures begin with the same fields, including the 
address family, with the same sizes; what follows it is address-family-dependent data.  "struct sockaddr" is a template 
for all sockaddr structures; sa_family is the address family, and sa_data is the data.  Other sockaddr structures 
overlay the *entire* structure; they also contain an address family field, followed by data.

                              char mac[link->sdl_alen];
                              memcpy(mac, LLADDR(link), link->sdl_alen);
                              printf("link sdl_alen: %i\n", link->sdl_alen);
                              printf("mac : %02x:%02x:%02x:%02x:%02x:%02x\n", mac[1], mac[2], mac[3], mac[4], mac[5], 
mac[6]);

That will print garbage for a PPP interface, as sdl_alen is 0 for PPP interfaces, and will print only the first 6 
octets of the address for a FireWire interface.

Also, C arrays are zero-index, so that will not print the first octet of the address - and will print garbage as the 
last octet, *even for an 802.x MAC address*.

You want to do

                                if (link->sdl_alen != 0) {
                                        unsigned char *octetp;
                                        unsigned int i;
                                        char sep;

                                        printf("mac: ");
                                        sep = '\0';
                                        octetp = (unsigned char *)LLADDR(link);
                                        for (i = 0; i < link->sdl_alen; i++) {
                                                if (sep != '\0')
                                                        putchar(sep);
                                                printf("%02x", octetp[i]);
                                                sep = ':';
                                        }
                                        putchar('\n');
                                }

so that:

        1) you don't print anything if the address is zero-length;

        2) you print the exact number of octets there are in the address, even if it's less than or greater than 6;

        3) you print the first octet.-
This is the tcpdump-workers list.
Visit https://cod.sandelman.ca/ to unsubscribe.


Current thread: