/*
- * 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;
return q;
}
-
static Boolean
_nat64_prefix_set(const char *if_name,
int32_t num_prefixes,
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)
}
}
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);
}
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,
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;
}
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 */