GetPrimaryMACAddress/GetPrimaryMACAddress.c
/*  | 
File: GetPrimaryMACAddress.c  | 
Abstract: Command-line tool demonstrating how to do retrieve the Ethernet MAC  | 
address of the built-in Ethernet interface from the I/O Registry on Mac OS X.  | 
Techniques shown include finding the primary (built-in) Ethernet interface,  | 
finding the parent Ethernet controller, and retrieving properties from the  | 
controller's I/O Registry entry.  | 
Version: 1.3  | 
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple  | 
 Inc. ("Apple") in consideration of your agreement to the following | 
terms, and your use, installation, modification or redistribution of  | 
this Apple software constitutes acceptance of these terms. If you do  | 
not agree with these terms, please do not use, install, modify or  | 
redistribute this Apple software.  | 
In consideration of your agreement to abide by the following terms, and  | 
subject to these terms, Apple grants you a personal, non-exclusive  | 
license, under Apple's copyrights in this original Apple software (the  | 
"Apple Software"), to use, reproduce, modify and redistribute the Apple  | 
Software, with or without modifications, in source and/or binary forms;  | 
provided that if you redistribute the Apple Software in its entirety and  | 
without modifications, you must retain this notice and the following  | 
text and disclaimers in all such redistributions of the Apple Software.  | 
Neither the name, trademarks, service marks or logos of Apple Inc. may  | 
be used to endorse or promote products derived from the Apple Software  | 
without specific prior written permission from Apple. Except as  | 
expressly stated in this notice, no other rights or licenses, express or  | 
implied, are granted by Apple herein, including but not limited to any  | 
patent rights that may be infringed by your derivative works or by other  | 
works in which the Apple Software may be incorporated.  | 
The Apple Software is provided by Apple on an "AS IS" basis. APPLE  | 
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION  | 
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS  | 
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND  | 
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.  | 
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL  | 
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  | 
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  | 
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,  | 
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED  | 
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),  | 
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE  | 
POSSIBILITY OF SUCH DAMAGE.  | 
Copyright (C) 2011 Apple Inc. All Rights Reserved.  | 
*/  | 
#include <stdio.h>  | 
#include <CoreFoundation/CoreFoundation.h>  | 
#include <IOKit/IOKitLib.h>  | 
#include <IOKit/network/IOEthernetInterface.h>  | 
#include <IOKit/network/IONetworkInterface.h>  | 
#include <IOKit/network/IOEthernetController.h>  | 
static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices);  | 
static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize);  | 
// Returns an iterator containing the primary (built-in) Ethernet interface. The caller is responsible for  | 
// releasing the iterator after the caller is done with it.  | 
static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices)  | 
{ | 
kern_return_t kernResult;  | 
CFMutableDictionaryRef matchingDict;  | 
CFMutableDictionaryRef propertyMatchDict;  | 
// Ethernet interfaces are instances of class kIOEthernetInterfaceClass.  | 
// IOServiceMatching is a convenience function to create a dictionary with the key kIOProviderClassKey and  | 
// the specified value.  | 
matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);  | 
// Note that another option here would be:  | 
    // matchingDict = IOBSDMatching("en0"); | 
