2 * Copyright (c) 2011-2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
29 #include <sys/socket.h>
30 #include <dispatch/dispatch.h>
33 #include "libSystemConfiguration_client.h"
34 #include "network_information.h"
35 #include "network_information_priv.h"
37 static nwi_state_t G_nwi_state
= NULL
;
38 static pthread_mutex_t nwi_store_lock
= PTHREAD_MUTEX_INITIALIZER
;
39 static boolean_t nwi_store_token_valid
= FALSE
;
41 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
42 static int nwi_store_token
;
44 static boolean_t nwi_store_force_refresh
= FALSE
;
47 #pragma mark Network information [nwi] client support
50 // Note: protected by __nwi_configuration_queue()
51 static int nwi_active
= 0;
52 static libSC_info_client_t
*nwi_client
= NULL
;
55 static dispatch_queue_t
56 __nwi_configuration_queue()
58 static dispatch_once_t once
;
59 static dispatch_queue_t q
;
61 dispatch_once(&once
, ^{
62 q
= dispatch_queue_create(NWI_SERVICE_NAME
, NULL
);
70 _nwi_state_initialize(void)
72 const char *nwi_key
= nwi_state_get_notify_key();
73 uint32_t status
= notify_register_check(nwi_key
,
76 if (status
!= NOTIFY_STATUS_OK
) {
77 fprintf(stderr
, "nwi_state: registration failed (%u)\n", status
);
80 nwi_store_token_valid
= TRUE
;
86 _nwi_set_alias(nwi_state
* state
, nwi_ifstate
* ifstate
)
88 nwi_ifstate
* ifstate_alias
;
92 af_alias
= (af
== AF_INET
)?AF_INET6
:AF_INET
;
95 nwi_state_get_ifstate_with_name(state
, af_alias
,
98 if (ifstate_alias
!= NULL
) {
99 ifstate_alias
->af_alias
= ifstate
;
101 ifstate
->af_alias
= ifstate_alias
;
107 _nwi_state_reset_alias(nwi_state_t state
) {
110 for (i
= 0; i
< state
->ipv4_count
; i
++) {
111 state
->nwi_ifstates
[i
].af_alias
= NULL
;
114 for (i
= state
->ipv6_start
;
115 i
< state
->ipv6_start
+ state
->ipv6_count
; i
++) {
116 _nwi_set_alias(state
, &state
->nwi_ifstates
[i
]);
122 #pragma mark Network information [nwi] APIs
126 * Function: nwi_state_get_notify_key
128 * Returns the BSD notify key to use to monitor when the state changes.
131 * The nwi_state_copy API uses this notify key to monitor when the state
132 * changes, so each invocation of nwi_state_copy returns the current
136 nwi_state_get_notify_key()
138 #if !TARGET_IPHONE_SIMULATOR
139 return "com.apple.system.SystemConfiguration.nwi";
140 #else // !TARGET_IPHONE_SIMULATOR
141 return "com.apple.iOS_Simulator.SystemConfiguration.nwi";
142 #endif // !TARGET_IPHONE_SIMULATOR
145 #define ATOMIC_CMPXCHG(p, o, n) __sync_bool_compare_and_swap((p), (o), (n))
146 #define ATOMIC_INC(p) __sync_fetch_and_add((p), 1) // return (n++);
147 #define ATOMIC_DEC(p) __sync_sub_and_fetch((p), 1) // return (--n);
150 _nwi_state_force_refresh()
152 ATOMIC_CMPXCHG(&nwi_store_force_refresh
, FALSE
, TRUE
);
156 nwi_state_retain(nwi_state_t state
)
158 ATOMIC_INC(&state
->ref
);
163 * Function: nwi_state_release
165 * Release the memory associated with the network state.
168 nwi_state_release(nwi_state_t state
)
170 if (ATOMIC_DEC(&state
->ref
) > 0) {
171 // if not last reference
175 // release connection reference on 1-->0 transition
177 dispatch_sync(__nwi_configuration_queue(), ^{
178 if (--nwi_active
== 0) {
179 // if last reference, drop connection
180 libSC_info_client_release(nwi_client
);
193 _nwi_state_copy_data()
195 nwi_state_t nwi_state
= NULL
;
196 static const char *proc_name
= NULL
;
197 xpc_object_t reqdict
;
200 dispatch_sync(__nwi_configuration_queue(), ^{
201 if ((nwi_active
++ == 0) || (nwi_client
== NULL
)) {
202 static dispatch_once_t once
;
203 static const char *service_name
= NWI_SERVICE_NAME
;
205 dispatch_once(&once
, ^{
208 // get [XPC] service name
209 name
= getenv(service_name
);
210 if ((name
!= NULL
) && (issetugid() == 0)) {
211 service_name
= strdup(name
);
215 proc_name
= getprogname();
219 libSC_info_client_create(__nwi_configuration_queue(), // dispatch queue
220 service_name
, // XPC service name
221 "Network information"); // service description
222 if (nwi_client
== NULL
) {
228 if ((nwi_client
== NULL
) || !nwi_client
->active
) {
229 // if network information server not available
234 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
237 if (proc_name
!= NULL
) {
238 xpc_dictionary_set_string(reqdict
, NWI_PROC_NAME
, proc_name
);
242 xpc_dictionary_set_int64(reqdict
, NWI_REQUEST
, NWI_REQUEST_COPY
);
244 // send request to the DNS configuration server
245 reply
= libSC_send_message_with_reply_sync(nwi_client
, reqdict
);
246 xpc_release(reqdict
);
252 dataRef
= xpc_dictionary_get_data(reply
, NWI_CONFIGURATION
, &dataLen
);
253 if (dataRef
!= NULL
) {
254 nwi_state
= malloc(dataLen
);
255 bcopy((void *)dataRef
, nwi_state
, dataLen
);
257 nwi_state
->svr
= TRUE
;
267 * Function: nwi_state_copy
269 * Returns the current network state information.
270 * Release after use by calling nwi_state_release().
275 boolean_t force_refresh
;
276 nwi_state_t nwi_state
= NULL
;
277 nwi_state_t old_state
= NULL
;
279 pthread_once(&initialized
, _nwi_state_initialize
);
280 pthread_mutex_lock(&nwi_store_lock
);
282 force_refresh
= ATOMIC_CMPXCHG(&nwi_store_force_refresh
, TRUE
, FALSE
);
284 if (G_nwi_state
!= NULL
) {
288 if (nwi_store_token_valid
== FALSE
) {
289 /* have to throw cached copy away every time */
293 status
= notify_check(nwi_store_token
, &check
);
294 if (status
!= NOTIFY_STATUS_OK
) {
295 fprintf(stderr
, "nwi notify_check: failed with %u\n",
297 /* assume that it changed, throw cached copy away */
301 if (check
!= 0 || force_refresh
) {
302 /* new need snapshot */
303 old_state
= G_nwi_state
;
307 /* Let's populate the cache if it's empty */
308 if (G_nwi_state
== NULL
) {
309 G_nwi_state
= _nwi_state_copy_data();
310 if (G_nwi_state
!= NULL
) {
311 /* one reference for G_nwi_state */
312 nwi_state_retain(G_nwi_state
);
313 _nwi_state_reset_alias(G_nwi_state
);
316 if (G_nwi_state
!= NULL
) {
317 /* another reference for this caller */
318 nwi_state_retain(G_nwi_state
);
320 nwi_state
= G_nwi_state
;
321 pthread_mutex_unlock(&nwi_store_lock
);
323 if (old_state
!= NULL
) {
324 /* get rid of G_nwi_state reference */
325 nwi_state_release(old_state
);
331 * Function: _nwi_state_ack
333 * Acknowledge receipt and any changes associated with the [new or
334 * updated] network state.
337 _nwi_state_ack(nwi_state_t state
, const char *bundle_id
)
339 xpc_object_t reqdict
;
345 if ((nwi_client
== NULL
) || !nwi_client
->active
) {
346 // if network information server not available
350 dispatch_sync(__nwi_configuration_queue(), ^{
351 nwi_active
++; // keep connection active (for the life of the process)
355 reqdict
= xpc_dictionary_create(NULL
, NULL
, 0);
358 xpc_dictionary_set_int64(reqdict
, NWI_REQUEST
, NWI_REQUEST_ACKNOWLEDGE
);
361 xpc_dictionary_set_uint64(reqdict
, NWI_GENERATION
, state
->generation_count
);
363 // send acknowledgement to the DNS configuration server
364 xpc_connection_send_message(nwi_client
->connection
, reqdict
);
366 xpc_release(reqdict
);
371 * Function: nwi_state_get_generation
373 * Returns the generation (mach_time) of the nwi_state data.
374 * Every time the data is updated due to changes
375 * in the network, this value will change.
378 nwi_state_get_generation(nwi_state_t state
)
380 return (state
->generation_count
);
384 * Function: nwi_ifstate_get_generation
386 * Returns the generation (mach_time) of the nwi_ifstate data.
389 nwi_ifstate_get_generation(nwi_ifstate_t ifstate
)
391 return (ifstate
->if_generation_count
);
395 * Function: nwi_ifstate_get_ifname
397 * Return the interface name of the specified ifstate.
400 nwi_ifstate_get_ifname(nwi_ifstate_t ifstate
)
402 return (ifstate
!= NULL
?ifstate
->ifname
:NULL
);
407 flags_from_af(int af
)
409 return ((af
== AF_INET
)
410 ? NWI_IFSTATE_FLAGS_HAS_IPV4
411 : NWI_IFSTATE_FLAGS_HAS_IPV6
);
414 * Function: nwi_ifstate_get_flags
416 * Return the flags for the given ifstate (see above for bit definitions).
419 nwi_ifstate_get_flags(nwi_ifstate_t ifstate
)
421 nwi_ifstate_t alias
= ifstate
->af_alias
;
422 nwi_ifstate_flags flags
= 0ULL;
424 flags
|= flags_from_af(ifstate
->af
);
425 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_DNS
) != 0) {
426 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
430 flags
|= flags_from_af(alias
->af
);
431 if ((alias
->flags
& NWI_IFSTATE_FLAGS_HAS_DNS
) != 0) {
432 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
439 * Function: nwi_state_get_first_ifstate
441 * Returns the first and highest priority interface that has connectivity
442 * for the specified address family 'af'. 'af' is either AF_INET or AF_INET6.
443 * The connectivity provided is for general networking. To get information
444 * about an interface that isn't available for general networking, use
445 * nwi_state_get_ifstate().
447 * Use nwi_ifstate_get_next() to get the next, lower priority interface
450 * Returns NULL if no connectivity for the specified address family is
454 nwi_state_get_first_ifstate(nwi_state_t state
, int af
)
456 nwi_ifstate_t ifstate
;
463 nwi_state_get_ifstate_with_index(state
, af
, 0);
465 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
)
475 * Function: nwi_state_get_ifstate
477 * Return information for the specified interface 'ifname'.
479 * This API directly returns the ifstate for the specified interface.
480 * This is the only way to access information about an interface that isn't
481 * available for general networking.
483 * Returns NULL if no information is available for that interface.
486 nwi_state_get_ifstate(nwi_state_t state
, const char * ifname
)
488 nwi_ifstate_t ifstate
= nwi_state_get_ifstate_with_name(state
, AF_INET
, ifname
);
489 if (ifstate
== NULL
) {
490 ifstate
= nwi_state_get_ifstate_with_name(state
, AF_INET6
, ifname
);
497 * Function: nwi_ifstate_get_next
499 * Returns the next, lower priority nwi_ifstate_t after the specified
500 * 'ifstate' for the protocol family 'af'.
502 * Returns NULL when the end of the list is reached.
505 nwi_ifstate_get_next(nwi_ifstate_t ifstate
, int af
)
507 nwi_ifstate_t alias
, next
;
510 (af
== ifstate
->af
)?ifstate
:ifstate
->af_alias
;
516 /* We don't return interfaces marked rank never */
517 if ((alias
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
523 if ((next
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) == 0) {
530 * Function: nwi_ifstate_compare_rank
532 * Compare the relative rank of two nwi_ifstate_t objects.
534 * The "rank" indicates the importance of the underlying interface.
537 * 0 if ifstate1 and ifstate2 are ranked equally
538 * -1 if ifstate1 is ranked ahead of ifstate2
539 * 1 if ifstate2 is ranked ahead of ifstate1
542 nwi_ifstate_compare_rank(nwi_ifstate_t ifstate1
, nwi_ifstate_t ifstate2
)
544 return RankCompare(ifstate1
->rank
, ifstate2
->rank
);
548 * nwi_state_get_reachability_flags
550 * returns the global reachability flags for a given address family.
551 * If no address family is passed in, it returns the global reachability
552 * flags for either families.
554 * The reachability flags returned follow the definition of
555 * SCNetworkReachabilityFlags.
557 * If the flags are zero (i.e. do not contain kSCNetworkReachabilityFlagsReachable), there is no connectivity.
559 * Otherwise, at least kSCNetworkReachabilityFlagsReachable is set:
561 * No other connection flags are set.
562 * Reachable and no ConnectionRequired
563 * If we have connectivity for the specified address family (and we'd
564 * be returning the reachability flags associated with the default route)
565 * Reachable and ConnectionRequired
566 * If we do not currently have an active/primary network but we may
567 * be able to establish connectivity.
568 * Reachable and OnDemand
569 * If we do not currently have an active/primary network but we may
570 * be able to establish connective on demand.
571 * Reachable and TransientConnection
572 * This connection is transient.
574 * This connection will be going over the cellular network.
577 nwi_state_get_reachability_flags(nwi_state_t nwi_state
, int af
)
579 if (nwi_state
== NULL
) {
582 if (af
== AF_INET
|| af
== AF_INET6
) {
583 nwi_ifstate_t ifstate
;
585 ifstate
= nwi_state_get_first_ifstate(nwi_state
, af
);
587 if (ifstate
!= NULL
) {
588 return ifstate
->reach_flags
;
591 return (af
== AF_INET
) ? nwi_state
->reach_flags_v4
: nwi_state
->reach_flags_v6
;
593 nwi_ifstate_t ifstate_v4
;
594 nwi_ifstate_t ifstate_v6
;
596 ifstate_v4
= nwi_state_get_first_ifstate(nwi_state
, AF_INET
);
597 ifstate_v6
= nwi_state_get_first_ifstate(nwi_state
, AF_INET6
);
599 if (ifstate_v4
!= NULL
) {
600 if (ifstate_v6
!= NULL
) {
601 if (nwi_ifstate_compare_rank(ifstate_v4
, ifstate_v6
) > 0) {
602 return ifstate_v6
->reach_flags
;
604 return ifstate_v4
->reach_flags
;
607 return ifstate_v4
->reach_flags
;
610 if (ifstate_v6
!= NULL
) {
611 return ifstate_v6
->reach_flags
;
615 if (nwi_state
->reach_flags_v4
!= 0) {
616 return nwi_state
->reach_flags_v4
;
618 // This is the case where both ifstate are NULL.
619 return nwi_state
->reach_flags_v6
;
624 * nwi_ifstate_get_vpn_server
626 * returns a sockaddr representation of the vpn server address.
627 * NULL if PPP/VPN/IPSec server address does not exist.
629 const struct sockaddr
*
630 nwi_ifstate_get_vpn_server(nwi_ifstate_t ifstate
)
632 const struct sockaddr
* vpn_server_addr
;
634 vpn_server_addr
= (const struct sockaddr
*)(void *)
635 &ifstate
->vpn_server_address
;
637 if (vpn_server_addr
->sa_family
== 0) {
640 return vpn_server_addr
;
644 * nwi_ifstate_get_reachability_flags
646 * returns the reachability flags for the interface given an address family.
647 * The flags returned are those determined outside of
648 * the routing table. [None, ConnectionRequired, OnDemand,
649 * Transient Connection, WWAN].
652 nwi_ifstate_get_reachability_flags(nwi_ifstate_t ifstate
)
654 return ifstate
->reach_flags
;
658 * nwi_ifstate_get_signature
660 * returns the signature and its length for an ifstate given an address family.
661 * If AF_UNSPEC is passed in, the signature for a given ifstate is returned.
663 * If the signature does not exist, NULL is returned.
666 nwi_ifstate_get_signature(nwi_ifstate_t ifstate
, int af
, int * length
)
668 nwi_ifstate_t i_state
= NULL
;
676 i_state
= (ifstate
->af
== af
) ? ifstate
: ifstate
->af_alias
;
683 if (i_state
!= NULL
) {
684 if ((i_state
->flags
& NWI_IFSTATE_FLAGS_HAS_SIGNATURE
) != 0) {
685 *length
= sizeof(i_state
->signature
);
686 return (i_state
->signature
);
696 _nwi_ifstate_is_in_list(nwi_ifstate_t ifstate
, int af
)
698 nwi_ifstate_t i_state
;
700 i_state
= (ifstate
->af
== af
) ? ifstate
: ifstate
->af_alias
;
701 if (i_state
== NULL
) {
705 if ((nwi_ifstate_get_flags(i_state
) & NWI_IFSTATE_FLAGS_NOT_IN_LIST
) == 0) {
713 * nwi_ifstate_get_dns_signature
715 * returns the signature and its length for given
716 * ifstate with a valid dns configuration.
718 * If the signature does not exist, NULL is returned.
722 nwi_ifstate_get_dns_signature(nwi_ifstate_t ifstate
, int * length
)
724 const uint8_t * signature
= NULL
;
725 const uint8_t * v4_signature
;
726 int v4_signature_len
;
727 const uint8_t * v6_signature
;
728 int v6_signature_len
;
732 if ((nwi_ifstate_get_flags(ifstate
) & NWI_IFSTATE_FLAGS_HAS_DNS
) == 0) {
736 v4_signature
= nwi_ifstate_get_signature(ifstate
, AF_INET
, &v4_signature_len
);
737 v6_signature
= nwi_ifstate_get_signature(ifstate
, AF_INET6
, &v6_signature_len
);
738 if (v4_signature
== NULL
&& v6_signature
== NULL
) {
742 if (_nwi_ifstate_is_in_list(ifstate
, AF_INET
) == TRUE
) {
743 signature
= v4_signature
;
744 *length
= v4_signature_len
;
746 if (_nwi_ifstate_is_in_list(ifstate
, AF_INET6
) != TRUE
&& v4_signature_len
> 0) {
747 /* v6 is ranked never, v4 is ranked never but has a valid signature */
748 signature
= v4_signature
;
749 *length
= v4_signature_len
;
751 /* v6 is not ranked never or v4 has no signature */
752 signature
= v6_signature
;
753 *length
= v6_signature_len
;
762 #pragma mark Network information [nwi] test code
768 main(int argc
, char **argv
)
770 dns_config_t
*config
;
772 config
= dns_configuration_copy();
773 if (config
!= NULL
) {
774 dns_configuration_free(&config
);