]> git.saurik.com Git - apple/configd.git/blob - nwi/network_information.c
configd-596.12.tar.gz
[apple/configd.git] / nwi / network_information.c
1 /*
2 * Copyright (c) 2011-2013 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 <xpc/xpc.h>
32
33 #include "libSystemConfiguration_client.h"
34 #include "network_information.h"
35 #include "network_information_priv.h"
36
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;
40
41 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
42 static int nwi_store_token;
43
44 static boolean_t nwi_store_force_refresh = FALSE;
45
46 #pragma mark -
47 #pragma mark Network information [nwi] client support
48
49
50 // Note: protected by __nwi_configuration_queue()
51 static int nwi_active = 0;
52 static libSC_info_client_t *nwi_client = NULL;
53
54
55 static dispatch_queue_t
56 __nwi_configuration_queue()
57 {
58 static dispatch_once_t once;
59 static dispatch_queue_t q;
60
61 dispatch_once(&once, ^{
62 q = dispatch_queue_create(NWI_SERVICE_NAME, NULL);
63 });
64
65 return q;
66 }
67
68 static
69 void
70 _nwi_state_initialize(void)
71 {
72 const char *nwi_key = nwi_state_get_notify_key();
73 uint32_t status = notify_register_check(nwi_key,
74 &nwi_store_token);
75
76 if (status != NOTIFY_STATUS_OK) {
77 fprintf(stderr, "nwi_state: registration failed (%u)\n", status);
78 }
79 else {
80 nwi_store_token_valid = TRUE;
81 }
82 }
83
84 static
85 void
86 _nwi_set_alias(nwi_state* state, nwi_ifstate* ifstate)
87 {
88 nwi_ifstate* ifstate_alias;
89 int af = ifstate->af;
90 int af_alias;
91
92 af_alias = (af == AF_INET)?AF_INET6:AF_INET;
93
94 ifstate_alias =
95 nwi_state_get_ifstate_with_name(state, af_alias,
96 ifstate->ifname);
97
98 if (ifstate_alias != NULL) {
99 ifstate_alias->af_alias = ifstate;
100 }
101 ifstate->af_alias = ifstate_alias;
102 return;
103 }
104
105 static
106 void
107 _nwi_state_reset_alias(nwi_state_t state) {
108 int i;
109
110 for (i = 0; i < state->ipv4_count; i++) {
111 state->nwi_ifstates[i].af_alias = NULL;
112 }
113
114 for (i = state->ipv6_start;
115 i < state->ipv6_start + state->ipv6_count; i++) {
116 _nwi_set_alias(state, &state->nwi_ifstates[i]);
117 }
118 }
119
120
121 #pragma mark -
122 #pragma mark Network information [nwi] APIs
123
124
125 /*
126 * Function: nwi_state_get_notify_key
127 * Purpose:
128 * Returns the BSD notify key to use to monitor when the state changes.
129 *
130 * Note:
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
133 * information.
134 */
135 const char *
136 nwi_state_get_notify_key()
137 {
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
143 }
144
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);
148
149 void
150 _nwi_state_force_refresh()
151 {
152 ATOMIC_CMPXCHG(&nwi_store_force_refresh, FALSE, TRUE);
153 }
154
155 static void
156 nwi_state_retain(nwi_state_t state)
157 {
158 ATOMIC_INC(&state->ref);
159 return;
160 }
161
162 /*
163 * Function: nwi_state_release
164 * Purpose:
165 * Release the memory associated with the network state.
166 */
167 void
168 nwi_state_release(nwi_state_t state)
169 {
170 if (ATOMIC_DEC(&state->ref) > 0) {
171 // if not last reference
172 return;
173 }
174
175 // release connection reference on 1-->0 transition
176 if (state->svr) {
177 dispatch_sync(__nwi_configuration_queue(), ^{
178 if (--nwi_active == 0) {
179 // if last reference, drop connection
180 libSC_info_client_release(nwi_client);
181 nwi_client = NULL;
182 }
183 });
184 }
185
186 // release nwi_state
187 free(state);
188
189 return;
190 }
191
192 static nwi_state *
193 _nwi_state_copy_data()
194 {
195 nwi_state_t nwi_state = NULL;
196 static const char *proc_name = NULL;
197 xpc_object_t reqdict;
198 xpc_object_t reply;
199
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;
204
205 dispatch_once(&once, ^{
206 const char *name;
207
208 // get [XPC] service name
209 name = getenv(service_name);
210 if ((name != NULL) && (issetugid() == 0)) {
211 service_name = strdup(name);
212 }
213
214 // get process name
215 proc_name = getprogname();
216 });
217
218 nwi_client =
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) {
223 --nwi_active;
224 }
225 }
226 });
227
228 if ((nwi_client == NULL) || !nwi_client->active) {
229 // if network information server not available
230 return NULL;
231 }
232
233 // create message
234 reqdict = xpc_dictionary_create(NULL, NULL, 0);
235
236 // set process name
237 if (proc_name != NULL) {
238 xpc_dictionary_set_string(reqdict, NWI_PROC_NAME, proc_name);
239 }
240
241 // set request
242 xpc_dictionary_set_int64(reqdict, NWI_REQUEST, NWI_REQUEST_COPY);
243
244 // send request to the DNS configuration server
245 reply = libSC_send_message_with_reply_sync(nwi_client, reqdict);
246 xpc_release(reqdict);
247
248 if (reply != NULL) {
249 const void *dataRef;
250 size_t dataLen = 0;
251
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);
256 nwi_state->ref = 0;
257 nwi_state->svr = TRUE;
258 }
259
260 xpc_release(reply);
261 }
262
263 return nwi_state;
264 }
265
266 /*
267 * Function: nwi_state_copy
268 * Purpose:
269 * Returns the current network state information.
270 * Release after use by calling nwi_state_release().
271 */
272 nwi_state_t
273 nwi_state_copy(void)
274 {
275 boolean_t force_refresh;
276 nwi_state_t nwi_state = NULL;
277 nwi_state_t old_state = NULL;
278
279 pthread_once(&initialized, _nwi_state_initialize);
280 pthread_mutex_lock(&nwi_store_lock);
281
282 force_refresh = ATOMIC_CMPXCHG(&nwi_store_force_refresh, TRUE, FALSE);
283
284 if (G_nwi_state != NULL) {
285 int check = 0;
286 uint32_t status;
287
288 if (nwi_store_token_valid == FALSE) {
289 /* have to throw cached copy away every time */
290 check = 1;
291 }
292 else {
293 status = notify_check(nwi_store_token, &check);
294 if (status != NOTIFY_STATUS_OK) {
295 fprintf(stderr, "nwi notify_check: failed with %u\n",
296 status);
297 /* assume that it changed, throw cached copy away */
298 check = 1;
299 }
300 }
301 if (check != 0 || force_refresh) {
302 /* new need snapshot */
303 old_state = G_nwi_state;
304 G_nwi_state = NULL;
305 }
306 }
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);
314 }
315 }
316 if (G_nwi_state != NULL) {
317 /* another reference for this caller */
318 nwi_state_retain(G_nwi_state);
319 }
320 nwi_state = G_nwi_state;
321 pthread_mutex_unlock(&nwi_store_lock);
322
323 if (old_state != NULL) {
324 /* get rid of G_nwi_state reference */
325 nwi_state_release(old_state);
326 }
327 return nwi_state;
328 }
329
330 /*
331 * Function: _nwi_state_ack
332 * Purpose:
333 * Acknowledge receipt and any changes associated with the [new or
334 * updated] network state.
335 */
336 void
337 _nwi_state_ack(nwi_state_t state, const char *bundle_id)
338 {
339 xpc_object_t reqdict;
340
341 if (state == NULL) {
342 return; // ASSERT
343 }
344
345 if ((nwi_client == NULL) || !nwi_client->active) {
346 // if network information server not available
347 return;
348 }
349
350 dispatch_sync(__nwi_configuration_queue(), ^{
351 nwi_active++; // keep connection active (for the life of the process)
352 });
353
354 // create message
355 reqdict = xpc_dictionary_create(NULL, NULL, 0);
356
357 // set request
358 xpc_dictionary_set_int64(reqdict, NWI_REQUEST, NWI_REQUEST_ACKNOWLEDGE);
359
360 // set generation
361 xpc_dictionary_set_uint64(reqdict, NWI_GENERATION, state->generation_count);
362
363 // send acknowledgement to the DNS configuration server
364 xpc_connection_send_message(nwi_client->connection, reqdict);
365
366 xpc_release(reqdict);
367 return;
368 }
369
370 /*
371 * Function: nwi_state_get_generation
372 * Purpose:
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.
376 */
377 uint64_t
378 nwi_state_get_generation(nwi_state_t state)
379 {
380 return (state->generation_count);
381 }
382
383 /*
384 * Function: nwi_ifstate_get_generation
385 * Purpose:
386 * Returns the generation (mach_time) of the nwi_ifstate data.
387 */
388 uint64_t
389 nwi_ifstate_get_generation(nwi_ifstate_t ifstate)
390 {
391 return (ifstate->if_generation_count);
392 }
393
394 /*
395 * Function: nwi_ifstate_get_ifname
396 * Purpose:
397 * Return the interface name of the specified ifstate.
398 */
399 const char *
400 nwi_ifstate_get_ifname(nwi_ifstate_t ifstate)
401 {
402 return (ifstate != NULL?ifstate->ifname:NULL);
403
404 }
405
406 static uint64_t
407 flags_from_af(int af)
408 {
409 return ((af == AF_INET)
410 ? NWI_IFSTATE_FLAGS_HAS_IPV4
411 : NWI_IFSTATE_FLAGS_HAS_IPV6);
412 }
413 /*
414 * Function: nwi_ifstate_get_flags
415 * Purpose:
416 * Return the flags for the given ifstate (see above for bit definitions).
417 */
418 nwi_ifstate_flags
419 nwi_ifstate_get_flags(nwi_ifstate_t ifstate)
420 {
421 nwi_ifstate_t alias = ifstate->af_alias;
422 nwi_ifstate_flags flags = 0ULL;
423
424 flags |= flags_from_af(ifstate->af);
425 if ((ifstate->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0) {
426 flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
427
428 }
429 if (alias != NULL) {
430 flags |= flags_from_af(alias->af);
431 if ((alias->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0) {
432 flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
433 }
434 }
435 return flags;
436 }
437
438 /*
439 * Function: nwi_state_get_first_ifstate
440 * Purpose:
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().
446 *
447 * Use nwi_ifstate_get_next() to get the next, lower priority interface
448 * in the list.
449 *
450 * Returns NULL if no connectivity for the specified address family is
451 * available.
452 */
453 nwi_ifstate_t
454 nwi_state_get_first_ifstate(nwi_state_t state, int af)
455 {
456 nwi_ifstate_t ifstate;
457
458 if (state == NULL) {
459 return NULL;
460 }
461
462 ifstate =
463 nwi_state_get_ifstate_with_index(state, af, 0);
464
465 if ((ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST)
466 != 0) {
467 ifstate = NULL;
468 }
469
470 return ifstate;
471
472 }
473
474 /*
475 * Function: nwi_state_get_ifstate
476 * Purpose:
477 * Return information for the specified interface 'ifname'.
478 *
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.
482 *
483 * Returns NULL if no information is available for that interface.
484 */
485 nwi_ifstate_t
486 nwi_state_get_ifstate(nwi_state_t state, const char * ifname)
487 {
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);
491 }
492 return ifstate;
493
494 }
495
496 /*
497 * Function: nwi_ifstate_get_next
498 * Purpose:
499 * Returns the next, lower priority nwi_ifstate_t after the specified
500 * 'ifstate' for the protocol family 'af'.
501 *
502 * Returns NULL when the end of the list is reached.
503 */
504 nwi_ifstate_t
505 nwi_ifstate_get_next(nwi_ifstate_t ifstate, int af)
506 {
507 nwi_ifstate_t alias, next;
508
509 alias =
510 (af == ifstate->af)?ifstate:ifstate->af_alias;
511
512 if (alias == NULL) {
513 return NULL;
514 }
515
516 /* We don't return interfaces marked rank never */
517 if ((alias->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0) {
518 return NULL;
519 }
520
521 next = ++alias;
522
523 if ((next->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) == 0) {
524 return next;
525 }
526 return NULL;
527 }
528
529 /*
530 * Function: nwi_ifstate_compare_rank
531 * Purpose:
532 * Compare the relative rank of two nwi_ifstate_t objects.
533 *
534 * The "rank" indicates the importance of the underlying interface.
535 *
536 * Returns:
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
540 */
541 int
542 nwi_ifstate_compare_rank(nwi_ifstate_t ifstate1, nwi_ifstate_t ifstate2)
543 {
544 return RankCompare(ifstate1->rank, ifstate2->rank);
545 }
546
547 /*
548 * nwi_state_get_reachability_flags
549 *
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.
553 *
554 * The reachability flags returned follow the definition of
555 * SCNetworkReachabilityFlags.
556 *
557 * If the flags are zero (i.e. do not contain kSCNetworkReachabilityFlagsReachable), there is no connectivity.
558 *
559 * Otherwise, at least kSCNetworkReachabilityFlagsReachable is set:
560 * Reachable only
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.
573 * Reachable and WWAN
574 * This connection will be going over the cellular network.
575 */
576 uint32_t
577 nwi_state_get_reachability_flags(nwi_state_t nwi_state, int af)
578 {
579 if (nwi_state == NULL) {
580 return (0);
581 }
582 if (af == AF_INET || af == AF_INET6) {
583 nwi_ifstate_t ifstate;
584
585 ifstate = nwi_state_get_first_ifstate(nwi_state, af);
586
587 if (ifstate != NULL) {
588 return ifstate->reach_flags;
589 }
590
591 return (af == AF_INET) ? nwi_state->reach_flags_v4 : nwi_state->reach_flags_v6;
592 } else {
593 nwi_ifstate_t ifstate_v4;
594 nwi_ifstate_t ifstate_v6;
595
596 ifstate_v4 = nwi_state_get_first_ifstate(nwi_state, AF_INET);
597 ifstate_v6 = nwi_state_get_first_ifstate(nwi_state, AF_INET6);
598
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;
603 } else {
604 return ifstate_v4->reach_flags;
605 }
606 } else {
607 return ifstate_v4->reach_flags;
608 }
609 } else {
610 if (ifstate_v6 != NULL) {
611 return ifstate_v6->reach_flags;
612 }
613 }
614
615 if (nwi_state->reach_flags_v4 != 0) {
616 return nwi_state->reach_flags_v4;
617 }
618 // This is the case where both ifstate are NULL.
619 return nwi_state->reach_flags_v6;
620 }
621 }
622
623 /*
624 * nwi_ifstate_get_vpn_server
625 *
626 * returns a sockaddr representation of the vpn server address.
627 * NULL if PPP/VPN/IPSec server address does not exist.
628 */
629 const struct sockaddr *
630 nwi_ifstate_get_vpn_server(nwi_ifstate_t ifstate)
631 {
632 const struct sockaddr * vpn_server_addr;
633
634 vpn_server_addr = (const struct sockaddr *)(void *)
635 &ifstate->vpn_server_address;
636
637 if (vpn_server_addr->sa_family == 0) {
638 return NULL;
639 }
640 return vpn_server_addr;
641 }
642
643 /*
644 * nwi_ifstate_get_reachability_flags
645 *
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].
650 */
651 uint32_t
652 nwi_ifstate_get_reachability_flags(nwi_ifstate_t ifstate)
653 {
654 return ifstate->reach_flags;
655 }
656
657 /*
658 * nwi_ifstate_get_signature
659 *
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.
662 *
663 * If the signature does not exist, NULL is returned.
664 */
665 const uint8_t *
666 nwi_ifstate_get_signature(nwi_ifstate_t ifstate, int af, int * length)
667 {
668 nwi_ifstate_t i_state = NULL;
669
670 switch (af) {
671 case AF_UNSPEC:
672 i_state = ifstate;
673 break;
674 case AF_INET:
675 case AF_INET6:
676 i_state = (ifstate->af == af) ? ifstate : ifstate->af_alias;
677 break;
678 default:
679 break;
680
681 }
682
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);
687 }
688 }
689
690 *length = 0;
691 return NULL;
692 }
693
694 static inline
695 boolean_t
696 _nwi_ifstate_is_in_list(nwi_ifstate_t ifstate, int af)
697 {
698 nwi_ifstate_t i_state;
699
700 i_state = (ifstate->af == af) ? ifstate : ifstate->af_alias;
701 if (i_state == NULL) {
702 return FALSE;
703 }
704
705 if ((nwi_ifstate_get_flags(i_state) & NWI_IFSTATE_FLAGS_NOT_IN_LIST) == 0) {
706 return TRUE;
707 }
708
709 return FALSE;
710 }
711
712 /*
713 * nwi_ifstate_get_dns_signature
714 *
715 * returns the signature and its length for given
716 * ifstate with a valid dns configuration.
717 *
718 * If the signature does not exist, NULL is returned.
719 *
720 */
721 const uint8_t *
722 nwi_ifstate_get_dns_signature(nwi_ifstate_t ifstate, int * length)
723 {
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;
729
730 *length = 0;
731
732 if ((nwi_ifstate_get_flags(ifstate) & NWI_IFSTATE_FLAGS_HAS_DNS) == 0) {
733 return NULL;
734 }
735
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) {
739 return NULL;
740 }
741
742 if (_nwi_ifstate_is_in_list(ifstate, AF_INET) == TRUE) {
743 signature = v4_signature;
744 *length = v4_signature_len;
745 } else {
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;
750 } else {
751 /* v6 is not ranked never or v4 has no signature */
752 signature = v6_signature;
753 *length = v6_signature_len;
754 }
755 }
756
757 return signature;
758 }
759
760
761 #pragma mark -
762 #pragma mark Network information [nwi] test code
763
764
765 #ifdef MAIN
766
767 int
768 main(int argc, char **argv)
769 {
770 dns_config_t *config;
771
772 config = dns_configuration_copy();
773 if (config != NULL) {
774 dns_configuration_free(&config);
775 }
776
777 exit(0);
778 }
779
780 #endif