views:

341

answers:

2

Are you having a problem figuring out how to get interface info on Mac OS X using ioctl/SIOCGIFADDR/SIOCGIFCONF?

I had a lot of trouble getting code that worked fine on Linux to work on Mac OS X today.

A: 

This thread matched my problem somewhat:

http://discussions.apple.com/thread.jspa?messageID=10935410&tstart=0

This thread helped a lot:

https://lists.isc.org/pipermail/dhcp-hackers/2007-September/000767.html

because that thread eventually mentions that getifaddrs() should be used instead. The man page on Ubuntu 10.04 had a great example of how to use getifaddrs and using it as a reference helped me figure out code that worked on both Mac and Linux. I don't want anyone else to waste time on something so simple, so I'm posting and answering myself here. Hoping that my post helps you...

Mark
A: 

The mechanism to get MAC addresses is entirely different on BSD-derived OSes than on Linux. This includes OS X.

Here's code I use that works on Linux and OS X, and probably on the BSDs, too:

#if defined(HAVE_SIOCGIFHWADDR)
bool get_mac_address(char* mac_addr, const char* if_name = "eth0")
{
    struct ifreq ifinfo;
    strcpy(ifinfo.ifr_name, if_name);
    int sd = socket(AF_INET, SOCK_DGRAM, 0);
    int result = ioctl(sd, SIOCGIFHWADDR, &ifinfo);
    close(sd);

    if ((result == 0) && (ifinfo.ifr_hwaddr.sa_family == 1)) {
        memcpy(mac_addr, ifinfo.ifr_hwaddr.sa_data, IFHWADDRLEN);
        return true;
    }
    else {
        return false;
    }
}
#elif defined(HAVE_GETIFADDRS)
bool get_mac_address(char* mac_addr, const char* if_name = "en0")
{
    ifaddrs* iflist;
    bool found = false;
    if (getifaddrs(&iflist) == 0) {
        for (ifaddrs* cur = iflist; cur; cur = cur->ifa_next) {
            if ((cur->ifa_addr->sa_family == AF_LINK) &&
                    (strcmp(cur->ifa_name, if_name) == 0) &&
                    cur->ifa_addr) {
                sockaddr_dl* sdl = (sockaddr_dl*)cur->ifa_addr;
                memcpy(mac_addr, LLADDR(sdl), sdl->sdl_alen);
                found = true;
                break;
            }
        }

        freeifaddrs(iflist);
    }
    return found;
}
#else
#   error no definition for get_mac_address() on this platform!
#endif

It's up to you to work out how to get the right HAVE_* macro defined for the platform. I happen to use autoconf for this, but you may have another way of dealing with platform differences.

Notice that the default interface name parameter for these functions is the default for the first Ethernet interface on Linux and OS X boxes. You may need to override this for other OSes, or pass another value if you're interested in the MAC address for a different interface.

Warren Young