2 * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * April 17, 2017 Allan Nathanson <ajn@apple.com>
32 #include "nat64-configuration.h"
34 #include <TargetConditionals.h>
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <SystemConfiguration/SystemConfiguration.h>
37 #include <SystemConfiguration/SCPrivate.h>
38 #if TEST_NAT64_CONFIGURATION
39 static Boolean G_set_prefixes_force_failure
;
40 #define my_if_nametoindex if_nametoindex
42 #include "ip_plugin.h"
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
51 #include <sys/sockio.h>
52 #include <netinet/in.h>
53 #include <nw/private.h>
54 #include <sys/queue.h>
60 static dispatch_queue_t
61 nat64_dispatch_queue(void)
63 static dispatch_once_t once
;
64 static dispatch_queue_t q
;
66 dispatch_once(&once
, ^{
67 q
= dispatch_queue_create("nat64 prefix request queue", NULL
);
74 _nat64_prefix_set(const char *if_name
,
76 nw_nat64_prefix_t
*prefixes
)
78 struct if_nat64req req
;
82 // pass NAT64 prefixes to the kernel
83 memset(&req
, 0, sizeof(req
));
84 strlcpy(req
.ifnat64_name
, if_name
, sizeof(req
.ifnat64_name
));
86 if (num_prefixes
== 0) {
87 SC_log(LOG_NOTICE
, "%s: nat64 prefix unavailable", if_name
);
90 for (int32_t i
= 0; i
< num_prefixes
; i
++) {
91 char prefix_str
[NW_NAT64_PREFIX_STR_LENGTH
] = {0};
93 nw_nat64_write_prefix_to_string(&prefixes
[i
], prefix_str
, sizeof(prefix_str
));
94 SC_log(LOG_NOTICE
, "%s: nat64 prefix[%d] = %s", if_name
, i
, prefix_str
);
96 if (i
< NAT64_MAX_NUM_PREFIXES
) {
97 req
.ifnat64_prefixes
[i
].prefix_len
= prefixes
[i
].length
;
98 memcpy(&req
.ifnat64_prefixes
[i
].ipv6_prefix
,
100 MIN(sizeof(req
.ifnat64_prefixes
[i
].ipv6_prefix
), sizeof(prefixes
[i
].data
))); // MIN(16, 12)
104 s
= socket(AF_INET
, SOCK_DGRAM
, 0);
106 SC_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
109 ret
= ioctl(s
, SIOCSIFNAT64PREFIX
, &req
);
112 if ((errno
!= ENOENT
) || (num_prefixes
!= 0)) {
113 SC_log(LOG_ERR
, "%s: ioctl(SIOCSIFNAT64PREFIX) failed: %s", if_name
, strerror(errno
));
118 SC_log(LOG_NOTICE
, "%s: nat64 prefix%s updated", if_name
, (num_prefixes
!= 1) ? "es" : "");
124 _nat64_prefix_post(CFStringRef interface
,
125 int32_t num_prefixes
,
126 nw_nat64_prefix_t
*prefixes
,
127 CFAbsoluteTime start_time
)
129 #if TEST_NAT64_CONFIGURATION
130 #pragma unused(interface)
131 #pragma unused(num_prefixes)
132 #pragma unused(prefixes)
133 #pragma unused(start_time)
135 #else /* TEST_NAT64_CONFIGURATION */
139 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
140 kSCDynamicStoreDomainState
,
143 if (num_prefixes
>= 0) {
145 CFMutableDictionaryRef plat_dict
;
147 plat_dict
= CFDictionaryCreateMutable(NULL
,
149 &kCFTypeDictionaryKeyCallBacks
,
150 &kCFTypeDictionaryValueCallBacks
);
151 /* prefixes (if available) */
152 if (num_prefixes
> 0) {
153 CFMutableArrayRef prefix_array
;
155 prefix_array
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
156 for (int32_t i
= 0; i
< num_prefixes
; i
++) {
157 char prefix_str
[NW_NAT64_PREFIX_STR_LENGTH
] = {0};
160 nw_nat64_write_prefix_to_string(&prefixes
[i
], prefix_str
, sizeof(prefix_str
));
161 str
= CFStringCreateWithCString(NULL
, prefix_str
, kCFStringEncodingASCII
);
162 CFArrayAppendValue(prefix_array
, str
);
165 CFDictionarySetValue(plat_dict
, kSCPropNetNAT64PrefixList
, prefix_array
);
166 CFRelease(prefix_array
);
169 date
= CFDateCreate(NULL
, start_time
);
170 CFDictionarySetValue(plat_dict
,
171 kSCPropNetNAT64PLATDiscoveryStartTime
,
175 /* completion time */
176 date
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
177 CFDictionarySetValue(plat_dict
,
178 kSCPropNetNAT64PLATDiscoveryCompletionTime
,
182 (void)SCDynamicStoreSetValue(NULL
, key
, plat_dict
);
183 SC_log(LOG_NOTICE
, "%@: PLAT discovery complete %@",
184 interface
, plat_dict
);
185 CFRelease(plat_dict
);
187 (void)SCDynamicStoreRemoveValue(NULL
, key
);
190 #endif /* TEST_NAT64_CONFIGURATION */
194 static nw_nat64_prefixes_resolver_t
195 _nat64_resolver_create(unsigned int if_index
)
197 nw_interface_t interface
;
198 nw_parameters_t params
;
199 nw_nat64_prefixes_resolver_t resolver
;
201 params
= nw_parameters_create();
202 interface
= nw_interface_create_with_index(if_index
);
203 if (interface
== NULL
) {
205 "nw_interface_create_with_index(%u) failed",
209 nw_parameters_require_interface(params
, interface
);
210 nw_parameters_set_required_address_family(params
, AF_INET6
);
211 nw_release(interface
);
212 resolver
= nw_nat64_prefixes_resolver_create(params
);
218 ** NAT64PrefixRequest
220 struct NAT64PrefixRequest
;
221 typedef struct NAT64PrefixRequest NAT64PrefixRequest
, * NAT64PrefixRequestRef
;
222 #define NAT64PrefixRequest_LIST_ENTRY LIST_ENTRY(NAT64PrefixRequest)
223 #define NAT64PrefixRequest_LIST_HEAD LIST_HEAD(NAT64PrefixRequestHead, \
225 static NAT64PrefixRequest_LIST_HEAD S_request_head
;
226 static struct NAT64PrefixRequestHead
* S_request_head_p
= &S_request_head
;
228 typedef CF_ENUM(uint16_t, RequestFlags
) {
229 kRequestFlagsNone
= 0x0000,
230 kRequestFlagsValid
= 0x0001,
233 struct NAT64PrefixRequest
{
234 NAT64PrefixRequest_LIST_ENTRY link
;
235 nw_nat64_prefixes_resolver_t resolver
;
236 const char * if_name
;
237 CFStringRef if_name_cf
;
238 unsigned int if_index
;
239 unsigned int retain_count
;
244 NAT64PrefixRequestFlagsIsSet(NAT64PrefixRequestRef request
, RequestFlags flags
)
246 return ((request
->flags
& flags
) != 0);
250 NAT64PrefixRequestFlagsSet(NAT64PrefixRequestRef request
, RequestFlags flags
)
252 request
->flags
|= flags
;
256 NAT64PrefixRequestFlagsClear(NAT64PrefixRequestRef request
, RequestFlags flags
)
258 request
->flags
&= ~flags
;
261 static NAT64PrefixRequestRef
262 NAT64PrefixRequestFindInterface(CFStringRef if_name_cf
)
264 NAT64PrefixRequestRef scan
;
266 LIST_FOREACH(scan
, S_request_head_p
, link
) {
267 if (CFEqual(if_name_cf
, scan
->if_name_cf
)) {
275 NAT64PrefixRequestRetain(NAT64PrefixRequestRef request
)
277 request
->retain_count
++;
278 SC_log(LOG_DEBUG
, "%s: %s %p %u",
279 request
->if_name
, __FUNCTION__
,
280 request
, request
->retain_count
);
284 static NAT64PrefixRequestRef
285 NAT64PrefixRequestCreate(CFStringRef if_name_cf
)
287 unsigned int if_index
;
289 NAT64PrefixRequestRef request
;
291 if_name
= _SC_cfstring_to_cstring(if_name_cf
, NULL
, 0,
292 kCFStringEncodingASCII
);
293 if (if_name
== NULL
) {
295 "%@: could not convert interface name",
299 if_index
= my_if_nametoindex(if_name
);
302 "%s: interface does not exist", if_name
);
303 CFAllocatorDeallocate(NULL
, if_name
);
306 request
= malloc(sizeof(*request
));
307 SC_log(LOG_DEBUG
, "%@: %s %p", if_name_cf
, __FUNCTION__
, request
);
308 bzero(request
, sizeof(*request
));
309 request
->if_name_cf
= CFRetain(if_name_cf
);
310 request
->if_name
= if_name
;
311 request
->if_index
= if_index
;
312 LIST_INSERT_HEAD(S_request_head_p
, request
, link
);
313 NAT64PrefixRequestFlagsSet(request
, kRequestFlagsValid
);
314 NAT64PrefixRequestRetain(request
);
319 NAT64PrefixRequestStopResolver(NAT64PrefixRequestRef request
)
321 if (request
->resolver
!= NULL
) {
322 SC_log(LOG_DEBUG
, "%s: %s",
323 request
->if_name
, __FUNCTION__
);
324 nw_nat64_prefixes_resolver_cancel(request
->resolver
);
325 nw_release(request
->resolver
);
326 request
->resolver
= NULL
;
332 NAT64PrefixRequestInvalidate(NAT64PrefixRequestRef request
)
334 SC_log(LOG_DEBUG
, "%s: %s", request
->if_name
, __FUNCTION__
);
335 NAT64PrefixRequestStopResolver(request
);
336 if (NAT64PrefixRequestFlagsIsSet(request
, kRequestFlagsValid
)) {
337 NAT64PrefixRequestFlagsClear(request
, kRequestFlagsValid
);
338 LIST_REMOVE(request
, link
);
344 NAT64PrefixRequestRelease(NAT64PrefixRequestRef request
)
346 if (request
->retain_count
== 0) {
347 SC_log(LOG_ERR
, "%s: retain count is zero %p",
348 __FUNCTION__
, request
);
351 request
->retain_count
--;
354 request
->if_name
, __FUNCTION__
, request
, request
->retain_count
);
355 if (request
->retain_count
!= 0) {
358 NAT64PrefixRequestInvalidate(request
);
359 SC_log(LOG_DEBUG
, "%s %s: deallocate %p",
360 request
->if_name
, __FUNCTION__
, request
);
361 if (request
->if_name_cf
!= NULL
) {
362 CFRelease(request
->if_name_cf
);
363 request
->if_name_cf
= NULL
;
365 if (request
->if_name
!= NULL
) {
366 CFAllocatorDeallocate(NULL
, (void *)request
->if_name
);
367 request
->if_name
= NULL
;
374 NAT64PrefixRequestStart(NAT64PrefixRequestRef request
)
376 dispatch_block_t cancel_handler
;
377 nw_nat64_copy_prefixes_block_t handler
;
378 nw_nat64_prefixes_resolver_t resolver
;
379 CFAbsoluteTime start_time
;
381 SC_log(LOG_INFO
, "%s: %s", request
->if_name
, __FUNCTION__
);
382 if (request
->resolver
!= NULL
) {
383 SC_log(LOG_DEBUG
, "%s %s: resolver is already active",
384 request
->if_name
, __FUNCTION__
);
387 resolver
= _nat64_resolver_create(request
->if_index
);
388 if (resolver
== NULL
) {
391 NAT64PrefixRequestRetain(request
);
393 SC_log(LOG_DEBUG
, "%s: NAT64 resolver cancelled",
395 NAT64PrefixRequestRelease(request
);
398 start_time
= CFAbsoluteTimeGetCurrent();
399 handler
= ^(int32_t num_prefixes
, nw_nat64_prefix_t
*prefixes
) {
400 Boolean remove_resolver
= FALSE
;
402 if (!NAT64PrefixRequestFlagsIsSet(request
,
403 kRequestFlagsValid
)) {
404 SC_log(LOG_INFO
, "%s: NAT64 request is stale %p",
405 request
->if_name
, request
);
408 if (prefixes
!= NULL
) {
409 /* set prefixes on the interface */
410 _nat64_prefix_set(request
->if_name
,
411 num_prefixes
, prefixes
);
412 remove_resolver
= TRUE
;
414 SC_log(LOG_ERR
, "%s: NAT64 no prefixes",
417 _nat64_prefix_post(request
->if_name_cf
,
418 num_prefixes
, prefixes
, start_time
);
419 #if TEST_NAT64_CONFIGURATION
420 if (G_set_prefixes_force_failure
) {
421 remove_resolver
= TRUE
;
423 #endif /* TEST_NAT64_CONFIGURATION */
424 if (remove_resolver
) {
425 /* remove resolver */
426 NAT64PrefixRequestInvalidate(request
);
427 NAT64PrefixRequestRelease(request
);
431 nw_nat64_prefixes_resolver_set_cancel_handler(resolver
, cancel_handler
);
432 nw_nat64_prefixes_resolver_set_update_handler(resolver
,
433 nat64_dispatch_queue(),
435 nw_nat64_prefixes_resolver_start(resolver
);
436 request
->resolver
= resolver
;
444 _nat64_process_prefix_request(const void *value
, void *context
)
446 #pragma unused(context)
447 CFStringRef interface
= (CFStringRef
)value
;
448 NAT64PrefixRequestRef request
;
450 request
= NAT64PrefixRequestFindInterface(interface
);
451 if (request
!= NULL
) {
455 /* start a new request */
456 request
= NAT64PrefixRequestCreate(interface
);
457 if (request
!= NULL
) {
458 NAT64PrefixRequestStart(request
);
464 _nat64_process_prefix_update(const void *value
, void *context
)
466 #pragma unused(context)
467 CFStringRef interface
= (CFStringRef
)value
;
468 NAT64PrefixRequestRef request
;
470 request
= NAT64PrefixRequestFindInterface(interface
);
471 if (request
== NULL
) {
472 SC_log(LOG_DEBUG
, "%@ %s: no existing request",
473 interface
, __FUNCTION__
);
477 /* destroy the old one, start a new one */
478 SC_log(LOG_INFO
, "%@: %s", interface
, __FUNCTION__
);
479 NAT64PrefixRequestInvalidate(request
);
480 NAT64PrefixRequestRelease(request
);
482 /* start a new request */
483 request
= NAT64PrefixRequestCreate(interface
);
484 if (request
!= NULL
) {
485 NAT64PrefixRequestStart(request
);
491 _nat64_process_cancel_request(const void * value
, void * context
)
493 #pragma unused(context)
494 CFStringRef interface
= (CFStringRef
)value
;
495 NAT64PrefixRequestRef request
;
497 /* if there's an in-flight request, remove it */
498 request
= NAT64PrefixRequestFindInterface(interface
);
499 if (request
== NULL
) {
501 SC_log(LOG_DEBUG
, "%@ %s: no active NAT64 request",
502 interface
, __FUNCTION__
);
505 SC_log(LOG_DEBUG
, "%s %s: removing NAT64 request",
506 request
->if_name
, __FUNCTION__
);
507 _nat64_prefix_set(request
->if_name
, 0, NULL
);
508 NAT64PrefixRequestInvalidate(request
);
509 NAT64PrefixRequestRelease(request
);
515 #pragma mark NAT64 prefix functions (for IPMonitor)
520 is_nat64_prefix_request(CFStringRef change
, CFStringRef
*interface
)
522 CFArrayRef components
;
523 static CFStringRef prefix
= NULL
;
525 static dispatch_once_t once
;
527 dispatch_once(&once
, ^{
528 prefix
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
532 if (!CFStringHasPrefix(change
, prefix
) ||
533 !CFStringHasSuffix(change
, kSCEntNetNAT64PrefixRequest
)) {
537 components
= CFStringCreateArrayBySeparatingStrings(NULL
, change
, CFSTR("/"));
538 if (CFArrayGetCount(components
) == 5) {
539 *interface
= CFArrayGetValueAtIndex(components
, 3);
540 CFRetain(*interface
);
543 CFRelease(components
);
549 __private_extern__
void
550 nat64_prefix_request_add_pattern(CFMutableArrayRef patterns
)
554 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
555 kSCDynamicStoreDomainState
,
557 kSCEntNetNAT64PrefixRequest
);
558 CFArrayAppendValue(patterns
, pattern
);
564 nat64_configuration_update_locked(CFSetRef requests
, CFSetRef updates
,
565 CFSetRef cancellations
)
567 if (cancellations
!= NULL
) {
568 CFSetApplyFunction(cancellations
,
569 _nat64_process_cancel_request
,
572 // for any interface that changed, refresh the nat64 prefix
573 if (updates
!= NULL
) {
574 CFSetApplyFunction(updates
, _nat64_process_prefix_update
, NULL
);
577 // for any requested interface, query the nat64 prefix
578 if (requests
!= NULL
) {
579 CFSetApplyFunction(requests
, _nat64_process_prefix_request
,
587 nat64_configuration_update(CFSetRef requests
, CFSetRef updates
,
588 CFSetRef cancellations
)
590 dispatch_block_t update_block
;
592 if (requests
!= NULL
) {
595 if (updates
!= NULL
) {
598 if (cancellations
!= NULL
) {
599 CFRetain(cancellations
);
603 "NAT64 requests %@ updates %@ cancellations %@",
604 requests
, updates
, cancellations
);
605 nat64_configuration_update_locked(requests
, updates
,
607 if (requests
!= NULL
) {
610 if (updates
!= NULL
) {
613 if (cancellations
!= NULL
) {
614 CFRelease(cancellations
);
617 dispatch_async(nat64_dispatch_queue(), update_block
);
621 #if TEST_NAT64_CONFIGURATION
623 main(int argc
, char * argv
[])
625 CFStringRef if_name_cf
;
628 set
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
629 for (int i
= 1; i
< argc
; i
++) {
630 if_name_cf
= CFStringCreateWithCString(NULL
,
632 kCFStringEncodingASCII
);
633 CFSetAddValue(set
, if_name_cf
);
634 CFRelease(if_name_cf
);
636 if (CFSetGetCount(set
) == 0) {
637 fprintf(stderr
, "nothing to do\n");
640 SC_log(LOG_NOTICE
, "Starting %@", set
);
641 nat64_configuration_update(set
, NULL
, NULL
);
644 SC_log(LOG_NOTICE
, "Starting 2 %@", set
);
645 nat64_configuration_update(set
, NULL
, NULL
);
648 SC_log(LOG_NOTICE
, "Updating");
649 nat64_configuration_update(NULL
, set
, NULL
);
652 SC_log(LOG_NOTICE
, "Cancelling");
653 nat64_configuration_update(NULL
, NULL
, set
);
656 G_set_prefixes_force_failure
= TRUE
;
657 SC_log(LOG_NOTICE
, "Starting (with forced failure) %@", set
);
658 nat64_configuration_update(set
, NULL
, NULL
);
661 SC_log(LOG_NOTICE
, "Starting (with forced failure 2) %@", set
);
662 nat64_configuration_update(set
, NULL
, NULL
);
668 #endif /* TEST_NAT64_CONFIGURATION */