]> git.saurik.com Git - apple/configd.git/blob - nwi/network_information.c
configd-802.40.13.tar.gz
[apple/configd.git] / nwi / network_information.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
25 #include <pthread.h>
26 #include <notify.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <sys/socket.h>
30 #include <dispatch/dispatch.h>
31 #include <xpc/xpc.h>
32
33 #include "libSystemConfiguration_client.h"
34 #include "network_information.h"
35 #include "network_information_priv.h"
36
37 static nwi_state_t G_nwi_state = NULL;
38 static pthread_mutex_t nwi_store_lock = PTHREAD_MUTEX_INITIALIZER;
39 static boolean_t nwi_store_token_valid = FALSE;
40
41 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
42 static int nwi_store_token;
43
44 static boolean_t nwi_store_force_refresh = FALSE;
45
46 #pragma mark -
47 #pragma mark Network information [nwi] client support
48
49
50 // Note: protected by __nwi_configuration_queue()
51 static int nwi_active = 0;
52 static libSC_info_client_t *nwi_client = NULL;
53
54
55 static dispatch_queue_t
56 __nwi_configuration_queue()
57 {
58 static dispatch_once_t once;
59 static dispatch_queue_t q;
60
61 dispatch_once(&once, ^{
62 q = dispatch_queue_create(NWI_SERVICE_NAME, NULL);
63 });
64
65 return q;
66 }
67
68 static
69 void
70 _nwi_state_initialize(void)
71 {
72 const char *nwi_key = nwi_state_get_notify_key();
73 uint32_t status = notify_register_check(nwi_key,
74 &nwi_store_token);
75
76 if (status != NOTIFY_STATUS_OK) {
77 fprintf(stderr, "nwi_state: registration failed (%u)\n", status);
78 }
79 else {
80 nwi_store_token_valid = TRUE;
81 }
82 }
83
84 #pragma mark -
85 #pragma mark Network information [nwi] APIs
86
87
88 /*
89 * Function: nwi_state_get_notify_key
90 * Purpose:
91 * Returns the BSD notify key to use to monitor when the state changes.
92 *
93 * Note:
94 * The nwi_state_copy API uses this notify key to monitor when the state
95 * changes, so each invocation of nwi_state_copy returns the current
96 * information.
97 */
98 const char *
99 nwi_state_get_notify_key()
100 {
101 return "com.apple.system.SystemConfiguration.nwi";
102 }
103
104 #define ATOMIC_CMPXCHG(p, o, n) __sync_bool_compare_and_swap((p), (o), (n))
105 #define ATOMIC_INC(p) __sync_fetch_and_add((p), 1) // return (n++);
106 #define ATOMIC_DEC(p) __sync_sub_and_fetch((p), 1) // return (--n);
107
108 void
109 _nwi_state_force_refresh()
110 {
111 ATOMIC_CMPXCHG(&nwi_store_force_refresh, FALSE, TRUE);
112 }
113
114 static void
115 nwi_state_retain(nwi_state_t state)
116 {
117 ATOMIC_INC(&state->ref);
118 return;
119 }
120
121 /*
122 * Function: nwi_state_release
123 * Purpose:
124 * Release the memory associated with the network state.
125 */
126 void
127 nwi_state_release(nwi_state_t state)
128 {
129 if (ATOMIC_DEC(&state->ref) > 0) {
130 // if not last reference
131 return;
132 }
133
134 // release connection reference on 1-->0 transition
135 dispatch_sync(__nwi_configuration_queue(), ^{
136 if (--nwi_active == 0) {
137 // if last reference, drop connection
138 libSC_info_client_release(nwi_client);
139 nwi_client = NULL;
140 }
141 });
142
143 // release nwi_state
144 nwi_state_free(state);
145
146 return;
147 }
148
149 static nwi_state *
150 _nwi_state_copy_data()
151 {
152 nwi_state_t nwi_state = NULL;
153 static const char *proc_name = NULL;
154 xpc_object_t reqdict;
155 xpc_object_t reply;
156
157 dispatch_sync(__nwi_configuration_queue(), ^{
158 if ((nwi_active++ == 0) || (nwi_client == NULL)) {
159 static dispatch_once_t once;
160 static const char *service_name = NWI_SERVICE_NAME;
161
162 dispatch_once(&once, ^{
163 const char *name;
164
165 // get [XPC] service name
166 name = getenv(service_name);
167 if ((name != NULL) && (issetugid() == 0)) {
168 service_name = strdup(name);
169 }
170
171 // get process name
172 proc_name = getprogname();
173 });
174
175 nwi_client =
176 libSC_info_client_create(__nwi_configuration_queue(), // dispatch queue
177 service_name, // XPC service name
178 "Network information"); // service description
179 if (nwi_client == NULL) {
180 --nwi_active;
181 }
182 }
183 });
184
185 if ((nwi_client == NULL) || !nwi_client->active) {
186 // if network information server not available
187 return NULL;
188 }
189
190 // create message
191 reqdict = xpc_dictionary_create(NULL, NULL, 0);
192
193 // set process name
194 if (proc_name != NULL) {
195 xpc_dictionary_set_string(reqdict, NWI_PROC_NAME, proc_name);
196 }
197
198 // set request
199 xpc_dictionary_set_int64(reqdict, NWI_REQUEST, NWI_REQUEST_COPY);
200
201 // send request to the DNS configuration server
202 reply = libSC_send_message_with_reply_sync(nwi_client, reqdict);
203 xpc_release(reqdict);
204
205 if (reply != NULL) {
206 const void *dataRef;
207 size_t dataLen = 0;
208
209 dataRef = xpc_dictionary_get_data(reply, NWI_CONFIGURATION, &dataLen);
210 if (dataRef != NULL) {
211 nwi_state = malloc(dataLen);
212 bcopy((void *)dataRef, nwi_state, dataLen);
213 if (nwi_state->version != NWI_STATE_VERSION) {
214 /* make sure the version matches */
215 nwi_state_free(nwi_state);
216 nwi_state = NULL;
217 }
218 else {
219 nwi_state->ref = 0;
220 }
221 }
222
223 xpc_release(reply);
224 }
225
226 return nwi_state;
227 }
228
229 /*
230 * Function: nwi_state_copy
231 * Purpose:
232 * Returns the current network state information.
233 * Release after use by calling nwi_state_release().
234 */
235 nwi_state_t
236 nwi_state_copy(void)
237 {
238 boolean_t force_refresh;
239 nwi_state_t nwi_state = NULL;
240 nwi_state_t old_state = NULL;
241
242 pthread_once(&initialized, _nwi_state_initialize);
243 pthread_mutex_lock(&nwi_store_lock);
244
245 force_refresh = ATOMIC_CMPXCHG(&nwi_store_force_refresh, TRUE, FALSE);
246
247 if (G_nwi_state != NULL) {
248 int check = 0;
249 uint32_t status;
250
251 if (nwi_store_token_valid == FALSE) {
252 /* have to throw cached copy away every time */
253 check = 1;
254 }
255 else {
256 status = notify_check(nwi_store_token, &check);
257 if (status != NOTIFY_STATUS_OK) {
258 fprintf(stderr, "nwi notify_check: failed with %u\n",
259 status);
260 /* assume that it changed, throw cached copy away */
261 check = 1;
262 }
263 }
264 if (check != 0 || force_refresh) {
265 /* new need snapshot */
266 old_state = G_nwi_state;
267 G_nwi_state = NULL;
268 }
269 }
270 /* Let's populate the cache if it's empty */
271 if (G_nwi_state == NULL) {
272 G_nwi_state = _nwi_state_copy_data();
273 if (G_nwi_state != NULL) {
274 /* one reference for G_nwi_state */
275 nwi_state_retain(G_nwi_state);
276 }
277 }
278 if (G_nwi_state != NULL) {
279 /* another reference for this caller */
280 nwi_state_retain(G_nwi_state);
281 }
282 nwi_state = G_nwi_state;
283 pthread_mutex_unlock(&nwi_store_lock);
284
285 if (old_state != NULL) {
286 /* get rid of G_nwi_state reference */
287 nwi_state_release(old_state);
288 }
289 return nwi_state;
290 }
291
292 /*
293 * Function: _nwi_state_ack
294 * Purpose:
295 * Acknowledge receipt and any changes associated with the [new or
296 * updated] network state.
297 */
298 void
299 _nwi_state_ack(nwi_state_t state, const char *bundle_id)
300 {
301 xpc_object_t reqdict;
302
303 if (state == NULL) {
304 return; // ASSERT
305 }
306
307 if ((nwi_client == NULL) || !nwi_client->active) {
308 // if network information server not available
309 return;
310 }
311
312 dispatch_sync(__nwi_configuration_queue(), ^{
313 nwi_active++; // keep connection active (for the life of the process)
314 });
315
316 // create message
317 reqdict = xpc_dictionary_create(NULL, NULL, 0);
318
319 // set request
320 xpc_dictionary_set_int64(reqdict, NWI_REQUEST, NWI_REQUEST_ACKNOWLEDGE);
321
322 // set generation
323 xpc_dictionary_set_uint64(reqdict, NWI_GENERATION, state->generation_count);
324
325 // send acknowledgement to the DNS configuration server
326 xpc_connection_send_message(nwi_client->connection, reqdict);
327
328 xpc_release(reqdict);
329 return;
330 }
331
332 /*
333 * Function: nwi_state_get_generation
334 * Purpose:
335 * Returns the generation (mach_time) of the nwi_state data.
336 * Every time the data is updated due to changes
337 * in the network, this value will change.
338 */
339 uint64_t
340 nwi_state_get_generation(nwi_state_t state)
341 {
342 return (state->generation_count);
343 }
344
345 /*
346 * Function: nwi_ifstate_get_generation
347 * Purpose:
348 * Returns the generation (mach_time) of the nwi_ifstate data.
349 */
350 uint64_t
351 nwi_ifstate_get_generation(nwi_ifstate_t ifstate)
352 {
353 return (ifstate->if_generation_count);
354 }
355
356 /*
357 * Function: nwi_ifstate_get_ifname
358 * Purpose:
359 * Return the interface name of the specified ifstate.
360 */
361 const char *
362 nwi_ifstate_get_ifname(nwi_ifstate_t ifstate)
363 {
364 return ((ifstate != NULL) ? ifstate->ifname : NULL);
365 }
366
367 static uint64_t
368 flags_from_af(int af)
369 {
370 return ((af == AF_INET)
371 ? NWI_IFSTATE_FLAGS_HAS_IPV4
372 : NWI_IFSTATE_FLAGS_HAS_IPV6);
373 }
374 /*
375 * Function: nwi_ifstate_get_flags
376 * Purpose:
377 * Return the flags for the given ifstate (see above for bit definitions).
378 */
379 nwi_ifstate_flags
380 nwi_ifstate_get_flags(nwi_ifstate_t ifstate)
381 {
382 nwi_ifstate_t alias = NULL;
383 nwi_ifstate_flags flags = 0ULL;
384
385 if (ifstate->af_alias_offset != 0) {
386 alias = ifstate + ifstate->af_alias_offset;
387 }
388 flags |= flags_from_af(ifstate->af);
389 if ((ifstate->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0) {
390 flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
391
392 }
393 if (alias != NULL) {
394 flags |= flags_from_af(alias->af);
395 if ((alias->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0) {
396 flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
397 }
398 }
399 return flags;
400 }
401
402 /*
403 * Function: nwi_state_get_first_ifstate
404 * Purpose:
405 * Returns the first and highest priority interface that has connectivity
406 * for the specified address family 'af'. 'af' is either AF_INET or AF_INET6.
407 * The connectivity provided is for general networking. To get information
408 * about an interface that isn't available for general networking, use
409 * nwi_state_get_ifstate().
410 *
411 * Use nwi_ifstate_get_next() to get the next, lower priority interface
412 * in the list.
413 *
414 * Returns NULL if no connectivity for the specified address family is
415 * available.
416 */
417 nwi_ifstate_t
418 nwi_state_get_first_ifstate(nwi_state_t state, int af)
419 {
420 nwi_ifstate_t ifstate;
421
422 if (state == NULL) {
423 return NULL;
424 }
425
426 ifstate = nwi_state_get_ifstate_with_index(state, af, 0);
427 if (ifstate == NULL) {
428 return NULL;
429 }
430 if ((ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST)
431 != 0) {
432 ifstate = NULL;
433 }
434
435 return ifstate;
436
437 }
438
439 /*
440 * Function: nwi_state_get_ifstate
441 * Purpose:
442 * Return information for the specified interface 'ifname'.
443 *
444 * This API directly returns the ifstate for the specified interface.
445 * This is the only way to access information about an interface that isn't
446 * available for general networking.
447 *
448 * Returns NULL if no information is available for that interface.
449 */
450 nwi_ifstate_t
451 nwi_state_get_ifstate(nwi_state_t state, const char * ifname)
452 {
453 nwi_ifstate_t ifstate = nwi_state_get_ifstate_with_name(state, AF_INET, ifname);
454 if (ifstate == NULL) {
455 ifstate = nwi_state_get_ifstate_with_name(state, AF_INET6, ifname);
456 }
457 return ifstate;
458
459 }
460
461 /*
462 * Function: nwi_ifstate_get_next
463 * Purpose:
464 * Returns the next, lower priority nwi_ifstate_t after the specified
465 * 'ifstate' for the protocol family 'af'.
466 *
467 * Returns NULL when the end of the list is reached, or we reach an
468 * item that is not in the list.
469 */
470 nwi_ifstate_t
471 nwi_ifstate_get_next(nwi_ifstate_t ifstate, int af)
472 {
473 ifstate = nwi_ifstate_get_alias(ifstate, af);
474 if (ifstate == NULL
475 || ((ifstate->flags
476 & (NWI_IFSTATE_FLAGS_NOT_IN_LIST
477 | NWI_IFSTATE_FLAGS_LAST_ITEM))
478 != 0)) {
479 return (NULL);
480 }
481 ifstate++;
482 if ((ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0) {
483 return (NULL);
484 }
485 return (ifstate);
486 }
487
488 /*
489 * Function: nwi_ifstate_compare_rank
490 * Purpose:
491 * Compare the relative rank of two nwi_ifstate_t objects.
492 *
493 * The "rank" indicates the importance of the underlying interface.
494 *
495 * Returns:
496 * 0 if ifstate1 and ifstate2 are ranked equally
497 * -1 if ifstate1 is ranked ahead of ifstate2
498 * 1 if ifstate2 is ranked ahead of ifstate1
499 */
500 int
501 nwi_ifstate_compare_rank(nwi_ifstate_t ifstate1, nwi_ifstate_t ifstate2)
502 {
503 return RankCompare(ifstate1->rank, ifstate2->rank);
504 }
505
506 /*
507 * nwi_state_get_reachability_flags
508 *
509 * returns the global reachability flags for a given address family.
510 * If no address family is passed in, it returns the global reachability
511 * flags for either families.
512 *
513 * The reachability flags returned follow the definition of
514 * SCNetworkReachabilityFlags.
515 *
516 * If the flags are zero (i.e. do not contain kSCNetworkReachabilityFlagsReachable), there is no connectivity.
517 *
518 * Otherwise, at least kSCNetworkReachabilityFlagsReachable is set:
519 * Reachable only
520 * No other connection flags are set.
521 * Reachable and no ConnectionRequired
522 * If we have connectivity for the specified address family (and we'd
523 * be returning the reachability flags associated with the default route)
524 * Reachable and ConnectionRequired
525 * If we do not currently have an active/primary network but we may
526 * be able to establish connectivity.
527 * Reachable and OnDemand
528 * If we do not currently have an active/primary network but we may
529 * be able to establish connective on demand.
530 * Reachable and TransientConnection
531 * This connection is transient.
532 * Reachable and WWAN
533 * This connection will be going over the cellular network.
534 */
535 uint32_t
536 nwi_state_get_reachability_flags(nwi_state_t nwi_state, int af)
537 {
538 if (nwi_state == NULL) {
539 return (0);
540 }
541 if (af == AF_INET || af == AF_INET6) {
542 nwi_ifstate_t ifstate;
543
544 ifstate = nwi_state_get_first_ifstate(nwi_state, af);
545
546 if (ifstate != NULL) {
547 return ifstate->reach_flags;
548 }
549
550 return (af == AF_INET) ? nwi_state->reach_flags_v4 : nwi_state->reach_flags_v6;
551 } else {
552 nwi_ifstate_t ifstate_v4;
553 nwi_ifstate_t ifstate_v6;
554
555 ifstate_v4 = nwi_state_get_first_ifstate(nwi_state, AF_INET);
556 ifstate_v6 = nwi_state_get_first_ifstate(nwi_state, AF_INET6);
557
558 if (ifstate_v4 != NULL) {
559 if (ifstate_v6 != NULL) {
560 if (nwi_ifstate_compare_rank(ifstate_v4, ifstate_v6) > 0) {
561 return ifstate_v6->reach_flags;
562 } else {
563 return ifstate_v4->reach_flags;
564 }
565 } else {
566 return ifstate_v4->reach_flags;
567 }
568 } else {
569 if (ifstate_v6 != NULL) {
570 return ifstate_v6->reach_flags;
571 }
572 }
573
574 if (nwi_state->reach_flags_v4 != 0) {
575 return nwi_state->reach_flags_v4;
576 }
577 // This is the case where both ifstate are NULL.
578 return nwi_state->reach_flags_v6;
579 }
580 }
581
582 /*
583 * nwi_ifstate_get_vpn_server
584 *
585 * returns a sockaddr representation of the vpn server address.
586 * NULL if PPP/VPN/IPSec server address does not exist.
587 */
588 const struct sockaddr *
589 nwi_ifstate_get_vpn_server(nwi_ifstate_t ifstate)
590 {
591 const struct sockaddr * vpn_server_addr;
592
593 vpn_server_addr = (const struct sockaddr *)(void *)
594 &ifstate->vpn_server_address;
595
596 if (vpn_server_addr->sa_family == 0) {
597 return NULL;
598 }
599 return vpn_server_addr;
600 }
601
602 /*
603 * nwi_ifstate_get_reachability_flags
604 *
605 * returns the reachability flags for the interface given an address family.
606 * The flags returned are those determined outside of
607 * the routing table. [None, ConnectionRequired, OnDemand,
608 * Transient Connection, WWAN].
609 */
610 uint32_t
611 nwi_ifstate_get_reachability_flags(nwi_ifstate_t ifstate)
612 {
613 return ifstate->reach_flags;
614 }
615
616 /*
617 * nwi_ifstate_get_signature
618 *
619 * returns the signature and its length for an ifstate given an address family.
620 * If AF_UNSPEC is passed in, the signature for a given ifstate is returned.
621 *
622 * If the signature does not exist, NULL is returned.
623 */
624 const uint8_t *
625 nwi_ifstate_get_signature(nwi_ifstate_t ifstate, int af, int * length)
626 {
627 nwi_ifstate_t i_state = NULL;
628
629 switch (af) {
630 case AF_UNSPEC:
631 i_state = ifstate;
632 break;
633 case AF_INET:
634 case AF_INET6:
635 i_state = nwi_ifstate_get_alias(ifstate, af);
636 break;
637 default:
638 break;
639
640 }
641
642 if (i_state != NULL) {
643 if ((i_state->flags & NWI_IFSTATE_FLAGS_HAS_SIGNATURE) != 0) {
644 *length = sizeof(i_state->signature);
645 return (i_state->signature);
646 }
647 }
648
649 *length = 0;
650 return NULL;
651 }
652
653 static inline
654 boolean_t
655 _nwi_ifstate_is_in_list(nwi_ifstate_t ifstate, int af)
656 {
657 nwi_ifstate_t i_state;
658
659 i_state = nwi_ifstate_get_alias(ifstate, af);
660 if (i_state == NULL) {
661 return FALSE;
662 }
663
664 if ((nwi_ifstate_get_flags(i_state) & NWI_IFSTATE_FLAGS_NOT_IN_LIST)
665 == 0) {
666 return TRUE;
667 }
668
669 return FALSE;
670 }
671
672 /*
673 * nwi_ifstate_get_dns_signature
674 *
675 * returns the signature and its length for given
676 * ifstate with a valid dns configuration.
677 *
678 * If the signature does not exist, NULL is returned.
679 *
680 */
681 const uint8_t *
682 nwi_ifstate_get_dns_signature(nwi_ifstate_t ifstate, int * length)
683 {
684 const uint8_t * signature = NULL;
685 const uint8_t * v4_signature;
686 int v4_signature_len;
687 const uint8_t * v6_signature;
688 int v6_signature_len;
689
690 *length = 0;
691
692 if ((nwi_ifstate_get_flags(ifstate) & NWI_IFSTATE_FLAGS_HAS_DNS) == 0) {
693 return NULL;
694 }
695
696 v4_signature = nwi_ifstate_get_signature(ifstate, AF_INET, &v4_signature_len);
697 v6_signature = nwi_ifstate_get_signature(ifstate, AF_INET6, &v6_signature_len);
698 if (v4_signature == NULL && v6_signature == NULL) {
699 return NULL;
700 }
701
702 if (_nwi_ifstate_is_in_list(ifstate, AF_INET) == TRUE) {
703 signature = v4_signature;
704 *length = v4_signature_len;
705 } else {
706 if (_nwi_ifstate_is_in_list(ifstate, AF_INET6) != TRUE && v4_signature_len > 0) {
707 /* v6 is ranked never, v4 is ranked never but has a valid signature */
708 signature = v4_signature;
709 *length = v4_signature_len;
710 } else {
711 /* v6 is not ranked never or v4 has no signature */
712 signature = v6_signature;
713 *length = v6_signature_len;
714 }
715 }
716
717 return signature;
718 }
719
720 unsigned int
721 nwi_state_get_interface_names(nwi_state_t state,
722 const char * names[],
723 unsigned int names_count)
724 {
725 int i;
726 nwi_ifindex_t * scan;
727
728 if (names == NULL || names_count == 0) {
729 return (state->if_list_count);
730 }
731 for (i = 0, scan = nwi_state_if_list(state);
732 i < state->if_list_count; i++, scan++) {
733 names[i] = state->ifstate_list[*scan].ifname;
734 }
735 return (state->if_list_count);
736 }
737
738 #pragma mark -
739 #pragma mark Network information [nwi] test code
740
741
742 #ifdef TEST_NWI
743
744 #include <arpa/inet.h>
745
746 typedef union {
747 const struct sockaddr * sa;
748 const struct sockaddr_in * sin;
749 const struct sockaddr_in6 * sin6;
750 } my_sockaddr_t;
751
752 static const char *
753 my_sockaddr_ntop(const struct sockaddr * sa, char * buf, int buf_len)
754 {
755 my_sockaddr_t addr;
756 const void * addr_ptr = NULL;
757
758 addr.sa = sa;
759 switch (sa->sa_family) {
760 case AF_INET:
761 addr_ptr = &addr.sin->sin_addr;
762 break;
763 case AF_INET6:
764 addr_ptr = &addr.sin6->sin6_addr;
765 break;
766 default:
767 addr_ptr = NULL;
768 break;
769 }
770 if (addr_ptr == NULL) {
771 return (NULL);
772 }
773 return (inet_ntop(addr.sa->sa_family, addr_ptr, buf, buf_len));
774 }
775
776 static void
777 nwi_ifstate_print(nwi_ifstate_t ifstate)
778 {
779 const char * addr_str;
780 void * address;
781 char addr_ntopbuf[INET6_ADDRSTRLEN];
782 const char * diff_str;
783 char vpn_ntopbuf[INET6_ADDRSTRLEN];
784 const struct sockaddr * vpn_addr;
785 const char * vpn_addr_str = NULL;
786
787 address = nwi_ifstate_get_address(ifstate);
788 addr_str = inet_ntop(ifstate->af, address,
789 addr_ntopbuf, sizeof(addr_ntopbuf));
790 vpn_addr = nwi_ifstate_get_vpn_server(ifstate);
791 if (vpn_addr != NULL) {
792 vpn_addr_str = my_sockaddr_ntop(vpn_addr, vpn_ntopbuf,
793 sizeof(vpn_ntopbuf));
794 }
795 diff_str = nwi_ifstate_get_diff_str(ifstate);
796 printf("%s%s%s%s rank 0x%x iaddr %s%s%s reach_flags 0x%x\n",
797 ifstate->ifname,
798 diff_str,
799 (ifstate->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0
800 ? " dns" : "",
801 (ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0
802 ? " never" : "",
803 ifstate->rank,
804 addr_str,
805 (vpn_addr_str != NULL) ? " vpn_server_addr: " : "",
806 (vpn_addr_str != NULL) ? vpn_addr_str : "",
807 ifstate->reach_flags);
808 return;
809 }
810
811 static void
812 traverse_ifstates(nwi_state_t state)
813 {
814 nwi_ifstate_t alias;
815 int i;
816 nwi_ifstate_t scan;
817
818 scan = nwi_state_get_first_ifstate(state, AF_INET);
819 printf("IPv4 traverse list:\n");
820 for (i = 0; scan != NULL; i++) {
821 printf("[%d] flags=0x%llx ", i, scan->flags);
822 nwi_ifstate_print(scan);
823 alias = nwi_ifstate_get_alias(scan, nwi_other_af(scan->af));
824 scan = nwi_ifstate_get_next(scan, AF_INET);
825 if (alias != NULL) {
826 printf("\t alias is ");
827 nwi_ifstate_print(alias);
828 }
829 }
830 printf("IPv6 traverse list:\n");
831 scan = nwi_state_get_first_ifstate(state, AF_INET6);
832 for (i = 0; scan != NULL; i++) {
833 printf("[%d] flags=0x%llx ", i, scan->flags);
834 alias = nwi_ifstate_get_alias(scan, nwi_other_af(scan->af));
835 nwi_ifstate_print(scan);
836 scan = nwi_ifstate_get_next(scan, AF_INET6);
837 if (alias != NULL) {
838 printf("\t alias is ");
839 nwi_ifstate_print(alias);
840 }
841 }
842 }
843
844 static void
845 nwi_state_print_common(nwi_state_t state, bool diff)
846 {
847 unsigned int count = 0;
848 int i;
849 nwi_ifstate_t scan;
850
851 if (state == NULL) {
852 return;
853 }
854 printf("nwi_state = { "
855 "gen=%llu max_if=%u #v4=%u #v6=%u "
856 "reach_flags=(v4=0x%x, v6=0x%x) }\n",
857 state->generation_count,
858 state->max_if_count,
859 state->ipv4_count,
860 state->ipv6_count,
861 nwi_state_get_reachability_flags(state, AF_INET),
862 nwi_state_get_reachability_flags(state, AF_INET6));
863 if (state->ipv4_count) {
864 printf("IPv4:\n");
865 for (i = 0, scan = nwi_state_ifstate_list(state, AF_INET);
866 i < state->ipv4_count; i++, scan++) {
867 printf("[%d] ", i);
868 nwi_ifstate_print(scan);
869 }
870 }
871 if (state->ipv6_count) {
872 printf("IPv6:\n");
873 for (i = 0, scan = nwi_state_ifstate_list(state, AF_INET6);
874 i < state->ipv6_count; i++, scan++) {
875 printf("[%d] ", i);
876 nwi_ifstate_print(scan);
877 }
878 }
879 if (!diff) {
880 count = nwi_state_get_interface_names(state, NULL, 0);
881 if (count > 0) {
882 const char * names[count];
883
884 count = nwi_state_get_interface_names(state, names,
885 count);
886 printf("%d interfaces%s", count,
887 (count != 0) ? ": " : "");
888 for (i = 0; i < count; i++) {
889 printf("%s%s", (i == 0) ? "" : ", ", names[i]);
890 }
891 printf("\n");
892 }
893 else {
894 printf("0 interfaces\n");
895 }
896 traverse_ifstates(state);
897 }
898 printf("-----------------------------------\n");
899 return;
900 }
901
902 static void
903 nwi_state_print(nwi_state_t state)
904 {
905 nwi_state_print_common(state, FALSE);
906 }
907
908 static void
909 nwi_state_print_diff(nwi_state_t state)
910 {
911 printf("DIFF\n");
912 nwi_state_print_common(state, TRUE);
913 }
914
915 static void
916 doit(void)
917 {
918 struct in_addr addr = { 0 };
919 struct in6_addr addr6;
920 nwi_ifstate_t ifstate;
921 nwi_state_t state;
922 nwi_state_t diff_state;
923 nwi_state_t new_state;
924 nwi_state_t old_state;
925 nwi_state_t old_state_copy;
926
927 state = nwi_state_new(NULL, 0);
928 nwi_state_print(state);
929 state = nwi_state_new(NULL, 1);
930 nwi_state_print(state);
931 state = nwi_state_new(state, 2);
932 nwi_state_print(state);
933 state = nwi_state_new(state, 10);
934 nwi_state_print(state);
935
936 bzero(&addr6, sizeof(addr6));
937 /* populate old_state */
938 old_state = nwi_state_new(NULL, 5);
939 for (int i = 0; i < 5; i++) {
940 char ifname[IFNAMSIZ];
941
942 snprintf(ifname, sizeof(ifname), "en%d", i);
943 addr.s_addr = htonl((i % 2) ? i : (i + 1));
944 ifstate = nwi_state_add_ifstate(old_state, ifname, AF_INET, 0,
945 (i % 2) ? (i - 1) : (i + 1),
946 &addr,
947 NULL,
948 0);
949 addr6.__u6_addr.__u6_addr32[0] = htonl(i);
950 ifstate = nwi_state_add_ifstate(old_state, ifname, AF_INET6, 0,
951 (i % 2) ? (10 - i) : i,
952 &addr6,
953 NULL,
954 0);
955 }
956 nwi_state_finalize(old_state);
957 nwi_state_print(old_state);
958
959 diff_state = nwi_state_diff(NULL, old_state);
960 nwi_state_print_diff(diff_state);
961 nwi_state_free(diff_state);
962
963 /* remember the old state */
964 old_state_copy = nwi_state_make_copy(old_state);
965
966 /* create new state */
967 new_state = nwi_state_new(old_state, 10);
968 nwi_state_print(new_state);
969
970 for (int i = 0; i < 10; i++) {
971 char ifname[IFNAMSIZ];
972 uint64_t flags;
973
974 snprintf(ifname, sizeof(ifname), "en%d", i);
975 addr6.__u6_addr.__u6_addr32[0] = htonl(i);
976 flags = (i > 6) ? NWI_IFSTATE_FLAGS_NOT_IN_LIST : 0;
977 ifstate = nwi_state_add_ifstate(new_state, ifname, AF_INET6,
978 flags,
979 i,
980 &addr6,
981 NULL,
982 0);
983 }
984 for (int i = 9; i >= 0; i--) {
985 char ifname[IFNAMSIZ];
986
987 snprintf(ifname, sizeof(ifname), "en%d", i);
988 addr.s_addr = htonl(i);
989 if (i != 3) {
990 ifstate = nwi_state_add_ifstate(new_state,
991 ifname, AF_INET,
992 0,
993 i,
994 &addr,
995 NULL,
996 0);
997 }
998 }
999 nwi_state_finalize(new_state);
1000 nwi_state_print(new_state);
1001
1002 diff_state = nwi_state_diff(old_state_copy, new_state);
1003 nwi_state_print_diff(diff_state);
1004 nwi_state_free(diff_state);
1005
1006 diff_state = nwi_state_diff(new_state, old_state_copy);
1007 nwi_state_print_diff(diff_state);
1008 nwi_state_free(diff_state);
1009
1010 nwi_state_free(old_state_copy);
1011 nwi_state_free(new_state);
1012 return;
1013 }
1014
1015 int
1016 main()
1017 {
1018 doit();
1019 exit(0);
1020 return (0);
1021 }
1022
1023 #endif /* TEST_NWI */
1024
1025 #ifdef TEST_NWI_STATE
1026
1027 int
1028 main(int argc, char * argv[])
1029 {
1030 nwi_state_t state = nwi_state_copy();
1031
1032 exit(0);
1033 return (0);
1034 }
1035
1036 #endif