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