]> git.saurik.com Git - apple/configd.git/blobdiff - SystemConfiguration.fproj/SCNetworkInterfaceProvider.c
configd-963.200.27.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkInterfaceProvider.c
diff --git a/SystemConfiguration.fproj/SCNetworkInterfaceProvider.c b/SystemConfiguration.fproj/SCNetworkInterfaceProvider.c
new file mode 100644 (file)
index 0000000..65074b2
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ * Copyright (c) 2018 Apple 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
+ *
+ * January 17, 2018            Dieter Siegmund (dieter@apple.com)
+ * - initial revision
+ */
+
+/*
+ * SCNetworkInterfaceProvider.c
+ */
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFRuntime.h>
+#include <libkern/OSAtomic.h>
+#include "SCNetworkConfigurationPrivate.h"
+#include "SCNetworkConfigurationInternal.h"
+#include "SCNetworkInterfaceProvider.h"
+
+static void
+my_CFRelease(void * t)
+{
+    void * * obj = (void * *)t;
+    if (obj && *obj) {
+       CFRelease(*obj);
+       *obj = NULL;
+    }
+    return;
+}
+
+/**
+ ** ObjectWrapper
+ **/
+
+typedef struct {
+    const void *       obj;
+    int32_t            retain_count;
+} ObjectWrapper, * ObjectWrapperRef;
+
+static const void *
+ObjectWrapperRetain(const void * info)
+{
+    ObjectWrapperRef   wrapper = (ObjectWrapperRef)info;
+
+    (void)OSAtomicIncrement32(&wrapper->retain_count);
+    return (info);
+}
+
+static ObjectWrapperRef
+ObjectWrapperAllocate(const void * obj)
+{
+    ObjectWrapperRef   wrapper;
+
+    wrapper = (ObjectWrapperRef)malloc(sizeof(*wrapper));
+    wrapper->obj = obj;
+    wrapper->retain_count = 1;
+    return (wrapper);
+}
+
+static void
+ObjectWrapperRelease(const void * info)
+{
+    int32_t            new_val;
+    ObjectWrapperRef   wrapper = (ObjectWrapperRef)info;
+
+    new_val = OSAtomicDecrement32(&wrapper->retain_count);
+    if (new_val == 0) {
+       free(wrapper);
+    }
+    else {
+       assert(new_val > 0);
+    }
+    return;
+}
+
+static void
+ObjectWrapperSetObject(ObjectWrapperRef wrapper, const void * obj)
+{
+    wrapper->obj = obj;
+}
+
+static const void *
+ObjectWrapperGetObject(ObjectWrapperRef wrapper)
+{
+    return (wrapper->obj);
+}
+
+static SCDynamicStoreRef
+StoreObjectWrapperAllocate(const void * obj,
+                          CFStringRef name,
+                          SCDynamicStoreCallBack handler,
+                          CFArrayRef keys,
+                          CFArrayRef patterns,
+                          dispatch_queue_t queue,
+                          ObjectWrapperRef * ret_wrapper)
+{
+    SCDynamicStoreContext      context = {
+       .version = 0,
+       .info = NULL,
+       .retain = ObjectWrapperRetain,
+       .release = ObjectWrapperRelease,
+       .copyDescription = NULL
+    };
+    SCDynamicStoreRef          store;
+    ObjectWrapperRef           wrapper;
+
+    wrapper = ObjectWrapperAllocate(obj);
+    context.info = wrapper;
+    store = SCDynamicStoreCreate(NULL, name, handler, &context);
+    if (store == NULL) {
+       SC_log(LOG_NOTICE,
+              "%@: SCDynamicStoreCreate failed", name);
+    }
+    else if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) {
+       SC_log(LOG_NOTICE,
+              "%@: SCDynamicStoreSetNoticationKeys failed", name);
+       CFRelease(store);
+       store = NULL;
+    }
+    else if (queue != NULL
+            && !SCDynamicStoreSetDispatchQueue(store, queue)) {
+       SC_log(LOG_NOTICE,
+              "%@: SCDynamicStoreSetDispatchQueue failed", name);
+       CFRelease(store);
+       store = NULL;
+    }
+    if (store == NULL) {
+       ObjectWrapperRelease(wrapper);
+       wrapper = NULL;
+    }
+    *ret_wrapper = wrapper;
+    return (store);
+}
+
+/**
+ ** CF object glue code
+ **/
+static CFStringRef     __SCNetworkInterfaceProviderCopyDebugDesc(CFTypeRef cf);
+static void            __SCNetworkInterfaceProviderDeallocate(CFTypeRef cf);
+
+static CFTypeID __kSCNetworkInterfaceProviderTypeID    = _kCFRuntimeNotATypeID;
+
+static const CFRuntimeClass __SCNetworkInterfaceProviderClass = {
+    0,                                         /* version */
+    "SCNetworkInterfaceProvider",              /* className */
+    NULL,                                      /* init */
+    NULL,                                      /* copy */
+    __SCNetworkInterfaceProviderDeallocate,    /* deallocate */
+    NULL,                                      /* equal */
+    NULL,                                      /* hash */
+    NULL,                                      /* copyFormattingDesc */
+    __SCNetworkInterfaceProviderCopyDebugDesc  /* copyDebugDesc */
+};
+
+struct __SCNetworkInterfaceProvider {
+    CFRuntimeBase                              cf_base;
+
+    IPMonitorControlRef                                control;
+    SCDynamicStoreRef                          store;
+    ObjectWrapperRef                           wrapper;
+    dispatch_queue_t                           queue;
+
+    SCNetworkInterfaceProviderEventHandler     handler;
+    CFStringRef                                        if_name;
+    SCNetworkInterfaceRef                      if_type;
+    Boolean                                    enabled;
+    Boolean                                    needed;
+};
+
+
+static CFStringRef
+__SCNetworkInterfaceProviderCopyDebugDesc(CFTypeRef cf)
+{
+    CFAllocatorRef             allocator = CFGetAllocator(cf);
+    SCNetworkInterfaceProviderRef provider = (SCNetworkInterfaceProviderRef)cf;
+    CFMutableStringRef         result;
+
+    result = CFStringCreateMutable(allocator, 0);
+    CFStringAppendFormat(result, NULL,
+                        CFSTR("<SCNetworkInterfaceProvider %@ %@ <%p>"),
+                        provider->if_type, provider->if_name, cf);
+    return (result);
+}
+
+static void
+SCNetworkInterfaceProviderDeallocate(SCNetworkInterfaceProviderRef provider)
+{
+    provider->enabled = FALSE;
+    my_CFRelease(&provider->control);
+    if (provider->wrapper != NULL) {
+       ObjectWrapperSetObject(provider->wrapper, NULL);
+       ObjectWrapperRelease(provider->wrapper);
+       provider->wrapper = NULL;
+    }
+    if (provider->store != NULL) {
+       SCDynamicStoreSetDispatchQueue(provider->store, NULL);
+       my_CFRelease(&provider->store);
+    }
+    if (provider->queue != NULL) {
+       dispatch_release(provider->queue);
+       provider->queue = NULL;
+    }
+    if (provider->handler != NULL) {
+       Block_release(provider->handler);
+       provider->handler = NULL;
+    }
+    my_CFRelease(&provider->if_name);
+    my_CFRelease(&provider->if_type);
+}
+
+static void
+__SCNetworkInterfaceProviderDeallocate(CFTypeRef cf)
+{
+    SCNetworkInterfaceProviderRef provider = (SCNetworkInterfaceProviderRef)cf;
+
+    if (provider->queue != NULL) {
+       dispatch_sync(provider->queue, ^{
+               SCNetworkInterfaceProviderDeallocate(provider);
+           });
+    }
+    else {
+       SCNetworkInterfaceProviderDeallocate(provider);
+    }
+    return;
+}
+
+/**
+ ** Supporting Functions
+ **/
+static void
+__SCNetworkInterfaceProviderRegisterClass(void)
+{
+    static dispatch_once_t     once;
+    dispatch_block_t           once_block;
+
+    once_block = ^{
+       __kSCNetworkInterfaceProviderTypeID
+       = _CFRuntimeRegisterClass(&__SCNetworkInterfaceProviderClass);
+    };
+    dispatch_once(&once, once_block);
+    return;
+}
+
+static SCNetworkInterfaceProviderRef
+__SCNetworkInterfaceProviderAllocate(CFAllocatorRef allocator)
+{
+    SCNetworkInterfaceProviderRef      provider;
+    int                                        size;
+
+    __SCNetworkInterfaceProviderRegisterClass();
+    size = sizeof(*provider) - sizeof(CFRuntimeBase);
+    provider = (SCNetworkInterfaceProviderRef)
+       _CFRuntimeCreateInstance(allocator,
+                                __kSCNetworkInterfaceProviderTypeID,
+                                size, NULL);
+    memset(((void *)provider) + sizeof(CFRuntimeBase), 0, size);
+    return (provider);
+}
+
+static void
+SCNetworkInterfaceProviderCheck(SCNetworkInterfaceProviderRef provider)
+{
+    Boolean    advisory_set;
+
+    if (!provider->enabled || provider->handler == NULL) {
+       return;
+    }
+    advisory_set
+       = IPMonitorControlAnyInterfaceAdvisoryIsSet(provider->control);
+    if (provider->needed != advisory_set) {
+       SCNetworkInterfaceProviderEvent         event;
+
+       event = advisory_set
+           ? kSCNetworkInterfaceProviderEventActivationRequested
+           : kSCNetworkInterfaceProviderEventActivationNoLongerRequested;
+       (provider->handler)(event, NULL);
+       provider->needed = advisory_set;
+    }
+    return;
+}
+
+static void
+StoreHandleChanges(SCDynamicStoreRef store, CFArrayRef changes, void * info)
+{
+#pragma unused(store)
+#pragma unused(changes)
+    SCNetworkInterfaceProviderRef      provider;
+    ObjectWrapperRef                   wrapper = (ObjectWrapperRef)info;
+
+    provider = (SCNetworkInterfaceProviderRef)ObjectWrapperGetObject(wrapper);
+    if (provider == NULL) {
+       /* provider has been deallocated */
+       return;
+    }
+    SCNetworkInterfaceProviderCheck(provider);
+    return;
+}
+
+
+/**
+ ** SCNetworkInterfaceProvider SPI
+ **/
+SCNetworkInterfaceProviderRef
+SCNetworkInterfaceProviderCreate(CFStringRef type,
+                                CFStringRef ifname,
+                                CFDictionaryRef options)
+{
+    IPMonitorControlRef                        control;
+    CFStringRef                                pattern;
+    CFArrayRef                         patterns;
+    SCNetworkInterfaceProviderRef      provider;
+    dispatch_queue_t                   queue;
+    SCDynamicStoreRef                  store = NULL;
+    ObjectWrapperRef                   wrapper = NULL;
+
+    if (options != NULL || ifname == NULL || type == NULL) {
+       _SCErrorSet(kSCStatusInvalidArgument);
+       return (NULL);
+    }
+    control = IPMonitorControlCreate();
+    if (control == NULL) {
+       _SCErrorSet(kSCStatusFailed);
+       return (NULL);
+    }
+    pattern
+       = IPMonitorControlCopyInterfaceAdvisoryNotificationKey(kSCCompAnyRegex);
+    patterns = CFArrayCreate(NULL, (const void * *)&pattern, 1,
+                            &kCFTypeArrayCallBacks);
+    CFRelease(pattern);
+#define OUR_NAME       "SCNetworkInterfaceProvider"
+    queue = dispatch_queue_create(OUR_NAME, NULL);
+    provider = __SCNetworkInterfaceProviderAllocate(NULL);
+    store = StoreObjectWrapperAllocate(provider,
+                                      CFSTR(OUR_NAME),
+                                      StoreHandleChanges,
+                                      NULL,
+                                      patterns,
+                                      queue,
+                                      &wrapper);
+    CFRelease(patterns);
+    if (store == NULL) {
+       dispatch_release(queue);
+       CFRelease(provider);
+       provider = NULL;
+       CFRelease(control);
+    }
+    else {
+       provider->control = control;
+       provider->store = store;
+       provider->wrapper = wrapper;
+       provider->queue = queue;
+       provider->if_name = CFRetain(ifname);
+       provider->if_type = CFRetain(type);
+    }
+    return (provider);
+}
+
+void
+SCNetworkInterfaceProviderSetEventHandler(SCNetworkInterfaceProviderRef provider,
+                                         SCNetworkInterfaceProviderEventHandler handler)
+{
+    if (handler == NULL) {
+       /* can't clear handler once set */
+       return;
+    }
+    dispatch_sync(provider->queue, ^{
+           if (provider->enabled) {
+               /* enabling before setting the handler isn't allowed */
+               SC_log(LOG_NOTICE,
+                      "%s: call SCNetworkInterfaceSetEventHandler before "
+                      " SCNetworkInterfaceProviderResume", __FUNCTION__);
+               return;
+           }
+           if (provider->handler != NULL) {
+               /* can't change the handler once set */
+               SC_log(LOG_NOTICE,
+                      "%s: ignoring second invocation of "
+                      "SCNetworkInterfaceSetEventHandler", __FUNCTION__);
+               return;
+           }
+           provider->handler = Block_copy(handler);
+       });
+    return;
+}
+
+void
+SCNetworkInterfaceProviderResume(SCNetworkInterfaceProviderRef provider)
+{
+    dispatch_async(provider->queue, ^{
+           if (!provider->enabled) {
+               provider->enabled = TRUE;
+               SCNetworkInterfaceProviderCheck(provider);
+           }
+       });
+    return;
+}
+
+#if TEST_SCNetworkInterfaceProvider
+
+/*
+  xcrun -sdk iphoneos.internal cc -o scnip SCNetworkInterfaceProvider.c -DTEST_SCNetworkInterfaceProvider -framework CoreFoundation -framework SystemConfiguration -arch arm64 -I ../IPMonitorControl ../IPMonitorControl/IPMonitorControl.c -DSC_LOG_HANDLE=__log_SCNetworkInterfaceProvider
+*/
+
+__private_extern__ os_log_t
+__log_SCNetworkInterfaceProvider(void)
+{
+       static os_log_t log     = NULL;
+
+       if (log == NULL) {
+               log = os_log_create("com.apple.SystemConfiguration", "SCNetworkConfiguration");
+       }
+
+       return log;
+}
+
+static void
+event_handler(SCNetworkInterfaceProviderRef provider,
+             SCNetworkInterfaceProviderEvent event,
+             CFDictionaryRef event_data)
+{
+    printf("<%p> event %d\n", provider, event);
+}
+
+int
+main(int argc, char * argv[])
+{
+    SCNetworkInterfaceProviderEventHandler     handler;
+    SCNetworkInterfaceProviderRef              provider;
+
+    provider
+       = SCNetworkInterfaceProviderCreate(kSCNetworkInterfaceTypeWWAN,
+                                          CFSTR("pdp_ip10"),
+                                          NULL);
+    if (provider == NULL) {
+       fprintf(stderr, "SCNetworkInterfaceProviderCreate failed\n");
+       exit(1);
+    }
+    handler = ^(SCNetworkInterfaceProviderEvent event,
+               CFDictionaryRef event_data) {
+       event_handler(provider, event, event_data);
+    };
+    SCNetworkInterfaceProviderSetEventHandler(provider, handler);
+    SCNetworkInterfaceProviderResume(provider);
+    dispatch_main();
+    exit(0);
+    return (0);
+}
+#endif