2 * Copyright (c) 2011-2013 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_information_priv.h"
36 const sa_family_t nwi_af_list
[] = {AF_INET
, AF_INET6
};
38 static __inline__
size_t
39 nwi_state_compute_size(unsigned int n
)
41 return (offsetof(nwi_state
, nwi_ifstates
[n
]));
46 nwi_state_copy_priv(nwi_state_t src
)
48 nwi_state_t dest
= NULL
;
54 dest
= malloc(src
->size
);
57 bcopy(src
, dest
, src
->size
);
67 nwi_state_new(nwi_state_t old_state
, int elems
)
69 nwi_state_t state
= NULL
;
72 if (old_state
== NULL
&& elems
== 0) {
76 /* Need to insert a last node for each of the v4/v6 list */
77 new_size
= (elems
!= 0)
78 ? (uint32_t)(sizeof(nwi_state
) + nwi_state_compute_size((elems
+1) * 2))
81 /* Should we reallocate? */
82 if (old_state
!= NULL
) {
83 if (old_state
->size
>= new_size
) {
88 state
= malloc(new_size
);
89 bzero(state
, new_size
);
90 state
->size
= new_size
;
93 * v4 list is stored 0 to elems,
94 * v6 list is stored elems + 1 to 2 * elems + 2
96 state
->ipv6_start
= elems
+ 1;
98 if (old_state
!= NULL
) {
99 state
->ipv6_count
= old_state
->ipv6_count
;
100 if (state
->ipv6_count
> 0) {
101 bcopy((void*) &old_state
->nwi_ifstates
[old_state
->ipv6_start
],
102 (void*) &state
->nwi_ifstates
[state
->ipv6_start
],
103 old_state
->ipv6_count
* sizeof(nwi_ifstate
));
106 state
->ipv4_count
= old_state
->ipv4_count
;
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
));
115 state
->ipv4_count
= 0;
116 state
->ipv6_count
= 0;
118 nwi_state_set_last(state
, AF_INET
);
119 nwi_state_set_last(state
, AF_INET6
);
128 nwi_ifstate_get_last(nwi_state_t state
, int af
, uint32_t** last
)
133 assert(state
!= NULL
);
135 count
= (af
== AF_INET
) ? &state
->ipv4_count
136 : &state
->ipv6_count
;
138 idx
= (af
== AF_INET
) ? state
->ipv4_count
139 : (state
->ipv6_start
+ state
->ipv6_count
);
143 return &state
->nwi_ifstates
[idx
];
148 nwi_ifstate_set_signature(nwi_ifstate_t ifstate
, uint8_t * signature
)
150 bcopy(signature
, ifstate
->signature
, sizeof(ifstate
->signature
));
151 ifstate
->flags
|= NWI_IFSTATE_FLAGS_HAS_SIGNATURE
;
157 nwi_insert_ifstate(nwi_state_t state
,
158 const char * ifname
, int af
,
159 uint64_t flags
, Rank rank
,
161 struct sockaddr
* vpn_server_addr
,
162 uint32_t reach_flags
)
164 nwi_ifstate_t ifstate
;
166 /* Will only insert unique elements in the list */
167 ifstate
= nwi_state_get_ifstate_with_name(state
, af
, ifname
);
169 /* Already present, just ignore it */
170 if (ifstate
!= NULL
) {
171 if (ifstate
->rank
< rank
) {
176 if (ifstate
== NULL
) {
179 /* We need to append it as the last element */
180 ifstate
= nwi_ifstate_get_last(state
, af
, &last
);
181 bzero(ifstate
, sizeof(*ifstate
));
182 strlcpy(ifstate
->ifname
, ifname
, sizeof(ifstate
->ifname
));
187 /* We need to update the address/rank/flag fields for the existing/new
192 ifstate
->iaddr
= *((struct in_addr
*) ifa
);
195 ifstate
->iaddr6
= *((struct in6_addr
*) ifa
);
203 if (vpn_server_addr
!= NULL
&& vpn_server_addr
->sa_family
!= 0) {
204 _nwi_ifstate_set_vpn_server(ifstate
, vpn_server_addr
);
206 _nwi_ifstate_set_vpn_server(ifstate
, NULL
);
209 ifstate
->reach_flags
= reach_flags
;
210 ifstate
->rank
= rank
;
211 ifstate
->flags
= flags
;
218 nwi_state_clear(nwi_state_t state
, int af
)
222 count
= (af
== AF_INET
)
223 ?&state
->ipv4_count
:&state
->ipv6_count
;
226 nwi_state_set_last(state
, af
);
233 nwi_state_set_last(nwi_state_t state
, int af
)
241 /* The last element is an element with the flags set as
242 * NWI_IFSTATE_FLAGS_NOT_IN_LIST */
243 last_elem_idx
= (af
== AF_INET
) ? state
->ipv4_count
244 : (state
->ipv6_start
+ state
->ipv6_count
);
246 state
->nwi_ifstates
[last_elem_idx
].ifname
[0] = '\0';
247 state
->nwi_ifstates
[last_elem_idx
].flags
= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
254 #define rank_change "R"
258 nwi_ifstate_get_address(nwi_ifstate_t ifstate
)
260 return (void *)&ifstate
->iaddr
;
265 nwi_ifstate_get_diff_str(nwi_ifstate_t ifstate
)
267 if (strcmp(ifstate
->diff_str
, rank_change
) == 0) {
270 return ifstate
->diff_str
;
276 nwi_ifstate_has_changed(nwi_ifstate_t ifstate1
, nwi_ifstate_t ifstate2
)
278 if (ifstate1
->flags
!= ifstate2
->flags
) {
282 if (ifstate1
->af
== AF_INET
) {
283 if (memcmp(&ifstate1
->iaddr
, &ifstate2
->iaddr
, sizeof(struct in_addr
)) != 0) {
287 if (memcmp(&ifstate1
->iaddr6
, &ifstate2
->iaddr6
, sizeof(struct in6_addr
)) != 0) {
297 nwi_ifstate_append(nwi_state_t state
, nwi_ifstate_t scan
)
299 nwi_ifstate_t new_ifstate
= NULL
;
302 new_ifstate
= nwi_ifstate_get_last(state
, scan
->af
, &last
);
303 memcpy(new_ifstate
, scan
, sizeof(*scan
));
311 nwi_ifstate_set_diff_str(nwi_ifstate_t ifstate
, const char *diff_str
)
313 ifstate
->diff_str
= diff_str
;
318 nwi_ifstate_set_added_or_changed_str(nwi_state_t state
, nwi_state_t old_state
, nwi_ifstate_t ifstate
)
320 nwi_ifstate_t existing_ifstate
, new_ifstate
;
322 existing_ifstate
= nwi_state_get_ifstate_with_name(old_state
,
324 nwi_ifstate_get_ifname(ifstate
));
326 /* Add the element that is not in the store */
327 new_ifstate
= nwi_ifstate_append(state
, ifstate
);
329 /* These are potentially "added" elements unless they are
331 nwi_ifstate_set_diff_str(new_ifstate
, added
);
333 if (existing_ifstate
!= NULL
) {
334 if (nwi_ifstate_has_changed(existing_ifstate
, new_ifstate
) == TRUE
) {
335 nwi_ifstate_set_diff_str(new_ifstate
, changed
);
336 } else if (existing_ifstate
->rank
!= new_ifstate
->rank
) {
337 nwi_ifstate_set_diff_str(new_ifstate
, rank_change
);
339 nwi_ifstate_set_diff_str(new_ifstate
, unchanged
);
347 nwi_ifstate_set_removed_str(nwi_state_t state
, nwi_ifstate_t ifstate
)
349 nwi_ifstate_t existing_ifstate
;
351 existing_ifstate
= nwi_state_get_ifstate_with_name(state
,
353 nwi_ifstate_get_ifname(ifstate
));
355 /* Any elements that has not been added means that they are removed */
356 if (existing_ifstate
== NULL
) {
357 nwi_ifstate_t new_ifstate
= nwi_ifstate_append(state
, ifstate
);
358 nwi_ifstate_set_diff_str(new_ifstate
, deleted
);
365 nwi_state_merge_added(nwi_state_t state
, nwi_state_t old_state
,
366 nwi_state_t new_state
)
371 if (new_state
== NULL
) {
375 if (new_state
->ipv4_count
) {
376 for (i
= 0, scan
= new_state
->nwi_ifstates
;
377 i
< new_state
->ipv4_count
; i
++, scan
++) {
378 nwi_ifstate_set_added_or_changed_str(state
, old_state
, scan
);
380 nwi_state_set_last(state
, AF_INET
);
383 if (new_state
->ipv6_count
) {
384 for (i
= 0, scan
= new_state
->nwi_ifstates
+ new_state
->ipv6_start
;
385 i
< new_state
->ipv6_count
; i
++, scan
++) {
386 nwi_ifstate_set_added_or_changed_str(state
, old_state
, scan
);
388 nwi_state_set_last(state
, AF_INET6
);
394 nwi_state_merge_removed(nwi_state_t state
, nwi_state_t old_state
)
399 if (old_state
== NULL
) {
403 if (old_state
->ipv4_count
) {
404 for (i
= 0, scan
= old_state
->nwi_ifstates
;
405 i
< old_state
->ipv4_count
; i
++, scan
++) {
406 nwi_ifstate_set_removed_str(state
, scan
);
408 nwi_state_set_last(state
, AF_INET
);
411 if (old_state
->ipv6_count
) {
412 for (i
= 0, scan
= old_state
->nwi_ifstates
+ old_state
->ipv6_start
;
413 i
< old_state
->ipv6_count
; i
++, scan
++) {
414 nwi_ifstate_set_removed_str(state
, scan
);
416 nwi_state_set_last(state
, AF_INET6
);
423 nwi_state_diff(nwi_state_t old_state
, nwi_state_t new_state
)
428 if (old_state
!= NULL
) {
429 total_count
= old_state
->ipv4_count
+ old_state
->ipv6_count
;
432 if (new_state
!= NULL
) {
433 total_count
+= new_state
->ipv4_count
+ new_state
->ipv6_count
;
436 if (total_count
== 0) {
440 diff
= nwi_state_new(NULL
, total_count
);
442 nwi_state_merge_added(diff
, old_state
, new_state
);
443 nwi_state_merge_removed(diff
, old_state
);
445 /* Diff consists of a nwi_state_t with annotated diff_str's */
451 _nwi_ifstate_set_generation(nwi_ifstate_t ifstate
, uint64_t generation_count
)
453 ifstate
->if_generation_count
= generation_count
;
460 _nwi_ifstate_has_changed(nwi_state_t state
, const char * ifname
)
462 nwi_ifstate_t ifstate
;
464 /* If either the v4 ifstate or the v6 ifstate
465 * has changed, then report that the interface has changed */
466 ifstate
= nwi_state_get_ifstate_with_name(state
,
471 && ifstate
->diff_str
!= NULL
472 && strcmp(ifstate
->diff_str
, unchanged
) != 0
473 && strcmp(ifstate
->diff_str
, rank_change
) != 0) {
477 ifstate
= nwi_state_get_ifstate_with_name(state
,
482 && ifstate
->diff_str
!= NULL
483 && strcmp(ifstate
->diff_str
, unchanged
) != 0
484 && strcmp(ifstate
->diff_str
, rank_change
) != 0) {
492 _nwi_state_update_interface_generations(nwi_state_t old_state
, nwi_state_t state
, nwi_state_t changes
)
495 uint64_t generation_count
;
498 if (state
== NULL
|| changes
== NULL
) {
502 /* cache the generation count */
503 generation_count
= state
->generation_count
;
505 if (state
->ipv4_count
) {
506 for (i
= 0, scan
= state
->nwi_ifstates
;
507 i
< state
->ipv4_count
; i
++, scan
++) {
508 if (_nwi_ifstate_has_changed(changes
, scan
->ifname
) == TRUE
) {
509 /* Update the interface generation count */
510 _nwi_ifstate_set_generation(scan
, generation_count
);
512 nwi_ifstate_t old_ifstate
;
514 old_ifstate
= nwi_state_get_ifstate_with_name(old_state
,
518 /* Set the current generation count */
519 _nwi_ifstate_set_generation(scan
,
520 old_ifstate
->if_generation_count
);
525 if (state
->ipv6_count
) {
526 for (i
= 0, scan
= state
->nwi_ifstates
+ state
->ipv6_start
;
527 i
< state
->ipv6_count
; i
++, scan
++) {
528 /* The generation count has been already updated in
529 * the ipv4 case, just skip it. */
530 if (nwi_ifstate_get_generation(scan
) ==
534 if (_nwi_ifstate_has_changed(changes
, scan
->ifname
) == TRUE
) {
535 /* update the interface generation count */
536 _nwi_ifstate_set_generation(scan
, generation_count
);
538 nwi_ifstate_t old_ifstate
;
540 old_ifstate
= nwi_state_get_ifstate_with_name(old_state
,
543 assert(old_ifstate
!= NULL
);
545 /* Set the current generation count */
546 _nwi_ifstate_set_generation(scan
,
547 old_ifstate
->if_generation_count
);