#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <mach/mach.h>
#include <net/if_types.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCDPlugin.h>
#include <SystemConfiguration/SCPrivate.h> // for SCLog(), SCPrint()
#include <SystemConfiguration/SCValidation.h>
#define KERNEL_PRIVATE
#include <sys/sockio.h>
#undef KERNEL_PRIVATE
#ifdef SIOCGETVLAN
#include <SystemConfiguration/VLANConfiguration.h>
#include <SystemConfiguration/VLANConfigurationPrivate.h>
#endif
#include <IOKit/IOKitLib.h>
#include <IOKit/IOBSD.h>
#include <IOKit/network/IONetworkController.h>
#include <IOKit/network/IONetworkInterface.h>
#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;
nPath = CFStringCreateMutableCopy(NULL, 0, path);
(void) CFStringFindAndReplace(nPath,
CFSTR("@"),
CFSTR("/"),
CFRangeMake(0, CFStringGetLength(nPath)),
0);
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;
type1 = CFDictionaryGetValue((CFDictionaryRef)val1, CFSTR(kIOInterfaceType));
type2 = CFDictionaryGetValue((CFDictionaryRef)val2, CFSTR(kIOInterfaceType));
res = CFNumberCompare(type1, type2, NULL);
if (res != kCFCompareEqualTo) {
return (res);
}
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);
}
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);
}
}
}
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) {
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 (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;
}
#ifdef SIOCGETVLAN
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;
}
#endif
#define INDEX_BAD (-1)
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);
}
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")
static __inline__ boolean_t
pathIsAirPort(CFStringRef path)
{
CFRange r;
r = CFStringFind(path, kAirPortDriverPath, 0);
if (r.location == kCFNotFound) {
return (FALSE);
}
return (TRUE);
}
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);
}
if (lookupInterfaceByType(S_dblist, if_dict, &where) != NULL) {
CFArrayRemoveValueAtIndex(S_dblist, where);
}
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);
}
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;
}
static void
waitForQuiet(mach_port_t masterPort)
{
mach_timespec_t t;
kern_return_t wait_ret;
t.tv_sec = 4;
t.tv_nsec = 0;
wait_ret = IOKitWaitQuiet(masterPort, &t);
return;
}
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;
}
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;
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;
}
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++; }
}
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) {
CFBooleanRef builtin;
builtin = CFDictionaryGetValue(if_dict,
CFSTR(kIOBuiltin));
if (builtin && CFBooleanGetValue(builtin)) {
is_builtin = TRUE;
next_unit = i_builtin++;
}
else {
#if defined(__ppc__)
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);
#ifdef SIOCGETVLAN
updateVLANConfiguration();
#endif
CFRelease(if_list);
}
return;
}
void
load(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;
}
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;
}
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,
&S_iter );
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);
}
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;
}
#ifdef MAIN
int
main(int argc, char ** argv)
{
load(CFBundleGetMainBundle(),
(argc > 1) ? TRUE : FALSE);
CFRunLoopRun();
exit(0);
return 0;
}
#endif