Back in 2011 this post was a bare copy-paste of a getifaddrs() snippet for reading an iPhone’s Wi-Fi MAC address. Fifteen years later the code still compiles, but the reason you wanted that address is long gone. Here is the original for the record, why the technique died, and what to use instead.
The original 2011 snippet
The classic trick: walk the interface list with getifaddrs(), look for an AF_LINK socket of type IFT_ETHER (on the iPhone that is en0, the Wi-Fi interface), and format sdl_data as colon-separated hex bytes:
#if !defined(IFT_ETHER)
#define IFT_ETHER 0x6 /* Ethernet CSMACD */
#endif
- (IBAction)testAction:(id)sender
{
#pragma unused(sender)
BOOL success;
struct ifaddrs *addrs;
const struct ifaddrs *cursor;
const struct sockaddr_dl *dlAddr;
const uint8_t *base;
int i;
success = getifaddrs(&addrs) == 0;
if (success) {
cursor = addrs;
while (cursor != NULL) {
fprintf(stderr, "%s\n", cursor->ifa_name);
if ((cursor->ifa_addr->sa_family == AF_LINK)
&& (((const struct sockaddr_dl *) cursor->ifa_addr)->sdl_type == IFT_ETHER)) {
dlAddr = (const struct sockaddr_dl *) cursor->ifa_addr;
fprintf(stderr, " sdl_nlen = %d\n", dlAddr->sdl_nlen);
fprintf(stderr, " sdl_alen = %d\n", dlAddr->sdl_alen);
base = (const uint8_t *) &dlAddr->sdl_data[dlAddr->sdl_nlen];
fprintf(stderr, " ");
for (i = 0; i < dlAddr->sdl_alen; i++) {
if (i != 0) {
fprintf(stderr, ":");
}
fprintf(stderr, "%02x", base[i]);
}
fprintf(stderr, "\n");
}
cursor = cursor->ifa_next;
}
freeifaddrs(addrs);
}
}
The technique is standard BSD sockets: getifaddrs() is documented in the Apple man pages archive and on Linux, so the same code shape works on macOS to this day.
Why the device MAC is dead as an identifier
- From 2011 on, this snippet mostly existed as a workaround while Apple closed UDID access. Vendors hashed the MAC address into a “unique” device ID.
- Since iOS 7 (2013), iOS returns the fixed value
02:00:00:00:00:00to any app that asks for the MAC address, so this snippet stopped being able to see the real one. - Since iOS 8, the device randomizes the MAC it uses for Wi-Fi scan probes, and since iOS 14 a private, per-network MAC is the default for actual connections. On current iOS you choose Off / Fixed / Rotating per network — see Apple’s private Wi-Fi addresses page; Apple’s platform security guide (Wi-Fi privacy) covers the details.
What to use instead
If you need a per-vendor device identifier, Apple’s supported answers are identifierForVendor (stable until all of your apps are uninstalled) and DeviceCheck / App Attest, which let your server store a small per-device flag that survives reinstalls without identifying the user. If you just need the MAC for network diagnostics, it is still printed on the box and visible in Settings → General → About → Wi-Fi Address — the one place it survives un-randomized.