]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/nat64-configuration.c
configd-963.30.1.tar.gz
[apple/configd.git] / Plugins / IPMonitor / nat64-configuration.c
1 /*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
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>
38 #include "ip_plugin.h"
39
40 #define INET6 1
41
42 #include <string.h>
43 #include <net/if.h>
44 #include <sys/ioctl.h>
45 #include <sys/socket.h>
46 #include <sys/sockio.h>
47 #include <network/nat64.h>
48
49
50 static CFMutableSetRef nat64_prefix_requests = NULL;
51
52
53 static dispatch_queue_t
54 nat64_dispatch_queue()
55 {
56 static dispatch_once_t once;
57 static dispatch_queue_t q;
58
59 dispatch_once(&once, ^{
60 q = dispatch_queue_create("nat64 prefix request queue", NULL);
61 });
62
63 return q;
64 }
65
66
67 static __inline__ void
68 _nat64_prefix_request_complete(const char *if_name,
69 int32_t num_prefixes,
70 nw_nat64_prefix_t *prefixes)
71 {
72 struct if_nat64req req;
73 int ret;
74 int s;
75
76 SC_log(LOG_DEBUG, "%s: _nat64_prefix_request_complete", if_name);
77
78 // pass NAT64 prefixes to the kernel
79 bzero(&req, sizeof(req));
80 strlcpy(req.ifnat64_name, if_name, sizeof(req.ifnat64_name));
81
82 if (num_prefixes == 0) {
83 SC_log(LOG_INFO, "%s: nat64 prefix not (or no longer) available", if_name);
84 }
85
86 for (int32_t i = 0; i < num_prefixes; i++) {
87 char prefix_str[NW_NAT64_PREFIX_STR_LENGTH] = {0};
88
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);
91
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)
97 }
98 }
99
100 s = socket(AF_INET, SOCK_DGRAM, 0);
101 if (s == -1) {
102 SC_log(LOG_ERR, "socket() failed: %s", strerror(errno));
103 return;
104 }
105 ret = ioctl(s, SIOCSIFNAT64PREFIX, &req);
106 close(s);
107 if (ret == -1) {
108 if ((errno != ENOENT) || (num_prefixes != 0)) {
109 SC_log(LOG_ERR, "%s: ioctl(SIOCSIFNAT64PREFIX) failed: %s", if_name, strerror(errno));
110 }
111 return;
112 }
113
114 SC_log(LOG_INFO, "%s: nat64 prefix%s updated", if_name, (num_prefixes != 1) ? "es" : "");
115 return;
116 }
117
118
119 static void
120 _nat64_prefix_request_start(const void *value)
121 {
122 unsigned int if_index;
123 char *if_name;
124 CFStringRef interface = (CFStringRef)value;
125 bool ok;
126
127 SC_log(LOG_DEBUG, "%@: _nat64_prefix_request_start", interface);
128
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);
132 return;
133 }
134
135 if_index = my_if_nametoindex(if_name);
136 if (if_index == 0) {
137 SC_log(LOG_NOTICE, "%s: no interface index", if_name);
138 CFAllocatorDeallocate(NULL, if_name);
139 return;
140 }
141
142 // keep track of interfaces with active nat64 prefix requests
143 CFSetAddValue(nat64_prefix_requests, interface);
144
145 CFRetain(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) {
150 // update interface
151 _nat64_prefix_request_complete(if_name, num_prefixes, prefixes);
152 } else {
153 SC_log(LOG_ERR,
154 "%s: nw_nat64_copy_prefixes_async() num_prefixes(%d) < 0",
155 if_name,
156 num_prefixes);
157 }
158
159 if (num_prefixes <= 0) {
160 // remove from active list
161 CFSetRemoveValue(nat64_prefix_requests, interface);
162 }
163
164 // cleanup
165 CFRelease(interface);
166 CFAllocatorDeallocate(NULL, if_name);
167 });
168 if (!ok) {
169 SC_log(LOG_ERR, "%s: nw_nat64_copy_prefixes_async() failed", if_name);
170
171 // remove from active list
172 CFSetRemoveValue(nat64_prefix_requests, interface);
173
174 CFRelease(interface);
175 CFAllocatorDeallocate(NULL, if_name);
176 }
177
178 return;
179 }
180
181
182 static void
183 _nat64_prefix_request(const void *value, void *context)
184 {
185 CFSetRef changes = (CFSetRef)context;
186 CFStringRef interface = (CFStringRef)value;
187
188 if (!CFSetContainsValue(nat64_prefix_requests, interface) ||
189 ((changes != NULL) && CFSetContainsValue(changes, interface))) {
190 // if new request
191 // ... or a [refresh] request that hasn't already been started
192 _nat64_prefix_request_start(interface);
193 }
194
195 return;
196 }
197
198
199 static void
200 _nat64_prefix_update(const void *value, void *context)
201 {
202 #pragma unused(context)
203 CFStringRef interface = (CFStringRef)value;
204
205 if (CFSetContainsValue(nat64_prefix_requests, interface)) {
206 _nat64_prefix_request_start(interface);
207 }
208
209 return;
210 }
211
212
213 #pragma mark -
214 #pragma mark NAT64 prefix functions (for IPMonitor)
215
216
217 __private_extern__
218 Boolean
219 is_nat64_prefix_request(CFStringRef change, CFStringRef *interface)
220 {
221 CFArrayRef components;
222 static CFStringRef prefix = NULL;
223 Boolean yn = FALSE;
224 static dispatch_once_t once;
225
226 dispatch_once(&once, ^{
227 prefix = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState);
228 });
229
230 *interface = NULL;
231 if (!CFStringHasPrefix(change, prefix) ||
232 !CFStringHasSuffix(change, kSCEntNetNAT64PrefixRequest)) {
233 return FALSE;
234 }
235
236 components = CFStringCreateArrayBySeparatingStrings(NULL, change, CFSTR("/"));
237 if (CFArrayGetCount(components) == 5) {
238 *interface = CFArrayGetValueAtIndex(components, 3);
239 CFRetain(*interface);
240 yn = TRUE;
241 }
242 CFRelease(components);
243
244 return yn;
245 }
246
247
248 __private_extern__ void
249 nat64_prefix_request_add_pattern(CFMutableArrayRef patterns)
250 {
251 CFStringRef pattern;
252
253 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
254 kSCDynamicStoreDomainState,
255 kSCCompAnyRegex,
256 kSCEntNetNAT64PrefixRequest);
257 CFArrayAppendValue(patterns, pattern);
258 CFRelease(pattern);
259 return;
260 }
261
262
263 __private_extern__
264 void
265 nat64_configuration_init(CFBundleRef bundle)
266 {
267 #pragma unused(bundle)
268 nat64_prefix_requests = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
269 return;
270 }
271
272
273 __private_extern__
274 void
275 nat64_configuration_update(CFSetRef requests, CFSetRef changes)
276 {
277 // for any interface that changed, refresh the nat64 prefix
278 if (changes != NULL) {
279 CFSetApplyFunction(changes, _nat64_prefix_update, NULL);
280 }
281
282 // for any requested interface, query the nat64 prefix
283 if (requests != NULL) {
284 CFSetApplyFunction(requests, _nat64_prefix_request, (void *)changes);
285 }
286
287 return;
288 }