// but en0: isn't necessarily the primary interface, especially on systems with multiple Ethernet ports.  | 
    if (NULL == matchingDict) { | 
        printf("IOServiceMatching returned a NULL dictionary.\n"); | 
}  | 
    else { | 
// Each IONetworkInterface object has a Boolean property with the key kIOPrimaryInterface. Only the  | 
// primary (built-in) interface has this property set to TRUE.  | 
// IOServiceGetMatchingServices uses the default matching criteria defined by IOService. This considers  | 
// only the following properties plus any family-specific matching in this order of precedence  | 
// (see IOService::passiveMatch):  | 
//  | 
// kIOProviderClassKey (IOServiceMatching)  | 
// kIONameMatchKey (IOServiceNameMatching)  | 
// kIOPropertyMatchKey  | 
// kIOPathMatchKey  | 
// kIOMatchedServiceCountKey  | 
// family-specific matching  | 
// kIOBSDNameKey (IOBSDNameMatching)  | 
// kIOLocationMatchKey  | 
// The IONetworkingFamily does not define any family-specific matching. This means that in  | 
// order to have IOServiceGetMatchingServices consider the kIOPrimaryInterface property, we must  | 
// add that property to a separate dictionary and then add that to our matching dictionary  | 
// specifying kIOPropertyMatchKey.  | 
propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,  | 
&kCFTypeDictionaryKeyCallBacks,  | 
&kCFTypeDictionaryValueCallBacks);  | 
        if (NULL == propertyMatchDict) { | 
            printf("CFDictionaryCreateMutable returned a NULL dictionary.\n"); | 
}  | 
        else { | 
// Set the value in the dictionary of the property with the given key, or add the key  | 
// to the dictionary if it doesn't exist. This call retains the value object passed in.  | 
CFDictionarySetValue(propertyMatchDict, CFSTR(kIOPrimaryInterface), kCFBooleanTrue);  | 
// Now add the dictionary containing the matching value for kIOPrimaryInterface to our main  | 
// matching dictionary. This call will retain propertyMatchDict, so we can release our reference  | 
// on propertyMatchDict after adding it to matchingDict.  | 
CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);  | 
CFRelease(propertyMatchDict);  | 
}  | 
}  | 
// IOServiceGetMatchingServices retains the returned iterator, so release the iterator when we're done with it.  | 
// IOServiceGetMatchingServices also consumes a reference on the matching dictionary so we don't need to release  | 
// the dictionary explicitly.  | 
kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, matchingServices);  | 
    if (KERN_SUCCESS != kernResult) { | 
        printf("IOServiceGetMatchingServices returned 0x%08x\n", kernResult); | 
}  | 
return kernResult;  | 
}  | 
// Given an iterator across a set of Ethernet interfaces, return the MAC address of the last one.  | 
// If no interfaces are found the MAC address is set to an empty string.  | 
// In this sample the iterator should contain just the primary interface.  | 
static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize)  | 
{ | 
io_object_t intfService;  | 
io_object_t controllerService;  | 
kern_return_t kernResult = KERN_FAILURE;  | 
// Make sure the caller provided enough buffer space. Protect against buffer overflow problems.  | 
    if (bufferSize < kIOEthernetAddressSize) { | 
return kernResult;  | 
}  | 
// Initialize the returned address  | 
bzero(MACAddress, bufferSize);  | 
// IOIteratorNext retains the returned object, so release it when we're done with it.  | 
while ((intfService = IOIteratorNext(intfIterator)))  | 
    { | 
CFTypeRef MACAddressAsCFData;  | 
// IONetworkControllers can't be found directly by the IOServiceGetMatchingServices call,  | 
// since they are hardware nubs and do not participate in driver matching. In other words,  | 
// registerService() is never called on them. So we've found the IONetworkInterface and will  | 
// get its parent controller by asking for it specifically.  | 
// IORegistryEntryGetParentEntry retains the returned object, so release it when we're done with it.  | 
kernResult = IORegistryEntryGetParentEntry(intfService,  | 
kIOServicePlane,  | 
&controllerService);  | 
        if (KERN_SUCCESS != kernResult) { | 
            printf("IORegistryEntryGetParentEntry returned 0x%08x\n", kernResult); | 
}  | 
        else { | 
// Retrieve the MAC address property from the I/O Registry in the form of a CFData  | 
MACAddressAsCFData = IORegistryEntryCreateCFProperty(controllerService,  | 
CFSTR(kIOMACAddress),  | 
kCFAllocatorDefault,  | 
0);  | 
            if (MACAddressAsCFData) { | 
CFShow(MACAddressAsCFData); // for display purposes only; output goes to stderr  | 
// Get the raw bytes of the MAC address from the CFData  | 
CFDataGetBytes(MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress);  | 
CFRelease(MACAddressAsCFData);  | 
}  | 
// Done with the parent Ethernet controller object so we release it.  | 
(void) IOObjectRelease(controllerService);  | 
}  | 
// Done with the Ethernet interface object so we release it.  | 
(void) IOObjectRelease(intfService);  | 
}  | 
return kernResult;  | 
}  | 
int main(int argc, char *argv[])  | 
{ | 
kern_return_t kernResult = KERN_SUCCESS;  | 
io_iterator_t intfIterator;  | 
UInt8 MACAddress[kIOEthernetAddressSize];  | 
kernResult = FindEthernetInterfaces(&intfIterator);  | 
    if (KERN_SUCCESS != kernResult) { | 
        printf("FindEthernetInterfaces returned 0x%08x\n", kernResult); | 
}  | 
    else { | 
kernResult = GetMACAddress(intfIterator, MACAddress, sizeof(MACAddress));  | 
        if (KERN_SUCCESS != kernResult) { | 
            printf("GetMACAddress returned 0x%08x\n", kernResult); | 
}  | 
        else { | 
            printf("This system's built-in MAC address is %02x:%02x:%02x:%02x:%02x:%02x.\n", | 
MACAddress[0], MACAddress[1], MACAddress[2], MACAddress[3], MACAddress[4], MACAddress[5]);  | 
}  | 
}  | 
(void) IOObjectRelease(intfIterator); // Release the iterator.  | 
return kernResult;  | 
}  | 
Copyright © 2011 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2011-05-06