]> git.saurik.com Git - apple/configd.git/blob - nwi/network_information_priv.c
configd-453.19.tar.gz
[apple/configd.git] / nwi / network_information_priv.c
1 /*
2 * Copyright (c) 2011 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 #include <arpa/inet.h>
25 #include <notify.h>
26 #include <string.h>
27 #include <sys/socket.h>
28 #include <stdlib.h>
29 #include <syslog.h>
30 #include <stdbool.h>
31 #include "network_information_priv.h"
32 #include <limits.h>
33
34 sa_family_t nwi_af_list[] = {AF_INET, AF_INET6};
35
36 static __inline__ unsigned int
37 nwi_state_compute_size(unsigned int n)
38 {
39 return (offsetof(nwi_state, nwi_ifstates[n]));
40
41 }
42 __private_extern__
43 nwi_state_t
44 nwi_state_copy_priv(nwi_state_t src)
45 {
46 nwi_state_t dest = NULL;
47
48 if (src == NULL) {
49 return dest;
50 }
51
52 dest = malloc(src->size);
53
54 if (dest != NULL) {
55 bcopy(src, dest, src->size);
56
57 dest->ref = 1;
58 }
59 return dest;
60 }
61
62 __private_extern__
63 nwi_state_t
64 nwi_state_new(nwi_state_t old_state, int elems)
65 {
66 nwi_state_t state = NULL;
67 int new_size;
68
69 if (old_state == NULL && elems == 0) {
70 return NULL;
71 }
72
73 /* Need to insert a last node for each of the v4/v6 list */
74 new_size = (elems != 0)?
75 (sizeof(nwi_state) + nwi_state_compute_size((elems+1) * 2)):0;
76
77 /* Should we reallocate? */
78 if (old_state != NULL) {
79 if (old_state->size >= new_size) {
80 return (old_state);
81 }
82 }
83
84 state = malloc(new_size);
85 if (state == NULL) {
86 return NULL;
87 }
88
89 state->size = new_size;
90
91 /*
92 * v4 list is stored 0 to elems,
93 * v6 list is stored elems + 1 to 2 * elems + 2
94 */
95 state->ipv6_start = elems + 1;
96
97 if (old_state != NULL) {
98 state->ipv6_count = old_state->ipv6_count;
99 if (state->ipv6_count > 0) {
100 bcopy((void*) &old_state->nwi_ifstates[old_state->ipv6_start],
101 (void*) &state->nwi_ifstates[state->ipv6_start],
102 old_state->ipv6_count * sizeof(nwi_ifstate));
103 }
104
105 state->ipv4_count = old_state->ipv4_count;
106
107 if (state->ipv4_count > 0) {
108 bcopy((void*) old_state->nwi_ifstates,
109 (void*) state->nwi_ifstates,
110 old_state->ipv4_count * sizeof(nwi_ifstate));
111 }
112
113 free(old_state);
114 } else {
115 state->ipv4_count = 0;
116 state->ipv6_count = 0;
117 }
118 nwi_state_set_last(state, AF_INET);
119 nwi_state_set_last(state, AF_INET6);
120
121 state->ref = 1;
122 return state;
123 }
124
125 static inline
126 nwi_ifstate_t nwi_ifstate_get_last(nwi_state_t state, int af, uint32_t** last)
127 {
128 uint32_t* count;
129 int idx;
130
131 count = (af == AF_INET)
132 ?&state->ipv4_count:&state->ipv6_count;
133
134 idx = (af == AF_INET)
135 ?state->ipv4_count:(state->ipv6_start + state->ipv6_count);
136
137 *last = count;
138
139 return &state->nwi_ifstates[idx];
140
141 }
142
143 __private_extern__
144 void
145 nwi_insert_ifstate(nwi_state_t state,
146 const char* ifname, int af,
147 uint64_t flags, Rank rank,
148 void* ifa)
149 {
150 nwi_ifstate_t ifstate;
151
152 /* Will only insert unique elements in the list */
153 ifstate = nwi_state_get_ifstate_with_name(state, af, ifname);
154
155 /* Already present, just ignore it */
156 if (ifstate != NULL) {
157 if (ifstate->rank < rank) {
158 return;
159 }
160 }
161
162 if (ifstate == NULL) {
163 uint32_t *last;
164
165 /* We need to append it as the last element */
166 ifstate = nwi_ifstate_get_last(state, af, &last);
167 strcpy(ifstate->ifname, ifname);
168 ifstate->af_alias = NULL;
169 ifstate->af = af;
170 ifstate->diff_ch = NULL;
171 (*last)++;
172 }
173
174 /* We need to update the address/rank/flag fields for the existing/new
175 * element */
176 if (ifa != NULL) {
177 switch (af) {
178 case AF_INET:
179 ifstate->iaddr = *((struct in_addr *) ifa);
180 break;
181 case AF_INET6:
182 ifstate->iaddr6 = *((struct in6_addr *) ifa);
183 break;
184 default:
185 break;
186 }
187
188 }
189
190 ifstate->rank = rank;
191 ifstate->flags = flags;
192
193 return;
194 }
195
196 __private_extern__
197 void
198 nwi_state_clear(nwi_state_t state, int af)
199 {
200 uint32_t* count;
201
202 count = (af == AF_INET)
203 ?&state->ipv4_count:&state->ipv6_count;
204
205 *count = 0;
206 nwi_state_set_last(state, af);
207 return;
208
209 }
210
211 __private_extern__
212 void
213 nwi_state_set_last(nwi_state_t state, int af)
214 {
215 int last_elem_idx;
216
217 if (state == NULL) {
218 return;
219 }
220
221 /* The last element is an element with the flags set as
222 * NWI_IFSTATE_FLAGS_NOT_IN_LIST */
223 last_elem_idx = (af == AF_INET)
224 ?state->ipv4_count
225 :(state->ipv6_start + state->ipv6_count);
226
227 state->nwi_ifstates[last_elem_idx].ifname[0] = '\0';
228 state->nwi_ifstates[last_elem_idx].flags
229 |= NWI_IFSTATE_FLAGS_NOT_IN_LIST;
230 }
231
232 __private_extern__
233 void
234 _nwi_state_dump(int level, nwi_state_t state)
235 {
236 const char * addr_str;
237 void * address;
238 int i;
239 char ntopbuf[INET6_ADDRSTRLEN];
240 nwi_ifstate_t scan;
241
242
243 if (state == NULL) {
244 syslog(level, "<empty nwi_state>");
245 return;
246 }
247 syslog(level, "nwi_state = { gen = %llu size = %u #ipv4 = %u #ipv6 = %u }",
248 state->generation_count,
249 state->size,
250 state->ipv4_count,
251 state->ipv6_count);
252
253 if (state->ipv4_count) {
254 syslog(level, "IPv4:");
255 for (i = 0, scan = state->nwi_ifstates;
256 i < state->ipv4_count; i++, scan++) {
257 bool has_dns = (scan->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0;
258 bool never = (scan->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0;
259
260 address = nwi_ifstate_get_address(scan);
261 addr_str = inet_ntop(scan->af, address, ntopbuf, sizeof(ntopbuf));
262
263 syslog(level, " [%d]: %s%s%s%s rank %u iaddr: %s " ,
264 i, scan->ifname, scan->diff_ch != NULL?scan->diff_ch:"",
265 has_dns ? " dns" : "",
266 never ? " never" : "",
267 scan->rank,
268 addr_str);
269 }
270 }
271 if (state->ipv6_count) {
272 syslog(level, "IPv6:");
273 for (i = 0, scan = state->nwi_ifstates + state->ipv6_start;
274 i < state->ipv6_count; i++, scan++) {
275 bool has_dns = (scan->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0;
276 bool never = (scan->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0;
277
278 address = nwi_ifstate_get_address(scan);
279 addr_str = inet_ntop(scan->af, address, ntopbuf, sizeof(ntopbuf));
280 syslog(level, " [%d]: %s%s%s%s rank %u iaddr6: %s ",
281 i, scan->ifname, scan->diff_ch != NULL?scan->diff_ch:"",
282 has_dns ? " dns" : "",
283 never ? " never" : "",
284 scan->rank,
285 addr_str);
286 }
287 }
288 return;
289 }
290
291
292 #define unchanged ""
293 #define added "+"
294 #define deleted "-"
295 #define changed "!"
296
297 __private_extern__
298 void *
299 nwi_ifstate_get_address(nwi_ifstate_t ifstate)
300 {
301 return (void *)&ifstate->iaddr;
302 }
303
304 __private_extern__
305 const char *
306 nwi_ifstate_get_diff_str(nwi_ifstate_t ifstate)
307 {
308 return ifstate->diff_ch;
309 }
310
311 static
312 inline
313 boolean_t
314 nwi_ifstate_has_changed(nwi_ifstate_t ifstate1, nwi_ifstate_t ifstate2)
315 {
316 if (ifstate1->rank != ifstate2->rank) {
317 return TRUE;
318 }
319
320 if (ifstate1->flags != ifstate2->flags) {
321 return TRUE;
322 }
323
324 if (ifstate1->af == AF_INET) {
325 if (memcmp(&ifstate1->iaddr, &ifstate2->iaddr, sizeof(struct in_addr)) != 0) {
326 return TRUE;
327 }
328 } else {
329 if (memcmp(&ifstate1->iaddr6, &ifstate2->iaddr6, sizeof(struct in6_addr)) != 0) {
330 return TRUE;
331 }
332 }
333 return FALSE;
334 }
335
336 static
337 inline
338 nwi_ifstate_t
339 nwi_ifstate_append(nwi_state_t state, nwi_ifstate_t scan)
340 {
341 nwi_ifstate_t new_ifstate = NULL;
342 uint32_t *last;
343
344 new_ifstate = nwi_ifstate_get_last(state, scan->af, &last);
345 memcpy(new_ifstate, scan, sizeof(*scan));
346 (*last)++;
347 return new_ifstate;
348 }
349
350 static
351 inline
352 void
353 nwi_ifstate_set_diff_str(nwi_ifstate_t ifstate, const char * ch)
354 {
355 ifstate->diff_ch = ch;
356 }
357
358 static
359 void
360 nwi_state_merge_added(nwi_state_t state, nwi_state_t old_state,
361 nwi_state_t new_state)
362 {
363 int idx;
364 nwi_ifstate_t scan;
365
366 /* Iterate through v4 and v6 list and annotate the diff flags */
367 for (idx = 0; idx < sizeof(nwi_af_list)/sizeof(nwi_af_list[0]); idx++) {
368 scan = nwi_state_get_first_ifstate(new_state, nwi_af_list[idx]);
369
370 while (scan != NULL) {
371 nwi_ifstate_t existing_ifstate, new_ifstate;
372 const char* ifname;
373
374 ifname = nwi_ifstate_get_ifname(scan);
375
376 existing_ifstate = nwi_state_get_ifstate_with_name(old_state, scan->af, ifname);
377
378 /* Add the element that is not in the store */
379 new_ifstate = nwi_ifstate_append(state, scan);
380
381 /* These are potentially "added" elements unless they are
382 * in the old list */
383 nwi_ifstate_set_diff_str(new_ifstate, added);
384
385 if (existing_ifstate != NULL) {
386 if (nwi_ifstate_has_changed(existing_ifstate, new_ifstate) == TRUE) {
387 nwi_ifstate_set_diff_str(new_ifstate, changed);
388 } else {
389 nwi_ifstate_set_diff_str(new_ifstate, unchanged);
390 }
391 }
392 scan = nwi_ifstate_get_next(scan, scan->af);
393 }
394 nwi_state_set_last(state, nwi_af_list[idx]);
395 }
396
397 return;
398 }
399
400 static
401 void
402 nwi_state_merge_removed(nwi_state_t state, nwi_state_t old_state)
403 {
404 int idx;
405 nwi_ifstate_t scan;
406
407 /* Iterate through v4 and v6 list and annotate the diff flags */
408 for (idx = 0; idx < sizeof(nwi_af_list)/sizeof(nwi_af_list[0]); idx++) {
409 scan = nwi_state_get_first_ifstate(old_state, nwi_af_list[idx]);
410
411 while (scan != NULL) {
412 nwi_ifstate_t existing_ifstate;
413 const char* ifname;
414
415 ifname = nwi_ifstate_get_ifname(scan);
416
417 existing_ifstate = nwi_state_get_ifstate_with_name(state, scan->af, ifname);
418
419 /* Any elements that has not been added means that they are removed */
420 if (existing_ifstate == NULL) {
421 nwi_ifstate_t new_ifstate = nwi_ifstate_append(state, scan);
422 nwi_ifstate_set_diff_str(new_ifstate, deleted);
423 }
424 scan = nwi_ifstate_get_next(scan, scan->af);
425 }
426 nwi_state_set_last(state, nwi_af_list[idx]);
427 }
428
429 }
430
431
432 __private_extern__
433 nwi_state_t
434 nwi_state_diff(nwi_state_t old_state, nwi_state_t new_state)
435 {
436 nwi_state_t diff;
437 int total_count = 0;
438
439 if (old_state != NULL) {
440 total_count = old_state->ipv4_count + old_state->ipv6_count;
441 }
442
443 if (new_state != NULL) {
444 total_count += new_state->ipv4_count + new_state->ipv6_count;
445 }
446
447 if (total_count == 0) {
448 return NULL;
449 }
450
451 diff = nwi_state_new(NULL, total_count);
452
453 nwi_state_merge_added(diff, old_state, new_state);
454 nwi_state_merge_removed(diff, old_state);
455
456 /* Diff consists of a nwi_state_t with annotated diff_ch's */
457 return diff;
458 }
459