2 * Copyright (c) 2017 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 #include <network/nat64.h>
50 static CFMutableSetRef nat64_prefix_requests
= NULL
;
53 static dispatch_queue_t
54 nat64_dispatch_queue()
56 static dispatch_once_t once
;
57 static dispatch_queue_t q
;
59 dispatch_once(&once
, ^{
60 q
= dispatch_queue_create("nat64 prefix request queue", NULL
);
67 static __inline__
void
68 _nat64_prefix_request_complete(const char *if_name
,
70 nw_nat64_prefix_t
*prefixes
)
72 struct if_nat64req req
;
76 SC_log(LOG_DEBUG
, "%s: _nat64_prefix_request_complete", if_name
);
78 // pass NAT64 prefixes to the kernel
79 bzero(&req
, sizeof(req
));
80 strlcpy(req
.ifnat64_name
, if_name
, sizeof(req
.ifnat64_name
));
82 if (num_prefixes
== 0) {
83 SC_log(LOG_INFO
, "%s: nat64 prefix not (or no longer) available", if_name
);
86 for (int32_t i
= 0; i
< num_prefixes
; i
++) {
87 char prefix_str
[NW_NAT64_PREFIX_STR_LENGTH
] = {0};
89 nw_nat64_write_prefix_to_string(&prefixes
[i
], prefix_str
, sizeof(prefix_str
));
90 SC_log(LOG_DEBUG
, "%s: nat64 prefix[%d] = %s", if_name
, i
, prefix_str
);
92 if (i
< NAT64_MAX_NUM_PREFIXES
) {
93 req
.ifnat64_prefixes
[i
].prefix_len
= prefixes
[i
].length
;
94 bcopy(&prefixes
[i
].data
,
95 &req
.ifnat64_prefixes
[i
].ipv6_prefix
,
96 MIN(sizeof(req
.ifnat64_prefixes
[i
].ipv6_prefix
), sizeof(prefixes
[i
].data
))); // MIN(16, 12)
100 s
= socket(AF_INET
, SOCK_DGRAM
, 0);
102 SC_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
105 ret
= ioctl(s
, SIOCSIFNAT64PREFIX
, &req
);
108 if ((errno
!= ENOENT
) || (num_prefixes
!= 0)) {
109 SC_log(LOG_ERR
, "%s: ioctl(SIOCSIFNAT64PREFIX) failed: %s", if_name
, strerror(errno
));
114 SC_log(LOG_INFO
, "%s: nat64 prefix%s updated", if_name
, (num_prefixes
!= 1) ? "es" : "");
120 _nat64_prefix_request_start(const void *value
)
122 unsigned int if_index
;
124 CFStringRef interface
= (CFStringRef
)value
;
127 SC_log(LOG_DEBUG
, "%@: _nat64_prefix_request_start", interface
);
129 if_name
= _SC_cfstring_to_cstring(interface
, NULL
, 0, kCFStringEncodingASCII
);
130 if (if_name
== NULL
) {
131 SC_log(LOG_NOTICE
, "%@: could not convert interface name", interface
);
135 if_index
= my_if_nametoindex(if_name
);
137 SC_log(LOG_NOTICE
, "%s: no interface index", if_name
);
138 CFAllocatorDeallocate(NULL
, if_name
);
142 // keep track of interfaces with active nat64 prefix requests
143 CFSetAddValue(nat64_prefix_requests
, interface
);
146 ok
= nw_nat64_copy_prefixes_async(&if_index
,
147 nat64_dispatch_queue(),
148 ^(int32_t num_prefixes
, nw_nat64_prefix_t
*prefixes
) {
149 if (num_prefixes
>= 0) {
151 _nat64_prefix_request_complete(if_name
, num_prefixes
, prefixes
);
154 "%s: nw_nat64_copy_prefixes_async() num_prefixes(%d) < 0",
159 if (num_prefixes
<= 0) {
160 // remove from active list
161 CFSetRemoveValue(nat64_prefix_requests
, interface
);
165 CFRelease(interface
);
166 CFAllocatorDeallocate(NULL
, if_name
);
169 SC_log(LOG_ERR
, "%s: nw_nat64_copy_prefixes_async() failed", if_name
);
171 // remove from active list
172 CFSetRemoveValue(nat64_prefix_requests
, interface
);
174 CFRelease(interface
);
175 CFAllocatorDeallocate(NULL
, if_name
);
183 _nat64_prefix_request(const void *value
, void *context
)
185 CFSetRef changes
= (CFSetRef
)context
;
186 CFStringRef interface
= (CFStringRef
)value
;
188 if (!CFSetContainsValue(nat64_prefix_requests
, interface
) ||
189 ((changes
!= NULL
) && CFSetContainsValue(changes
, interface
))) {
191 // ... or a [refresh] request that hasn't already been started
192 _nat64_prefix_request_start(interface
);
200 _nat64_prefix_update(const void *value
, void *context
)
202 #pragma unused(context)
203 CFStringRef interface
= (CFStringRef
)value
;
205 if (CFSetContainsValue(nat64_prefix_requests
, interface
)) {
206 _nat64_prefix_request_start(interface
);
214 #pragma mark NAT64 prefix functions (for IPMonitor)
219 is_nat64_prefix_request(CFStringRef change
, CFStringRef
*interface
)
221 CFArrayRef components
;
222 static CFStringRef prefix
= NULL
;
224 static dispatch_once_t once
;
226 dispatch_once(&once
, ^{
227 prefix
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
231 if (!CFStringHasPrefix(change
, prefix
) ||
232 !CFStringHasSuffix(change
, kSCEntNetNAT64PrefixRequest
)) {
236 components
= CFStringCreateArrayBySeparatingStrings(NULL
, change
, CFSTR("/"));
237 if (CFArrayGetCount(components
) == 5) {
238 *interface
= CFArrayGetValueAtIndex(components
, 3);
239 CFRetain(*interface
);
242 CFRelease(components
);
248 __private_extern__
void
249 nat64_prefix_request_add_pattern(CFMutableArrayRef patterns
)
253 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
254 kSCDynamicStoreDomainState
,
256 kSCEntNetNAT64PrefixRequest
);
257 CFArrayAppendValue(patterns
, pattern
);
265 nat64_configuration_init(CFBundleRef bundle
)
267 #pragma unused(bundle)
268 nat64_prefix_requests
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
275 nat64_configuration_update(CFSetRef requests
, CFSetRef changes
)
277 // for any interface that changed, refresh the nat64 prefix
278 if (changes
!= NULL
) {
279 CFSetApplyFunction(changes
, _nat64_prefix_update
, NULL
);
282 // for any requested interface, query the nat64 prefix
283 if (requests
!= NULL
) {
284 CFSetApplyFunction(requests
, _nat64_prefix_request
, (void *)changes
);