]>
Commit | Line | Data |
---|---|---|
1ef45fa4 | 1 | /* |
afb19109 | 2 | * Copyright (c) 2017-2019 Apple Inc. All rights reserved. |
1ef45fa4 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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 | |
11 | * file. | |
12 | * | |
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. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /* | |
25 | * Modification History | |
26 | * | |
27 | * April 17, 2017 Allan Nathanson <ajn@apple.com> | |
28 | * - initial revision | |
29 | */ | |
30 | ||
31 | ||
32 | #include "nat64-configuration.h" | |
33 | ||
34 | #include <TargetConditionals.h> | |
35 | #include <CoreFoundation/CoreFoundation.h> | |
36 | #include <SystemConfiguration/SystemConfiguration.h> | |
37 | #include <SystemConfiguration/SCPrivate.h> | |
afb19109 A |
38 | #if TEST_NAT64_CONFIGURATION |
39 | static Boolean G_set_prefixes_force_failure; | |
40 | #define my_if_nametoindex if_nametoindex | |
41 | #else | |
1ef45fa4 | 42 | #include "ip_plugin.h" |
afb19109 | 43 | #endif |
1ef45fa4 A |
44 | |
45 | #define INET6 1 | |
46 | ||
47 | #include <string.h> | |
48 | #include <net/if.h> | |
49 | #include <sys/ioctl.h> | |
50 | #include <sys/socket.h> | |
51 | #include <sys/sockio.h> | |
afb19109 | 52 | #include <netinet/in.h> |
f715d946 | 53 | #include <nw/private.h> |
afb19109 | 54 | #include <sys/queue.h> |
1ef45fa4 A |
55 | |
56 | ||
afb19109 A |
57 | /** |
58 | ** Support functions | |
59 | **/ | |
1ef45fa4 | 60 | static dispatch_queue_t |
afb19109 | 61 | nat64_dispatch_queue(void) |
1ef45fa4 A |
62 | { |
63 | static dispatch_once_t once; | |
64 | static dispatch_queue_t q; | |
65 | ||
66 | dispatch_once(&once, ^{ | |
67 | q = dispatch_queue_create("nat64 prefix request queue", NULL); | |
68 | }); | |
69 | ||
70 | return q; | |
71 | } | |
72 | ||
f715d946 A |
73 | static Boolean |
74 | _nat64_prefix_set(const char *if_name, | |
75 | int32_t num_prefixes, | |
76 | nw_nat64_prefix_t *prefixes) | |
1ef45fa4 A |
77 | { |
78 | struct if_nat64req req; | |
79 | int ret; | |
80 | int s; | |
81 | ||
1ef45fa4 | 82 | // pass NAT64 prefixes to the kernel |
afb19109 | 83 | memset(&req, 0, sizeof(req)); |
1ef45fa4 A |
84 | strlcpy(req.ifnat64_name, if_name, sizeof(req.ifnat64_name)); |
85 | ||
86 | if (num_prefixes == 0) { | |
afb19109 | 87 | SC_log(LOG_NOTICE, "%s: nat64 prefix unavailable", if_name); |
1ef45fa4 A |
88 | } |
89 | ||
90 | for (int32_t i = 0; i < num_prefixes; i++) { | |
91 | char prefix_str[NW_NAT64_PREFIX_STR_LENGTH] = {0}; | |
92 | ||
93 | nw_nat64_write_prefix_to_string(&prefixes[i], prefix_str, sizeof(prefix_str)); | |
afb19109 | 94 | SC_log(LOG_NOTICE, "%s: nat64 prefix[%d] = %s", if_name, i, prefix_str); |
1ef45fa4 A |
95 | |
96 | if (i < NAT64_MAX_NUM_PREFIXES) { | |
97 | req.ifnat64_prefixes[i].prefix_len = prefixes[i].length; | |
afb19109 A |
98 | memcpy(&req.ifnat64_prefixes[i].ipv6_prefix, |
99 | &prefixes[i].data, | |
100 | MIN(sizeof(req.ifnat64_prefixes[i].ipv6_prefix), sizeof(prefixes[i].data))); // MIN(16, 12) | |
1ef45fa4 A |
101 | } |
102 | } | |
103 | ||
104 | s = socket(AF_INET, SOCK_DGRAM, 0); | |
105 | if (s == -1) { | |
106 | SC_log(LOG_ERR, "socket() failed: %s", strerror(errno)); | |
f715d946 | 107 | return (FALSE); |
1ef45fa4 A |
108 | } |
109 | ret = ioctl(s, SIOCSIFNAT64PREFIX, &req); | |
110 | close(s); | |
111 | if (ret == -1) { | |
112 | if ((errno != ENOENT) || (num_prefixes != 0)) { | |
113 | SC_log(LOG_ERR, "%s: ioctl(SIOCSIFNAT64PREFIX) failed: %s", if_name, strerror(errno)); | |
114 | } | |
f715d946 | 115 | return (FALSE); |
1ef45fa4 A |
116 | } |
117 | ||
afb19109 | 118 | SC_log(LOG_NOTICE, "%s: nat64 prefix%s updated", if_name, (num_prefixes != 1) ? "es" : ""); |
f715d946 A |
119 | return (TRUE); |
120 | } | |
121 | ||
122 | ||
123 | static void | |
124 | _nat64_prefix_post(CFStringRef interface, | |
125 | int32_t num_prefixes, | |
126 | nw_nat64_prefix_t *prefixes, | |
127 | CFAbsoluteTime start_time) | |
128 | { | |
afb19109 A |
129 | #if TEST_NAT64_CONFIGURATION |
130 | #pragma unused(interface) | |
131 | #pragma unused(num_prefixes) | |
132 | #pragma unused(prefixes) | |
133 | #pragma unused(start_time) | |
134 | return; | |
135 | #else /* TEST_NAT64_CONFIGURATION */ | |
136 | ||
f715d946 A |
137 | CFStringRef key; |
138 | ||
139 | key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, | |
140 | kSCDynamicStoreDomainState, | |
141 | interface, | |
142 | kSCEntNetNAT64); | |
143 | if (num_prefixes >= 0) { | |
144 | CFDateRef date; | |
145 | CFMutableDictionaryRef plat_dict; | |
146 | ||
147 | plat_dict = CFDictionaryCreateMutable(NULL, | |
148 | 0, | |
149 | &kCFTypeDictionaryKeyCallBacks, | |
150 | &kCFTypeDictionaryValueCallBacks); | |
151 | /* prefixes (if available) */ | |
152 | if (num_prefixes > 0) { | |
153 | CFMutableArrayRef prefix_array; | |
154 | ||
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}; | |
158 | CFStringRef str; | |
159 | ||
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); | |
163 | CFRelease(str); | |
164 | } | |
165 | CFDictionarySetValue(plat_dict, kSCPropNetNAT64PrefixList, prefix_array); | |
166 | CFRelease(prefix_array); | |
167 | } | |
168 | /* start time */ | |
169 | date = CFDateCreate(NULL, start_time); | |
170 | CFDictionarySetValue(plat_dict, | |
171 | kSCPropNetNAT64PLATDiscoveryStartTime, | |
172 | date); | |
173 | CFRelease(date); | |
174 | ||
175 | /* completion time */ | |
176 | date = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); | |
177 | CFDictionarySetValue(plat_dict, | |
178 | kSCPropNetNAT64PLATDiscoveryCompletionTime, | |
179 | date); | |
180 | CFRelease(date); | |
181 | ||
182 | (void)SCDynamicStoreSetValue(NULL, key, plat_dict); | |
afb19109 | 183 | SC_log(LOG_NOTICE, "%@: PLAT discovery complete %@", |
f715d946 A |
184 | interface, plat_dict); |
185 | CFRelease(plat_dict); | |
186 | } else { | |
187 | (void)SCDynamicStoreRemoveValue(NULL, key); | |
188 | } | |
189 | CFRelease(key); | |
afb19109 | 190 | #endif /* TEST_NAT64_CONFIGURATION */ |
1ef45fa4 A |
191 | return; |
192 | } | |
193 | ||
afb19109 A |
194 | static nw_nat64_prefixes_resolver_t |
195 | _nat64_resolver_create(unsigned int if_index) | |
196 | { | |
197 | nw_interface_t interface; | |
198 | nw_parameters_t params; | |
199 | nw_nat64_prefixes_resolver_t resolver; | |
200 | ||
201 | params = nw_parameters_create(); | |
202 | interface = nw_interface_create_with_index(if_index); | |
203 | if (interface == NULL) { | |
204 | SC_log(LOG_NOTICE, | |
205 | "nw_interface_create_with_index(%u) failed", | |
206 | if_index); | |
207 | return (NULL); | |
208 | } | |
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); | |
213 | nw_release(params); | |
214 | return (resolver); | |
215 | } | |
216 | ||
217 | /** | |
218 | ** NAT64PrefixRequest | |
219 | **/ | |
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, \ | |
224 | NAT64PrefixRequest) | |
225 | static NAT64PrefixRequest_LIST_HEAD S_request_head; | |
226 | static struct NAT64PrefixRequestHead * S_request_head_p = &S_request_head; | |
227 | ||
228 | typedef CF_ENUM(uint16_t, RequestFlags) { | |
229 | kRequestFlagsNone = 0x0000, | |
230 | kRequestFlagsValid = 0x0001, | |
231 | }; | |
232 | ||
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; | |
240 | RequestFlags flags; | |
241 | }; | |
242 | ||
243 | static Boolean | |
244 | NAT64PrefixRequestFlagsIsSet(NAT64PrefixRequestRef request, RequestFlags flags) | |
245 | { | |
246 | return ((request->flags & flags) != 0); | |
247 | } | |
1ef45fa4 A |
248 | |
249 | static void | |
afb19109 | 250 | NAT64PrefixRequestFlagsSet(NAT64PrefixRequestRef request, RequestFlags flags) |
1ef45fa4 | 251 | { |
afb19109 A |
252 | request->flags |= flags; |
253 | } | |
1ef45fa4 | 254 | |
afb19109 A |
255 | static void |
256 | NAT64PrefixRequestFlagsClear(NAT64PrefixRequestRef request, RequestFlags flags) | |
257 | { | |
258 | request->flags &= ~flags; | |
259 | } | |
1ef45fa4 | 260 | |
afb19109 A |
261 | static NAT64PrefixRequestRef |
262 | NAT64PrefixRequestFindInterface(CFStringRef if_name_cf) | |
263 | { | |
264 | NAT64PrefixRequestRef scan; | |
265 | ||
266 | LIST_FOREACH(scan, S_request_head_p, link) { | |
267 | if (CFEqual(if_name_cf, scan->if_name_cf)) { | |
268 | return (scan); | |
269 | } | |
1ef45fa4 | 270 | } |
afb19109 A |
271 | return (NULL); |
272 | } | |
273 | ||
274 | static void | |
275 | NAT64PrefixRequestRetain(NAT64PrefixRequestRef request) | |
276 | { | |
277 | request->retain_count++; | |
278 | SC_log(LOG_DEBUG, "%s: %s %p %u", | |
279 | request->if_name, __FUNCTION__, | |
280 | request, request->retain_count); | |
281 | return; | |
282 | } | |
1ef45fa4 | 283 | |
afb19109 A |
284 | static NAT64PrefixRequestRef |
285 | NAT64PrefixRequestCreate(CFStringRef if_name_cf) | |
286 | { | |
287 | unsigned int if_index; | |
288 | char * if_name; | |
289 | NAT64PrefixRequestRef request; | |
290 | ||
291 | if_name = _SC_cfstring_to_cstring(if_name_cf, NULL, 0, | |
292 | kCFStringEncodingASCII); | |
293 | if (if_name == NULL) { | |
294 | SC_log(LOG_ERR, | |
295 | "%@: could not convert interface name", | |
296 | if_name_cf); | |
297 | return (NULL); | |
298 | } | |
1ef45fa4 A |
299 | if_index = my_if_nametoindex(if_name); |
300 | if (if_index == 0) { | |
afb19109 A |
301 | SC_log(LOG_NOTICE, |
302 | "%s: interface does not exist", if_name); | |
1ef45fa4 | 303 | CFAllocatorDeallocate(NULL, if_name); |
afb19109 | 304 | return (NULL); |
1ef45fa4 | 305 | } |
afb19109 A |
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); | |
315 | return (request); | |
316 | } | |
1ef45fa4 | 317 | |
afb19109 A |
318 | static void |
319 | NAT64PrefixRequestStopResolver(NAT64PrefixRequestRef request) | |
320 | { | |
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; | |
327 | } | |
328 | return; | |
329 | } | |
1ef45fa4 | 330 | |
afb19109 A |
331 | static void |
332 | NAT64PrefixRequestInvalidate(NAT64PrefixRequestRef request) | |
333 | { | |
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); | |
1ef45fa4 | 339 | } |
afb19109 A |
340 | return; |
341 | } | |
1ef45fa4 | 342 | |
afb19109 A |
343 | static void |
344 | NAT64PrefixRequestRelease(NAT64PrefixRequestRef request) | |
345 | { | |
346 | if (request->retain_count == 0) { | |
347 | SC_log(LOG_ERR, "%s: retain count is zero %p", | |
348 | __FUNCTION__, request); | |
349 | return; | |
350 | } | |
351 | request->retain_count--; | |
352 | SC_log(LOG_DEBUG, | |
353 | "%s: %s %p %u", | |
354 | request->if_name, __FUNCTION__, request, request->retain_count); | |
355 | if (request->retain_count != 0) { | |
356 | return; | |
357 | } | |
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; | |
364 | } | |
365 | if (request->if_name != NULL) { | |
366 | CFAllocatorDeallocate(NULL, (void *)request->if_name); | |
367 | request->if_name = NULL; | |
368 | } | |
369 | free(request); | |
1ef45fa4 A |
370 | return; |
371 | } | |
372 | ||
afb19109 A |
373 | static void |
374 | NAT64PrefixRequestStart(NAT64PrefixRequestRef request) | |
375 | { | |
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; | |
380 | ||
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__); | |
385 | return; | |
386 | } | |
387 | resolver = _nat64_resolver_create(request->if_index); | |
388 | if (resolver == NULL) { | |
389 | return; | |
390 | } | |
391 | NAT64PrefixRequestRetain(request); | |
392 | cancel_handler = ^{ | |
393 | SC_log(LOG_DEBUG, "%s: NAT64 resolver cancelled", | |
394 | request->if_name); | |
395 | NAT64PrefixRequestRelease(request); | |
396 | return; | |
397 | }; | |
398 | start_time = CFAbsoluteTimeGetCurrent(); | |
399 | handler = ^(int32_t num_prefixes, nw_nat64_prefix_t *prefixes) { | |
da12db89 | 400 | Boolean remove_resolver = FALSE; |
afb19109 A |
401 | |
402 | if (!NAT64PrefixRequestFlagsIsSet(request, | |
403 | kRequestFlagsValid)) { | |
404 | SC_log(LOG_INFO, "%s: NAT64 request is stale %p", | |
405 | request->if_name, request); | |
406 | return; | |
407 | } | |
408 | if (prefixes != NULL) { | |
409 | /* set prefixes on the interface */ | |
da12db89 A |
410 | _nat64_prefix_set(request->if_name, |
411 | num_prefixes, prefixes); | |
412 | remove_resolver = TRUE; | |
afb19109 A |
413 | } else { |
414 | SC_log(LOG_ERR, "%s: NAT64 no prefixes", | |
415 | request->if_name); | |
416 | } | |
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) { | |
da12db89 | 421 | remove_resolver = TRUE; |
afb19109 A |
422 | } |
423 | #endif /* TEST_NAT64_CONFIGURATION */ | |
da12db89 | 424 | if (remove_resolver) { |
afb19109 A |
425 | /* remove resolver */ |
426 | NAT64PrefixRequestInvalidate(request); | |
427 | NAT64PrefixRequestRelease(request); | |
428 | return; | |
429 | } | |
430 | }; | |
431 | nw_nat64_prefixes_resolver_set_cancel_handler(resolver, cancel_handler); | |
432 | nw_nat64_prefixes_resolver_set_update_handler(resolver, | |
433 | nat64_dispatch_queue(), | |
434 | handler); | |
435 | nw_nat64_prefixes_resolver_start(resolver); | |
436 | request->resolver = resolver; | |
437 | return; | |
438 | } | |
1ef45fa4 | 439 | |
afb19109 A |
440 | /** |
441 | ** Set iterators | |
442 | **/ | |
1ef45fa4 | 443 | static void |
afb19109 | 444 | _nat64_process_prefix_request(const void *value, void *context) |
1ef45fa4 | 445 | { |
afb19109 A |
446 | #pragma unused(context) |
447 | CFStringRef interface = (CFStringRef)value; | |
448 | NAT64PrefixRequestRef request; | |
1ef45fa4 | 449 | |
afb19109 A |
450 | request = NAT64PrefixRequestFindInterface(interface); |
451 | if (request != NULL) { | |
452 | return; | |
1ef45fa4 A |
453 | } |
454 | ||
afb19109 A |
455 | /* start a new request */ |
456 | request = NAT64PrefixRequestCreate(interface); | |
457 | if (request != NULL) { | |
458 | NAT64PrefixRequestStart(request); | |
459 | } | |
1ef45fa4 A |
460 | return; |
461 | } | |
462 | ||
1ef45fa4 | 463 | static void |
afb19109 | 464 | _nat64_process_prefix_update(const void *value, void *context) |
1ef45fa4 A |
465 | { |
466 | #pragma unused(context) | |
afb19109 A |
467 | CFStringRef interface = (CFStringRef)value; |
468 | NAT64PrefixRequestRef request; | |
1ef45fa4 | 469 | |
afb19109 A |
470 | request = NAT64PrefixRequestFindInterface(interface); |
471 | if (request == NULL) { | |
472 | SC_log(LOG_DEBUG, "%@ %s: no existing request", | |
473 | interface, __FUNCTION__); | |
474 | return; | |
1ef45fa4 A |
475 | } |
476 | ||
afb19109 A |
477 | /* destroy the old one, start a new one */ |
478 | SC_log(LOG_INFO, "%@: %s", interface, __FUNCTION__); | |
479 | NAT64PrefixRequestInvalidate(request); | |
480 | NAT64PrefixRequestRelease(request); | |
481 | ||
482 | /* start a new request */ | |
483 | request = NAT64PrefixRequestCreate(interface); | |
484 | if (request != NULL) { | |
485 | NAT64PrefixRequestStart(request); | |
486 | } | |
487 | return; | |
488 | } | |
489 | ||
490 | static void | |
491 | _nat64_process_cancel_request(const void * value, void * context) | |
492 | { | |
493 | #pragma unused(context) | |
494 | CFStringRef interface = (CFStringRef)value; | |
495 | NAT64PrefixRequestRef request; | |
496 | ||
497 | /* if there's an in-flight request, remove it */ | |
498 | request = NAT64PrefixRequestFindInterface(interface); | |
499 | if (request == NULL) { | |
500 | /* no resolver */ | |
501 | SC_log(LOG_DEBUG, "%@ %s: no active NAT64 request", | |
502 | interface, __FUNCTION__); | |
503 | return; | |
504 | } | |
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); | |
1ef45fa4 A |
510 | return; |
511 | } | |
512 | ||
513 | ||
514 | #pragma mark - | |
515 | #pragma mark NAT64 prefix functions (for IPMonitor) | |
516 | ||
517 | ||
518 | __private_extern__ | |
519 | Boolean | |
520 | is_nat64_prefix_request(CFStringRef change, CFStringRef *interface) | |
521 | { | |
522 | CFArrayRef components; | |
523 | static CFStringRef prefix = NULL; | |
524 | Boolean yn = FALSE; | |
525 | static dispatch_once_t once; | |
526 | ||
527 | dispatch_once(&once, ^{ | |
528 | prefix = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState); | |
529 | }); | |
530 | ||
531 | *interface = NULL; | |
532 | if (!CFStringHasPrefix(change, prefix) || | |
533 | !CFStringHasSuffix(change, kSCEntNetNAT64PrefixRequest)) { | |
534 | return FALSE; | |
535 | } | |
536 | ||
537 | components = CFStringCreateArrayBySeparatingStrings(NULL, change, CFSTR("/")); | |
538 | if (CFArrayGetCount(components) == 5) { | |
539 | *interface = CFArrayGetValueAtIndex(components, 3); | |
540 | CFRetain(*interface); | |
541 | yn = TRUE; | |
542 | } | |
543 | CFRelease(components); | |
544 | ||
545 | return yn; | |
546 | } | |
547 | ||
548 | ||
549 | __private_extern__ void | |
550 | nat64_prefix_request_add_pattern(CFMutableArrayRef patterns) | |
551 | { | |
552 | CFStringRef pattern; | |
553 | ||
554 | pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, | |
555 | kSCDynamicStoreDomainState, | |
556 | kSCCompAnyRegex, | |
557 | kSCEntNetNAT64PrefixRequest); | |
558 | CFArrayAppendValue(patterns, pattern); | |
559 | CFRelease(pattern); | |
560 | return; | |
561 | } | |
562 | ||
afb19109 A |
563 | static void |
564 | nat64_configuration_update_locked(CFSetRef requests, CFSetRef updates, | |
565 | CFSetRef cancellations) | |
1ef45fa4 | 566 | { |
afb19109 A |
567 | if (cancellations != NULL) { |
568 | CFSetApplyFunction(cancellations, | |
569 | _nat64_process_cancel_request, | |
570 | NULL); | |
571 | } | |
572 | // for any interface that changed, refresh the nat64 prefix | |
573 | if (updates != NULL) { | |
574 | CFSetApplyFunction(updates, _nat64_process_prefix_update, NULL); | |
575 | } | |
576 | ||
577 | // for any requested interface, query the nat64 prefix | |
578 | if (requests != NULL) { | |
579 | CFSetApplyFunction(requests, _nat64_process_prefix_request, | |
580 | NULL); | |
581 | } | |
1ef45fa4 A |
582 | return; |
583 | } | |
584 | ||
1ef45fa4 A |
585 | __private_extern__ |
586 | void | |
afb19109 A |
587 | nat64_configuration_update(CFSetRef requests, CFSetRef updates, |
588 | CFSetRef cancellations) | |
1ef45fa4 | 589 | { |
afb19109 | 590 | dispatch_block_t update_block; |
1ef45fa4 | 591 | |
1ef45fa4 | 592 | if (requests != NULL) { |
afb19109 | 593 | CFRetain(requests); |
1ef45fa4 | 594 | } |
afb19109 A |
595 | if (updates != NULL) { |
596 | CFRetain(updates); | |
597 | } | |
598 | if (cancellations != NULL) { | |
599 | CFRetain(cancellations); | |
600 | } | |
601 | update_block = ^{ | |
602 | SC_log(LOG_DEBUG, | |
603 | "NAT64 requests %@ updates %@ cancellations %@", | |
604 | requests, updates, cancellations); | |
605 | nat64_configuration_update_locked(requests, updates, | |
606 | cancellations); | |
607 | if (requests != NULL) { | |
608 | CFRelease(requests); | |
609 | } | |
610 | if (updates != NULL) { | |
611 | CFRelease(updates); | |
612 | } | |
613 | if (cancellations != NULL) { | |
614 | CFRelease(cancellations); | |
615 | } | |
616 | }; | |
617 | dispatch_async(nat64_dispatch_queue(), update_block); | |
1ef45fa4 A |
618 | return; |
619 | } | |
afb19109 A |
620 | |
621 | #if TEST_NAT64_CONFIGURATION | |
622 | int | |
623 | main(int argc, char * argv[]) | |
624 | { | |
625 | CFStringRef if_name_cf; | |
626 | CFMutableSetRef set; | |
627 | ||
628 | set = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); | |
629 | for (int i = 1; i < argc; i++) { | |
630 | if_name_cf = CFStringCreateWithCString(NULL, | |
631 | argv[i], | |
632 | kCFStringEncodingASCII); | |
633 | CFSetAddValue(set, if_name_cf); | |
634 | CFRelease(if_name_cf); | |
635 | } | |
636 | if (CFSetGetCount(set) == 0) { | |
637 | fprintf(stderr, "nothing to do\n"); | |
638 | exit(0); | |
639 | } | |
640 | SC_log(LOG_NOTICE, "Starting %@", set); | |
641 | nat64_configuration_update(set, NULL, NULL); | |
642 | sleep(2); | |
643 | ||
644 | SC_log(LOG_NOTICE, "Starting 2 %@", set); | |
645 | nat64_configuration_update(set, NULL, NULL); | |
646 | sleep(2); | |
647 | ||
648 | SC_log(LOG_NOTICE, "Updating"); | |
649 | nat64_configuration_update(NULL, set, NULL); | |
650 | sleep(2); | |
651 | ||
652 | SC_log(LOG_NOTICE, "Cancelling"); | |
653 | nat64_configuration_update(NULL, NULL, set); | |
654 | sleep(2); | |
655 | ||
656 | G_set_prefixes_force_failure = TRUE; | |
657 | SC_log(LOG_NOTICE, "Starting (with forced failure) %@", set); | |
658 | nat64_configuration_update(set, NULL, NULL); | |
659 | sleep(2); | |
660 | ||
661 | SC_log(LOG_NOTICE, "Starting (with forced failure 2) %@", set); | |
662 | nat64_configuration_update(set, NULL, NULL); | |
663 | ||
664 | dispatch_main(); | |
665 | exit(0); | |
666 | return (0); | |
667 | } | |
668 | #endif /* TEST_NAT64_CONFIGURATION */ |