]> git.saurik.com Git - apple/configd.git/blobdiff - Plugins/IPMonitor/nat64-configuration.c
configd-1109.101.1.tar.gz
[apple/configd.git] / Plugins / IPMonitor / nat64-configuration.c
index 80ec61d61f22c386468f4d6c9cd205ad909a4f88..b44479f44cf5b34e869e16ddc3af5fb3c72a3074 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018 Apple Inc. All rights reserved.
+ * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
 #include <CoreFoundation/CoreFoundation.h>
 #include <SystemConfiguration/SystemConfiguration.h>
 #include <SystemConfiguration/SCPrivate.h>
+#if TEST_NAT64_CONFIGURATION
+static Boolean                 G_set_prefixes_force_failure;
+#define my_if_nametoindex      if_nametoindex
+#else
 #include "ip_plugin.h"
+#endif
 
 #define        INET6   1
 
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/sockio.h>
-#if __has_include(<nw/private.h>)
+#include <netinet/in.h>
 #include <nw/private.h>
-#else // __has_include(<nw/private.h>)
-#include <network/nat64.h>
-#endif // __has_include(<nw/private.h>)
-
-
-static CFMutableSetRef nat64_prefix_requests   = NULL;
+#include <sys/queue.h>
 
 
+/**
+ ** Support functions
+ **/
 static dispatch_queue_t
-nat64_dispatch_queue()
+nat64_dispatch_queue(void)
 {
        static dispatch_once_t  once;
        static dispatch_queue_t q;
@@ -67,7 +70,6 @@ nat64_dispatch_queue()
        return q;
 }
 
