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__
unsigned int
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 (sizeof(nwi_state
) + nwi_state_compute_size((elems
+1) * 2)):0;
80 /* Should we reallocate? */
81 if (old_state
!= NULL
) {
82 if (old_state
->size
>= new_size
) {
87 state
= malloc(new_size
);
92 bzero(state
, new_size
);
93 state
->size
= new_size
;
96 * v4 list is stored 0 to elems,
97 * v6 list is stored elems + 1 to 2 * elems + 2
99 state
->ipv6_start
= elems
+ 1;
101 if (old_state
!= NULL
) {
102 state
->ipv6_count
= old_state
->ipv6_count
;
103 if (state
->ipv6_count
> 0) {
104 bcopy((void*) &old_state
->nwi_ifstates
[old_state
->ipv6_start
],
105 (void*) &state
->nwi_ifstates
[state
->ipv6_start
],
106 old_state
->ipv6_count
* sizeof(nwi_ifstate
));
109 state
->ipv4_count
= old_state
->ipv4_count
;
110 if (state
->ipv4_count
> 0) {
111 bcopy((void*) old_state
->nwi_ifstates
,
112 (void*) state
->nwi_ifstates
,
113 old_state
->ipv4_count
* sizeof(nwi_ifstate
));
118 state
->ipv4_count
= 0;
119 state
->ipv6_count
= 0;
121 nwi_state_set_last(state
, AF_INET
);
122 nwi_state_set_last(state
, AF_INET6
);
131 nwi_ifstate_get_last(nwi_state_t state
, int af
, uint32_t** last
)
136 assert(state
!= NULL
);
138 count
= (af
== AF_INET
) ? &state
->ipv4_count
139 : &state
->ipv6_count
;
141 idx
= (af
== AF_INET
) ? state
->ipv4_count
142 : (state
->ipv6_start
+ state
->ipv6_count
);
146 return &state
->nwi_ifstates
[idx
];
151 nwi_ifstate_set_signature(nwi_ifstate_t ifstate
, uint8_t * signature
)
153 bcopy(signature
, ifstate
->signature
, sizeof(ifstate
->signature
));
154 ifstate
->flags
|= NWI_IFSTATE_FLAGS_HAS_SIGNATURE
;
160 nwi_insert_ifstate(nwi_state_t state
,
161 const char * ifname
, int af
,
162 uint64_t flags
, Rank rank
,
164 struct sockaddr
* vpn_server_addr
,
165 uint32_t reach_flags
)
167 nwi_ifstate_t ifstate
;
169 /* Will only insert unique elements in the list */
170 ifstate
= nwi_state_get_ifstate_with_name(state
, af
, ifname
);
172 /* Already present, just ignore it */
173 if (ifstate
!= NULL
) {
174 if (ifstate
->rank
< rank
) {
179 if (ifstate
== NULL
) {
182 /* We need to append it as the last element */
183 ifstate
= nwi_ifstate_get_last(state
, af
, &last
);
184 bzero(ifstate
, sizeof(*ifstate
));
185 strlcpy(ifstate
->ifname
, ifname
, sizeof(ifstate
->ifname
));
190 /* We need to update the address/rank/flag fields for the existing/new
195 ifstate
->iaddr
= *((struct in_addr
*) ifa
);
198 ifstate
->iaddr6
= *((struct in6_addr
*) ifa
);
206 if (vpn_server_addr
!= NULL
&& vpn_server_addr
->sa_family
!= 0) {
207 _nwi_ifstate_set_vpn_server(ifstate
, vpn_server_addr
);
209 _nwi_ifstate_set_vpn_server(ifstate
, NULL
);
212 ifstate
->reach_flags
= reach_flags
;
213 ifstate
->rank
= rank
;
214 ifstate
->flags
= flags
;
221 nwi_state_clear(nwi_state_t state
, int af
)
225 count
= (af
== AF_INET
)
226 ?&state
->ipv4_count
:&state
->ipv6_count
;
229 nwi_state_set_last(state
, af
);
236 nwi_state_set_last(nwi_state_t state
, int af
)
244 /* The last element is an element with the flags set as
245 * NWI_IFSTATE_FLAGS_NOT_IN_LIST */
246 last_elem_idx
= (af
== AF_INET
) ? state
->ipv4_count
247 : (state
->ipv6_start
+ state
->ipv6_count
);
249 state
->nwi_ifstates
[last_elem_idx
].ifname
[0] = '\0';
250 state
->nwi_ifstates
[last_elem_idx
].flags
= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
257 #define rank_change "R"
261 nwi_ifstate_get_address(nwi_ifstate_t ifstate
)
263 return (void *)&ifstate
->iaddr
;
268 nwi_ifstate_get_diff_str(nwi_ifstate_t ifstate
)
270 if (strcmp(ifstate
->diff_str
, rank_change
) == 0) {
273 return ifstate
->diff_str
;
279 nwi_ifstate_has_changed(nwi_ifstate_t ifstate1
, nwi_ifstate_t ifstate2
)
281 if (ifstate1
->flags
!= ifstate2
->flags
) {
285 if (ifstate1
->af
== AF_INET
) {
286 if (memcmp(&ifstate1
->iaddr
, &ifstate2
->iaddr
, sizeof(struct in_addr
)) != 0) {
290 if (memcmp(&ifstate1
->iaddr6
, &ifstate2
->iaddr6
, sizeof(struct in6_addr
)) != 0) {
300 nwi_ifstate_append(nwi_state_t state
, nwi_ifstate_t scan
)
302 nwi_ifstate_t new_ifstate
= NULL
;
305 new_ifstate
= nwi_ifstate_get_last(state
, scan
->af
, &last
);
306 memcpy(new_ifstate
, scan
, sizeof(*scan
));
314 nwi_ifstate_set_diff_str(nwi_ifstate_t ifstate
, const char *diff_str
)
316 ifstate
->diff_str
= diff_str
;
321 nwi_ifstate_set_added_or_changed_str(nwi_state_t state
, nwi_state_t old_state
, nwi_ifstate_t ifstate
)
323 nwi_ifstate_t existing_ifstate
, new_ifstate
;
325 existing_ifstate
= nwi_state_get_ifstate_with_name(old_state
,
327 nwi_ifstate_get_ifname(ifstate
));
329 /* Add the element that is not in the store */
330 new_ifstate
= nwi_ifstate_append(state
, ifstate
);
332 /* These are potentially "added" elements unless they are
334 nwi_ifstate_set_diff_str(new_ifstate
, added
);
336 if (existing_ifstate
!= NULL
) {
337 if (nwi_ifstate_has_changed(existing_ifstate
, new_ifstate
) == TRUE
) {
338 nwi_ifstate_set_diff_str(new_ifstate
, changed
);
339 } else if (existing_ifstate
->rank
!= new_ifstate
->rank
) {
340 nwi_ifstate_set_diff_str(new_ifstate
, rank_change
);
342 nwi_ifstate_set_diff_str(new_ifstate
, unchanged
);
350 nwi_ifstate_set_removed_str(nwi_state_t state
, nwi_ifstate_t ifstate
)
352 nwi_ifstate_t existing_ifstate
;
354 existing_ifstate
= nwi_state_get_ifstate_with_name(state
,
356 nwi_ifstate_get_ifname(ifstate
));
358 /* Any elements that has not been added means that they are removed */
359 if (existing_ifstate
== NULL
) {
360 nwi_ifstate_t new_ifstate
= nwi_ifstate_append(state
, ifstate
);
361 nwi_ifstate_set_diff_str(new_ifstate
, deleted
);
368 nwi_state_merge_added(nwi_state_t state
, nwi_state_t old_state
,
369 nwi_state_t new_state
)
374 if (new_state
== NULL
) {
378 if (new_state
->ipv4_count
) {
379 for (i
= 0, scan
= new_state
->nwi_ifstates
;
380 i
< new_state
->ipv4_count
; i
++, scan
++) {
381 nwi_ifstate_set_added_or_changed_str(state
, old_state
, scan
);
383 nwi_state_set_last(state
, AF_INET
);
386 if (new_state
->ipv6_count
) {
387 for (i
= 0, scan
= new_state
->nwi_ifstates
+ new_state
->ipv6_start
;
388 i
< new_state
->ipv6_count
; i
++, scan
++) {
389 nwi_ifstate_set_added_or_changed_str(state
, old_state
, scan
);
391 nwi_state_set_last(state
, AF_INET6
);
397 nwi_state_merge_removed(nwi_state_t state
, nwi_state_t old_state
)
402 if (old_state
== NULL
) {
406 if (old_state
->ipv4_count
) {
407 for (i
= 0, scan
= old_state
->nwi_ifstates
;
408 i
< old_state
->ipv4_count
; i
++, scan
++) {
409 nwi_ifstate_set_removed_str(state
, scan
);
411 nwi_state_set_last(state
, AF_INET
);
414 if (old_state
->ipv6_count
) {
415 for (i
= 0, scan
= old_state
->nwi_ifstates
+ old_state
->ipv6_start
;
416 i
< old_state
->ipv6_count
; i
++, scan
++) {
417 nwi_ifstate_set_removed_str(state
, scan
);
419 nwi_state_set_last(state
, AF_INET6
);
426 nwi_state_diff(nwi_state_t old_state
, nwi_state_t new_state
)
431 if (old_state
!= NULL
) {
432 total_count
= old_state
->ipv4_count
+ old_state
->ipv6_count
;
435 if (new_state
!= NULL
) {
436 total_count
+= new_state
->ipv4_count
+ new_state
->ipv6_count
;
439 if (total_count
== 0) {
443 diff
= nwi_state_new(NULL
, total_count
);
445 nwi_state_merge_added(diff
, old_state
, new_state
);
446 nwi_state_merge_removed(diff
, old_state
);
448 /* Diff consists of a nwi_state_t with annotated diff_str's */
454 _nwi_ifstate_set_generation(nwi_ifstate_t ifstate
, uint64_t generation_count
)
456 ifstate
->if_generation_count
= generation_count
;
463 _nwi_ifstate_has_changed(nwi_state_t state
, const char * ifname
)
465 nwi_ifstate_t ifstate
;
467 /* If either the v4 ifstate or the v6 ifstate
468 * has changed, then report that the interface has changed */
469 ifstate
= nwi_state_get_ifstate_with_name(state
,
474 && ifstate
->diff_str
!= NULL
475 && strcmp(ifstate
->diff_str
, unchanged
) != 0
476 && strcmp(ifstate
->diff_str
, rank_change
) != 0) {
480 ifstate
= nwi_state_get_ifstate_with_name(state
,
485 && ifstate
->diff_str
!= NULL
486 && strcmp(ifstate
->diff_str
, unchanged
) != 0
487 && strcmp(ifstate
->diff_str
, rank_change
) != 0) {
495 _nwi_state_update_interface_generations(nwi_state_t old_state
, nwi_state_t state
, nwi_state_t changes
)
498 uint64_t generation_count
;
501 if (state
== NULL
|| changes
== NULL
) {
505 /* cache the generation count */
506 generation_count
= state
->generation_count
;
508 if (state
->ipv4_count
) {
509 for (i
= 0, scan
= state
->nwi_ifstates
;
510 i
< state
->ipv4_count
; i
++, scan
++) {
511 if (_nwi_ifstate_has_changed(changes
, scan
->ifname
) == TRUE
) {
512 /* Update the interface generation count */
513 _nwi_ifstate_set_generation(scan
, generation_count
);
515 nwi_ifstate_t old_ifstate
;
517 old_ifstate
= nwi_state_get_ifstate_with_name(old_state
,
521 /* Set the current generation count */
522 _nwi_ifstate_set_generation(scan
,
523 old_ifstate
->if_generation_count
);
528 if (state
->ipv6_count
) {
529 for (i
= 0, scan
= state
->nwi_ifstates
+ state
->ipv6_start
;
530 i
< state
->ipv6_count
; i
++, scan
++) {
531 /* The generation count has been already updated in
532 * the ipv4 case, just skip it. */
533 if (nwi_ifstate_get_generation(scan
) ==
537 if (_nwi_ifstate_has_changed(changes
, scan
->ifname
) == TRUE
) {
538 /* update the interface generation count */
539 _nwi_ifstate_set_generation(scan
, generation_count
);
541 nwi_ifstate_t old_ifstate
;
543 old_ifstate
= nwi_state_get_ifstate_with_name(old_state
,
546 assert(old_ifstate
!= NULL
);
548 /* Set the current generation count */
549 _nwi_ifstate_set_generation(scan
,
550 old_ifstate
->if_generation_count
);