]> git.saurik.com Git - apple/configd.git/blob - nwi/network_information_priv.c
configd-801.1.1.tar.gz
[apple/configd.git] / nwi / network_information_priv.c
1 /*
2 * Copyright (c) 2011-2015 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 <assert.h>
26 #include <notify.h>
27 #include <string.h>
28 #include <sys/socket.h>
29 #include <stdlib.h>
30 #include <stdbool.h>
31 #include "network_information_priv.h"
32 #include <limits.h>
33 #include <stdio.h>
34 #include <syslog.h>
35
36 #define NWI_IFSTATE_FLAGS(flags) \
37 ((flags) & NWI_IFSTATE_FLAGS_MASK)
38
39 #define NWI_IFSTATE_FLAGS_GET_DIFF(flags) \
40 (((flags) & NWI_IFSTATE_FLAGS_DIFF_MASK) >> 8)
41
42 #define NWI_IFSTATE_FLAGS_FROM_DIFF(diff) \
43 (((diff) << 8) & NWI_IFSTATE_FLAGS_DIFF_MASK)
44
45 #define NWI_IFSTATE_DIFF_UNCHANGED 0
46 #define NWI_IFSTATE_DIFF_ADDED 1
47 #define NWI_IFSTATE_DIFF_REMOVED 2
48 #define NWI_IFSTATE_DIFF_CHANGED 3
49 #define NWI_IFSTATE_DIFF_RANK_UP 4
50 #define NWI_IFSTATE_DIFF_RANK_DOWN 5
51
52
53 static void
54 nwi_state_fix_af_aliases(nwi_state_t state, uint32_t old_max_if_count)
55 {
56 int64_t offset;
57 int i;
58 nwi_ifstate_t ifstate;
59
60 offset = state->max_if_count - old_max_if_count;
61 if (offset < 0) {
62 syslog(LOG_ERR, "new count %d < old count %d",
63 state->max_if_count, old_max_if_count);
64 return;
65 }
66
67 /* iterate IPv4 list and add the offset to any entries with an alias */
68 for (i = 0, ifstate = nwi_state_ifstate_list(state, AF_INET);
69 i < state->ipv4_count;
70 i++, ifstate++) {
71 if (ifstate->af_alias_offset != 0) {
72 /* IPv6 is higher in memory, alias is forward */
73 ifstate->af_alias_offset += offset;
74 }
75 }
76
77 /* iterate IPv6 list and add the offset to any entries with an alias */
78 for (i = 0, ifstate = nwi_state_ifstate_list(state, AF_INET6);
79 i < state->ipv6_count;
80 i++, ifstate++) {
81 if (ifstate->af_alias_offset != 0) {
82 /* IPv6 is higher in memory, alias is backward */
83 ifstate->af_alias_offset -= offset;
84 }
85 }
86 return;
87 }
88
89 static void
90 nwi_state_add_to_if_list(nwi_state_t state, nwi_ifstate_t ifstate)
91 {
92 int i;
93 nwi_ifindex_t * scan;
94
95 if ((ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_IFLIST) != 0) {
96 /* doesn't get added to interface list */
97 return;
98 }
99 if (state->if_list_count >= state->max_if_count) {
100 /* sanity check */
101 return;
102 }
103 for (i = 0, scan = nwi_state_if_list(state);
104 i < state->if_list_count;
105 i++, scan++) {
106 nwi_ifstate_t this;
107
108 this = state->ifstate_list + *scan;
109 if (strcmp(this->ifname, ifstate->ifname) == 0) {
110 /* it's already in the list */
111 return;
112 }
113 }
114 /* add it to the end */
115 *scan = (nwi_ifindex_t)(ifstate - state->ifstate_list);
116 state->if_list_count++;
117 return;
118 }
119
120 static void
121 nwi_state_set_if_list(nwi_state_t state)
122 {
123 nwi_ifstate_t scan_v4;
124 nwi_ifstate_t scan_v6;
125 int v4;
126 int v6;
127
128 v4 = 0;
129 v6 = 0;
130 state->if_list_count = 0;
131 scan_v4 = nwi_state_get_ifstate_with_index(state, AF_INET, v4);
132 scan_v6 = nwi_state_get_ifstate_with_index(state, AF_INET6, v6);
133 while (scan_v4 != NULL || scan_v6 != NULL) {
134 bool add_v4 = FALSE;
135
136 if (scan_v4 != NULL && scan_v6 != NULL) {
137 /* add the higher rank of v4 or v6 */
138 if (scan_v4->rank <= scan_v6->rank) {
139 add_v4 = TRUE;
140 }
141 }
142 else if (scan_v4 != NULL) {
143 add_v4 = TRUE;
144 }
145 if (add_v4) {
146 /* add v4 interface */
147 nwi_state_add_to_if_list(state, scan_v4);
148 v4++;
149 scan_v4 = nwi_state_get_ifstate_with_index(state,
150 AF_INET,
151 v4);
152 }
153 else {
154 /* add v6 interface, move to next item */
155 nwi_state_add_to_if_list(state, scan_v6);
156 v6++;
157 scan_v6 = nwi_state_get_ifstate_with_index(state,
158 AF_INET6,
159 v6);
160 }
161 }
162 return;
163 }
164
165 __private_extern__
166 nwi_state_t
167 nwi_state_make_copy(nwi_state_t src)
168 {
169 nwi_state_t dest = NULL;
170 size_t size;
171
172 if (src == NULL) {
173 return dest;
174 }
175 size = nwi_state_size(src);
176 dest = malloc(size);
177
178 if (dest != NULL) {
179 bcopy(src, dest, size);
180 }
181 return dest;
182 }
183
184 __private_extern__
185 nwi_state_t
186 nwi_state_new(nwi_state_t old_state, int max_if_count)
187 {
188 size_t size;
189 nwi_state_t state = NULL;
190
191 if (old_state == NULL && max_if_count == 0) {
192 return NULL;
193 }
194
195 /* Should we reallocate? */
196 if (old_state != NULL) {
197 if (old_state->max_if_count >= max_if_count) {
198 /* if we're staying the same or shrinking, don't grow */
199 return (old_state);
200 }
201 }
202 size = nwi_state_compute_size(max_if_count);
203 state = malloc(size);
204 bzero(state, size);
205 state->max_if_count = max_if_count;
206 state->version = NWI_STATE_VERSION;
207
208 if (old_state != NULL) {
209 state->ipv6_count = old_state->ipv6_count;
210 if (state->ipv6_count > 0) {
211 bcopy((void *)&old_state->ifstate_list[old_state->max_if_count],
212 (void *)&state->ifstate_list[state->max_if_count],
213 old_state->ipv6_count * sizeof(nwi_ifstate));
214 }
215
216 state->ipv4_count = old_state->ipv4_count;
217 if (state->ipv4_count > 0) {
218 bcopy((void *)old_state->ifstate_list,
219 (void *)state->ifstate_list,
220 old_state->ipv4_count * sizeof(nwi_ifstate));
221 }
222 /* we grew the arrays so re-compute the offsets */
223 nwi_state_fix_af_aliases(state, old_state->max_if_count);
224 nwi_state_set_if_list(state);
225 nwi_state_free(old_state);
226 } else {
227 state->ipv4_count = 0;
228 state->ipv6_count = 0;
229 }
230 return state;
231 }
232
233 __private_extern__ void
234 nwi_state_finalize(nwi_state_t state)
235 {
236 if (state == NULL) {
237 return;
238 }
239 nwi_state_set_if_list(state);
240 return;
241 }
242
243 static __inline__
244 nwi_ifstate_t
245 nwi_state_get_last_ifstate(nwi_state_t state, int af, nwi_ifindex_t** last)
246 {
247 nwi_ifindex_t * count;
248 int idx;
249
250 assert(state != NULL);
251
252 count = (af == AF_INET) ? &state->ipv4_count
253 : &state->ipv6_count;
254
255 idx = (af == AF_INET) ? state->ipv4_count
256 : (state->max_if_count + state->ipv6_count);
257
258 *last = count;
259
260 return &state->ifstate_list[idx];
261 }
262
263 __private_extern__
264 void
265 nwi_ifstate_set_signature(nwi_ifstate_t ifstate, uint8_t * signature)
266 {
267 bcopy(signature, ifstate->signature, sizeof(ifstate->signature));
268 ifstate->flags |= NWI_IFSTATE_FLAGS_HAS_SIGNATURE;
269 return;
270 }
271
272 static void
273 nwi_state_add_ifstate_alias(nwi_state_t state, nwi_ifstate_t ifstate)
274 {
275 nwi_ifstate_t alias;
276
277 alias = nwi_state_get_ifstate_with_name(state,
278 nwi_other_af(ifstate->af),
279 ifstate->ifname);
280 if (alias == NULL) {
281 return;
282 }
283 ifstate->af_alias_offset = (nwi_ifindex_t)(alias - ifstate);
284 alias->af_alias_offset = (nwi_ifindex_t)(ifstate - alias);
285 return;
286 }
287
288 __private_extern__ nwi_ifstate_t
289 nwi_state_add_ifstate(nwi_state_t state,
290 const char * ifname, int af,
291 uint64_t flags, Rank rank,
292 void * ifa,
293 struct sockaddr * vpn_server_addr,
294 uint32_t reach_flags)
295 {
296 nwi_ifstate_t ifstate;
297
298 /* Will only add unique elements to the list */
299 ifstate = nwi_state_get_ifstate_with_name(state, af, ifname);
300
301 /* Already present, just ignore it */
302 if (ifstate != NULL) {
303 if (ifstate->rank < rank) {
304 /* always true because they are added in order */
305 return NULL;
306 }
307 }
308 else {
309 /* add to the end */
310 nwi_ifindex_t count;
311 nwi_ifindex_t * count_p;
312
313 /* make sure we aren't already full */
314 ifstate = nwi_state_get_last_ifstate(state, af, &count_p);
315 count = *count_p;
316 if (count == state->max_if_count) {
317 /* should not happen */
318 syslog(LOG_ERR,
319 "nwi_state_add_ifstate: full at count %d\n",
320 count);
321 return (NULL);
322 }
323 if (count > 0) {
324 /* previous ifstate is no longer last */
325 nwi_ifstate_t prev = ifstate - 1;
326
327 prev->flags &= ~NWI_IFSTATE_FLAGS_LAST_ITEM;
328 }
329 bzero(ifstate, sizeof(*ifstate));
330 strlcpy(ifstate->ifname, ifname, sizeof(ifstate->ifname));
331 ifstate->af = af;
332 /* this is the new last ifstate */
333 ifstate->flags |= NWI_IFSTATE_FLAGS_LAST_ITEM;
334 (*count_p)++;
335
336 /* add the alias */
337 nwi_state_add_ifstate_alias(state, ifstate);
338 }
339
340 /* We need to update the address/rank/flag fields for the existing/new
341 * element */
342 if (ifa != NULL) {
343 switch (af) {
344 case AF_INET:
345 ifstate->iaddr = *((struct in_addr *) ifa);
346 break;
347 case AF_INET6:
348 ifstate->iaddr6 = *((struct in6_addr *) ifa);
349 break;
350 default:
351 break;
352 }
353
354 }
355
356 if (vpn_server_addr != NULL && vpn_server_addr->sa_family != 0) {
357 _nwi_ifstate_set_vpn_server(ifstate, vpn_server_addr);
358 } else {
359 _nwi_ifstate_set_vpn_server(ifstate, NULL);
360 }
361
362 ifstate->reach_flags = reach_flags;
363 ifstate->rank = rank;
364 ifstate->flags &= ~NWI_IFSTATE_FLAGS_MASK;
365 ifstate->flags |= NWI_IFSTATE_FLAGS(flags);
366 return ifstate;
367 }
368
369 __private_extern__
370 void
371 nwi_state_clear(nwi_state_t state, int af)
372 {
373 if (af == AF_INET) {
374 state->ipv4_count = 0;
375 }
376 else {
377 state->ipv6_count = 0;
378 }
379 return;
380
381 }
382
383 __private_extern__
384 void *
385 nwi_ifstate_get_address(nwi_ifstate_t ifstate)
386 {
387 return (void *)&ifstate->iaddr;
388 }
389
390
391 static __inline__ uint8_t
392 nwi_ifstate_get_diff(nwi_ifstate_t ifstate)
393 {
394 return (NWI_IFSTATE_FLAGS_GET_DIFF(ifstate->flags));
395 }
396
397 __private_extern__ const char *
398 nwi_ifstate_get_diff_str(nwi_ifstate_t ifstate)
399 {
400 const char * strings[] = {
401 "",
402 "+",
403 "-",
404 "!",
405 "/",
406 "\\"
407 };
408 uint8_t diff;
409
410 diff = nwi_ifstate_get_diff(ifstate);
411 if (diff < sizeof(strings) / sizeof(strings[0])) {
412 return (strings[diff]);
413 }
414 return ("?");
415 }
416
417 __private_extern__ nwi_ifstate_difference_t
418 nwi_ifstate_get_difference(nwi_ifstate_t diff_ifstate)
419 {
420 nwi_ifstate_difference_t diff;
421
422 switch (nwi_ifstate_get_diff(diff_ifstate)) {
423 case NWI_IFSTATE_DIFF_ADDED:
424 case NWI_IFSTATE_DIFF_CHANGED:
425 diff = knwi_ifstate_difference_changed;
426 break;
427 case NWI_IFSTATE_DIFF_REMOVED:
428 diff = knwi_ifstate_difference_removed;
429 break;
430 default:
431 diff = knwi_ifstate_difference_none;
432 break;
433 }
434 return (diff);
435 }
436
437 static inline boolean_t
438 nwi_ifstate_has_changed(nwi_ifstate_t ifstate1, nwi_ifstate_t ifstate2)
439 {
440 if (NWI_IFSTATE_FLAGS(ifstate1->flags)
441 != NWI_IFSTATE_FLAGS(ifstate2->flags)) {
442 return TRUE;
443 }
444
445 if (ifstate1->af == AF_INET) {
446 if (memcmp(&ifstate1->iaddr, &ifstate2->iaddr, sizeof(struct in_addr)) != 0) {
447 return TRUE;
448 }
449 } else {
450 if (memcmp(&ifstate1->iaddr6, &ifstate2->iaddr6, sizeof(struct in6_addr)) != 0) {
451 return TRUE;
452 }
453 }
454 return FALSE;
455 }
456
457 static inline nwi_ifstate_t
458 nwi_state_diff_append(nwi_state_t state, nwi_ifstate_t scan)
459 {
460 nwi_ifstate_t new_ifstate = NULL;
461 nwi_ifindex_t * last;
462
463 new_ifstate = nwi_state_get_last_ifstate(state, scan->af, &last);
464 memcpy(new_ifstate, scan, sizeof(*scan));
465 (*last)++;
466 return new_ifstate;
467 }
468
469 static inline void
470 nwi_ifstate_set_diff(nwi_ifstate_t ifstate, uint8_t diff)
471 {
472 ifstate->flags &= ~NWI_IFSTATE_FLAGS_DIFF_MASK;
473 if (diff != NWI_IFSTATE_DIFF_UNCHANGED) {
474 ifstate->flags |= NWI_IFSTATE_FLAGS_FROM_DIFF(diff);
475 }
476 }
477
478 static void
479 nwi_state_diff_add_change(nwi_state_t diff, nwi_state_t old,
480 nwi_ifstate_t ifstate)
481 {
482 nwi_ifstate_t existing;
483 nwi_ifstate_t new;
484
485 existing = nwi_state_get_ifstate_with_name(old,
486 ifstate->af,
487 nwi_ifstate_get_ifname(ifstate));
488 new = nwi_state_diff_append(diff, ifstate);
489 if (existing != NULL) {
490 if (nwi_ifstate_has_changed(existing, new)) {
491 nwi_ifstate_set_diff(new,
492 NWI_IFSTATE_DIFF_CHANGED);
493 } else if (existing->rank < new->rank) {
494 nwi_ifstate_set_diff(new,
495 NWI_IFSTATE_DIFF_RANK_DOWN);
496 } else if (existing->rank > new->rank) {
497 nwi_ifstate_set_diff(new,
498 NWI_IFSTATE_DIFF_RANK_UP);
499 } else {
500 nwi_ifstate_set_diff(new,
501 NWI_IFSTATE_DIFF_UNCHANGED);
502 }
503 } else {
504 nwi_ifstate_set_diff(new, NWI_IFSTATE_DIFF_ADDED);
505 }
506 return;
507 }
508
509 static void
510 nwi_state_diff_remove(nwi_state_t state, nwi_ifstate_t ifstate)
511 {
512 nwi_ifstate_t removed_ifstate;
513
514 if (nwi_state_get_ifstate_with_name(state,
515 ifstate->af,
516 nwi_ifstate_get_ifname(ifstate))
517 != NULL) {
518 /* there's still an ifstate */
519 return;
520 }
521 removed_ifstate = nwi_state_diff_append(state, ifstate);
522 nwi_ifstate_set_diff(removed_ifstate, NWI_IFSTATE_DIFF_REMOVED);
523 return;
524 }
525
526 static void
527 nwi_state_diff_populate(nwi_state_t diff, nwi_state_t old, nwi_state_t new)
528 {
529 int i;
530 nwi_ifstate_t scan;
531
532 if (new != NULL) {
533 /* check for adds/changes */
534 if (new->ipv4_count) {
535 for (i = 0, scan = new->ifstate_list;
536 i < new->ipv4_count; i++, scan++) {
537 nwi_state_diff_add_change(diff, old, scan);
538 }
539 }
540 if (new->ipv6_count) {
541 scan = new->ifstate_list + new->max_if_count;
542 for (i = 0;
543 i < new->ipv6_count; i++, scan++) {
544 nwi_state_diff_add_change(diff, old, scan);
545 }
546 }
547 }
548 if (old != NULL) {
549 /* check for removes */
550 if (old->ipv4_count) {
551 for (i = 0, scan = old->ifstate_list;
552 i < old->ipv4_count; i++, scan++) {
553 nwi_state_diff_remove(diff, scan);
554 }
555 }
556 if (old->ipv6_count) {
557 scan = old->ifstate_list + old->max_if_count;
558 for (i = 0;
559 i < old->ipv6_count; i++, scan++) {
560 nwi_state_diff_remove(diff, scan);
561 }
562 }
563 }
564 return;
565 }
566
567 static int
568 nwi_state_max_af_count(nwi_state_t state)
569 {
570 if (state->ipv4_count >= state->ipv6_count) {
571 return (state->ipv4_count);
572 }
573 return (state->ipv6_count);
574 }
575
576 __private_extern__ nwi_state_t
577 nwi_state_diff(nwi_state_t old, nwi_state_t new)
578 {
579 nwi_state_t diff;
580 int total_count = 0;
581
582 /*
583 * Compute the worst case total number of elements we need:
584 * the max count of (IPv4, IPv6) in the old
585 * + the max count of (IPv4, IPv6) in the new
586 * Worst case assumes that the old and new share none of the
587 * same interfaces.
588 */
589 if (old != NULL) {
590 total_count += nwi_state_max_af_count(old);
591 }
592 if (new != NULL) {
593 total_count += nwi_state_max_af_count(new);
594 }
595 if (total_count == 0) {
596 return NULL;
597 }
598
599 diff = nwi_state_new(NULL, total_count);
600 nwi_state_diff_populate(diff, old, new);
601
602 /* diff consists of a nwi_state_t with diff flags on each ifstate */
603 return diff;
604 }
605
606 static __inline__
607 void
608 _nwi_ifstate_set_generation(nwi_ifstate_t ifstate, uint64_t generation_count)
609 {
610 ifstate->if_generation_count = generation_count;
611
612 return;
613 }
614
615 static
616 boolean_t
617 _nwi_ifstate_has_changed(nwi_state_t state, const char * ifname)
618 {
619 nwi_ifstate_t ifstate;
620
621 /* If either the v4 ifstate or the v6 ifstate
622 * has changed, then report that the interface has changed */
623 ifstate = nwi_state_get_ifstate_with_name(state,
624 AF_INET,
625 ifname);
626
627 if (ifstate != NULL
628 && nwi_ifstate_get_diff(ifstate) != NWI_IFSTATE_DIFF_UNCHANGED) {
629 return (TRUE);
630 }
631
632 ifstate = nwi_state_get_ifstate_with_name(state,
633 AF_INET6,
634 ifname);
635
636 if (ifstate != NULL
637 && nwi_ifstate_get_diff(ifstate) != NWI_IFSTATE_DIFF_UNCHANGED) {
638 return (TRUE);
639 }
640 return (FALSE);
641 }
642
643 __private_extern__
644 void
645 _nwi_state_update_interface_generations(nwi_state_t old_state, nwi_state_t state, nwi_state_t changes)
646 {
647 int i;
648 uint64_t generation_count;
649 nwi_ifstate_t scan;
650
651 if (state == NULL || changes == NULL) {
652 return;
653 }
654
655 /* cache the generation count */
656 generation_count = state->generation_count;
657
658 for (i = 0, scan = nwi_state_ifstate_list(state, AF_INET);
659 i < state->ipv4_count; i++, scan++) {
660 if (_nwi_ifstate_has_changed(changes, scan->ifname) == TRUE) {
661 /* Update the interface generation count */
662 _nwi_ifstate_set_generation(scan, generation_count);
663 } else {
664 nwi_ifstate_t old_ifstate;
665
666 old_ifstate = nwi_state_get_ifstate_with_name(old_state,
667 AF_INET,
668 scan->ifname);
669
670 /* Set the current generation count */
671 _nwi_ifstate_set_generation(scan,
672 old_ifstate->if_generation_count);
673 }
674 }
675 for (i = 0, scan = nwi_state_ifstate_list(state, AF_INET6);
676 i < state->ipv6_count; i++, scan++) {
677 /* The generation count has been already updated in
678 * the ipv4 case, just skip it. */
679 if (nwi_ifstate_get_generation(scan) ==
680 generation_count) {
681 continue;
682 }
683 if (_nwi_ifstate_has_changed(changes, scan->ifname) == TRUE) {
684 /* update the interface generation count */
685 _nwi_ifstate_set_generation(scan, generation_count);
686 } else {
687 nwi_ifstate_t old_ifstate;
688
689 old_ifstate = nwi_state_get_ifstate_with_name(old_state,
690 AF_INET6,
691 scan->ifname);
692 assert(old_ifstate != NULL);
693
694 /* Set the current generation count */
695 _nwi_ifstate_set_generation(scan,
696 old_ifstate->if_generation_count);
697 }
698 }
699 return;
700 }
701
702 __private_extern__
703 void
704 _nwi_state_compute_sha1_hash(nwi_state_t state,
705 unsigned char hash[CC_SHA1_DIGEST_LENGTH])
706 {
707 if (state == NULL) {
708 bzero(hash, CC_SHA1_DIGEST_LENGTH);
709 }
710 else {
711 CC_SHA1_CTX ctx;
712 uint64_t generation_save;
713
714 generation_save = state->generation_count;
715
716 /* zero out the generation count before computing hash */
717 state->generation_count = 0;
718
719 /* compute hash */
720 CC_SHA1_Init(&ctx);
721 CC_SHA1_Update(&ctx, state, (CC_LONG)nwi_state_size(state));
722 CC_SHA1_Final(hash, &ctx);
723
724 /* restore generation */
725 state->generation_count = generation_save;
726 }
727
728 return;
729 }