-
 static Boolean
 _nat64_prefix_set(const char           *if_name,
                  int32_t               num_prefixes,
@@ -77,27 +79,25 @@ _nat64_prefix_set(const char                *if_name,
        int                     ret;
        int                     s;
 
-       SC_log(LOG_DEBUG, "%s: _nat64_prefix_set", if_name);
-
        // pass NAT64 prefixes to the kernel
-       bzero(&req, sizeof(req));
+       memset(&req, 0, sizeof(req));
        strlcpy(req.ifnat64_name, if_name, sizeof(req.ifnat64_name));
 
        if (num_prefixes == 0) {
-               SC_log(LOG_INFO, "%s: nat64 prefix not (or no longer) available", if_name);
+               SC_log(LOG_NOTICE, "%s: nat64 prefix unavailable", if_name);
        }
 
        for (int32_t i = 0; i < num_prefixes; i++) {
                char    prefix_str[NW_NAT64_PREFIX_STR_LENGTH]  = {0};
 
                nw_nat64_write_prefix_to_string(&prefixes[i], prefix_str, sizeof(prefix_str));
-               SC_log(LOG_DEBUG, "%s: nat64 prefix[%d] = %s", if_name, i, prefix_str);
+               SC_log(LOG_NOTICE, "%s: nat64 prefix[%d] = %s", if_name, i, prefix_str);
 
                if (i < NAT64_MAX_NUM_PREFIXES) {
                        req.ifnat64_prefixes[i].prefix_len = prefixes[i].length;
-                       bcopy(&prefixes[i].data,
-                             &req.ifnat64_prefixes[i].ipv6_prefix,
-                             MIN(sizeof(req.ifnat64_prefixes[i].ipv6_prefix), sizeof(prefixes[i].data)));      // MIN(16, 12)
+                       memcpy(&req.ifnat64_prefixes[i].ipv6_prefix,
+                              &prefixes[i].data,
+                              MIN(sizeof(req.ifnat64_prefixes[i].ipv6_prefix), sizeof(prefixes[i].data)));     // MIN(16, 12)
                }
        }
 
@@ -115,7 +115,7 @@ _nat64_prefix_set(const char                *if_name,
                return (FALSE);
        }
 
-       SC_log(LOG_INFO, "%s: nat64 prefix%s updated", if_name, (num_prefixes != 1) ? "es" : "");
+       SC_log(LOG_NOTICE, "%s: nat64 prefix%s updated", if_name, (num_prefixes != 1) ? "es" : "");
        return (TRUE);
 }
 
@@ -126,6 +126,14 @@ _nat64_prefix_post(CFStringRef             interface,
                   nw_nat64_prefix_t    *prefixes,
                   CFAbsoluteTime       start_time)
 {
+#if TEST_NAT64_CONFIGURATION
+#pragma unused(interface)
+#pragma unused(num_prefixes)
+#pragma unused(prefixes)
+#pragma unused(start_time)
+       return;
+#else /* TEST_NAT64_CONFIGURATION */
+
        CFStringRef     key;
 
        key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
@@ -172,113 +180,333 @@ _nat64_prefix_post(CFStringRef          interface,
                CFRelease(date);
 
                (void)SCDynamicStoreSetValue(NULL, key, plat_dict);
-               SC_log(LOG_INFO, "%@: PLAT discovery complete %@",
+               SC_log(LOG_NOTICE, "%@: PLAT discovery complete %@",
                       interface, plat_dict);
                CFRelease(plat_dict);
        } else {
                (void)SCDynamicStoreRemoveValue(NULL, key);
        }
        CFRelease(key);
+#endif /* TEST_NAT64_CONFIGURATION */
        return;
 }
 
+static nw_nat64_prefixes_resolver_t
+_nat64_resolver_create(unsigned int if_index)
+{
+       nw_interface_t                  interface;
+       nw_parameters_t                 params;
+       nw_nat64_prefixes_resolver_t    resolver;
+
+       params = nw_parameters_create();
+       interface = nw_interface_create_with_index(if_index);
+       if (interface == NULL) {
+               SC_log(LOG_NOTICE,
+                      "nw_interface_create_with_index(%u) failed",
+                      if_index);
+               return (NULL);
+       }
+       nw_parameters_require_interface(params, interface);
+       nw_parameters_set_required_address_family(params, AF_INET6);
+       nw_release(interface);
+       resolver = nw_nat64_prefixes_resolver_create(params);
+       nw_release(params);
+       return (resolver);
+}
+
+/**
+ ** NAT64PrefixRequest
+ **/
+struct NAT64PrefixRequest;
+typedef struct NAT64PrefixRequest NAT64PrefixRequest, * NAT64PrefixRequestRef;
+#define NAT64PrefixRequest_LIST_ENTRY LIST_ENTRY(NAT64PrefixRequest)
+#define NAT64PrefixRequest_LIST_HEAD LIST_HEAD(NAT64PrefixRequestHead, \
+                                              NAT64PrefixRequest)
+static NAT64PrefixRequest_LIST_HEAD    S_request_head;
+static struct NAT64PrefixRequestHead *         S_request_head_p = &S_request_head;
+
+typedef CF_ENUM(uint16_t, RequestFlags) {
+       kRequestFlagsNone = 0x0000,
+       kRequestFlagsValid = 0x0001,
+};
+
+struct NAT64PrefixRequest {
+       NAT64PrefixRequest_LIST_ENTRY   link;
+       nw_nat64_prefixes_resolver_t    resolver;
+       const char *                    if_name;
+       CFStringRef                     if_name_cf;
+       unsigned int                    if_index;
+       unsigned int                    retain_count;
+       RequestFlags                    flags;
+};
+
+static Boolean
+NAT64PrefixRequestFlagsIsSet(NAT64PrefixRequestRef request, RequestFlags flags)
+{
+    return ((request->flags & flags) != 0);
+}
 
 static void
-_nat64_prefix_request_start(const void *value)
+NAT64PrefixRequestFlagsSet(NAT64PrefixRequestRef request, RequestFlags flags)
 {
-       unsigned int    if_index;
-       char            *if_name;
-       CFStringRef     interface       = (CFStringRef)value;
-       bool            ok;
-       CFAbsoluteTime  start_time;
+    request->flags |= flags;
+}
 
-       SC_log(LOG_DEBUG, "%@: _nat64_prefix_request_start", interface);
+static void
+NAT64PrefixRequestFlagsClear(NAT64PrefixRequestRef request, RequestFlags flags)
+{
+    request->flags &= ~flags;
+}
 
-       if_name = _SC_cfstring_to_cstring(interface, NULL, 0, kCFStringEncodingASCII);
-       if (if_name == NULL) {
-               SC_log(LOG_NOTICE, "%@: could not convert interface name", interface);
-               return;
+static NAT64PrefixRequestRef
+NAT64PrefixRequestFindInterface(CFStringRef if_name_cf)
+{
+       NAT64PrefixRequestRef           scan;
+
+       LIST_FOREACH(scan, S_request_head_p, link) {
+               if (CFEqual(if_name_cf, scan->if_name_cf)) {
+                       return (scan);
+               }
        }
+       return (NULL);
+}
+
+static void
+NAT64PrefixRequestRetain(NAT64PrefixRequestRef request)
+{
+       request->retain_count++;
+       SC_log(LOG_DEBUG, "%s: %s %p %u",
+              request->if_name, __FUNCTION__,
+              request, request->retain_count);
+       return;
+}
 
+static NAT64PrefixRequestRef
+NAT64PrefixRequestCreate(CFStringRef if_name_cf)
+{
+       unsigned int            if_index;
+       char *                  if_name;
+       NAT64PrefixRequestRef   request;
+
+       if_name = _SC_cfstring_to_cstring(if_name_cf, NULL, 0,
+                                         kCFStringEncodingASCII);
+       if (if_name == NULL) {
+               SC_log(LOG_ERR,
+                      "%@: could not convert interface name",
+                      if_name_cf);
+               return (NULL);
+       }
        if_index = my_if_nametoindex(if_name);
        if (if_index == 0) {
-               SC_log(LOG_NOTICE, "%s: no interface index", if_name);
+               SC_log(LOG_NOTICE,
+                      "%s: interface does not exist", if_name);
                CFAllocatorDeallocate(NULL, if_name);
-               return;
+               return (NULL);
        }
+       request = malloc(sizeof(*request));
+       SC_log(LOG_DEBUG, "%@: %s %p", if_name_cf, __FUNCTION__, request);
+       bzero(request, sizeof(*request));
+       request->if_name_cf = CFRetain(if_name_cf);
+       request->if_name = if_name;
+       request->if_index = if_index;
+       LIST_INSERT_HEAD(S_request_head_p, request, link);
+       NAT64PrefixRequestFlagsSet(request, kRequestFlagsValid);
+       NAT64PrefixRequestRetain(request);
+       return (request);
+}
 
-       // keep track of interfaces with active nat64 prefix requests
-       CFSetAddValue(nat64_prefix_requests, interface);
+static void
+NAT64PrefixRequestStopResolver(NAT64PrefixRequestRef request)
+{
+       if (request->resolver != NULL) {
+               SC_log(LOG_DEBUG, "%s: %s",
+                      request->if_name, __FUNCTION__);
+               nw_nat64_prefixes_resolver_cancel(request->resolver);
+               nw_release(request->resolver);
+               request->resolver = NULL;
+       }
+       return;
+}
 
-       CFRetain(interface);
-       start_time = CFAbsoluteTimeGetCurrent();
-       ok = nw_nat64_copy_prefixes_async(&if_index,
-                                         nat64_dispatch_queue(),
-                                         ^(int32_t num_prefixes, nw_nat64_prefix_t *prefixes) {
-                                                 if (num_prefixes >= 0) {
-                                                         // update interface
-                                                         if (!_nat64_prefix_set(if_name, num_prefixes, prefixes)) {
-                                                                 num_prefixes = -1;
-                                                         }
-                                                 } else {
-                                                         SC_log(LOG_ERR,
-                                                                "%s: nw_nat64_copy_prefixes_async() num_prefixes(%d) < 0",
-                                                                if_name,
-                                                                num_prefixes);
-                                                 }
-
-                                                 if (num_prefixes <= 0) {
-                                                         // remove from active list
-                                                         CFSetRemoveValue(nat64_prefix_requests, interface);
-                                                 }
-
-                                                 _nat64_prefix_post(interface, num_prefixes, prefixes, start_time);
-
-                                                 // cleanup
-                                                 CFRelease(interface);
-                                                 CFAllocatorDeallocate(NULL, if_name);
-                                         });
-       if (!ok) {
-               SC_log(LOG_ERR, "%s: nw_nat64_copy_prefixes_async() failed", if_name);
-
-               // remove from active list
-               CFSetRemoveValue(nat64_prefix_requests, interface);
-
-               CFRelease(interface);
-               CFAllocatorDeallocate(NULL, if_name);
+static void
+NAT64PrefixRequestInvalidate(NAT64PrefixRequestRef request)
+{
+       SC_log(LOG_DEBUG, "%s: %s", request->if_name, __FUNCTION__);
+       NAT64PrefixRequestStopResolver(request);
+       if (NAT64PrefixRequestFlagsIsSet(request, kRequestFlagsValid)) {
+               NAT64PrefixRequestFlagsClear(request, kRequestFlagsValid);
+               LIST_REMOVE(request, link);
        }
+       return;
+}
 
+static void
+NAT64PrefixRequestRelease(NAT64PrefixRequestRef request)
+{
+       if (request->retain_count == 0) {
+               SC_log(LOG_ERR, "%s: retain count is zero %p",
+                      __FUNCTION__, request);
+               return;
+       }
+       request->retain_count--;
+       SC_log(LOG_DEBUG,
+              "%s: %s %p %u",
+              request->if_name, __FUNCTION__, request, request->retain_count);
+       if (request->retain_count != 0) {
+               return;
+       }
+       NAT64PrefixRequestInvalidate(request);
+       SC_log(LOG_DEBUG, "%s %s: deallocate %p",
+              request->if_name, __FUNCTION__, request);
+       if (request->if_name_cf != NULL) {
+               CFRelease(request->if_name_cf);
+               request->if_name_cf = NULL;
+       }
+       if (request->if_name != NULL) {
+               CFAllocatorDeallocate(NULL, (void *)request->if_name);
+               request->if_name = NULL;
+       }
+       free(request);
        return;
 }
 
+static void
+NAT64PrefixRequestStart(NAT64PrefixRequestRef request)
+{
+       dispatch_block_t                cancel_handler;
+       nw_nat64_copy_prefixes_block_t  handler;
+       nw_nat64_prefixes_resolver_t    resolver;
+       CFAbsoluteTime                  start_time;
+
+       SC_log(LOG_INFO, "%s: %s",  request->if_name, __FUNCTION__);
+       if (request->resolver != NULL) {
+               SC_log(LOG_DEBUG, "%s %s: resolver is already active",
+                      request->if_name, __FUNCTION__);
+               return;
+       }
+       resolver = _nat64_resolver_create(request->if_index);
+       if (resolver == NULL) {
+               return;
+       }
+       NAT64PrefixRequestRetain(request);
+       cancel_handler = ^{
+               SC_log(LOG_DEBUG, "%s: NAT64 resolver cancelled",
+                      request->if_name);
+               NAT64PrefixRequestRelease(request);
+               return;
+       };
+       start_time = CFAbsoluteTimeGetCurrent();
+       handler = ^(int32_t num_prefixes, nw_nat64_prefix_t *prefixes) {
+               Boolean remove_resolver = FALSE;
+
+               if (!NAT64PrefixRequestFlagsIsSet(request,
+                                                 kRequestFlagsValid)) {
+                       SC_log(LOG_INFO, "%s: NAT64 request is stale %p",
+                              request->if_name, request);
+                       return;
+               }
+               if (prefixes != NULL) {
+                       /* set prefixes on the interface */
+                       _nat64_prefix_set(request->if_name,
+                                         num_prefixes, prefixes);
+                       remove_resolver = TRUE;
+               } else {
+                       SC_log(LOG_ERR, "%s: NAT64 no prefixes",
+                              request->if_name);
+               }
+               _nat64_prefix_post(request->if_name_cf,
+                                  num_prefixes, prefixes, start_time);
+#if TEST_NAT64_CONFIGURATION
+               if (G_set_prefixes_force_failure) {
+                       remove_resolver = TRUE;
+               }
+#endif /* TEST_NAT64_CONFIGURATION */
+               if (remove_resolver) {
+                       /* remove resolver */
+                       NAT64PrefixRequestInvalidate(request);
+                       NAT64PrefixRequestRelease(request);
+                       return;
+               }
+       };
+       nw_nat64_prefixes_resolver_set_cancel_handler(resolver, cancel_handler);
+       nw_nat64_prefixes_resolver_set_update_handler(resolver,
+                                                     nat64_dispatch_queue(),
+                                                     handler);
+       nw_nat64_prefixes_resolver_start(resolver);
+       request->resolver = resolver;
+       return;
+}
 
+/**
+ ** Set iterators
+ **/
 static void
-_nat64_prefix_request(const void *value, void *context)
+_nat64_process_prefix_request(const void *value, void *context)
 {
-       CFSetRef        changes         = (CFSetRef)context;
-       CFStringRef     interface       = (CFStringRef)value;
+#pragma unused(context)
+       CFStringRef             interface = (CFStringRef)value;
+       NAT64PrefixRequestRef   request;
 
-       if (!CFSetContainsValue(nat64_prefix_requests, interface) ||
-           ((changes != NULL) && CFSetContainsValue(changes, interface))) {
-               // if new request
-               // ... or a [refresh] request that hasn't already been started
-               _nat64_prefix_request_start(interface);
+       request = NAT64PrefixRequestFindInterface(interface);
+       if (request != NULL) {
+               return;
        }
 
+       /* start a new request */
+       request = NAT64PrefixRequestCreate(interface);
+       if (request != NULL) {
+               NAT64PrefixRequestStart(request);
+       }
        return;
 }
 
-
 static void
-_nat64_prefix_update(const void *value, void *context)
+_nat64_process_prefix_update(const void *value, void *context)
 {
 #pragma unused(context)
-       CFStringRef     interface       = (CFStringRef)value;
+       CFStringRef             interface = (CFStringRef)value;
+       NAT64PrefixRequestRef   request;
 
-       if (CFSetContainsValue(nat64_prefix_requests, interface)) {
-               _nat64_prefix_request_start(interface);
+       request = NAT64PrefixRequestFindInterface(interface);
+       if (request == NULL) {
+               SC_log(LOG_DEBUG, "%@ %s: no existing request",
+                      interface, __FUNCTION__);
+               return;
        }
 
+       /* destroy the old one, start a new one */
+       SC_log(LOG_INFO, "%@: %s", interface, __FUNCTION__);
+       NAT64PrefixRequestInvalidate(request);
+       NAT64PrefixRequestRelease(request);
+
+       /* start a new request */
+       request = NAT64PrefixRequestCreate(interface);
+       if (request != NULL) {
+               NAT64PrefixRequestStart(request);
+       }
+       return;
+}
+
+static void
+_nat64_process_cancel_request(const void * value, void * context)
+{
+#pragma unused(context)
+       CFStringRef             interface = (CFStringRef)value;
+       NAT64PrefixRequestRef   request;
+
+       /* if there's an in-flight request, remove it */
+       request = NAT64PrefixRequestFindInterface(interface);
+       if (request == NULL) {
+               /* no resolver */
+               SC_log(LOG_DEBUG, "%@ %s: no active NAT64 request",
+                      interface, __FUNCTION__);
+               return;
+       }
+       SC_log(LOG_DEBUG, "%s %s: removing NAT64 request",
+              request->if_name,  __FUNCTION__);
+       _nat64_prefix_set(request->if_name, 0, NULL);
+       NAT64PrefixRequestInvalidate(request);
+       NAT64PrefixRequestRelease(request);
        return;
 }
 
@@ -332,30 +560,109 @@ nat64_prefix_request_add_pattern(CFMutableArrayRef patterns)
        return;
 }
 
-
-__private_extern__
-void
-nat64_configuration_init(CFBundleRef bundle)
+static void
+nat64_configuration_update_locked(CFSetRef requests, CFSetRef updates,
+                                 CFSetRef cancellations)
 {
-#pragma unused(bundle)
-       nat64_prefix_requests = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+       if (cancellations != NULL) {
+               CFSetApplyFunction(cancellations,
+                                  _nat64_process_cancel_request,
+                                  NULL);
+       }
+       // for any interface that changed, refresh the nat64 prefix
+       if (updates != NULL) {
+               CFSetApplyFunction(updates, _nat64_process_prefix_update, NULL);
+       }
+
+       // for any requested interface, query the nat64 prefix
+       if (requests != NULL) {
+               CFSetApplyFunction(requests, _nat64_process_prefix_request,
+                                  NULL);
+       }
        return;
 }
 
-
 __private_extern__
 void
-nat64_configuration_update(CFSetRef requests, CFSetRef changes)
+nat64_configuration_update(CFSetRef requests, CFSetRef updates,
+                          CFSetRef cancellations)
 {
-       // for any interface that changed, refresh the nat64 prefix
-       if (changes != NULL) {
-               CFSetApplyFunction(changes, _nat64_prefix_update, NULL);
-       }
+       dispatch_block_t        update_block;
 
-       // for any requested interface, query the nat64 prefix
        if (requests != NULL) {
-               CFSetApplyFunction(requests, _nat64_prefix_request, (void *)changes);
+               CFRetain(requests);
        }
-
+       if (updates != NULL) {
+               CFRetain(updates);
+       }
+       if (cancellations != NULL) {
+               CFRetain(cancellations);
+       }
+       update_block = ^{
+               SC_log(LOG_DEBUG,
+                      "NAT64 requests %@ updates %@ cancellations %@",
+                      requests, updates, cancellations);
+               nat64_configuration_update_locked(requests, updates,
+                                                 cancellations);
+               if (requests != NULL) {
+                       CFRelease(requests);
+               }
+               if (updates != NULL) {
+                       CFRelease(updates);
+               }
+               if (cancellations != NULL) {
+                       CFRelease(cancellations);
+               }
+       };
+       dispatch_async(nat64_dispatch_queue(), update_block);
        return;
 }
+
+#if TEST_NAT64_CONFIGURATION
+int
+main(int argc, char * argv[])
+{
+       CFStringRef     if_name_cf;
+       CFMutableSetRef set;
+
+       set = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+       for (int i = 1; i < argc; i++) {
+               if_name_cf = CFStringCreateWithCString(NULL,
+                                                      argv[i],
+                                                      kCFStringEncodingASCII);
+               CFSetAddValue(set, if_name_cf);
+               CFRelease(if_name_cf);
+       }
+       if (CFSetGetCount(set) == 0) {
+               fprintf(stderr, "nothing to do\n");
+               exit(0);
+       }
+       SC_log(LOG_NOTICE, "Starting %@", set);
+       nat64_configuration_update(set, NULL, NULL);
+       sleep(2);
+
+       SC_log(LOG_NOTICE, "Starting 2 %@", set);
+       nat64_configuration_update(set, NULL, NULL);
+       sleep(2);
+
+       SC_log(LOG_NOTICE, "Updating");
+       nat64_configuration_update(NULL, set, NULL);
+       sleep(2);
+
+       SC_log(LOG_NOTICE, "Cancelling");
+       nat64_configuration_update(NULL, NULL, set);
+       sleep(2);
+
+       G_set_prefixes_force_failure = TRUE;
+       SC_log(LOG_NOTICE, "Starting (with forced failure) %@", set);
+       nat64_configuration_update(set, NULL, NULL);
+       sleep(2);
+
+       SC_log(LOG_NOTICE, "Starting (with forced failure 2) %@", set);
+       nat64_configuration_update(set, NULL, NULL);
+
+       dispatch_main();
+       exit(0);
+       return (0);
+}
+#endif /* TEST_NAT64_CONFIGURATION */