2 * Copyright (c) 2011-2015, 2017, 2018 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@
24 #include <arpa/inet.h>
28 #include <sys/socket.h>
31 #include "network_state_information_priv.h"
36 #define NWI_IFSTATE_FLAGS(flags) \
37 ((flags) & NWI_IFSTATE_FLAGS_MASK)
39 #define NWI_IFSTATE_FLAGS_GET_DIFF(flags) \
40 (((flags) & NWI_IFSTATE_FLAGS_DIFF_MASK) >> 8)
42 #define NWI_IFSTATE_FLAGS_FROM_DIFF(diff) \
43 (((diff) << 8) & NWI_IFSTATE_FLAGS_DIFF_MASK)
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
54 nwi_state_fix_af_aliases(nwi_state_t state
, uint32_t old_max_if_count
)
58 nwi_ifstate_t ifstate
;
60 offset
= state
->max_if_count
- old_max_if_count
;
62 syslog(LOG_ERR
, "new count %d < old count %d",
63 state
->max_if_count
, old_max_if_count
);
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
;
71 if (ifstate
->af_alias_offset
!= 0) {
72 /* IPv6 is higher in memory, alias is forward */
73 ifstate
->af_alias_offset
+= offset
;
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
;
81 if (ifstate
->af_alias_offset
!= 0) {
82 /* IPv6 is higher in memory, alias is backward */
83 ifstate
->af_alias_offset
-= offset
;
90 nwi_state_add_to_if_list(nwi_state_t state
, nwi_ifstate_t ifstate
)
95 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_IFLIST
) != 0) {
96 /* doesn't get added to interface list */
99 if (state
->if_list_count
>= state
->max_if_count
) {
103 for (i
= 0, scan
= nwi_state_if_list(state
);
104 i
< state
->if_list_count
;
106 nwi_ifstate_t this_ifstate
;
108 this_ifstate
= state
->ifstate_list
+ *scan
;
109 if (strcmp(this_ifstate
->ifname
, ifstate
->ifname
) == 0) {
110 /* it's already in the list */
114 /* add it to the end */
115 *scan
= (nwi_ifindex_t
)(ifstate
- state
->ifstate_list
);
116 state
->if_list_count
++;
121 nwi_state_set_if_list(nwi_state_t state
)
123 nwi_ifstate_t scan_v4
;
124 nwi_ifstate_t scan_v6
;
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 boolean_t add_v4
= FALSE
;
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
) {
142 else if (scan_v4
!= NULL
) {
146 /* add v4 interface */
147 nwi_state_add_to_if_list(state
, scan_v4
);
149 scan_v4
= nwi_state_get_ifstate_with_index(state
,
154 /* add v6 interface, move to next item */
155 nwi_state_add_to_if_list(state
, scan_v6
);
157 scan_v6
= nwi_state_get_ifstate_with_index(state
,
167 nwi_state_make_copy(nwi_state_t src
)
169 nwi_state_t dest
= NULL
;
175 size
= nwi_state_size(src
);
176 dest
= (nwi_state_t
)malloc(size
);
179 bcopy(src
, dest
, size
);
186 nwi_state_new(nwi_state_t old_state
, int max_if_count
)
189 nwi_state_t state
= NULL
;
191 if (old_state
== NULL
&& max_if_count
== 0) {
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 */
202 size
= nwi_state_compute_size(max_if_count
);
203 state
= (nwi_state_t
)malloc(size
);
205 state
->max_if_count
= max_if_count
;
206 state
->version
= NWI_STATE_VERSION
;
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
));
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
));
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
);
227 state
->ipv4_count
= 0;
228 state
->ipv6_count
= 0;
233 __private_extern__
void
234 nwi_state_finalize(nwi_state_t state
)
239 nwi_state_set_if_list(state
);
245 nwi_state_get_last_ifstate(nwi_state_t state
, int af
, nwi_ifindex_t
** last
)
247 nwi_ifindex_t
* count
;
250 assert(state
!= NULL
);
252 count
= (af
== AF_INET
) ? &state
->ipv4_count
253 : &state
->ipv6_count
;
255 idx
= (af
== AF_INET
) ? state
->ipv4_count
256 : (state
->max_if_count
+ state
->ipv6_count
);
260 return &state
->ifstate_list
[idx
];
265 nwi_ifstate_set_signature(nwi_ifstate_t ifstate
, uint8_t * signature
)
267 bcopy(signature
, ifstate
->signature
, sizeof(ifstate
->signature
));
268 ifstate
->flags
|= NWI_IFSTATE_FLAGS_HAS_SIGNATURE
;
273 nwi_state_add_ifstate_alias(nwi_state_t state
, nwi_ifstate_t ifstate
)
277 alias
= nwi_state_get_ifstate_with_name(state
,
278 nwi_other_af(ifstate
->af
),
283 ifstate
->af_alias_offset
= (nwi_ifindex_t
)(alias
- ifstate
);
284 alias
->af_alias_offset
= (nwi_ifindex_t
)(ifstate
- alias
);
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
,
293 struct sockaddr
* vpn_server_addr
,
294 uint32_t reach_flags
)
296 nwi_ifstate_t ifstate
;
298 /* Will only add unique elements to the list */
299 ifstate
= nwi_state_get_ifstate_with_name(state
, af
, ifname
);
301 /* Already present, just ignore it */
302 if (ifstate
!= NULL
) {
303 if (ifstate
->rank
< rank
) {
304 /* always true because they are added in order */
311 nwi_ifindex_t
* count_p
;
313 /* make sure we aren't already full */
314 ifstate
= nwi_state_get_last_ifstate(state
, af
, &count_p
);
316 if (count
== state
->max_if_count
) {
317 /* should not happen */
319 "nwi_state_add_ifstate: full at count %d\n",
324 /* previous ifstate is no longer last */
325 nwi_ifstate_t prev
= ifstate
- 1;
327 prev
->flags
&= ~NWI_IFSTATE_FLAGS_LAST_ITEM
;
329 bzero(ifstate
, sizeof(*ifstate
));
330 strlcpy(ifstate
->ifname
, ifname
, sizeof(ifstate
->ifname
));
332 /* this is the new last ifstate */
333 ifstate
->flags
|= NWI_IFSTATE_FLAGS_LAST_ITEM
;
337 nwi_state_add_ifstate_alias(state
, ifstate
);
340 /* We need to update the address/rank/flag fields for the existing/new
345 ifstate
->iaddr
= *((struct in_addr
*) ifa
);
348 ifstate
->iaddr6
= *((struct in6_addr
*) ifa
);
356 if (vpn_server_addr
!= NULL
&& vpn_server_addr
->sa_family
!= 0) {
357 _nwi_ifstate_set_vpn_server(ifstate
, vpn_server_addr
);
359 _nwi_ifstate_set_vpn_server(ifstate
, NULL
);
362 ifstate
->reach_flags
= reach_flags
;
363 ifstate
->rank
= rank
;
364 ifstate
->flags
&= ~NWI_IFSTATE_FLAGS_MASK
;
365 ifstate
->flags
|= NWI_IFSTATE_FLAGS(flags
);
371 nwi_state_clear(nwi_state_t state
, int af
)
374 state
->ipv4_count
= 0;
377 state
->ipv6_count
= 0;
385 nwi_ifstate_get_address(nwi_ifstate_t ifstate
)
387 return (void *)&ifstate
->iaddr
;
391 static __inline__
uint8_t
392 nwi_ifstate_get_diff(nwi_ifstate_t ifstate
)
394 return (NWI_IFSTATE_FLAGS_GET_DIFF(ifstate
->flags
));
397 __private_extern__
const char *
398 nwi_ifstate_get_diff_str(nwi_ifstate_t ifstate
)
400 const char * strings
[] = {
410 diff
= nwi_ifstate_get_diff(ifstate
);
411 if (diff
< sizeof(strings
) / sizeof(strings
[0])) {
412 return (strings
[diff
]);
417 __private_extern__ nwi_ifstate_difference_t
418 nwi_ifstate_get_difference(nwi_ifstate_t diff_ifstate
)
420 nwi_ifstate_difference_t diff
;
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
;
427 case NWI_IFSTATE_DIFF_REMOVED
:
428 diff
= knwi_ifstate_difference_removed
;
431 diff
= knwi_ifstate_difference_none
;
437 static inline boolean_t
438 nwi_ifstate_has_changed(nwi_ifstate_t ifstate1
, nwi_ifstate_t ifstate2
)
440 if (NWI_IFSTATE_FLAGS(ifstate1
->flags
)
441 != NWI_IFSTATE_FLAGS(ifstate2
->flags
)) {
445 if (ifstate1
->af
== AF_INET
) {
446 if (memcmp(&ifstate1
->iaddr
, &ifstate2
->iaddr
, sizeof(struct in_addr
)) != 0) {
450 if (memcmp(&ifstate1
->iaddr6
, &ifstate2
->iaddr6
, sizeof(struct in6_addr
)) != 0) {
457 static inline nwi_ifstate_t
458 nwi_state_diff_append(nwi_state_t state
, nwi_ifstate_t scan
)
460 nwi_ifindex_t
* last
;
461 nwi_ifstate_t new_ifstate
= NULL
;
463 new_ifstate
= nwi_state_get_last_ifstate(state
, scan
->af
, &last
);
464 memcpy(new_ifstate
, scan
, sizeof(*scan
));
470 nwi_ifstate_set_diff(nwi_ifstate_t ifstate
, uint8_t diff
)
472 ifstate
->flags
&= ~NWI_IFSTATE_FLAGS_DIFF_MASK
;
473 if (diff
!= NWI_IFSTATE_DIFF_UNCHANGED
) {
474 ifstate
->flags
|= NWI_IFSTATE_FLAGS_FROM_DIFF(diff
);
479 nwi_state_diff_add_change(nwi_state_t diff
, nwi_state_t old_ifstate
,
480 nwi_ifstate_t ifstate
)
482 nwi_ifstate_t existing
;
483 nwi_ifstate_t new_ifstate
;
485 existing
= nwi_state_get_ifstate_with_name(old_ifstate
,
487 nwi_ifstate_get_ifname(ifstate
));
488 new_ifstate
= nwi_state_diff_append(diff
, ifstate
);
489 if (existing
!= NULL
) {
490 if (nwi_ifstate_has_changed(existing
, new_ifstate
)) {
491 nwi_ifstate_set_diff(new_ifstate
,
492 NWI_IFSTATE_DIFF_CHANGED
);
493 } else if (existing
->rank
< new_ifstate
->rank
) {
494 nwi_ifstate_set_diff(new_ifstate
,
495 NWI_IFSTATE_DIFF_RANK_DOWN
);
496 } else if (existing
->rank
> new_ifstate
->rank
) {
497 nwi_ifstate_set_diff(new_ifstate
,
498 NWI_IFSTATE_DIFF_RANK_UP
);
500 nwi_ifstate_set_diff(new_ifstate
,
501 NWI_IFSTATE_DIFF_UNCHANGED
);
504 nwi_ifstate_set_diff(new_ifstate
, NWI_IFSTATE_DIFF_ADDED
);
510 nwi_state_diff_remove(nwi_state_t state
, nwi_ifstate_t ifstate
)
512 nwi_ifstate_t removed_ifstate
;
514 if (nwi_state_get_ifstate_with_name(state
,
516 nwi_ifstate_get_ifname(ifstate
))
518 /* there's still an ifstate */
521 removed_ifstate
= nwi_state_diff_append(state
, ifstate
);
522 nwi_ifstate_set_diff(removed_ifstate
, NWI_IFSTATE_DIFF_REMOVED
);
527 nwi_state_diff_populate(nwi_state_t diff
, nwi_state_t old_ifstate
, nwi_state_t new_ifstate
)
532 if (new_ifstate
!= NULL
) {
533 /* check for adds/changes */
534 if (new_ifstate
->ipv4_count
) {
535 for (i
= 0, scan
= new_ifstate
->ifstate_list
;
536 i
< new_ifstate
->ipv4_count
; i
++, scan
++) {
537 nwi_state_diff_add_change(diff
, old_ifstate
, scan
);
540 if (new_ifstate
->ipv6_count
) {
541 scan
= new_ifstate
->ifstate_list
+ new_ifstate
->max_if_count
;
543 i
< new_ifstate
->ipv6_count
; i
++, scan
++) {
544 nwi_state_diff_add_change(diff
, old_ifstate
, scan
);
548 if (old_ifstate
!= NULL
) {
549 /* check for removes */
550 if (old_ifstate
->ipv4_count
) {
551 for (i
= 0, scan
= old_ifstate
->ifstate_list
;
552 i
< old_ifstate
->ipv4_count
; i
++, scan
++) {
553 nwi_state_diff_remove(diff
, scan
);
556 if (old_ifstate
->ipv6_count
) {
557 scan
= old_ifstate
->ifstate_list
+ old_ifstate
->max_if_count
;
559 i
< old_ifstate
->ipv6_count
; i
++, scan
++) {
560 nwi_state_diff_remove(diff
, scan
);
568 nwi_state_max_af_count(nwi_state_t state
)
570 if (state
->ipv4_count
>= state
->ipv6_count
) {
571 return (state
->ipv4_count
);
573 return (state
->ipv6_count
);
576 __private_extern__ nwi_state_t
577 nwi_state_diff(nwi_state_t old_ifstate
, nwi_state_t new_ifstate
)
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
589 if (old_ifstate
!= NULL
) {
590 total_count
+= nwi_state_max_af_count(old_ifstate
);
592 if (new_ifstate
!= NULL
) {
593 total_count
+= nwi_state_max_af_count(new_ifstate
);
595 if (total_count
== 0) {
599 diff
= nwi_state_new(NULL
, total_count
);
600 nwi_state_diff_populate(diff
, old_ifstate
, new_ifstate
);
602 /* diff consists of a nwi_state_t with diff flags on each ifstate */
608 _nwi_ifstate_set_generation(nwi_ifstate_t ifstate
, uint64_t generation_count
)
610 ifstate
->if_generation_count
= generation_count
;
617 _nwi_ifstate_has_changed(nwi_state_t state
, const char * ifname
)
619 nwi_ifstate_t ifstate
;
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
,
628 && nwi_ifstate_get_diff(ifstate
) != NWI_IFSTATE_DIFF_UNCHANGED
) {
632 ifstate
= nwi_state_get_ifstate_with_name(state
,
637 && nwi_ifstate_get_diff(ifstate
) != NWI_IFSTATE_DIFF_UNCHANGED
) {
645 _nwi_state_update_interface_generations(nwi_state_t old_state
, nwi_state_t state
, nwi_state_t changes
)
648 uint64_t generation_count
;
651 if (state
== NULL
|| changes
== NULL
) {
655 /* cache the generation count */
656 generation_count
= state
->generation_count
;
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
)) {
661 /* Update the interface generation count */
662 _nwi_ifstate_set_generation(scan
, generation_count
);
664 nwi_ifstate_t old_ifstate
;
666 old_ifstate
= nwi_state_get_ifstate_with_name(old_state
,
669 assert(old_ifstate
!= NULL
);
671 /* Set the current generation count */
672 _nwi_ifstate_set_generation(scan
,
673 old_ifstate
->if_generation_count
);
676 for (i
= 0, scan
= nwi_state_ifstate_list(state
, AF_INET6
);
677 i
< state
->ipv6_count
; i
++, scan
++) {
678 /* The generation count has been already updated in
679 * the ipv4 case, just skip it. */
680 if (nwi_ifstate_get_generation(scan
) ==
684 if (_nwi_ifstate_has_changed(changes
, scan
->ifname
)) {
685 /* update the interface generation count */
686 _nwi_ifstate_set_generation(scan
, generation_count
);
688 nwi_ifstate_t old_ifstate
;
690 old_ifstate
= nwi_state_get_ifstate_with_name(old_state
,
693 assert(old_ifstate
!= NULL
);
695 /* Set the current generation count */
696 _nwi_ifstate_set_generation(scan
,
697 old_ifstate
->if_generation_count
);
705 _nwi_state_compute_sha1_hash(nwi_state_t state
,
706 unsigned char hash
[CC_SHA1_DIGEST_LENGTH
])
709 bzero(hash
, CC_SHA1_DIGEST_LENGTH
);
713 uint64_t generation_save
;
715 generation_save
= state
->generation_count
;
717 /* zero out the generation count before computing hash */
718 state
->generation_count
= 0;
722 CC_SHA1_Update(&ctx
, state
, (CC_LONG
)nwi_state_size(state
));
723 CC_SHA1_Final(hash
, &ctx
);
725 /* restore generation */
726 state
->generation_count
= generation_save
;