2 * Copyright (c) 2011 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 "network_information.h"
31 #include "network_information_priv.h"
33 static nwi_state_t G_nwi_state
= NULL
;
34 static pthread_mutex_t nwi_store_lock
= PTHREAD_MUTEX_INITIALIZER
;
35 static boolean_t nwi_store_token_valid
= FALSE
;
37 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
38 static int nwi_store_token
;
44 _nwi_state_initialize(void)
46 const char *nwi_key
= nwi_state_get_notify_key();
47 uint32_t status
= notify_register_check(nwi_key
,
50 if (status
!= NOTIFY_STATUS_OK
) {
51 fprintf(stderr
, "nwi_state: registration failed (%u)\n", status
);
54 nwi_store_token_valid
= TRUE
;
60 nwi_set_alias(nwi_state
* state
, nwi_ifstate
* ifstate
)
62 nwi_ifstate
* ifstate_alias
;
66 af_alias
= (af
== AF_INET
)?AF_INET6
:AF_INET
;
69 nwi_state_get_ifstate_with_name(state
, af_alias
,
72 if (ifstate_alias
!= NULL
) {
73 ifstate_alias
->af_alias
= ifstate
;
75 ifstate
->af_alias
= ifstate_alias
;
81 _nwi_state_reset_alias(nwi_state_t state
) {
84 for (i
= 0; i
< state
->ipv4_count
; i
++) {
85 state
->nwi_ifstates
[i
].af_alias
= NULL
;
88 for (i
= state
->ipv6_start
;
89 i
< state
->ipv6_start
+ state
->ipv6_count
; i
++) {
90 nwi_set_alias(state
, &state
->nwi_ifstates
[i
]);
96 * Function: nwi_state_get_notify_key
98 * Returns the BSD notify key to use to monitor when the state changes.
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
106 nwi_state_get_notify_key()
108 return "com.apple.system.SystemConfiguration.nwi";
111 #define ATOMIC_INC(p) __sync_fetch_and_add((p), 1) // return (n++);
112 #define ATOMIC_DEC(p) __sync_sub_and_fetch((p), 1) // return (--n);
115 nwi_state_retain(nwi_state_t state
)
117 ATOMIC_INC(&state
->ref
);
122 * Function: nwi_state_release
124 * Release the memory associated with the network state.
127 nwi_state_release(nwi_state_t state
)
129 if (ATOMIC_DEC(&state
->ref
) == 0) {
136 * Function: nwi_state_copy
138 * Returns the current network state information.
139 * Release after use by calling nwi_state_release().
144 nwi_state_t nwi_state
= NULL
;
145 nwi_state_t old_state
= NULL
;
147 pthread_once(&initialized
, _nwi_state_initialize
);
148 pthread_mutex_lock(&nwi_store_lock
);
150 if (G_nwi_state
!= NULL
) {
154 if (nwi_store_token_valid
== FALSE
) {
155 /* have to throw cached copy away every time */
159 status
= notify_check(nwi_store_token
, &check
);
160 if (status
!= NOTIFY_STATUS_OK
) {
161 fprintf(stderr
, "nwi notify_check: failed with %u\n",
163 /* assume that it changed, throw cached copy away */
168 /* new need snapshot */
169 old_state
= G_nwi_state
;
173 /* Let's populate the cache if it's empty */
174 if (G_nwi_state
== NULL
) {
175 G_nwi_state
= _nwi_state_copy();
176 if (G_nwi_state
!= NULL
) {
177 /* one reference for G_nwi_state */
178 nwi_state_retain(G_nwi_state
);
179 _nwi_state_reset_alias(G_nwi_state
);
182 if (G_nwi_state
!= NULL
) {
183 /* another reference for this caller */
184 nwi_state_retain(G_nwi_state
);
186 nwi_state
= G_nwi_state
;
187 pthread_mutex_unlock(&nwi_store_lock
);
189 if (old_state
!= NULL
) {
190 /* get rid of G_nwi_state reference */
191 nwi_state_release(old_state
);
197 * Function: _nwi_state_ack
199 * Acknowledge receipt and any changes associated with the [new or
200 * updated] network state.
203 _nwi_state_ack(nwi_state_t state
, const char *bundle_id
)
209 * Function: nwi_state_get_generation
211 * Returns the generation (mach_time) of the nwi_state data.
212 * Every time the data is updated due to changes
213 * in the network, this value will change.
216 nwi_state_get_generation(nwi_state_t state
)
218 return (state
->generation_count
);
222 * Function: nwi_ifstate_get_ifname
224 * Return the interface name of the specified ifstate.
227 nwi_ifstate_get_ifname(nwi_ifstate_t ifstate
)
229 return (ifstate
!= NULL
?ifstate
->ifname
:NULL
);
234 flags_from_af(int af
)
236 return ((af
== AF_INET
)
237 ? NWI_IFSTATE_FLAGS_HAS_IPV4
238 : NWI_IFSTATE_FLAGS_HAS_IPV6
);
241 * Function: nwi_ifstate_get_flags
243 * Return the flags for the given ifstate (see above for bit definitions).
246 nwi_ifstate_get_flags(nwi_ifstate_t ifstate
)
248 nwi_ifstate_t alias
= ifstate
->af_alias
;
249 nwi_ifstate_flags flags
= 0ULL;
251 flags
|= flags_from_af(ifstate
->af
);
252 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_DNS
) != 0) {
253 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
257 flags
|= flags_from_af(alias
->af
);
258 if ((alias
->flags
& NWI_IFSTATE_FLAGS_HAS_DNS
) != 0) {
259 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
266 * Function: nwi_state_get_first_ifstate
268 * Returns the first and highest priority interface that has connectivity
269 * for the specified address family 'af'. 'af' is either AF_INET or AF_INET6.
270 * The connectivity provided is for general networking. To get information
271 * about an interface that isn't available for general networking, use
272 * nwi_state_get_ifstate().
274 * Use nwi_ifstate_get_next() to get the next, lower priority interface
277 * Returns NULL if no connectivity for the specified address family is
281 nwi_state_get_first_ifstate(nwi_state_t state
, int af
)
283 nwi_ifstate_t ifstate
;
290 nwi_state_get_ifstate_with_index(state
, af
, 0);
292 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
)
302 * Function: nwi_state_get_ifstate
304 * Return information for the specified interface 'ifname'.
306 * This API directly returns the ifstate for the specified interface.
307 * This is the only way to access information about an interface that isn't
308 * available for general networking.
310 * Returns NULL if no information is available for that interface.
313 nwi_state_get_ifstate(nwi_state_t state
, const char * ifname
)
315 nwi_ifstate_t ifstate
= nwi_state_get_ifstate_with_name(state
, AF_INET
, ifname
);
316 if (ifstate
== NULL
) {
317 ifstate
= nwi_state_get_ifstate_with_name(state
, AF_INET6
, ifname
);
324 * Function: nwi_ifstate_get_next
326 * Returns the next, lower priority nwi_ifstate_t after the specified
327 * 'ifstate' for the protocol family 'af'.
329 * Returns NULL when the end of the list is reached.
332 nwi_ifstate_get_next(nwi_ifstate_t ifstate
, int af
)
334 nwi_ifstate_t alias
, next
;
337 (af
== ifstate
->af
)?ifstate
:ifstate
->af_alias
;
343 /* We don't return interfaces marked rank never */
344 if ((alias
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
350 if ((next
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) == 0) {
357 * Function: nwi_ifstate_compare_rank
359 * Compare the relative rank of two nwi_ifstate_t objects.
361 * The "rank" indicates the importance of the underlying interface.
364 * 0 if ifstate1 and ifstate2 are ranked equally
365 * -1 if ifstate1 is ranked ahead of ifstate2
366 * 1 if ifstate2 is ranked ahead of ifstate1
369 nwi_ifstate_compare_rank(nwi_ifstate_t ifstate1
, nwi_ifstate_t ifstate2
)
371 return RankCompare(ifstate1
->rank
, ifstate2
->rank
);