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