/* * Copyright (c) 2001-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Modification History * * October 3, 2003 Allan Nathanson * - sort new interfaces by IOKit path (rather than MAC address) to * help facilitate a more predictable interface-->name mapping for * like hardware configurations. * * June 23, 2001 Allan Nathanson * - update to public SystemConfiguration.framework APIs * * January 23, 2001 Dieter Siegmund * - initial revision */ /* * ifnamer.c * - module that receives IOKit Network Interface messages * and names any interface that currently does not have a name * - uses Interface Type and MACAddress as the unique identifying * keys; any interface that doesn't contain both of these properties * is ignored and not processed * - stores the Interface Type, MACAddress, and Unit in permanent storage * to give persistent interface names */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for SCLog(), SCPrint() #include #include #include #include #include #include #include #include #include #ifndef kIOBuiltin #define kIOBuiltin "IOBuiltin" #endif #ifndef kIOLocation #define kIOLocation "IOLocation" #endif #define kIONetworkStackUserCommand "IONetworkStackUserCommand" #define kIORegisterOne 1 #define MY_PLUGIN_NAME "InterfaceNamer" static boolean_t S_debug = FALSE; static CFMutableArrayRef S_dblist = NULL; static io_connect_t S_connect = MACH_PORT_NULL; static io_iterator_t S_iter = MACH_PORT_NULL; static IONotificationPortRef S_notify = NULL; static void writeInterfaceList(CFArrayRef ilist); static void displayInterface(CFDictionaryRef if_dict); static CFDictionaryRef lookupIOKitPath(CFStringRef if_path); static __inline__ CFComparisonResult compareMacAddress(CFDataRef addr1, CFDataRef addr2) { int len1; int len2; int clen; int res; len1 = CFDataGetLength(addr1); len2 = CFDataGetLength(addr2); if (len1 == len2) { if (len1 == 0) return (kCFCompareEqualTo); return (memcmp(CFDataGetBytePtr(addr1), CFDataGetBytePtr(addr2), len1)); } clen = len1; if (len2 < clen) clen = len2; res = memcmp(CFDataGetBytePtr(addr1), CFDataGetBytePtr(addr2), clen); if (res == 0) { return (len1 - len2); } return (res); } static CFComparisonResult if_unit_compare(const void *val1, const void *val2, void *context) { CFComparisonResult res; CFNumberRef type1; CFNumberRef type2; CFNumberRef unit1; CFNumberRef unit2; type1 = CFDictionaryGetValue((CFDictionaryRef)val1, CFSTR(kIOInterfaceType)); type2 = CFDictionaryGetValue((CFDictionaryRef)val2, CFSTR(kIOInterfaceType)); res = CFNumberCompare(type1, type2, NULL); if (res != kCFCompareEqualTo) { return (res); } unit1 = CFDictionaryGetValue((CFDictionaryRef)val1, CFSTR(kIOInterfaceUnit)); unit2 = CFDictionaryGetValue((CFDictionaryRef)val2, CFSTR(kIOInterfaceUnit)); return (CFNumberCompare(unit1, unit2, NULL)); } static CFArrayRef split_path(CFStringRef path) { CFArrayRef components; CFMutableStringRef nPath; // turn '@'s into '/'s nPath = CFStringCreateMutableCopy(NULL, 0, path); (void) CFStringFindAndReplace(nPath, CFSTR("@"), CFSTR("/"), CFRangeMake(0, CFStringGetLength(nPath)), 0); // split path into components to be compared components = CFStringCreateArrayBySeparatingStrings(NULL, nPath, CFSTR("/")); CFRelease(nPath); return components; } static CFComparisonResult if_path_compare(const void *val1, const void *val2, void *context) { CFBooleanRef builtin; Boolean builtin_val1 = FALSE; Boolean builtin_val2 = FALSE; CFArrayRef elements1 = NULL; CFArrayRef elements2 = NULL; CFIndex i; CFIndex n; CFIndex n1 = 0; CFIndex n2 = 0; CFStringRef path; CFComparisonResult res; CFNumberRef type1; CFNumberRef type2; /* sort by interface type */ type1 = CFDictionaryGetValue((CFDictionaryRef)val1, CFSTR(kIOInterfaceType)); type2 = CFDictionaryGetValue((CFDictionaryRef)val2, CFSTR(kIOInterfaceType)); res = CFNumberCompare(type1, type2, NULL); if (res != kCFCompareEqualTo) { return (res); } /* built-in interfaces sort first */ builtin = CFDictionaryGetValue((CFDictionaryRef)val1, CFSTR(kIOBuiltin)); if (isA_CFBoolean(builtin) != NULL) { builtin_val1 = CFBooleanGetValue(builtin); } builtin = CFDictionaryGetValue((CFDictionaryRef)val2, CFSTR(kIOBuiltin)); if (isA_CFBoolean(builtin) != NULL) { builtin_val2 = CFBooleanGetValue(builtin); } if (builtin_val1 != builtin_val2) { if (builtin_val1) { res = kCFCompareLessThan; } else { res = kCFCompareGreaterThan; } return (res); } /* ... and then sort built-in interfaces by "location" */ if (builtin_val1) { CFStringRef location1; CFStringRef location2; location1 = CFDictionaryGetValue((CFDictionaryRef)val1, CFSTR(kIOLocation)); location2 = CFDictionaryGetValue((CFDictionaryRef)val2, CFSTR(kIOLocation)); if (location1 != location2) { if (isA_CFString(location1)) { if (isA_CFString(location2)) { res = CFStringCompare(location1, location2, 0); } else { res = kCFCompareLessThan; } } else { res = kCFCompareGreaterThan; } if (res != kCFCompareEqualTo) { return (res); } } } /* ... and then sort by IOPathMatch */ path = CFDictionaryGetValue((CFDictionaryRef)val1, CFSTR(kIOPathMatchKey)); if (isA_CFString(path)) { elements1 = split_path(path); n1 = CFArrayGetCount(elements1); } else { goto done; } path = CFDictionaryGetValue((CFDictionaryRef)val2, CFSTR(kIOPathMatchKey)); if (isA_CFString(path)) { elements2 = split_path(path); n2 = CFArrayGetCount(elements2); } else { goto done; } n = (n1 <= n2) ? n1 : n2; for (i = 0; i < n; i++) { CFStringRef e1; CFStringRef e2; char *end; quad_t q1; quad_t q2; char *str; Boolean isNum; e1 = CFArrayGetValueAtIndex(elements1, i); e2 = CFArrayGetValueAtIndex(elements2, i); str = _SC_cfstring_to_cstring(e1, NULL, 0, kCFStringEncodingASCII); errno = 0; q1 = strtoq(str, &end, 16); isNum = ((*str != '\0') && (*end == '\0') && (errno == 0)); CFAllocatorDeallocate(NULL, str); if (isNum) { // if e1 is a valid numeric string str = _SC_cfstring_to_cstring(e2, NULL, 0, kCFStringEncodingASCII); errno = 0; q2 = strtoq(str, &end, 16); isNum = ((*str != '\0') && (*end == '\0') && (errno == 0)); CFAllocatorDeallocate(NULL, str); if (isNum) { // if e2 is also a valid numeric string if (q1 == q2) { res = kCFCompareEqualTo; continue; } else if (q1 < q2) { res = kCFCompareLessThan; } else { res = kCFCompareGreaterThan; } break; } } res = CFStringCompare(e1, e2, 0); if (res != kCFCompareEqualTo) { break; } } if (res == kCFCompareEqualTo) { if (n1 < n2) { res = kCFCompareLessThan; } else if (n1 < n2) { res = kCFCompareGreaterThan; } } done : if ( elements1 ) CFRelease( elements1 ); if ( elements2 ) CFRelease( elements2 ); return res; } static boolean_t addCFStringProperty( CFMutableDictionaryRef dict, const char * key, const char * string ) { boolean_t ret = false; CFStringRef valObj, keyObj; if ( (string == 0) || (key == 0) || (dict == 0) ) return false; keyObj = CFStringCreateWithCString(NULL, key, kCFStringEncodingASCII ); valObj = CFStringCreateWithCString(NULL, string, kCFStringEncodingASCII ); if (valObj && keyObj) { CFDictionarySetValue( dict, keyObj, valObj ); ret = true; } if ( keyObj ) CFRelease( keyObj ); if ( valObj ) CFRelease( valObj ); return ret; } static boolean_t addCFNumberProperty( CFMutableDictionaryRef dict, const char * key, unsigned int number ) { boolean_t ret = false; CFNumberRef numObj; CFStringRef keyObj; if ( (key == 0) || (dict == 0) ) return false; numObj = CFNumberCreate(NULL, kCFNumberLongType, &number); keyObj = CFStringCreateWithCString(NULL, key, kCFStringEncodingASCII ); if ( numObj && keyObj ) { CFDictionarySetValue( dict, keyObj, numObj ); ret = true; } if ( numObj ) CFRelease( numObj ); if ( keyObj ) CFRelease( keyObj ); return ret; } static void * read_file(char * filename, size_t * data_length) { void * data = NULL; size_t len = 0; int fd = -1; struct stat sb; *data_length = 0; if (stat(filename, &sb) < 0) goto done; len = sb.st_size; if (len == 0) goto done; data = malloc(len); if (data == NULL) goto done; fd = open(filename, O_RDONLY); if (fd < 0) goto done; if (read(fd, data, len) != len) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": read %s failed, %s"), filename, strerror(errno)); goto done; } done: if (fd >= 0) close(fd); if (data) { *data_length = len; } return (data); } static CFPropertyListRef readPropertyList(char * filename) { void * buf; size_t bufsize; CFDataRef data = NULL; CFPropertyListRef plist = NULL; CFStringRef errorString = NULL; buf = read_file(filename, &bufsize); if (buf == NULL) { return (NULL); } data = CFDataCreate(NULL, buf, bufsize); if (data == NULL) { goto error; } plist = CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListMutableContainers, &errorString); if (plist == NULL) { if (errorString) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ":%@"), errorString); CFRelease(errorString); } } error: if (data) CFRelease(data); if (buf) free(buf); return (plist); } #define IFNAMER_ID CFSTR("com.apple.SystemConfiguration.InterfaceNamer") #define INTERFACES CFSTR("Interfaces") #define NETWORK_INTERFACES_PREFS CFSTR("NetworkInterfaces.plist") #define OLD_NETWORK_INTERFACES_FILE "/var/db/NetworkInterfaces.xml" static CFMutableArrayRef readInterfaceList() { CFArrayRef ilist; CFMutableArrayRef plist = NULL; SCPreferencesRef prefs = NULL; prefs = SCPreferencesCreate(NULL, IFNAMER_ID, NETWORK_INTERFACES_PREFS); if (!prefs) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate failed, %s"), SCErrorString(SCError())); return (NULL); } ilist = SCPreferencesGetValue(prefs, INTERFACES); if (isA_CFArray(ilist)) { plist = CFArrayCreateMutableCopy(NULL, 0, ilist); } else { plist = (CFMutableArrayRef)readPropertyList(OLD_NETWORK_INTERFACES_FILE); if (plist == NULL) { goto done; } if (isA_CFArray(plist) == NULL) { CFRelease(plist); goto done; } writeInterfaceList(plist); (void)unlink(OLD_NETWORK_INTERFACES_FILE); } done: if (prefs) { CFRelease(prefs); } return (plist); } static void writeInterfaceList(CFArrayRef ilist) { SCPreferencesRef prefs; if (isA_CFArray(ilist) == NULL) { return; } prefs = SCPreferencesCreate(NULL, IFNAMER_ID, NETWORK_INTERFACES_PREFS); if (prefs == NULL) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate failed, %s"), SCErrorString(SCError())); return; } if (!SCPreferencesSetValue(prefs, INTERFACES, ilist)) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": SCPreferencesSetValue failed, %s"), SCErrorString(SCError())); goto done; } if (!SCPreferencesCommitChanges(prefs)) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": SCPreferencesCommitChanges failed, %s"), SCErrorString(SCError())); goto done; } done: CFRelease(prefs); return; } static void updateBondConfiguration(void) { BondPreferencesRef prefs; prefs = BondPreferencesCreate(NULL); if (prefs == NULL) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": BondPreferencesCreate failed, %s"), SCErrorString(SCError())); return; } if (!_BondPreferencesUpdateConfiguration(prefs)) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": _BondPreferencesUpdateConfiguration failed, %s"), SCErrorString(SCError())); goto done; } done: CFRelease(prefs); return; } static void updateVLANConfiguration(void) { VLANPreferencesRef prefs; prefs = VLANPreferencesCreate(NULL); if (prefs == NULL) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": VLANPreferencesCreate failed, %s"), SCErrorString(SCError())); return; } if (!_VLANPreferencesUpdateConfiguration(prefs)) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": _VLANPreferencesUpdateConfiguration failed, %s"), SCErrorString(SCError())); goto done; } done: CFRelease(prefs); return; } #define INDEX_BAD (-1) static CFDictionaryRef lookupInterfaceByType(CFArrayRef list, CFDictionaryRef if_dict, int * where) { CFDataRef addr; CFIndex i; CFIndex n; CFNumberRef type; if (where) { *where = INDEX_BAD; } if (list == NULL) { return (NULL); } addr = CFDictionaryGetValue(if_dict, CFSTR(kIOMACAddress)); type = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceType)); if (type == NULL || addr == NULL) { return (NULL); } n = CFArrayGetCount(list); for (i = 0; i < n; i++) { CFDictionaryRef dict = CFArrayGetValueAtIndex(list, i); CFDataRef a; CFNumberRef t; a = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress)); t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType)); if (a == NULL || t == NULL) continue; if (CFNumberCompare(type, t, NULL) == kCFCompareEqualTo && compareMacAddress(addr, a) == kCFCompareEqualTo) { if (where) { *where = i; } return (dict); } } return (NULL); } static CFDictionaryRef lookupInterfaceByUnit(CFArrayRef list, CFDictionaryRef if_dict, int * where) { CFIndex i; CFIndex n; CFNumberRef type; CFNumberRef unit; if (where) { *where = INDEX_BAD; } if (list == NULL) { return (NULL); } type = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceType)); unit = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceUnit)); if (type == NULL || unit == NULL) { return (NULL); } n = CFArrayGetCount(list); for (i = 0; i < n; i++) { CFDictionaryRef dict = CFArrayGetValueAtIndex(list, i); CFNumberRef t; CFNumberRef u; t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType)); u = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit)); if (t == NULL || u == NULL) { continue; } if (CFNumberCompare(type, t, NULL) == kCFCompareEqualTo && CFNumberCompare(unit, u, NULL) == kCFCompareEqualTo) { if (where) *where = i; return (dict); } } return (NULL); } #define kAirPortDriverPath CFSTR("AirPort") #define kIO80211InterfacePath CFSTR("IO80211Interface") #define APPLE_WIRELESS_80211 CFSTR("AppleWireless80211") static __inline__ boolean_t pathIsAirPort(CFStringRef path) { CFRange r; r = CFStringFind(path, kIO80211InterfacePath, 0); if (r.location != kCFNotFound) { return (TRUE); } r = CFStringFind(path, kAirPortDriverPath, 0); if (r.location != kCFNotFound) { return (TRUE); } r = CFStringFind(path, APPLE_WIRELESS_80211, 0); if (r.location != kCFNotFound) { return (TRUE); } return (FALSE); } static CFDictionaryRef lookupAirPortInterface(CFArrayRef list, int * where) { CFIndex i; CFIndex n; if (where) { *where = INDEX_BAD; } if (list == NULL) { return (NULL); } n = CFArrayGetCount(list); for (i = 0; i < n; i++) { CFDictionaryRef dict = CFArrayGetValueAtIndex(list, i); CFStringRef path; path = CFDictionaryGetValue(dict, CFSTR(kIOPathMatchKey)); if (path == NULL) { continue; } if (pathIsAirPort(path) == TRUE) { if (where) *where = i; return (dict); } } return (NULL); } static void insertInterface(CFMutableArrayRef list, CFDictionaryRef if_dict) { CFIndex i; CFNumberRef if_type; CFNumberRef if_unit; CFIndex n = CFArrayGetCount(list); CFComparisonResult res; if_type = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceType)); if_unit = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceUnit)); for (i = 0; i < n; i++) { CFDictionaryRef dict = CFArrayGetValueAtIndex(list, i); CFNumberRef type; CFNumberRef unit; type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType)); unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit)); res = CFNumberCompare(if_type, type, NULL); if (res == kCFCompareLessThan || (res == kCFCompareEqualTo && (CFNumberCompare(if_unit, unit, NULL) == kCFCompareLessThan))) { CFArrayInsertValueAtIndex(list, i, if_dict); return; } } CFArrayAppendValue(S_dblist, if_dict); return; } static void replaceInterface(CFDictionaryRef if_dict) { int where; if (S_dblist == NULL) { S_dblist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); } /* remove any dict that has our type/addr */ if (lookupInterfaceByType(S_dblist, if_dict, &where) != NULL) { CFArrayRemoveValueAtIndex(S_dblist, where); } /* remove any dict that has the same type/unit */ if (lookupInterfaceByUnit(S_dblist, if_dict, &where) != NULL) { CFArrayRemoveValueAtIndex(S_dblist, where); } insertInterface(S_dblist, if_dict); return; } static CFNumberRef getHighestUnitForType(CFNumberRef if_type) { int i; CFIndex n; CFNumberRef ret_unit = NULL; if (S_dblist == NULL) return (NULL); n = CFArrayGetCount(S_dblist); for (i = 0; i < n; i++) { CFDictionaryRef dict = CFArrayGetValueAtIndex(S_dblist, i); CFNumberRef type; CFNumberRef unit; type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType)); if (CFEqual(type, if_type)) { unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit)); if (ret_unit == NULL || (CFNumberCompare(unit, ret_unit, NULL) == kCFCompareGreaterThan)) { ret_unit = unit; } } } return (ret_unit); } //------------------------------------------------------------------------ // Register a single interface with the given service path to the // data link layer (BSD), using the specified unit number. static kern_return_t registerInterface(io_connect_t connect, CFStringRef path, CFNumberRef unit) { CFMutableDictionaryRef dict; kern_return_t kr = kIOReturnNoMemory; dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (dict == NULL || addCFNumberProperty(dict, kIONetworkStackUserCommand, kIORegisterOne) == FALSE) ; else { CFDictionarySetValue(dict, CFSTR(kIOPathMatchKey), path); CFDictionarySetValue(dict, CFSTR(kIOInterfaceUnit), unit); kr = IOConnectSetCFProperties(connect, dict); } if (dict) CFRelease( dict ); return kr; } /* * Note: this function blocks all other plug-ins until it completes */ static void waitForQuiet(mach_port_t masterPort) { mach_timespec_t t; kern_return_t wait_ret; t.tv_sec = 4; t.tv_nsec = 0; // kIOReturnTimeout if the wait timed out. // kIOReturnSuccess on success. wait_ret = IOKitWaitQuiet(masterPort, &t); return; } /* * Function: createNetworkStackObject * Purpose: * Get a reference to the single IONetworkStack object instance in * the kernel. Naming requests must be sent to this object, which is * attached as a client to all network interface objects in the system. * Note: * Call IOObjectRelease on the returned object. */ static io_object_t createNetworkStackObject(mach_port_t masterPort) { io_iterator_t iter = MACH_PORT_NULL; kern_return_t kr; io_object_t stack = MACH_PORT_NULL; kr = IOServiceGetMatchingServices(masterPort, IOServiceMatching("IONetworkStack"), &iter); if (iter != MACH_PORT_NULL) { if (kr == KERN_SUCCESS) { stack = IOIteratorNext(iter); } IOObjectRelease(iter); } return stack; } static void printMacAddress(CFDataRef data) { int i; CFIndex n = CFDataGetLength(data); for (i = 0; i < n; i++) { if (i != 0) SCPrint(TRUE, stdout, CFSTR(":")); SCPrint(TRUE, stdout, CFSTR("%02x"), CFDataGetBytePtr(data)[i]); } return; } /* * Function: getMacAddress * * Purpose: * Given an interface object if_obj, return its associated mac address. * The mac address is stored in the parent, the network controller object. * * Returns: * The CFDataRef containing the bytes of the mac address. */ static CFDataRef getMacAddress(io_object_t if_obj) { CFMutableDictionaryRef dict = NULL; CFDataRef data = NULL; kern_return_t kr; io_object_t parent_obj = MACH_PORT_NULL; /* get the parent node */ kr = IORegistryEntryGetParentEntry(if_obj, kIOServicePlane, &parent_obj); if (kr != KERN_SUCCESS) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": IORegistryEntryGetParentEntry returned 0x%x"), kr); goto failed; } /* get the dictionary associated with the node */ kr = IORegistryEntryCreateCFProperties(parent_obj, &dict, NULL, kNilOptions ); if (kr != KERN_SUCCESS) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": IORegistryEntryCreateCFProperties returned 0x%x"), kr); goto failed; } data = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress)); if (data) { CFRetain(data); } failed: if (dict) CFRelease(dict); if (parent_obj) IOObjectRelease(parent_obj); return (data); } static CFDictionaryRef getInterface(io_object_t if_obj) { CFBooleanRef builtin; kern_return_t kr; CFDataRef mac_address = NULL; CFStringRef location; CFMutableDictionaryRef new_if = NULL; io_string_t path; CFMutableDictionaryRef reginfo_if = NULL; CFDictionaryRef ret_dict = NULL; CFStringRef string; CFNumberRef type; CFNumberRef unit; kr = IORegistryEntryGetPath(if_obj, kIOServicePlane, path); if (kr != KERN_SUCCESS) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": IORegistryEntryGetPath returned 0x%x"), kr); goto failed; } kr = IORegistryEntryCreateCFProperties(if_obj, ®info_if, NULL, kNilOptions); if (kr != KERN_SUCCESS) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": IORegistryEntryCreateCFProperties returned 0x%x"), kr); goto failed; } type = isA_CFNumber(CFDictionaryGetValue(reginfo_if, CFSTR(kIOInterfaceType))); if (type == NULL) { goto failed; } mac_address = getMacAddress(if_obj); if (mac_address == NULL) { goto failed; } builtin = isA_CFBoolean(CFDictionaryGetValue(reginfo_if, CFSTR(kIOBuiltin))); if ((builtin == NULL) || !CFBooleanGetValue(builtin)) { builtin = isA_CFBoolean(CFDictionaryGetValue(reginfo_if, CFSTR(kIOPrimaryInterface))); } location = isA_CFString(CFDictionaryGetValue(reginfo_if, CFSTR(kIOLocation))); new_if = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (new_if == NULL) { goto failed; } CFDictionarySetValue(new_if, CFSTR(kIOInterfaceType), type); CFDictionarySetValue(new_if, CFSTR(kIOMACAddress), mac_address); if (builtin) { CFDictionarySetValue(new_if, CFSTR(kIOBuiltin), builtin); } if (location) { CFDictionarySetValue(new_if, CFSTR(kIOLocation), location); } addCFStringProperty(new_if, kIOPathMatchKey, path); unit = isA_CFNumber(CFDictionaryGetValue(reginfo_if, CFSTR(kIOInterfaceUnit))); if (unit) { CFDictionarySetValue(new_if, CFSTR(kIOInterfaceUnit), unit); } string = isA_CFString(CFDictionaryGetValue(reginfo_if, CFSTR(kIOBSDNameKey))); if (string) { CFDictionarySetValue(new_if, CFSTR(kIOBSDNameKey), string); } ret_dict = new_if; new_if = NULL; failed: if (new_if) { CFRelease(new_if); } if (reginfo_if) { CFRelease(reginfo_if); } if (mac_address) { CFRelease(mac_address); } return (ret_dict); } static CFDictionaryRef lookupIOKitPath(CFStringRef if_path) { CFDictionaryRef dict = NULL; io_registry_entry_t entry = MACH_PORT_NULL; kern_return_t kr; mach_port_t masterPort = MACH_PORT_NULL; io_string_t path; kr = IOMasterPort(bootstrap_port, &masterPort); if (kr != KERN_SUCCESS) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x\n"), kr); goto error; } _SC_cfstring_to_cstring(if_path, path, sizeof(path), kCFStringEncodingASCII); entry = IORegistryEntryFromPath(masterPort, path); if (entry == MACH_PORT_NULL) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": IORegistryEntryFromPath(%@) failed"), if_path); goto error; } dict = getInterface(entry); error: if (masterPort != MACH_PORT_NULL) { mach_port_deallocate(mach_task_self(), masterPort); } if (entry != MACH_PORT_NULL) { IOObjectRelease(entry); } return (dict); } static void displayInterface(CFDictionaryRef if_dict) { CFStringRef name; CFNumberRef type; CFNumberRef unit; name = CFDictionaryGetValue(if_dict, CFSTR(kIOBSDNameKey)); if (name) { SCPrint(TRUE, stdout, CFSTR("BSD Name: %@\n"), name); } unit = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceUnit)); if (unit) { SCPrint(TRUE, stdout, CFSTR("Unit: %@\n"), unit); } type = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceType)); SCPrint(TRUE, stdout, CFSTR("Type: %@\n"), type); SCPrint(TRUE, stdout, CFSTR("MAC address: ")); printMacAddress(CFDictionaryGetValue(if_dict, CFSTR(kIOMACAddress))); SCPrint(TRUE, stdout, CFSTR("\n")); } static void sort_interfaces_by_unit(CFMutableArrayRef if_list) { int count = CFArrayGetCount(if_list); CFRange range = CFRangeMake(0, count); if (count < 2) return; CFArraySortValues(if_list, range, if_unit_compare, NULL); return; } static void sort_interfaces_by_path(CFMutableArrayRef if_list) { int count = CFArrayGetCount(if_list); CFRange range = CFRangeMake(0, count); if (count < 2) return; CFArraySortValues(if_list, range, if_path_compare, NULL); return; } static void name_interfaces(CFArrayRef if_list) { CFIndex i; CFIndex n = CFArrayGetCount(if_list); CFIndex i_builtin = 0; CFIndex n_builtin = 0; if (S_debug) SCPrint(TRUE, stdout, CFSTR("\n")); for (i = 0; i < n; i++) { CFBooleanRef builtin; CFDictionaryRef if_dict; if_dict = CFArrayGetValueAtIndex(if_list, i); builtin = CFDictionaryGetValue(if_dict, CFSTR(kIOBuiltin)); if (builtin && CFBooleanGetValue(builtin)) { n_builtin++; // reserve unit number for built-in interface } } for (i = 0; i < n; i++) { CFDictionaryRef if_dict; CFNumberRef type; CFNumberRef unit; if (S_debug) { if (i != 0) SCPrint(TRUE, stdout, CFSTR("\n")); } if_dict = CFArrayGetValueAtIndex(if_list, i); unit = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceUnit)); type = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceType)); if (unit) { if (S_debug) { SCPrint(TRUE, stdout, CFSTR("Interface already has a unit number\n")); displayInterface(if_dict); } replaceInterface(if_dict); } else { CFDictionaryRef dbdict = NULL; kern_return_t kr = KERN_SUCCESS; CFStringRef path; CFNumberRef unit = NULL; path = CFDictionaryGetValue(if_dict, CFSTR(kIOPathMatchKey)); dbdict = lookupInterfaceByType(S_dblist, if_dict, NULL); if (dbdict == NULL && pathIsAirPort(path) == TRUE) { dbdict = lookupAirPortInterface(S_dblist, NULL); } if (dbdict != NULL) { unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit)); CFRetain(unit); } else { int if_type; boolean_t is_builtin = FALSE; int next_unit = 0; CFNumberGetValue(type, kCFNumberIntType, &if_type); if (if_type == IFT_ETHER) { /* ethernet */ CFBooleanRef builtin; builtin = CFDictionaryGetValue(if_dict, CFSTR(kIOBuiltin)); if (builtin && CFBooleanGetValue(builtin)) { is_builtin = TRUE; next_unit = i_builtin++; } else { #if defined(__ppc__) /* skip over slots reserved for built-in ethernet interface(s) */ next_unit = n_builtin; #endif } } if (is_builtin == FALSE) { unit = getHighestUnitForType(type); if (unit) { int high_unit; CFNumberGetValue(unit, kCFNumberIntType, &high_unit); if (high_unit >= next_unit) { next_unit = high_unit + 1; } } } unit = CFNumberCreate(NULL, kCFNumberIntType, &next_unit); } if (S_debug) { SCPrint(TRUE, stdout, CFSTR("Interface assigned unit %@ %s\n"), unit, dbdict ? "(from database)" : "(next available)"); } kr = registerInterface(S_connect, path, unit); if (kr != KERN_SUCCESS) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": failed to name the interface 0x%x"), kr); if (S_debug) { displayInterface(if_dict); } } else { CFDictionaryRef new_dict; path = CFDictionaryGetValue(if_dict, CFSTR(kIOPathMatchKey)); new_dict = lookupIOKitPath(path); if (new_dict != NULL) { CFNumberRef new_unit; new_unit = CFDictionaryGetValue(new_dict, CFSTR(kIOInterfaceUnit)); if (CFEqual(unit, new_unit) == FALSE) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": interface type %@ assigned " "unit %@ instead of %@"), type, new_unit, unit); } if (S_debug) { displayInterface(new_dict); } replaceInterface(new_dict); CFRelease(new_dict); } } CFRelease(unit); } } writeInterfaceList(S_dblist); return; } static void interfaceArrivalCallback( void * refcon, io_iterator_t iter ) { CFMutableArrayRef if_list = NULL; io_object_t obj; while ((obj = IOIteratorNext(iter))) { CFDictionaryRef dict; dict = getInterface(obj); if (dict) { if (if_list == NULL) { if_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); } if (if_list) CFArrayAppendValue(if_list, dict); CFRelease(dict); } IOObjectRelease(obj); } if (if_list) { sort_interfaces_by_path(if_list); name_interfaces(if_list); updateBondConfiguration(); updateVLANConfiguration(); CFRelease(if_list); } return; } __private_extern__ void load_InterfaceNamer(CFBundleRef bundle, Boolean bundleVerbose) { kern_return_t kr; mach_port_t masterPort = MACH_PORT_NULL; io_object_t stack = MACH_PORT_NULL; if (bundleVerbose) { S_debug++; } kr = IOMasterPort(bootstrap_port, &masterPort); if (kr != KERN_SUCCESS) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"), kr); goto error; } /* synchronize with any drivers that might be loading at boot time */ waitForQuiet(masterPort); stack = createNetworkStackObject(masterPort); if (stack == MACH_PORT_NULL) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": No network stack object")); goto error; } kr = IOServiceOpen(stack, mach_task_self(), 0, &S_connect); if (kr != KERN_SUCCESS) { SCPrint(TRUE, stdout, CFSTR(MY_PLUGIN_NAME ": IOServiceOpen returned 0x%x\n"), kr); goto error; } // Creates and returns a notification object for receiving IOKit // notifications of new devices or state changes. S_notify = IONotificationPortCreate(masterPort); if (S_notify == NULL) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": IONotificationPortCreate failed")); goto error; } kr = IOServiceAddMatchingNotification(S_notify, kIOFirstMatchNotification, IOServiceMatching("IONetworkInterface"), &interfaceArrivalCallback, (void *) S_notify, /* refCon */ &S_iter ); /* notification */ if (kr != KERN_SUCCESS) { SCLog(TRUE, LOG_INFO, CFSTR(MY_PLUGIN_NAME ": IOServiceAddMatchingNotification returned 0x%x"), kr); goto error; } S_dblist = readInterfaceList(); if (S_dblist) { sort_interfaces_by_unit(S_dblist); } // Get the current list of matches and arms the notification for // future interface arrivals. interfaceArrivalCallback((void *) S_notify, S_iter); CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(S_notify), kCFRunLoopDefaultMode); if (stack != MACH_PORT_NULL) { IOObjectRelease(stack); } if (masterPort != MACH_PORT_NULL) { mach_port_deallocate(mach_task_self(), masterPort); } return; error: if (stack != MACH_PORT_NULL) { IOObjectRelease(stack); } if (masterPort != MACH_PORT_NULL) { mach_port_deallocate(mach_task_self(), masterPort); } if (S_connect != MACH_PORT_NULL) { IOServiceClose(S_connect); S_connect = MACH_PORT_NULL; } if (S_iter != MACH_PORT_NULL) { IOObjectRelease(S_iter); S_iter = MACH_PORT_NULL; } if (S_notify != MACH_PORT_NULL) { IONotificationPortDestroy(S_notify); } return; } //------------------------------------------------------------------------ // Main function. #ifdef MAIN int main(int argc, char ** argv) { load_InterfaceNamer(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE); CFRunLoopRun(); /* not reached */ exit(0); return 0; } #endif /* MAIN */