]> git.saurik.com Git - apple/configd.git/blob - nwi/network_information.c
configd-453.16.tar.gz
[apple/configd.git] / nwi / network_information.c
1 /*
2 * Copyright (c) 2011 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 "network_information.h"
31 #include "network_information_priv.h"
32
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;
36
37 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
38 static int nwi_store_token;
39
40
41 /* Private */
42 static
43 void
44 _nwi_state_initialize(void)
45 {
46 const char *nwi_key = nwi_state_get_notify_key();
47 uint32_t status = notify_register_check(nwi_key,
48 &nwi_store_token);
49
50 if (status != NOTIFY_STATUS_OK) {
51 fprintf(stderr, "nwi_state: registration failed (%u)\n", status);
52 }
53 else {
54 nwi_store_token_valid = TRUE;
55 }
56 }
57
58 static
59 void
60 nwi_set_alias(nwi_state* state, nwi_ifstate* ifstate)
61 {
62 nwi_ifstate* ifstate_alias;
63 int af = ifstate->af;
64 int af_alias;
65
66 af_alias = (af == AF_INET)?AF_INET6:AF_INET;
67
68 ifstate_alias =
69 nwi_state_get_ifstate_with_name(state, af_alias,
70 ifstate->ifname);
71
72 if (ifstate_alias != NULL) {
73 ifstate_alias->af_alias = ifstate;
74 }
75 ifstate->af_alias = ifstate_alias;
76 return;
77 }
78
79 static
80 void
81 _nwi_state_reset_alias(nwi_state_t state) {
82 int i;
83
84 for (i = 0; i < state->ipv4_count; i++) {
85 state->nwi_ifstates[i].af_alias = NULL;
86 }
87
88 for (i = state->ipv6_start;
89 i < state->ipv6_start + state->ipv6_count; i++) {
90 nwi_set_alias(state, &state->nwi_ifstates[i]);
91 }
92 }
93
94 /* Public APIs' */
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 */
105 const char *
106 nwi_state_get_notify_key()
107 {
108 return "com.apple.system.SystemConfiguration.nwi";
109 }
110
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);
113
114 static void
115 nwi_state_retain(nwi_state_t state)
116 {
117 ATOMIC_INC(&state->ref);
118 return;
119 }
120
121 /*
122 * Function: nwi_state_release
123 * Purpose:
124 * Release the memory associated with the network state.
125 */
126 void
127 nwi_state_release(nwi_state_t state)
128 {
129 if (ATOMIC_DEC(&state->ref) == 0) {
130 free(state);
131 }
132 return;
133 }
134
135 /*
136 * Function: nwi_state_copy
137 * Purpose:
138 * Returns the current network state information.
139 * Release after use by calling nwi_state_release().
140 */
141 nwi_state_t
142 nwi_state_copy(void)
143 {
144 nwi_state_t nwi_state = NULL;
145 nwi_state_t old_state = NULL;
146
147 pthread_once(&initialized, _nwi_state_initialize);
148 pthread_mutex_lock(&nwi_store_lock);
149
150 if (G_nwi_state != NULL) {
151 int check = 0;
152 uint32_t status;
153
154 if (nwi_store_token_valid == FALSE) {
155 /* have to throw cached copy away every time */
156 check = 1;
157 }
158 else {
159 status = notify_check(nwi_store_token, &check);
160 if (status != NOTIFY_STATUS_OK) {
161 fprintf(stderr, "nwi notify_check: failed with %u\n",
162 status);
163 /* assume that it changed, throw cached copy away */
164 check = 1;
165 }
166 }
167 if (check != 0) {
168 /* new need snapshot */
169 old_state = G_nwi_state;
170 G_nwi_state = NULL;
171 }
172 }
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);
180 }
181 }
182 if (G_nwi_state != NULL) {
183 /* another reference for this caller */
184 nwi_state_retain(G_nwi_state);
185 }
186 nwi_state = G_nwi_state;
187 pthread_mutex_unlock(&nwi_store_lock);
188
189 if (old_state != NULL) {
190 /* get rid of G_nwi_state reference */
191 nwi_state_release(old_state);
192 }
193 return nwi_state;
194 }
195
196 /*
197 * Function: _nwi_state_ack
198 * Purpose:
199 * Acknowledge receipt and any changes associated with the [new or
200 * updated] network state.
201 */
202 void
203 _nwi_state_ack(nwi_state_t state, const char *bundle_id)
204 {
205 return;
206 }
207
208 /*
209 * Function: nwi_state_get_generation
210 * Purpose:
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.
214 */
215 uint64_t
216 nwi_state_get_generation(nwi_state_t state)
217 {
218 return (state->generation_count);
219 }
220
221 /*
222 * Function: nwi_ifstate_get_ifname
223 * Purpose:
224 * Return the interface name of the specified ifstate.
225 */
226 const char *
227 nwi_ifstate_get_ifname(nwi_ifstate_t ifstate)
228 {
229 return (ifstate != NULL?ifstate->ifname:NULL);
230
231 }
232
233 static uint64_t
234 flags_from_af(int af)
235 {
236 return ((af == AF_INET)
237 ? NWI_IFSTATE_FLAGS_HAS_IPV4
238 : NWI_IFSTATE_FLAGS_HAS_IPV6);
239 }
240 /*
241 * Function: nwi_ifstate_get_flags
242 * Purpose:
243 * Return the flags for the given ifstate (see above for bit definitions).
244 */
245 nwi_ifstate_flags
246 nwi_ifstate_get_flags(nwi_ifstate_t ifstate)
247 {
248 nwi_ifstate_t alias = ifstate->af_alias;
249 nwi_ifstate_flags flags = 0ULL;
250
251 flags |= flags_from_af(ifstate->af);
252 if ((ifstate->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0) {
253 flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
254
255 }
256 if (alias != NULL) {
257 flags |= flags_from_af(alias->af);
258 if ((alias->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0) {
259 flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
260 }
261 }
262 return flags;
263 }
264
265 /*
266 * Function: nwi_state_get_first_ifstate
267 * Purpose:
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().
273 *
274 * Use nwi_ifstate_get_next() to get the next, lower priority interface
275 * in the list.
276 *
277 * Returns NULL if no connectivity for the specified address family is
278 * available.
279 */
280 nwi_ifstate_t
281 nwi_state_get_first_ifstate(nwi_state_t state, int af)
282 {
283 nwi_ifstate_t ifstate;
284
285 if (state == NULL) {
286 return NULL;
287 }
288
289 ifstate =
290 nwi_state_get_ifstate_with_index(state, af, 0);
291
292 if ((ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST)
293 != 0) {
294 ifstate = NULL;
295 }
296
297 return ifstate;
298
299 }
300
301 /*
302 * Function: nwi_state_get_ifstate
303 * Purpose:
304 * Return information for the specified interface 'ifname'.
305 *
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.
309 *
310 * Returns NULL if no information is available for that interface.
311 */
312 nwi_ifstate_t
313 nwi_state_get_ifstate(nwi_state_t state, const char * ifname)
314 {
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);
318 }
319 return ifstate;
320
321 }
322
323 /*
324 * Function: nwi_ifstate_get_next
325 * Purpose:
326 * Returns the next, lower priority nwi_ifstate_t after the specified
327 * 'ifstate' for the protocol family 'af'.
328 *
329 * Returns NULL when the end of the list is reached.
330 */
331 nwi_ifstate_t
332 nwi_ifstate_get_next(nwi_ifstate_t ifstate, int af)
333 {
334 nwi_ifstate_t alias, next;
335
336 alias =
337 (af == ifstate->af)?ifstate:ifstate->af_alias;
338
339 if (alias == NULL) {
340 return NULL;
341 }
342
343 /* We don't return interfaces marked rank never */
344 if ((alias->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0) {
345 return NULL;
346 }
347
348 next = ++alias;
349
350 if ((next->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) == 0) {
351 return next;
352 }
353 return NULL;
354 }
355
356 /*
357 * Function: nwi_ifstate_compare_rank
358 * Purpose:
359 * Compare the relative rank of two nwi_ifstate_t objects.
360 *
361 * The "rank" indicates the importance of the underlying interface.
362 *
363 * Returns:
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
367 */
368 int
369 nwi_ifstate_compare_rank(nwi_ifstate_t ifstate1, nwi_ifstate_t ifstate2)
370 {
371 return RankCompare(ifstate1->rank, ifstate2->rank);
372 }