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