2 * Copyright (c) 2017, 2018 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 #include "ip_plugin.h"
44 #include <sys/ioctl.h>
45 #include <sys/socket.h>
46 #include <sys/sockio.h>
47 #if __has_include(<nw/private.h>)
48 #include <nw/private.h>
49 #else // __has_include(<nw/private.h>)
50 #include <network/nat64.h>
51 #endif // __has_include(<nw/private.h>)
54 static CFMutableSetRef nat64_prefix_requests
= NULL
;
57 static dispatch_queue_t
58 nat64_dispatch_queue()
60 static dispatch_once_t once
;
61 static dispatch_queue_t q
;
63 dispatch_once(&once
, ^{
64 q
= dispatch_queue_create("nat64 prefix request queue", NULL
);
72 _nat64_prefix_set(const char *if_name
,
74 nw_nat64_prefix_t
*prefixes
)
76 struct if_nat64req req
;
80 SC_log(LOG_DEBUG
, "%s: _nat64_prefix_set", if_name
);
82 // pass NAT64 prefixes to the kernel
83 bzero(&req
, sizeof(req
));
84 strlcpy(req
.ifnat64_name
, if_name
, sizeof(req
.ifnat64_name
));
86 if (num_prefixes
== 0) {
87 SC_log(LOG_INFO
, "%s: nat64 prefix not (or no longer) available", 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_DEBUG
, "%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 bcopy(&prefixes
[i
].data
,
99 &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_INFO
, "%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
)
131 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
132 kSCDynamicStoreDomainState
,
135 if (num_prefixes
>= 0) {
137 CFMutableDictionaryRef plat_dict
;
139 plat_dict
= CFDictionaryCreateMutable(NULL
,
141 &kCFTypeDictionaryKeyCallBacks
,
142 &kCFTypeDictionaryValueCallBacks
);
143 /* prefixes (if available) */
144 if (num_prefixes
> 0) {
145 CFMutableArrayRef prefix_array
;
147 prefix_array
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
148 for (int32_t i
= 0; i
< num_prefixes
; i
++) {
149 char prefix_str
[NW_NAT64_PREFIX_STR_LENGTH
] = {0};
152 nw_nat64_write_prefix_to_string(&prefixes
[i
], prefix_str
, sizeof(prefix_str
));
153 str
= CFStringCreateWithCString(NULL
, prefix_str
, kCFStringEncodingASCII
);
154 CFArrayAppendValue(prefix_array
, str
);
157 CFDictionarySetValue(plat_dict
, kSCPropNetNAT64PrefixList
, prefix_array
);
158 CFRelease(prefix_array
);
161 date
= CFDateCreate(NULL
, start_time
);
162 CFDictionarySetValue(plat_dict
,
163 kSCPropNetNAT64PLATDiscoveryStartTime
,
167 /* completion time */
168 date
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
169 CFDictionarySetValue(plat_dict
,
170 kSCPropNetNAT64PLATDiscoveryCompletionTime
,
174 (void)SCDynamicStoreSetValue(NULL
, key
, plat_dict
);
175 SC_log(LOG_INFO
, "%@: PLAT discovery complete %@",
176 interface
, plat_dict
);
177 CFRelease(plat_dict
);
179 (void)SCDynamicStoreRemoveValue(NULL
, key
);
187 _nat64_prefix_request_start(const void *value
)
189 unsigned int if_index
;
191 CFStringRef interface
= (CFStringRef
)value
;
193 CFAbsoluteTime start_time
;
195 SC_log(LOG_DEBUG
, "%@: _nat64_prefix_request_start", interface
);
197 if_name
= _SC_cfstring_to_cstring(interface
, NULL
, 0, kCFStringEncodingASCII
);
198 if (if_name
== NULL
) {
199 SC_log(LOG_NOTICE
, "%@: could not convert interface name", interface
);
203 if_index
= my_if_nametoindex(if_name
);
205 SC_log(LOG_NOTICE
, "%s: no interface index", if_name
);
206 CFAllocatorDeallocate(NULL
, if_name
);
210 // keep track of interfaces with active nat64 prefix requests
211 CFSetAddValue(nat64_prefix_requests
, interface
);
214 start_time
= CFAbsoluteTimeGetCurrent();
215 ok
= nw_nat64_copy_prefixes_async(&if_index
,
216 nat64_dispatch_queue(),
217 ^(int32_t num_prefixes
, nw_nat64_prefix_t
*prefixes
) {
218 if (num_prefixes
>= 0) {
220 if (!_nat64_prefix_set(if_name
, num_prefixes
, prefixes
)) {
225 "%s: nw_nat64_copy_prefixes_async() num_prefixes(%d) < 0",
230 if (num_prefixes
<= 0) {
231 // remove from active list
232 CFSetRemoveValue(nat64_prefix_requests
, interface
);
235 _nat64_prefix_post(interface
, num_prefixes
, prefixes
, start_time
);
238 CFRelease(interface
);
239 CFAllocatorDeallocate(NULL
, if_name
);
242 SC_log(LOG_ERR
, "%s: nw_nat64_copy_prefixes_async() failed", if_name
);
244 // remove from active list
245 CFSetRemoveValue(nat64_prefix_requests
, interface
);
247 CFRelease(interface
);
248 CFAllocatorDeallocate(NULL
, if_name
);
256 _nat64_prefix_request(const void *value
, void *context
)
258 CFSetRef changes
= (CFSetRef
)context
;
259 CFStringRef interface
= (CFStringRef
)value
;
261 if (!CFSetContainsValue(nat64_prefix_requests
, interface
) ||
262 ((changes
!= NULL
) && CFSetContainsValue(changes
, interface
))) {
264 // ... or a [refresh] request that hasn't already been started
265 _nat64_prefix_request_start(interface
);
273 _nat64_prefix_update(const void *value
, void *context
)
275 #pragma unused(context)
276 CFStringRef interface
= (CFStringRef
)value
;
278 if (CFSetContainsValue(nat64_prefix_requests
, interface
)) {
279 _nat64_prefix_request_start(interface
);
287 #pragma mark NAT64 prefix functions (for IPMonitor)
292 is_nat64_prefix_request(CFStringRef change
, CFStringRef
*interface
)
294 CFArrayRef components
;
295 static CFStringRef prefix
= NULL
;
297 static dispatch_once_t once
;
299 dispatch_once(&once
, ^{
300 prefix
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
304 if (!CFStringHasPrefix(change
, prefix
) ||
305 !CFStringHasSuffix(change
, kSCEntNetNAT64PrefixRequest
)) {
309 components
= CFStringCreateArrayBySeparatingStrings(NULL
, change
, CFSTR("/"));
310 if (CFArrayGetCount(components
) == 5) {
311 *interface
= CFArrayGetValueAtIndex(components
, 3);
312 CFRetain(*interface
);
315 CFRelease(components
);
321 __private_extern__
void
322 nat64_prefix_request_add_pattern(CFMutableArrayRef patterns
)
326 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
327 kSCDynamicStoreDomainState
,
329 kSCEntNetNAT64PrefixRequest
);
330 CFArrayAppendValue(patterns
, pattern
);
338 nat64_configuration_init(CFBundleRef bundle
)
340 #pragma unused(bundle)
341 nat64_prefix_requests
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
348 nat64_configuration_update(CFSetRef requests
, CFSetRef changes
)
350 // for any interface that changed, refresh the nat64 prefix
351 if (changes
!= NULL
) {
352 CFSetApplyFunction(changes
, _nat64_prefix_update
, NULL
);
355 // for any requested interface, query the nat64 prefix
356 if (requests
!= NULL
) {
357 CFSetApplyFunction(requests
, _nat64_prefix_request
, (void *)changes
);