#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/param.h>
#include <mach/mach.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_types.h>
#include <pthread.h>
#include <vproc.h>
#include <CommonCrypto/CommonDigest.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCDPlugin.h>
#include <SystemConfiguration/SCPrivate.h> // for SCLog(), SCPrint()
#include <SystemConfiguration/SCValidation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOKitLibPrivate.h>
#include <IOKit/IOBSD.h>
#include <IOKit/IOMessage.h>
#include <IOKit/network/IONetworkController.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IONetworkStack.h>
#include <IOKit/usb/USB.h>
#ifdef kIONetworkStackUserCommandKey
#define USE_REGISTRY_ENTRY_ID
#endif
#ifndef USE_REGISTRY_ENTRY_ID
#define kIONetworkStackUserCommandKey "IONetworkStackUserCommand"
enum {
kRegisterInterfaceWithFixedUnit = 0,
kRegisterInterface,
kRegisterAllInterfaces
};
#endif // !USE_REGISTRY_ENTRY_ID
#define kSCNetworkInterfaceInfo "SCNetworkInterfaceInfo"
#define kSCNetworkInterfaceType "SCNetworkInterfaceType"
#define kSCNetworkInterfaceActive "Active"
#define MY_PLUGIN_NAME "InterfaceNamer"
#define MY_PLUGIN_ID CFSTR("com.apple.SystemConfiguration." MY_PLUGIN_NAME)
#define WAIT_STACK_TIMEOUT_KEY "WaitStackTimeout"
#define WAIT_STACK_TIMEOUT_DEFAULT 300.0
#define WAIT_QUIET_TIMEOUT_KEY "WaitQuietTimeout"
#define WAIT_QUIET_TIMEOUT_DEFAULT 60.0
static io_connect_t S_connect = MACH_PORT_NULL;
static CFMutableArrayRef S_dblist = NULL;
static boolean_t S_debug = FALSE;
static CFMutableArrayRef S_iflist = NULL;
static io_iterator_t S_iter = MACH_PORT_NULL;
static IONotificationPortRef S_notify = NULL;
static CFMutableArrayRef S_prev_active_list = NULL;
static io_object_t S_quiet = MACH_PORT_NULL;
static io_iterator_t S_stack = MACH_PORT_NULL;
static CFMutableDictionaryRef S_state = NULL;
static CFRunLoopTimerRef S_timer = NULL;
static double S_stack_timeout = WAIT_STACK_TIMEOUT_DEFAULT;
static double S_quiet_timeout = WAIT_QUIET_TIMEOUT_DEFAULT;
#if !TARGET_OS_EMBEDDED
vproc_transaction_t S_vproc_transaction = NULL;
#endif // !TARGET_OS_EMBEDDED
static SCPreferencesRef S_prefs = NULL;
static CFArrayRef S_bonds = NULL;
static CFArrayRef S_bridges = NULL;
static CFArrayRef S_vlans = NULL;
static void
addTimestamp(CFMutableDictionaryRef dict, CFStringRef key)
{
CFAbsoluteTime now;
CFNumberRef val;
now = CFAbsoluteTimeGetCurrent();
val = CFNumberCreate(NULL, kCFNumberDoubleType, &now);
CFDictionaryAddValue(dict, key, val);
CFRelease(val);
return;
}
#define INTERFACES CFSTR("Interfaces")
#define NETWORK_INTERFACES_PREFS CFSTR("NetworkInterfaces.plist")
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 void
reportIssue(const char *signature, CFStringRef issue)
{
aslmsg m;
m = asl_new(ASL_TYPE_MSG);
asl_set(m, "com.apple.message.domain", "com.apple.SystemConfiguration." MY_PLUGIN_NAME);
asl_set(m, "com.apple.message.signature", signature);
asl_set(m, "com.apple.message.result", "failure");
SCLOG(NULL, m, ~ASL_LEVEL_ERR, CFSTR("%s\n%@"), signature, issue);
asl_free(m);
return;
}
static void
writeInterfaceList(CFArrayRef if_list)
{
CFArrayRef cur_list;
CFStringRef new_model;
CFStringRef old_model;
SCPreferencesRef prefs;
if (isA_CFArray(if_list) == NULL) {
return;
}
prefs = SCPreferencesCreate(NULL, MY_PLUGIN_ID, NETWORK_INTERFACES_PREFS);
if (prefs == NULL) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate failed, %s"),
SCErrorString(SCError()));
return;
}
cur_list = SCPreferencesGetValue(prefs, INTERFACES);
if (_SC_CFEqual(cur_list, if_list)) {
goto done;
}
old_model = SCPreferencesGetValue(prefs, MODEL);
new_model = _SC_hw_model();
if ((new_model != NULL) && !_SC_CFEqual(old_model, new_model)) {
if ((old_model != NULL) && (cur_list != NULL)) {
CFStringRef history;
CFStringRef issue;
history = CFStringCreateWithFormat(NULL, NULL,
CFSTR("%@:%@"),
INTERFACES,
old_model);
SCPreferencesSetValue(prefs, history, cur_list);
CFRelease(history);
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": Hardware model changed\n"
MY_PLUGIN_NAME ": created on \"%@\"\n"
MY_PLUGIN_NAME ": now on \"%@\""),
old_model,
new_model);
issue = CFStringCreateWithFormat(NULL, NULL,
CFSTR("%@ --> %@"),
old_model,
new_model);
reportIssue("Hardware model changed", issue);
CFRelease(issue);
}
if (!SCPreferencesSetValue(prefs, MODEL, new_model)) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": SCPreferencesSetValue failed, %s"),
SCErrorString(SCError()));
goto done;
}
}
if (!SCPreferencesSetValue(prefs, INTERFACES, if_list)) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": SCPreferencesSetValue failed, %s"),
SCErrorString(SCError()));
goto done;
}
if (!SCPreferencesCommitChanges(prefs)) {
SCLog((SCError() != EROFS), LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": SCPreferencesCommitChanges failed, %s"),
SCErrorString(SCError()));
goto done;
}
done:
CFRelease(prefs);
return;
}
static CF_RETURNS_RETAINED CFMutableArrayRef
readInterfaceList()
{
CFArrayRef if_list;
CFStringRef old_model;
CFMutableArrayRef plist = NULL;
SCPreferencesRef prefs = NULL;
prefs = SCPreferencesCreate(NULL, MY_PLUGIN_ID, NETWORK_INTERFACES_PREFS);
if (!prefs) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate failed, %s"),
SCErrorString(SCError()));
return (NULL);
}
if_list = SCPreferencesGetValue(prefs, INTERFACES);
if_list = isA_CFArray(if_list);
old_model = SCPreferencesGetValue(prefs, MODEL);
if (old_model != NULL) {
CFStringRef new_model;
new_model = _SC_hw_model();
if (!_SC_CFEqual(old_model, new_model)) {
if_list = NULL;
}
}
if (if_list != NULL) {
CFIndex i;
CFIndex n = CFArrayGetCount(if_list);
plist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < n; i++) {
CFDictionaryRef dict;
dict = CFArrayGetValueAtIndex(if_list, i);
if (isA_CFDictionary(dict) &&
CFDictionaryContainsKey(dict, CFSTR(kIOInterfaceType)) &&
CFDictionaryContainsKey(dict, CFSTR(kIOInterfaceUnit)) &&
CFDictionaryContainsKey(dict, CFSTR(kIOMACAddress))) {
CFArrayAppendValue(plist, dict);
}
}
}
if (prefs != NULL) {
CFRelease(prefs);
}
return (plist);
}
static CF_RETURNS_RETAINED CFMutableArrayRef
previouslyActiveInterfaces()
{
CFMutableArrayRef active;
CFIndex i;
CFIndex n;
if (S_dblist == NULL) {
return NULL;
}
active = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
n = CFArrayGetCount(S_dblist);
for (i = 0; i < n; i++) {
CFDictionaryRef if_dict;
if_dict = CFArrayGetValueAtIndex(S_dblist, i);
if (CFDictionaryContainsKey(if_dict, CFSTR(kSCNetworkInterfaceActive))) {
CFMutableDictionaryRef new_dict;
new_dict = CFDictionaryCreateMutableCopy(NULL, 0, if_dict);
CFDictionaryRemoveValue(new_dict, CFSTR(kSCNetworkInterfaceActive));
CFArraySetValueAtIndex(S_dblist, i, new_dict);
CFArrayAppendValue(active, new_dict);
CFRelease(new_dict);
}
}
return active;
}
static void
updateStore(void)
{
CFStringRef key;
key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@" MY_PLUGIN_NAME),
kSCDynamicStoreDomainPlugin);
(void)SCDynamicStoreSetValue(NULL, key, S_state);
CFRelease(key);
return;
}
#if !TARGET_OS_IPHONE
static void
updateBondInterfaceConfiguration(SCPreferencesRef prefs)
{
CFArrayRef interfaces;
interfaces = SCBondInterfaceCopyAll(prefs);
if ((interfaces != NULL) && (CFArrayGetCount(interfaces) == 0)) {
CFRelease(interfaces);
interfaces = NULL;
}
if (_SC_CFEqual(S_bonds, interfaces)) {
if (interfaces != NULL) CFRelease(interfaces);
return;
}
if (S_bonds != NULL) CFRelease(S_bonds);
S_bonds = interfaces;
if (!_SCBondInterfaceUpdateConfiguration(prefs)) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": _SCBondInterfaceUpdateConfiguration failed, %s"),
SCErrorString(SCError()));
}
return;
}
#endif // !TARGET_OS_IPHONE
static void
updateBridgeInterfaceConfiguration(SCPreferencesRef prefs)
{
CFArrayRef interfaces;
interfaces = SCBridgeInterfaceCopyAll(prefs);
if ((interfaces != NULL) && (CFArrayGetCount(interfaces) == 0)) {
CFRelease(interfaces);
interfaces = NULL;
}
if (_SC_CFEqual(S_bridges, interfaces)) {
if (interfaces != NULL) CFRelease(interfaces);
return;
}
if (S_bridges != NULL) CFRelease(S_bridges);
S_bridges = interfaces;
if (!_SCBridgeInterfaceUpdateConfiguration(prefs)) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": _SCBridgeInterfaceUpdateConfiguration failed, %s"),
SCErrorString(SCError()));
}
return;
}
static void
updateVLANInterfaceConfiguration(SCPreferencesRef prefs)
{
CFArrayRef interfaces;
interfaces = SCVLANInterfaceCopyAll(prefs);
if ((interfaces != NULL) && (CFArrayGetCount(interfaces) == 0)) {
CFRelease(interfaces);
interfaces = NULL;
}
if (_SC_CFEqual(S_vlans, interfaces)) {
if (interfaces != NULL) CFRelease(interfaces);
return;
}
if (S_vlans != NULL) CFRelease(S_vlans);
S_vlans = interfaces;
if (!_SCVLANInterfaceUpdateConfiguration(prefs)) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": _SCVLANInterfaceUpdateConfiguration failed, %s"),
SCErrorString(SCError()));
}
return;
}
static void
updateVirtualNetworkInterfaceConfiguration(SCPreferencesRef prefs,
SCPreferencesNotification notificationType,
void *info)
{
if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) {
return;
}
if (prefs == NULL) {
prefs = S_prefs;
if (S_bonds != NULL) {
CFRelease(S_bonds);
S_bonds = NULL;
}
if (S_bridges != NULL) {
CFRelease(S_bridges);
S_bridges = NULL;
}
if (S_vlans != NULL) {
CFRelease(S_vlans);
S_vlans = NULL;
}
}
#if !TARGET_OS_IPHONE
updateBondInterfaceConfiguration (prefs);
#endif // !TARGET_OS_IPHONE
updateBridgeInterfaceConfiguration(prefs);
updateVLANInterfaceConfiguration (prefs);
SCPreferencesSynchronize(prefs);
return;
}
static CFDictionaryRef
createInterfaceDict(SCNetworkInterfaceRef interface)
{
CFMutableDictionaryRef new_if;
CFTypeRef val;
new_if = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
val = _SCNetworkInterfaceCopyInterfaceInfo(interface);
if (val != NULL) {
CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceInfo), val);
CFRelease(val);
}
val = _SCNetworkInterfaceGetIOPath(interface);
if (val != NULL) {
CFDictionarySetValue(new_if, CFSTR(kIOPathMatchKey), val);
}
val = _SCNetworkInterfaceGetIOInterfaceType(interface);
if (val != NULL) {
CFDictionarySetValue(new_if, CFSTR(kIOInterfaceType), val);
}
val = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
if (val != NULL) {
CFDictionarySetValue(new_if, CFSTR(kIOInterfaceUnit), val);
}
val = _SCNetworkInterfaceGetHardwareAddress(interface);
if (val != NULL) {
CFDictionarySetValue(new_if, CFSTR(kIOMACAddress), val);
}
val = SCNetworkInterfaceGetBSDName(interface);
if (val != NULL) {
CFDictionarySetValue(new_if, CFSTR(kIOBSDNameKey), val);
}
val = SCNetworkInterfaceGetInterfaceType(interface);
if (val != NULL) {
CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceType), val);
}
CFDictionarySetValue(new_if,
CFSTR(kIOBuiltin),
_SCNetworkInterfaceIsBuiltin(interface) ? kCFBooleanTrue : kCFBooleanFalse);
CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceActive), kCFBooleanTrue);
return new_if;
}
static CFDictionaryRef
lookupInterfaceByAddress(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where)
{
CFDataRef addr;
CFIndex i;
CFIndex n;
CFNumberRef type;
if (db_list == NULL) {
return (NULL);
}
type = _SCNetworkInterfaceGetIOInterfaceType(interface);
addr = _SCNetworkInterfaceGetHardwareAddress(interface);
if (type == NULL || addr == NULL) {
return (NULL);
}
n = CFArrayGetCount(db_list);
for (i = 0; i < n; i++) {
CFDataRef a;
CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i);
CFNumberRef t;
t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
a = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
if (t == NULL || a == NULL)
continue;
if (CFEqual(type, t) && CFEqual(addr, a)) {
if (where) {
*where = i;
}
return (dict);
}
}
return (NULL);
}
static CFDictionaryRef
lookupInterfaceByUnit(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where)
{
CFIndex i;
CFIndex n;
CFNumberRef type;
CFNumberRef unit;
if (db_list == NULL) {
return (NULL);
}
type = _SCNetworkInterfaceGetIOInterfaceType(interface);
unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
if (type == NULL || unit == NULL) {
return (NULL);
}
n = CFArrayGetCount(db_list);
for (i = 0; i < n; i++) {
CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i);
CFNumberRef t;
CFNumberRef u;
t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
u = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
if (t == NULL || u == NULL) {
continue;
}
if (CFEqual(type, t) && CFEqual(unit, u)) {
if (where)
*where = i;
return (dict);
}
}
return (NULL);
}
typedef struct {
CFDictionaryRef match_info;
CFStringRef match_type;
CFBooleanRef match_builtin;
CFMutableArrayRef matches;
} matchContext, *matchContextRef;
static CF_RETURNS_RETAINED CFDictionaryRef
thinInterfaceInfo(CFDictionaryRef info)
{
CFNumberRef num;
int vid;
if (CFDictionaryGetValueIfPresent(info, CFSTR(kUSBVendorID), (const void **)&num)
&& isA_CFNumber(num)
&& CFNumberGetValue(num, kCFNumberIntType, &vid)
&& (vid == kIOUSBVendorIDAppleComputer)) {
CFMutableDictionaryRef thin;
thin = CFDictionaryCreateMutableCopy(NULL, 0, info);
CFDictionaryRemoveValue(thin, CFSTR(kUSBProductString));
CFDictionaryRemoveValue(thin, CFSTR(kUSBVendorID));
CFDictionaryRemoveValue(thin, CFSTR(kUSBProductID));
return thin;
}
return CFRetain(info);
}
static Boolean
matchInterfaceInfo(CFDictionaryRef known_info, CFDictionaryRef match_info)
{
Boolean match;
match = _SC_CFEqual(known_info, match_info);
if (!match &&
isA_CFDictionary(known_info) &&
isA_CFDictionary(match_info)) {
known_info = thinInterfaceInfo(known_info);
match_info = thinInterfaceInfo(match_info);
match = _SC_CFEqual(known_info, match_info);
CFRelease(known_info);
CFRelease(match_info);
}
return match;
}
static void
matchKnown(const void *value, void *context)
{
CFDictionaryRef known_dict = (CFDictionaryRef)value;
matchContextRef match_context = (matchContextRef)context;
{
CFStringRef known_type;
known_type = CFDictionaryGetValue(known_dict, CFSTR(kSCNetworkInterfaceType));
if (!_SC_CFEqual(known_type, match_context->match_type)) {
return;
}
}
{
CFDictionaryRef known_info;
known_info = CFDictionaryGetValue(known_dict, CFSTR(kSCNetworkInterfaceInfo));
if (!matchInterfaceInfo(known_info, match_context->match_info)) {
return;
}
}
if (match_context->match_builtin != NULL) {
CFBooleanRef known_builtin;
known_builtin = CFDictionaryGetValue(known_dict, CFSTR(kIOBuiltin));
if (!isA_CFBoolean(known_builtin)) {
known_builtin = kCFBooleanFalse;
}
if (!_SC_CFEqual(known_builtin, match_context->match_builtin)) {
return;
}
}
if (match_context->matches == NULL) {
match_context->matches = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(match_context->matches, known_dict);
return;
}
static void
matchUnnamed(const void *value, void *context)
{
SCNetworkInterfaceRef known_if = (SCNetworkInterfaceRef)value;
matchContextRef match_context = (matchContextRef)context;
if (match_context->matches == NULL) {
return;
}
{
CFStringRef known_type;
known_type = SCNetworkInterfaceGetInterfaceType(known_if);
if (!_SC_CFEqual(known_type, match_context->match_type)) {
return;
}
}
{
CFDictionaryRef known_info;
Boolean match;
known_info = _SCNetworkInterfaceCopyInterfaceInfo(known_if);
match = matchInterfaceInfo(known_info, match_context->match_info);
if (known_info != NULL) CFRelease(known_info);
if (!match) {
return;
}
}
if (match_context->match_builtin != NULL) {
CFBooleanRef known_builtin;
known_builtin = _SCNetworkInterfaceIsBuiltin(known_if) ? kCFBooleanTrue
: kCFBooleanFalse;
if (!_SC_CFEqual(known_builtin, match_context->match_builtin)) {
return;
}
}
CFRelease(match_context->matches);
match_context->matches = NULL;
return;
}
static CFDictionaryRef
lookupMatchingInterface(SCNetworkInterfaceRef interface,
CFArrayRef db_list, CFArrayRef if_list, CFIndex if_list_index,
CFBooleanRef builtin)
{
CFStringRef if_type;
CFDictionaryRef match = NULL;
matchContext match_context;
if_type = SCNetworkInterfaceGetInterfaceType(interface);
if (if_type == NULL) {
return NULL;
}
match_context.match_type = if_type;
match_context.match_info = _SCNetworkInterfaceCopyInterfaceInfo(interface);
match_context.match_builtin = builtin;
match_context.matches = NULL;
if (db_list != NULL) {
CFArrayApplyFunction(db_list,
CFRangeMake(0, CFArrayGetCount(db_list)),
matchKnown,
&match_context);
}
if (if_list != NULL) {
CFIndex if_list_count;
if_list_count = CFArrayGetCount(if_list);
if (if_list_index < if_list_count) {
CFArrayApplyFunction(if_list,
CFRangeMake(if_list_index, if_list_count - if_list_index),
matchUnnamed,
&match_context);
}
}
if (match_context.matches != NULL) {
if (CFArrayGetCount(match_context.matches) == 1) {
match = CFArrayGetValueAtIndex(match_context.matches, 0);
}
CFRelease(match_context.matches);
}
if (match != NULL) {
Boolean active = TRUE;
CFStringRef name;
name = CFDictionaryGetValue(match, CFSTR(kIOBSDNameKey));
if (isA_CFString(name)) {
int sock;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock != -1) {
struct ifreq ifr;
(void)_SC_cfstring_to_cstring(name, ifr.ifr_name, sizeof(ifr.ifr_name), kCFStringEncodingASCII);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
active = FALSE;
}
close(sock);
}
}
if (active) {
match = NULL;
}
}
if (match_context.match_info != NULL) CFRelease(match_context.match_info);
return match;
}
static void
insertInterface(CFMutableArrayRef db_list, SCNetworkInterfaceRef interface)
{
CFIndex i;
CFDictionaryRef if_dict;
CFStringRef if_name;
CFNumberRef if_type;
CFNumberRef if_unit;
CFIndex n = CFArrayGetCount(db_list);
CFComparisonResult res;
if_name = SCNetworkInterfaceGetBSDName(interface);
if (if_name != NULL) {
addTimestamp(S_state, if_name);
}
if_dict = createInterfaceDict(interface);
if_type = _SCNetworkInterfaceGetIOInterfaceType(interface);
if_unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
if ((if_type == NULL) || (if_unit == NULL)) {
CFRelease(if_dict);
return;
}
for (i = 0; i < n; i++) {
CFNumberRef db_type;
CFNumberRef db_unit;
CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i);
db_type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
db_unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
res = CFNumberCompare(if_type, db_type, NULL);
if (res == kCFCompareLessThan
|| (res == kCFCompareEqualTo
&& (CFNumberCompare(if_unit, db_unit, NULL)
== kCFCompareLessThan))) {
CFArrayInsertValueAtIndex(db_list, i, if_dict);
CFRelease(if_dict);
return;
}
}
CFArrayAppendValue(S_dblist, if_dict);
CFRelease(if_dict);
return;
}
static void
replaceInterface(SCNetworkInterfaceRef interface)
{
int n = 0;
CFIndex where;
if (S_dblist == NULL) {
S_dblist = CFArrayCreateMutable(NULL, 0,
&kCFTypeArrayCallBacks);
}
while (lookupInterfaceByAddress(S_dblist, interface, &where) != NULL) {
CFArrayRemoveValueAtIndex(S_dblist, where);
n++;
}
while (lookupInterfaceByUnit(S_dblist, interface, &where) != NULL) {
CFArrayRemoveValueAtIndex(S_dblist, where);
n++;
}
insertInterface(S_dblist, interface);
if (n > 1) {
CFStringRef issue;
issue = CFStringCreateWithFormat(NULL, NULL,
CFSTR("n = %d, %@"),
n,
interface);
reportIssue("Multiple interfaces updated", issue);
CFRelease(issue);
}
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;
type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
if (CFEqual(type, if_type)) {
CFNumberRef unit;
unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
if (ret_unit == NULL
|| (CFNumberCompare(unit, ret_unit, NULL)
== kCFCompareGreaterThan)) {
ret_unit = unit;
}
}
}
return (ret_unit);
}
static SCNetworkInterfaceRef
ensureInterfaceHasUnit(SCNetworkInterfaceRef net_if)
{
if (net_if != NULL
&& _SCNetworkInterfaceGetIOInterfaceUnit(net_if) == NULL) {
CFRelease(net_if);
net_if = NULL;
}
return (net_if);
}
#ifdef USE_REGISTRY_ENTRY_ID
static kern_return_t
registerInterfaceWithIORegistryEntryID(io_connect_t connect,
uint64_t entryID,
CFNumberRef unit,
const int command)
{
CFDataRef data;
CFMutableDictionaryRef dict;
kern_return_t kr;
CFNumberRef num;
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
num = CFNumberCreate(NULL, kCFNumberIntType, &command);
CFDictionarySetValue(dict, CFSTR(kIONetworkStackUserCommandKey), num);
CFRelease(num);
data = CFDataCreate(NULL, (void *) &entryID, sizeof(entryID));
CFDictionarySetValue(dict, CFSTR(kIORegistryEntryIDKey), data);
CFRelease(data);
CFDictionarySetValue(dict, CFSTR(kIOInterfaceUnit), unit);
kr = IOConnectSetCFProperties(connect, dict);
CFRelease(dict);
return kr;
}
static SCNetworkInterfaceRef
copyInterfaceForIORegistryEntryID(uint64_t entryID)
{
io_registry_entry_t entry = MACH_PORT_NULL;
SCNetworkInterfaceRef interface = NULL;
io_iterator_t iterator = MACH_PORT_NULL;
kern_return_t kr;
mach_port_t masterPort = MACH_PORT_NULL;
kr = IOMasterPort(bootstrap_port, &masterPort);
if (kr != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
kr);
goto error;
}
kr = IOServiceGetMatchingServices(masterPort,
IORegistryEntryIDMatching(entryID),
&iterator);
if ((kr != KERN_SUCCESS) || (iterator == MACH_PORT_NULL)) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IOServiceGetMatchingServices(0x%llx) returned 0x%x/%d"),
entryID,
kr,
iterator);
goto error;
}
entry = IOIteratorNext(iterator);
if (entry == MACH_PORT_NULL) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IORegistryEntryIDMatching(0x%llx) failed"),
entryID);
goto error;
}
interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(entry);
error:
if (masterPort != MACH_PORT_NULL) {
mach_port_deallocate(mach_task_self(), masterPort);
}
if (entry != MACH_PORT_NULL) {
IOObjectRelease(entry);
}
if (iterator != MACH_PORT_NULL) {
IOObjectRelease(iterator);
}
return (interface);
}
static SCNetworkInterfaceRef
copyNamedInterfaceForIORegistryEntryID(uint64_t entryID)
{
SCNetworkInterfaceRef net_if;
net_if = copyInterfaceForIORegistryEntryID(entryID);
return (ensureInterfaceHasUnit(net_if));
}
#else // USE_REGISTRY_ENTRY_ID
static kern_return_t
registerInterfaceWithIOServicePath(io_connect_t connect,
CFStringRef path,
CFNumberRef unit,
const int command)
{
CFMutableDictionaryRef dict;
kern_return_t kr;
CFNumberRef num;
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
num = CFNumberCreate(NULL, kCFNumberIntType, &command);
CFDictionarySetValue(dict, CFSTR(kIONetworkStackUserCommandKey), num);
CFRelease(num);
CFDictionarySetValue(dict, CFSTR(kIOPathMatchKey), path);
CFDictionarySetValue(dict, CFSTR(kIOInterfaceUnit), unit);
kr = IOConnectSetCFProperties(connect, dict);
CFRelease(dict);
return kr;
}
static SCNetworkInterfaceRef
copyInterfaceForIOKitPath(CFStringRef if_path)
{
io_registry_entry_t entry = MACH_PORT_NULL;
SCNetworkInterfaceRef interface = 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_ERR,
CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
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_ERR,
CFSTR(MY_PLUGIN_NAME ": IORegistryEntryFromPath(%@) failed"),
if_path);
goto error;
}
interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(entry);
error:
if (masterPort != MACH_PORT_NULL) {
mach_port_deallocate(mach_task_self(), masterPort);
}
if (entry != MACH_PORT_NULL) {
IOObjectRelease(entry);
}
return (interface);
}
static SCNetworkInterfaceRef
copyNamedInterfaceForIOKitPath(CFStringRef if_path)
{
SCNetworkInterfaceRef net_if;
net_if = copyInterfaceForIOKitPath(if_path);
return (ensureInterfaceHasUnit(net_if));
}
#endif // USE_REGISTRY_ENTRY_ID
static void
displayInterface(SCNetworkInterfaceRef interface)
{
CFStringRef addr;
CFStringRef name;
CFNumberRef type;
CFNumberRef unit;
name = SCNetworkInterfaceGetBSDName(interface);
unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
type = _SCNetworkInterfaceGetIOInterfaceType(interface);
addr = SCNetworkInterfaceGetHardwareAddressString(interface);
SCLog(TRUE, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": %s%@%sType: %@, %s%@%sMAC address: %@"),
(name != NULL) ? "BSD Name: " : "",
(name != NULL) ? name : CFSTR(""),
(name != NULL) ? ", " : "",
type,
(unit != NULL) ? "Unit: " : "",
(unit != NULL) ? (CFTypeRef)unit : (CFTypeRef)CFSTR(""),
(unit != NULL) ? ", " : "",
addr);
}
static int
builtinCount(CFArrayRef if_list, CFIndex last, CFNumberRef if_type)
{
CFIndex i;
int n = 0;
for (i = 0; i < last; i++) {
SCNetworkInterfaceRef builtin_if;
CFNumberRef builtin_type;
builtin_if = CFArrayGetValueAtIndex(if_list, i);
builtin_type = _SCNetworkInterfaceGetIOInterfaceType(builtin_if);
if (CFEqual(if_type, builtin_type)) {
if (_SCNetworkInterfaceIsBuiltin(builtin_if)) {
n++; }
}
}
return n;
}
static __inline__ boolean_t
isQuiet(void)
{
return (S_quiet == MACH_PORT_NULL);
}
static void
nameInterfaces(CFMutableArrayRef if_list)
{
CFIndex i;
CFIndex n = CFArrayGetCount(if_list);
for (i = 0; i < n; i++) {
uint64_t entryID;
SCNetworkInterfaceRef interface;
SCNetworkInterfaceRef new_interface;
CFStringRef path;
CFStringRef str;
CFNumberRef type;
CFNumberRef unit;
CFIndex where;
interface = CFArrayGetValueAtIndex(if_list, i);
path = _SCNetworkInterfaceGetIOPath(interface);
type = _SCNetworkInterfaceGetIOInterfaceType(interface);
unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
entryID = _SCNetworkInterfaceGetIORegistryEntryID(interface);
if (unit != NULL) {
if (S_debug) {
CFStringRef if_name;
if_name = SCNetworkInterfaceGetBSDName(interface);
if ((if_name == NULL) || !CFDictionaryContainsKey(S_state, if_name)) {
SCLog(TRUE, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": Interface already has a unit number"));
displayInterface(interface);
}
}
if ((S_prev_active_list != NULL)
&& lookupInterfaceByAddress(S_prev_active_list, interface, &where) != NULL) {
CFArrayRemoveValueAtIndex(S_prev_active_list, where);
}
replaceInterface(interface);
} else {
CFDictionaryRef dbdict;
boolean_t is_builtin;
kern_return_t kr;
int retries = 0;
dbdict = lookupInterfaceByAddress(S_dblist, interface, NULL);
if (dbdict != NULL) {
unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
CFRetain(unit);
SCLog(S_debug, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (from database)"),
unit);
}
if ((dbdict == NULL) && !isQuiet()) {
addTimestamp(S_state, path);
continue;
}
is_builtin = _SCNetworkInterfaceIsBuiltin(interface);
if (dbdict == NULL) {
dbdict = lookupMatchingInterface(interface,
S_dblist,
if_list,
i + 1,
is_builtin ? kCFBooleanTrue : kCFBooleanFalse);
if (dbdict != NULL) {
unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
CFRetain(unit);
SCLog(S_debug, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (updating database)"),
unit);
}
}
if ((dbdict != NULL) && (S_prev_active_list != NULL)) {
where = CFArrayGetFirstIndexOfValue(S_prev_active_list,
CFRangeMake(0, CFArrayGetCount(S_prev_active_list)),
dbdict);
if (where != kCFNotFound) {
CFArrayRemoveValueAtIndex(S_prev_active_list, where);
}
}
if (dbdict == NULL) {
int next_unit = 0;
if (is_builtin) {
next_unit = builtinCount(if_list, i, type);
} else {
next_unit = builtinCount(if_list, n, type);
unit = getHighestUnitForType(type);
if (unit != NULL) {
int high_unit;
CFNumberGetValue(unit, kCFNumberIntType, &high_unit);
if (high_unit >= next_unit) {
next_unit = high_unit + 1;
}
}
}
unit = CFNumberCreate(NULL, kCFNumberIntType, &next_unit);
SCLog(S_debug, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (%s)"),
unit,
is_builtin ? "built-in" : "next available");
}
retry :
#ifdef USE_REGISTRY_ENTRY_ID
kr = registerInterfaceWithIORegistryEntryID(S_connect,
entryID,
unit,
(dbdict == NULL) ? kIONetworkStackRegisterInterfaceWithLowestUnit
: kIONetworkStackRegisterInterfaceWithUnit);
new_interface = copyNamedInterfaceForIORegistryEntryID(entryID);
#else // USE_REGISTRY_ENTRY_ID
kr = registerInterfaceWithIOServicePath(S_connect,
path,
unit,
(dbdict == NULL) ? kRegisterInterface
: kRegisterInterfaceWithFixedUnit);
new_interface = copyNamedInterfaceForIOKitPath(path);
#endif // USE_REGISTRY_ENTRY_ID
if (new_interface == NULL) {
const char *signature;
signature = (dbdict == NULL) ? "failed to name new interface"
: "failed to name known interface";
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": %s, kr=0x%x\n"
MY_PLUGIN_NAME ": path = %@\n"
MY_PLUGIN_NAME ": id = 0x%llx\n"
MY_PLUGIN_NAME ": unit = %@"),
signature,
kr,
path,
entryID,
unit);
if (S_debug) {
displayInterface(interface);
}
str = CFStringCreateWithFormat(NULL, NULL,
CFSTR("kr=0x%x, path=%@, unit=%@"),
kr,
path,
unit);
reportIssue(signature, str);
CFRelease(str);
if ((dbdict != NULL) && (retries++ < 5)) {
usleep(50 * 1000); goto retry;
}
}
else {
CFNumberRef new_unit;
if (retries > 0) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": %s interface named after %d %s\n"
MY_PLUGIN_NAME ": path = %@\n"
MY_PLUGIN_NAME ": unit = %@"),
(dbdict == NULL) ? "New" : "Known",
retries,
(retries == 1) ? "try" : "tries",
path,
unit);
#ifdef SHOW_NAMING_FAILURE
str = CFStringCreateWithFormat(NULL,
NULL,
CFSTR("\"%s\" interface named after %d %s, unit = %@"),
(dbdict == NULL) ? "New" : "Known",
retries,
(retries == 1) ? "try" : "tries",
unit);
CFUserNotificationDisplayNotice(0,
kCFUserNotificationStopAlertLevel,
NULL,
NULL,
NULL,
str,
CFSTR("Please report repeated failures."),
NULL);
CFRelease(str);
#endif // SHOW_NAMING_FAILURE
}
new_unit = _SCNetworkInterfaceGetIOInterfaceUnit(new_interface);
if (CFEqual(unit, new_unit) == FALSE) {
SCLog(S_debug, LOG_INFO,
CFSTR(MY_PLUGIN_NAME
": interface type %@ assigned "
"unit %@ instead of %@"),
type, new_unit, unit);
}
if (S_debug) {
displayInterface(new_interface);
}
CFArraySetValueAtIndex(if_list, i, new_interface);
CFRelease(new_interface);
interface = new_interface;
if (is_builtin && (S_prev_active_list != NULL)) {
CFIndex where;
if (lookupInterfaceByUnit(S_prev_active_list, interface, &where) != NULL) {
SCLog(S_debug, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": and updated database (new address)"));
CFArrayRemoveValueAtIndex(S_prev_active_list, where);
}
}
replaceInterface(interface);
}
CFRelease(unit);
}
}
return;
}
static void
updateInterfaces()
{
if (S_connect == MACH_PORT_NULL) {
return;
}
if (S_iflist != NULL) {
CFIndex n;
n = CFArrayGetCount(S_iflist);
if (n > 1) {
CFArraySortValues(S_iflist, CFRangeMake(0, n), _SCNetworkInterfaceCompare, NULL);
}
nameInterfaces(S_iflist);
}
if (isQuiet()) {
writeInterfaceList(S_dblist);
updateVirtualNetworkInterfaceConfiguration(NULL, kSCPreferencesNotificationApply, NULL);
updateStore();
if (S_iflist != NULL) {
CFRelease(S_iflist);
S_iflist = NULL;
}
if (S_prev_active_list != NULL) {
if (S_debug) {
CFIndex i;
CFIndex n;
n = CFArrayGetCount(S_prev_active_list);
if (n > 0) {
SCLog(TRUE, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": Interface%s not [yet] active"),
(n > 1) ? "s" : "");
}
for (i = 0; i < n; i++) {
CFDictionaryRef if_dict;
CFStringRef name;
CFNumberRef type;
CFNumberRef unit;
if_dict = CFArrayGetValueAtIndex(S_prev_active_list, i);
name = CFDictionaryGetValue(if_dict, CFSTR(kIOBSDNameKey));
type = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceType));
unit = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceUnit));
SCLog(TRUE, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": %s%@%sType: %@, Unit: %@"),
(name != NULL) ? "BSD Name: " : "",
(name != NULL) ? name : CFSTR(""),
(name != NULL) ? ", " : "",
type,
unit);
}
}
CFRelease(S_prev_active_list);
S_prev_active_list = NULL;
}
} else {
if ((S_prev_active_list != NULL) && (CFArrayGetCount(S_prev_active_list) == 0)) {
addTimestamp(S_state, CFSTR("*RELEASE*"));
SCLog(S_debug, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": last boot interfaces have been named"));
updateStore();
CFRelease(S_prev_active_list);
S_prev_active_list = NULL;
}
}
return;
}
#if !TARGET_OS_EMBEDDED
static CFComparisonResult
compareMacAddress(const void *val1, const void *val2, void *context)
{
CFDataRef mac1 = (CFDataRef)val1;
CFDataRef mac2 = (CFDataRef)val2;
CFIndex n1;
CFIndex n2;
CFComparisonResult res;
n1 = CFDataGetLength(mac1);
n2 = CFDataGetLength(mac2);
if (n1 < n2) {
res = kCFCompareLessThan;
} else if (n2 > n1) {
res = kCFCompareGreaterThan;
} else {
res = bcmp(CFDataGetBytePtr(mac1), CFDataGetBytePtr(mac2), n1);
}
return res;
}
static CFStringRef
copyEthernetUUID()
{
CFDataRef addr;
CFMutableArrayRef addrs = NULL;
CFStringRef guid = NULL;
CFIndex i;
CFIndex n;
addrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
n = (S_dblist != NULL) ? CFArrayGetCount(S_dblist) : 0;
for (i = 0; i < n; i++) {
CFBooleanRef builtin;
CFDictionaryRef dict;
CFStringRef type;
dict = CFArrayGetValueAtIndex(S_dblist, i);
type = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceType));
if (!isA_CFString(type) || !CFEqual(type, kSCNetworkInterfaceTypeEthernet)) {
continue;
}
builtin = CFDictionaryGetValue(dict, CFSTR(kIOBuiltin));
if (!isA_CFBoolean(builtin) || !CFBooleanGetValue(builtin)) {
continue;
}
addr = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
if (!isA_CFData(addr) || (CFDataGetLength(addr) != ETHER_ADDR_LEN)) {
continue;
}
CFArrayAppendValue(addrs, addr);
}
if (CFArrayGetCount(addrs) == 0) {
for (i = 0; i < n; i++) {
CFDictionaryRef dict;
CFStringRef type;
dict = CFArrayGetValueAtIndex(S_dblist, i);
type = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceType));
if (!isA_CFString(type) || !CFEqual(type, kSCNetworkInterfaceTypeIEEE80211)) {
continue;
}
addr = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
if (!isA_CFData(addr) || (CFDataGetLength(addr) != ETHER_ADDR_LEN)) {
continue;
}
CFArrayAppendValue(addrs, addr);
}
}
n = CFArrayGetCount(addrs);
switch (n) {
case 0 :
break;
default :
CFArraySortValues(addrs, CFRangeMake(0, n), compareMacAddress, NULL);
case 1 : {
CFUUIDBytes bytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
CFUUIDRef uuid;
addr = CFArrayGetValueAtIndex(addrs, 0);
bcopy(CFDataGetBytePtr(addr),
(void *)&bytes + sizeof(bytes) - ETHER_ADDR_LEN,
ETHER_ADDR_LEN);
uuid = CFUUIDCreateFromUUIDBytes(NULL, bytes);
guid = CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
SCLog(TRUE, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": setting platform UUID [MAC] = %@"),
guid);
break;
}
}
if (addrs != NULL) CFRelease(addrs);
return guid;
}
#ifndef kIOPlatformUUIDKey
#define kIOPlatformUUIDKey "IOPlatformUUID"
#endif
static void
updatePlatformUUID()
{
CFStringRef guid = NULL;
kern_return_t kr;
io_registry_entry_t platform;
platform = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/");
if (platform == MACH_PORT_NULL) {
goto done;
}
guid = IORegistryEntryCreateCFProperty(platform, CFSTR(kIOPlatformUUIDKey), NULL, 0);
if (guid != NULL) {
goto done;
}
guid = copyEthernetUUID();
if (guid == NULL) {
CFUUIDRef uuid;
uuid = CFUUIDCreate(NULL);
guid = CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
SCLog(TRUE, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": setting platform UUID [random] = %@"),
guid);
}
if (getenv("DO_NOT_SET_PLATFORM_UUID") == NULL) {
kr = IORegistryEntrySetCFProperty(platform, CFSTR(kIOPlatformUUIDKey), guid);
if (kr != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IORegistryEntrySetCFProperty(platform UUID) failed, kr=0x%x"),
kr);
}
}
addTimestamp(S_state, CFSTR("*PLATFORM-UUID*"));
updateStore();
done :
if (S_vproc_transaction != NULL) {
vproc_transaction_end(NULL, S_vproc_transaction);
S_vproc_transaction = NULL;
}
if (platform != MACH_PORT_NULL) IOObjectRelease(platform);
if (guid != NULL) CFRelease(guid);
return;
}
#endif // !TARGET_OS_EMBEDDED
static void
interfaceArrivalCallback(void *refcon, io_iterator_t iter)
{
io_object_t obj;
while ((obj = IOIteratorNext(iter)) != MACH_PORT_NULL) {
SCNetworkInterfaceRef interface;
interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj);
if (interface != NULL) {
if (S_iflist == NULL) {
S_iflist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(S_iflist, interface);
CFRelease(interface);
}
IOObjectRelease(obj);
}
updateInterfaces();
return;
}
static void
stackCallback(void *refcon, io_iterator_t iter)
{
kern_return_t kr;
io_object_t stack;
stack = IOIteratorNext(iter);
if (stack == MACH_PORT_NULL) {
goto error;
}
kr = IOServiceOpen(stack, mach_task_self(), 0, &S_connect);
if (kr != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IOServiceOpen returned 0x%x"),
kr);
goto error;
}
addTimestamp(S_state, CFSTR("*STACK*"));
SCLog(S_debug, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": IONetworkStack found"));
if (S_stack != MACH_PORT_NULL) {
IOObjectRelease(S_stack);
S_stack = MACH_PORT_NULL;
}
if ((S_timer != NULL) && CFRunLoopTimerIsValid(S_timer)) {
CFRunLoopTimerSetNextFireDate(S_timer,
CFAbsoluteTimeGetCurrent() + S_quiet_timeout);
}
updateInterfaces();
error:
if (stack != MACH_PORT_NULL) {
IOObjectRelease(stack);
}
return;
}
static void
quietCallback(void *refcon,
io_service_t service,
natural_t messageType,
void *messageArgument)
{
if (messageArgument != NULL) {
return;
}
if (messageType == kIOMessageServiceBusyStateChange) {
addTimestamp(S_state, CFSTR("*QUIET*"));
SCLog(S_debug, LOG_INFO,
CFSTR(MY_PLUGIN_NAME ": IOKit quiet"));
}
if (S_connect == MACH_PORT_NULL) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": No network stack object"));
return;
}
if (S_quiet != MACH_PORT_NULL) {
IOObjectRelease(S_quiet);
S_quiet = MACH_PORT_NULL;
}
if (S_timer != NULL) {
CFRunLoopTimerInvalidate(S_timer);
CFRelease(S_timer);
S_timer = NULL;
}
interfaceArrivalCallback((void *)S_notify, S_iter);
#if !TARGET_OS_EMBEDDED
updatePlatformUUID();
#endif // !TARGET_OS_EMBEDDED
return;
}
static void
iterateRegistryBusy(io_iterator_t iterator, CFArrayRef nodes, CFMutableStringRef snapshot, int *count)
{
kern_return_t kr = kIOReturnSuccess;;
io_object_t obj;
while ((kr == kIOReturnSuccess) &&
((obj = IOIteratorNext(iterator)) != MACH_PORT_NULL)) {
uint64_t accumulated_busy_time;
uint32_t busy_state;
io_name_t location;
io_name_t name;
CFMutableArrayRef newNodes;
uint64_t state;
CFMutableStringRef str = NULL;
if (nodes == NULL) {
newNodes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
} else {
newNodes = CFArrayCreateMutableCopy(NULL, 0, nodes);
}
kr = IORegistryEntryGetName(obj, name);
if (kr != kIOReturnSuccess) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryEntryGetName returned 0x%x"),
kr);
goto next;
}
str = CFStringCreateMutable(NULL, 0);
CFStringAppendCString(str, name, kCFStringEncodingUTF8);
kr = IORegistryEntryGetLocationInPlane(obj, kIOServicePlane, location);
switch (kr) {
case kIOReturnSuccess :
CFStringAppendCString(str, "@", kCFStringEncodingUTF8);
CFStringAppendCString(str, location, kCFStringEncodingUTF8);
break;
case kIOReturnNotFound :
break;
default :
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryEntryGetLocationInPlane returned 0x%x"),
kr);
CFRelease(str);
goto next;
}
CFArrayAppendValue(newNodes, str);
CFRelease(str);
kr = IOServiceGetBusyStateAndTime(obj, &state, &busy_state, &accumulated_busy_time);
if (kr != kIOReturnSuccess) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": captureBusy IOServiceGetBusyStateAndTime returned 0x%x"),
kr);
goto next;
}
#ifdef TEST_SNAPSHOT
busy_state = 1;
#endif // TEST_SNAPSHOT
if (busy_state != 0) {
CFStringRef path;
if ((*count)++ == 0) {
CFStringAppend(snapshot, CFSTR("Busy services :"));
}
path = CFStringCreateByCombiningStrings(NULL, newNodes, CFSTR("/"));
CFStringAppendFormat(snapshot, NULL,
CFSTR("\n %@ [%s%s%s%d, %lld ms]"),
path,
(state & kIOServiceRegisteredState) ? "" : "!registered, ",
(state & kIOServiceMatchedState) ? "" : "!matched, ",
(state & kIOServiceInactiveState) ? "inactive, " : "",
busy_state,
accumulated_busy_time / kMillisecondScale);
CFRelease(path);
}
kr = IORegistryIteratorEnterEntry(iterator);
if (kr != kIOReturnSuccess) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryIteratorEnterEntry returned 0x%x"),
kr);
goto next;
}
iterateRegistryBusy(iterator, newNodes, snapshot, count);
kr = IORegistryIteratorExitEntry(iterator);
if (kr != kIOReturnSuccess) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryIteratorExitEntry returned 0x%x"),
kr);
}
next :
CFRelease(newNodes);
IOObjectRelease(obj);
}
return;
}
static CF_RETURNS_RETAINED CFStringRef
captureBusy()
{
int count = 0;
io_iterator_t iterator = MACH_PORT_NULL;
kern_return_t kr;
CFMutableStringRef snapshot;
snapshot = CFStringCreateMutable(NULL, 0);
kr = IORegistryCreateIterator(kIOMasterPortDefault,
kIOServicePlane,
0,
&iterator);
if (kr != kIOReturnSuccess) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryCreateIterator returned 0x%x"),
kr);
return snapshot;
}
iterateRegistryBusy(iterator, NULL, snapshot, &count);
if (count == 0) {
CFStringAppend(snapshot, CFSTR("w/no busy services"));
}
IOObjectRelease(iterator);
return snapshot;
}
static void
timerCallback(CFRunLoopTimerRef timer, void *info)
{
CFStringRef snapshot;
addTimestamp(S_state, CFSTR("*TIMEOUT*"));
snapshot = captureBusy();
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": timed out waiting for IOKit to quiesce\n%@"),
snapshot);
reportIssue("timed out waiting for IOKit to quiesce", snapshot);
CFRelease(snapshot);
quietCallback((void *)S_notify, MACH_PORT_NULL, 0, NULL);
return;
}
static Boolean
setup_IOKit(CFBundleRef bundle)
{
uint32_t busy;
kern_return_t kr;
mach_port_t masterPort = MACH_PORT_NULL;
Boolean ok = FALSE;
io_object_t root = MACH_PORT_NULL;
S_dblist = readInterfaceList();
if (S_dblist != NULL) {
CFIndex n;
n = CFArrayGetCount(S_dblist);
if (n > 1) {
CFArraySortValues(S_dblist, CFRangeMake(0, n), if_unit_compare, NULL);
}
}
S_prev_active_list = previouslyActiveInterfaces();
S_state = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
addTimestamp(S_state, CFSTR("*START*"));
kr = IOMasterPort(bootstrap_port, &masterPort);
if (kr != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
kr);
goto done;
}
S_notify = IONotificationPortCreate(masterPort);
if (S_notify == NULL) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IONotificationPortCreate failed"));
goto done;
}
root = IORegistryEntryFromPath(masterPort, kIOServicePlane ":/");
if (root == MACH_PORT_NULL) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IORegistryEntryFromPath failed"));
goto done;
}
kr = IOServiceAddInterestNotification(S_notify,
root,
kIOBusyInterest,
&quietCallback,
(void *)S_notify, &S_quiet); if (kr != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IOServiceAddInterestNotification returned 0x%x"),
kr);
goto done;
}
kr = IOServiceGetBusyState(root, &busy);
if (kr != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IOServiceGetBusyState returned 0x%x"),
kr);
goto done;
}
S_timer = CFRunLoopTimerCreate(NULL,
CFAbsoluteTimeGetCurrent() + S_stack_timeout,
0,
0,
0,
timerCallback,
NULL);
if (S_timer == NULL) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": CFRunLoopTimerCreate failed"));
goto done;
}
CFRunLoopAddTimer(CFRunLoopGetCurrent(), S_timer, kCFRunLoopDefaultMode);
kr = IOServiceAddMatchingNotification(S_notify,
kIOFirstMatchNotification,
IOServiceMatching("IONetworkStack"),
&stackCallback,
(void *)S_notify, &S_stack); if (kr != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IOServiceAddMatchingNotification returned 0x%x"),
kr);
goto done;
}
stackCallback((void *)S_notify, S_stack);
kr = IOServiceAddMatchingNotification(S_notify,
kIOFirstMatchNotification,
IOServiceMatching("IONetworkInterface"),
&interfaceArrivalCallback,
(void *)S_notify, &S_iter); if (kr != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": IOServiceAddMatchingNotification returned 0x%x"),
kr);
goto done;
}
interfaceArrivalCallback((void *)S_notify, S_iter);
quietCallback((void *)S_notify,
MACH_PORT_NULL,
kIOMessageServiceBusyStateChange,
(void *)(uintptr_t)busy);
CFRunLoopAddSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(S_notify),
kCFRunLoopDefaultMode);
#ifdef WAIT_PREVIOUS_BOOT_INTERFACES_OR_QUIET
CFRunLoopAddTimer(CFRunLoopGetCurrent(), S_timer, MY_PLUGIN_ID);
CFRunLoopAddSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(S_notify),
MY_PLUGIN_ID);
while (S_prev_active_list != NULL) {
int rlStatus;
rlStatus = CFRunLoopRunInMode(MY_PLUGIN_ID, 1.0e10, TRUE);
}
#endif
ok = TRUE;
done:
if (root != MACH_PORT_NULL) {
IOObjectRelease(root);
}
if (masterPort != MACH_PORT_NULL) {
mach_port_deallocate(mach_task_self(), masterPort);
}
return ok;
}
static Boolean
setup_Virtual(CFBundleRef bundle)
{
S_prefs = SCPreferencesCreate(NULL, CFSTR(MY_PLUGIN_NAME), NULL);
if (S_prefs == NULL) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate() failed: %s"),
SCErrorString(SCError()));
return FALSE;
}
if (!SCPreferencesSetCallback(S_prefs, updateVirtualNetworkInterfaceConfiguration, NULL)) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": SCPreferencesSetCallBack() failed: %s"),
SCErrorString(SCError()));
CFRelease(S_prefs);
return FALSE;
}
if (!SCPreferencesScheduleWithRunLoop(S_prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
if (SCError() != kSCStatusNoStoreServer) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": SCPreferencesScheduleWithRunLoop() failed: %s"),
SCErrorString(SCError()));
CFRelease(S_prefs);
return FALSE;
}
}
return TRUE;
}
static void *
exec_InterfaceNamer(void *arg)
{
CFBundleRef bundle = (CFBundleRef)arg;
CFDictionaryRef dict;
pthread_setname_np(MY_PLUGIN_NAME " thread");
dict = CFBundleGetInfoDictionary(bundle);
if (isA_CFDictionary(dict)) {
CFNumberRef num;
num = CFDictionaryGetValue(dict, CFSTR(WAIT_STACK_TIMEOUT_KEY));
if (num != NULL) {
if (!isA_CFNumber(num) ||
!CFNumberGetValue(num, kCFNumberDoubleType, &S_stack_timeout) ||
(S_stack_timeout <= 0.0)) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": " WAIT_STACK_TIMEOUT_KEY " value error"));
S_stack_timeout = WAIT_STACK_TIMEOUT_DEFAULT;
}
}
num = CFDictionaryGetValue(dict, CFSTR(WAIT_QUIET_TIMEOUT_KEY));
if (num != NULL) {
if (!isA_CFNumber(num) ||
!CFNumberGetValue(num, kCFNumberDoubleType, &S_quiet_timeout) ||
(S_quiet_timeout <= 0.0)) {
SCLog(TRUE, LOG_ERR,
CFSTR(MY_PLUGIN_NAME ": " WAIT_QUIET_TIMEOUT_KEY " value error"));
S_quiet_timeout = WAIT_QUIET_TIMEOUT_DEFAULT;
}
}
}
if (!setup_Virtual(bundle)) {
goto error;
}
if (!setup_IOKit(bundle)) {
goto error;
}
#if !TARGET_OS_EMBEDDED
S_vproc_transaction = vproc_transaction_begin(NULL);
#endif // !TARGET_OS_EMBEDDED
goto done;
error :
if (S_connect != MACH_PORT_NULL) {
IOServiceClose(S_connect);
S_connect = MACH_PORT_NULL;
}
if (S_dblist != NULL) {
CFRelease(S_dblist);
S_dblist = NULL;
}
if (S_iter != MACH_PORT_NULL) {
IOObjectRelease(S_iter);
S_iter = MACH_PORT_NULL;
}
if (S_notify != MACH_PORT_NULL) {
IONotificationPortDestroy(S_notify);
}
if (S_quiet != MACH_PORT_NULL) {
IOObjectRelease(S_quiet);
S_quiet = MACH_PORT_NULL;
}
if (S_stack != MACH_PORT_NULL) {
IOObjectRelease(S_stack);
S_stack = MACH_PORT_NULL;
}
if (S_state != NULL) {
CFRelease(S_state);
S_state = NULL;
}
if (S_timer != NULL) {
CFRunLoopTimerInvalidate(S_timer);
CFRelease(S_timer);
S_timer = NULL;
}
done :
CFRelease(bundle);
CFRunLoopRun();
return NULL;
}
__private_extern__
void
load_InterfaceNamer(CFBundleRef bundle, Boolean bundleVerbose)
{
pthread_attr_t tattr;
pthread_t tid;
if (bundleVerbose) {
S_debug = TRUE;
}
CFRetain(bundle);
pthread_attr_init(&tattr);
pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &tattr, exec_InterfaceNamer, bundle);
pthread_attr_destroy(&tattr);
return;
}
#ifdef MAIN
int
main(int argc, char ** argv)
{
CFBundleRef bundle;
_sc_log = FALSE;
_sc_verbose = (argc > 1) ? TRUE : FALSE;
S_debug = _sc_verbose;
bundle = CFBundleGetMainBundle();
CFRetain(bundle);
(void)exec_InterfaceNamer();
exit(0);
return 0;
}
#endif
#ifdef TEST_PLATFORM_UUID
int
main(int argc, char ** argv)
{
CFStringRef guid;
CFArrayRef interfaces;
_sc_log = FALSE;
_sc_verbose = (argc > 1) ? TRUE : FALSE;
S_dblist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
interfaces = SCNetworkInterfaceCopyAll();
if (interfaces != NULL) {
CFIndex i;
CFIndex n;
n = CFArrayGetCount(interfaces);
for (i = 0; i < n; i++) {
CFDictionaryRef dict;
SCNetworkInterfaceRef interface;
interface = CFArrayGetValueAtIndex(interfaces, i);
dict = createInterfaceDict(interface);
CFArrayAppendValue(S_dblist, dict);
CFRelease(dict);
}
CFRelease(interfaces);
}
guid = copyEthernetUUID();
SCPrint(TRUE, stdout, CFSTR("copyEthernetUUID() = %@\n"), (guid != NULL) ? guid : CFSTR("NULL"));
if (guid != NULL) CFRelease(guid);
updatePlatformUUID();
CFRelease(S_dblist);
exit(0);
return 0;
}
#endif
#ifdef TEST_SNAPSHOT
int
main(int argc, char ** argv)
{
CFStringRef snapshot;
_sc_log = FALSE;
_sc_verbose = (argc > 1) ? TRUE : FALSE;
snapshot = captureBusy();
SCPrint(TRUE, stdout, CFSTR("%@\n"), snapshot);
CFRelease(snapshot);
exit(0);
return 0;
}
#endif