--- /dev/null
+/*
+ * Copyright (c) 2017 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
+ *
+ * April 17, 2017 Allan Nathanson <ajn@apple.com>
+ * - initial revision
+ */
+
+
+#include "nat64-configuration.h"
+
+#include <TargetConditionals.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <SystemConfiguration/SCPrivate.h>
+#include "ip_plugin.h"
+
+#define INET6 1
+
+#include <string.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <network/nat64.h>
+
+
+static CFMutableSetRef nat64_prefix_requests = NULL;
+
+
+static dispatch_queue_t
+nat64_dispatch_queue()
+{
+ static dispatch_once_t once;
+ static dispatch_queue_t q;
+
+ dispatch_once(&once, ^{
+ q = dispatch_queue_create("nat64 prefix request queue", NULL);
+ });
+
+ return q;
+}
+
+
+static __inline__ void
+_nat64_prefix_request_complete(const char *if_name,
+ int32_t num_prefixes,
+ nw_nat64_prefix_t *prefixes)
+{
+ struct if_nat64req req;
+ int ret;
+ int s;
+
+ SC_log(LOG_DEBUG, "%s: _nat64_prefix_request_complete", if_name);
+
+ // pass NAT64 prefixes to the kernel
+ bzero(&req, 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);
+ }
+
+ 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);
+
+ 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)
+ }
+ }
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s == -1) {
+ SC_log(LOG_ERR, "socket() failed: %s", strerror(errno));
+ return;
+ }
+ ret = ioctl(s, SIOCSIFNAT64PREFIX, &req);
+ close(s);
+ if (ret == -1) {
+ if ((errno != ENOENT) || (num_prefixes != 0)) {
+ SC_log(LOG_ERR, "%s: ioctl(SIOCSIFNAT64PREFIX) failed: %s", if_name, strerror(errno));
+ }
+ return;
+ }
+
+ SC_log(LOG_INFO, "%s: nat64 prefix%s updated", if_name, (num_prefixes != 1) ? "es" : "");
+ return;
+}
+
+
+static void
+_nat64_prefix_request_start(const void *value)
+{
+ unsigned int if_index;
+ char *if_name;
+ CFStringRef interface = (CFStringRef)value;
+ bool ok;
+
+ SC_log(LOG_DEBUG, "%@: _nat64_prefix_request_start", interface);
+
+ 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;
+ }
+
+ if_index = my_if_nametoindex(if_name);
+ if (if_index == 0) {
+ SC_log(LOG_NOTICE, "%s: no interface index", if_name);
+ CFAllocatorDeallocate(NULL, if_name);
+ return;
+ }
+
+ // keep track of interfaces with active nat64 prefix requests
+ CFSetAddValue(nat64_prefix_requests, interface);
+
+ CFRetain(interface);
+ 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
+ _nat64_prefix_request_complete(if_name, num_prefixes, prefixes);
+ } 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);
+ }
+
+ // 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);
+ }
+
+ return;
+}
+
+
+static void
+_nat64_prefix_request(const void *value, void *context)
+{
+ CFSetRef changes = (CFSetRef)context;
+ CFStringRef interface = (CFStringRef)value;
+
+ 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);
+ }
+
+ return;
+}
+
+
+static void
+_nat64_prefix_update(const void *value, void *context)
+{
+#pragma unused(context)
+ CFStringRef interface = (CFStringRef)value;
+
+ if (CFSetContainsValue(nat64_prefix_requests, interface)) {
+ _nat64_prefix_request_start(interface);
+ }
+
+ return;
+}
+
+
+#pragma mark -
+#pragma mark NAT64 prefix functions (for IPMonitor)
+
+
+__private_extern__
+Boolean
+is_nat64_prefix_request(CFStringRef change, CFStringRef *interface)
+{
+ CFArrayRef components;
+ static CFStringRef prefix = NULL;
+ Boolean yn = FALSE;
+ static dispatch_once_t once;
+
+ dispatch_once(&once, ^{
+ prefix = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState);
+ });
+
+ *interface = NULL;
+ if (!CFStringHasPrefix(change, prefix) ||
+ !CFStringHasSuffix(change, kSCEntNetNAT64PrefixRequest)) {
+ return FALSE;
+ }
+
+ components = CFStringCreateArrayBySeparatingStrings(NULL, change, CFSTR("/"));
+ if (CFArrayGetCount(components) == 5) {
+ *interface = CFArrayGetValueAtIndex(components, 3);
+ CFRetain(*interface);
+ yn = TRUE;
+ }
+ CFRelease(components);
+
+ return yn;
+}
+
+
+__private_extern__ void
+nat64_prefix_request_add_pattern(CFMutableArrayRef patterns)
+{
+ CFStringRef pattern;
+
+ pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
+ kSCDynamicStoreDomainState,
+ kSCCompAnyRegex,
+ kSCEntNetNAT64PrefixRequest);
+ CFArrayAppendValue(patterns, pattern);
+ CFRelease(pattern);
+ return;
+}
+
+
+__private_extern__
+void
+nat64_configuration_init(CFBundleRef bundle)
+{
+#pragma unused(bundle)
+ nat64_prefix_requests = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+ return;
+}
+
+
+__private_extern__
+void
+nat64_configuration_update(CFSetRef requests, CFSetRef changes)
+{
+ // for any interface that changed, refresh the nat64 prefix
+ if (changes != NULL) {
+ CFSetApplyFunction(changes, _nat64_prefix_update, NULL);
+ }
+
+ // for any requested interface, query the nat64 prefix
+ if (requests != NULL) {
+ CFSetApplyFunction(requests, _nat64_prefix_request, (void *)changes);
+ }
+
+ return;
+}