1/* 2 * Copyright (c) 2000-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 * ip_plugin.c 26 * - decides which interface will be made the "primary" interface, 27 * that is, the one with the default route assigned 28 */ 29 30/* 31 * Modification History 32 * 33 * July 19, 2000 Dieter Siegmund (dieter@apple.com) 34 * - initial revision 35 * 36 * November 15, 2000 Dieter Siegmund (dieter@apple.com) 37 * - changed to use new configuration model 38 * 39 * March 19, 2001 Dieter Siegmund (dieter@apple.com) 40 * - use service state instead of interface state 41 * 42 * July 16, 2001 Allan Nathanson (ajn@apple.com) 43 * - update to public SystemConfiguration.framework APIs 44 * 45 * August 28, 2001 Dieter Siegmund (dieter@apple.com) 46 * - specify the interface name when installing the default route 47 * - this ensures that default traffic goes to the highest priority 48 * service when multiple interfaces are configured to be on the same subnet 49 * 50 * September 16, 2002 Dieter Siegmund (dieter@apple.com) 51 * - don't elect a link-local service to be primary unless it's the only 52 * one that's available 53 * 54 * July 16, 2003 Dieter Siegmund (dieter@apple.com) 55 * - modifications to support IPv6 56 * - don't elect a service to be primary if it doesn't have a default route 57 * 58 * July 29, 2003 Dieter Siegmund (dieter@apple.com) 59 * - support installing a default route to a router that's not on our subnet 60 * 61 * March 22, 2004 Allan Nathanson (ajn@apple.com) 62 * - create expanded DNS configuration 63 * 64 * June 20, 2006 Allan Nathanson (ajn@apple.com) 65 * - add SMB configuration 66 * 67 * December 5, 2007 Dieter Siegmund (dieter@apple.com) 68 * - added support for multiple scoped routes 69 */ 70 71#include <stdlib.h> 72#include <unistd.h> 73#include <string.h> 74#include <stdio.h> 75#include <sys/fcntl.h> 76#include <sys/ioctl.h> 77#include <sys/types.h> 78#include <sys/socket.h> 79#include <net/route.h> 80#include <net/if.h> 81#include <net/if_dl.h> 82#include <netinet/in.h> 83#include <netinet/icmp6.h> 84#include <netinet6/in6_var.h> 85#include <netinet6/nd6.h> 86#include <arpa/inet.h> 87#include <sys/sysctl.h> 88#include <limits.h> 89#include <notify.h> 90#include <mach/mach_time.h> 91#include <dispatch/dispatch.h> 92#include <CommonCrypto/CommonDigest.h> 93 94#include <SystemConfiguration/SystemConfiguration.h> 95#include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h> 96#include <SystemConfiguration/SCValidation.h> 97#include <SystemConfiguration/scprefs_observer.h> 98#include <SystemConfiguration/SCPrivate.h> /* for SCLog() */ 99#include "SCNetworkReachabilityInternal.h" 100#include "SCNetworkSignaturePrivate.h" 101#include <dnsinfo.h> 102#include "dnsinfo_server.h" 103 104#if defined(HAVE_IPSEC_STATUS) || defined(HAVE_VPN_STATUS) 105#include <ppp/PPPControllerPriv.h> 106#endif // !defined(HAVE_IPSEC_STATUS) || defined(HAVE_VPN_STATUS) 107 108#include <dns_sd.h> 109#ifndef kDNSServiceCompMulticastDNS 110#define kDNSServiceCompMulticastDNS "MulticastDNS" 111#endif 112#ifndef kDNSServiceCompPrivateDNS 113#define kDNSServiceCompPrivateDNS "PrivateDNS" 114#endif 115#include <network_information.h> 116#include "network_information_priv.h" 117#include "network_information_server.h" 118#include <ppp/ppp_msg.h> 119 120enum { 121 kProtocolFlagsNone = 0x0, 122 kProtocolFlagsIPv4 = 0x1, 123 kProtocolFlagsIPv6 = 0x2 124}; 125typedef uint8_t ProtocolFlags; 126 127enum { 128 kDebugFlag1 = 0x00000001, 129 kDebugFlag2 = 0x00000002, 130 kDebugFlag4 = 0x00000004, 131 kDebugFlag8 = 0x00000008, 132 kDebugFlagDefault = kDebugFlag1, 133 kDebugFlagAll = 0xffffffff 134}; 135 136#ifdef TEST_IPV4_ROUTELIST 137#define ROUTELIST_DEBUG(a, f) { if ((S_IPMonitor_debug & (f)) != 0) printf a ;} 138#else 139#define ROUTELIST_DEBUG(a, f) 140#endif 141 142#if !TARGET_IPHONE_SIMULATOR 143#include "set-hostname.h" 144#endif /* !TARGET_IPHONE_SIMULATOR */ 145 146#include "dns-configuration.h" 147#include "proxy-configuration.h" 148 149#if !TARGET_OS_IPHONE 150#include "smb-configuration.h" 151#endif /* !TARGET_OS_IPHONE */ 152 153/* 154 * Property: kIPIsCoupled 155 * Purpose: 156 * Used to indicate that the IPv4 and IPv6 services are coupled. 157 * Neither the IPv4 part nor the IPv6 part of a coupled service 158 * may become primary if IPv4 or IPv6 is primary for another interface. 159 * 160 * For example, if the service over en3 is "coupled" and has IPv6, 161 * and en0 is primary for just IPv4, IPv6 over en3 is not eligible 162 * to become primary for IPv6. 163 */ 164#define kIPIsCoupled CFSTR("IPIsCoupled") 165 166#define PPP_PREFIX "ppp" 167 168#define IP_FORMAT "%d.%d.%d.%d" 169#define IP_CH(ip) ((u_char *)(ip)) 170#define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3] 171 172#include "ip_plugin.h" 173#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) 174static SCLoggerRef S_IPMonitor_logger; 175#endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) 176 177static boolean_t S_bundle_logging_verbose; 178 179/* 180 * IPv4 Route management 181 */ 182 183typedef uint32_t RouteFlags; 184 185enum { 186 kRouteIsDirectToInterfaceFlag = 0x00000001, 187 kRouteIsNotSubnetLocalFlag = 0x00000002, 188 kRouteIsScopedFlag = 0x00000004, 189 kRouteIsNULLFlag = 0x00000008 190}; 191 192typedef struct { 193 struct in_addr dest; 194 struct in_addr mask; 195 struct in_addr gateway; 196 char ifname[IFNAMSIZ]; 197 unsigned int ifindex; 198 struct in_addr ifa; 199 Rank rank; 200 RouteFlags flags; 201} IPv4Route, *IPv4RouteRef; 202 203typedef struct { 204 int count; 205 int size; 206 boolean_t exclude_from_nwi; 207 IPv4Route list[1]; /* variable length */ 208} IPv4RouteList, *IPv4RouteListRef; 209 210enum { 211 kIPv4RouteListAddRouteCommand, 212 kIPv4RouteListRemoveRouteCommand 213}; 214 215/* 216 * Election Information 217 * - information about the current best services 218 */ 219typedef struct Candidate { 220 CFStringRef serviceID; 221 CFStringRef if_name; 222 union { 223 struct in_addr v4; 224 struct in6_addr v6; 225 } addr; 226 Rank rank; 227 boolean_t ip_is_coupled; 228 SCNetworkReachabilityFlags reachability_flags; 229 union { 230 struct sockaddr_in vpn_server_addr4; 231 struct sockaddr_in6 vpn_server_addr6; 232 } vpn_server_addr; 233 CFStringRef signature; 234} Candidate, * CandidateRef; 235 236typedef struct ElectionResults { 237 int count; 238 int size; 239 Candidate candidates[1]; 240} ElectionResults, * ElectionResultsRef; 241 242static __inline__ unsigned int 243ElectionResultsComputeSize(unsigned int n) 244{ 245 return (offsetof(ElectionResults, candidates[n])); 246} 247 248/* 249 * Type: Rank 250 * Purpose: 251 * A 32-bit value to encode the relative rank of a service. 252 * 253 * The top 8 bits are used to hold the rank assertion (first, last 254 * never, default). 255 * 256 * The bottom 24 bits are used to store the service index (i.e. the 257 * position within the service order array). 258 */ 259#define RANK_ASSERTION_MAKE(r) ((Rank)(r) << 24) 260#define kRankAssertionFirst RANK_ASSERTION_MAKE(0) 261#define kRankAssertionDefault RANK_ASSERTION_MAKE(1) 262#define kRankAssertionLast RANK_ASSERTION_MAKE(2) 263#define kRankAssertionNever RANK_ASSERTION_MAKE(3) 264#define kRankAssertionMask RANK_ASSERTION_MAKE(0xff) 265#define RANK_ASSERTION_MASK(r) ((Rank)(r) & kRankAssertionMask) 266 267#define RANK_INDEX_MAKE(r) ((Rank)(r)) 268#define kRankIndexMask RANK_INDEX_MAKE(0xffffff) 269#define RANK_INDEX_MASK(r) ((Rank)(r) & kRankIndexMask) 270 271static __inline__ Rank 272RankMake(uint32_t service_index, Rank primary_rank) 273{ 274 return (RANK_INDEX_MASK(service_index) | RANK_ASSERTION_MASK(primary_rank)); 275} 276 277static __inline__ Rank 278PrimaryRankGetRankAssertion(CFStringRef primaryRank) 279{ 280 if (CFEqual(primaryRank, kSCValNetServicePrimaryRankNever)) { 281 return kRankAssertionNever; 282 } else if (CFEqual(primaryRank, kSCValNetServicePrimaryRankFirst)) { 283 return kRankAssertionFirst; 284 } else if (CFEqual(primaryRank, kSCValNetServicePrimaryRankLast)) { 285 return kRankAssertionLast; 286 } 287 return kRankAssertionDefault; 288} 289 290typedef uint32_t IPv4RouteListApplyCommand; 291 292typedef void IPv4RouteListApplyCallBackFunc(IPv4RouteListApplyCommand cmd, 293 IPv4RouteRef route, void * arg); 294typedef IPv4RouteListApplyCallBackFunc * IPv4RouteListApplyCallBackFuncPtr; 295 296/* SCDynamicStore session */ 297static SCDynamicStoreRef S_session = NULL; 298 299/* debug output flags */ 300static uint32_t S_IPMonitor_debug = 0; 301static Boolean S_IPMonitor_verbose = FALSE; 302 303/* are we netbooted? If so, don't touch the default route */ 304static boolean_t S_netboot = FALSE; 305 306/* is scoped routing enabled? */ 307#ifdef RTF_IFSCOPE 308static boolean_t S_scopedroute = FALSE; 309static boolean_t S_scopedroute_v6 = FALSE; 310#endif /* RTF_IFSCOPE */ 311 312/* dictionary to hold per-service state: key is the serviceID */ 313static CFMutableDictionaryRef S_service_state_dict = NULL; 314static CFMutableDictionaryRef S_ipv4_service_rank_dict = NULL; 315static CFMutableDictionaryRef S_ipv6_service_rank_dict = NULL; 316 317/* dictionary to hold per-interface rank information */ 318static CFMutableDictionaryRef S_if_rank_dict = NULL; 319 320/* if set, a PPP interface overrides the primary */ 321static boolean_t S_ppp_override_primary = FALSE; 322 323/* the current primary serviceID's */ 324static CFStringRef S_primary_ipv4 = NULL; 325static CFStringRef S_primary_ipv6 = NULL; 326static CFStringRef S_primary_dns = NULL; 327static CFStringRef S_primary_proxies = NULL; 328 329/* the current election results */ 330static ElectionResultsRef S_ipv4_results; 331static ElectionResultsRef S_ipv6_results; 332 333static CFStringRef S_state_global_ipv4 = NULL; 334static CFStringRef S_state_global_ipv6 = NULL; 335static CFStringRef S_state_global_dns = NULL; 336static CFStringRef S_state_global_proxies = NULL; 337static CFStringRef S_state_service_prefix = NULL; 338static CFStringRef S_setup_global_ipv4 = NULL; 339static CFStringRef S_setup_service_prefix = NULL; 340 341static CFStringRef S_multicast_resolvers = NULL; 342static CFStringRef S_private_resolvers = NULL; 343 344#if !TARGET_IPHONE_SIMULATOR 345static IPv4RouteListRef S_ipv4_routelist = NULL; 346#endif /* !TARGET_IPHONE_SIMULATOR */ 347 348static const struct in_addr S_ip_zeros = { 0 }; 349static const struct in6_addr S_ip6_zeros = IN6ADDR_ANY_INIT; 350 351static boolean_t S_append_state = FALSE; 352 353static CFDictionaryRef S_dns_dict = NULL; 354 355static Boolean S_dnsinfo_synced = TRUE; 356 357static nwi_state_t S_nwi_state = NULL; 358static Boolean S_nwi_synced = TRUE; 359 360static CFDictionaryRef S_proxies_dict = NULL; 361 362// Note: access should be gated with __network_change_queue() 363static uint32_t S_network_change_needed = 0; 364#define NETWORK_CHANGE_NET 1<<0 365#define NETWORK_CHANGE_DNS 1<<1 366#define NETWORK_CHANGE_PROXY 1<<2 367#if !TARGET_OS_IPHONE 368#define NETWORK_CHANGE_SMB 1<<3 369#endif /* !TARGET_OS_IPHONE */ 370static struct timeval S_network_change_start; 371static Boolean S_network_change_timeout = FALSE; 372static dispatch_source_t S_network_change_timer = NULL; 373 374#if !TARGET_OS_IPHONE 375static CFStringRef S_primary_smb = NULL; 376static CFStringRef S_state_global_smb = NULL; 377static CFDictionaryRef S_smb_dict = NULL; 378#endif /* !TARGET_OS_IPHONE */ 379 380#if !TARGET_OS_IPHONE 381#define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf" 382#endif /* !TARGET_OS_IPHONE */ 383 384#ifndef KERN_NETBOOT 385#define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */ 386#endif //KERN_NETBOOT 387 388/** 389 ** entityType*, GetEntityChanges* 390 ** - definitions for the entity types we handle 391 **/ 392enum { 393 kEntityTypeIPv4 = 0, 394 kEntityTypeIPv6, 395 kEntityTypeDNS, 396 kEntityTypeProxies, 397#if !TARGET_OS_IPHONE 398 kEntityTypeSMB, 399#endif /* !TARGET_OS_IPHONE */ 400 ENTITY_TYPES_COUNT, 401 kEntityTypeVPNStatus, 402 kEntityTypeServiceOptions = 31 403}; 404typedef uint32_t EntityType; 405 406static const CFStringRef *entityTypeNames[ENTITY_TYPES_COUNT] = { 407 &kSCEntNetIPv4, /* 0 */ 408 &kSCEntNetIPv6, /* 1 */ 409 &kSCEntNetDNS, /* 2 */ 410 &kSCEntNetProxies, /* 3 */ 411#if !TARGET_OS_IPHONE 412 &kSCEntNetSMB, /* 4 */ 413#endif /* !TARGET_OS_IPHONE */ 414}; 415 416static Boolean 417S_dict_get_boolean(CFDictionaryRef dict, CFStringRef key, Boolean def_value); 418 419static __inline__ char 420ipvx_char(int af) 421{ 422 return ((af == AF_INET) ? '4' : '6'); 423} 424 425static __inline__ char 426ipvx_other_char(int af) 427{ 428 return ((af == AF_INET) ? '6' : '4'); 429} 430 431static IPv4RouteListRef 432ipv4_dict_get_routelist(CFDictionaryRef ipv4_dict) 433{ 434 CFDataRef routes; 435 IPv4RouteListRef routes_list = NULL; 436 437 if (isA_CFDictionary(ipv4_dict) == NULL) { 438 return (NULL); 439 } 440 441 routes = CFDictionaryGetValue(ipv4_dict, kIPv4DictRoutes); 442 443 if (routes != NULL) { 444 routes_list = (IPv4RouteListRef)(void*)CFDataGetBytePtr(routes); 445 } 446 return (routes_list); 447} 448 449static CFStringRef 450ipv4_dict_get_ifname(CFDictionaryRef ipv4_dict) 451{ 452 CFDictionaryRef ipv4_service_dict = NULL; 453 454 if (isA_CFDictionary(ipv4_dict) == NULL) { 455 return (NULL); 456 } 457 458 ipv4_service_dict = CFDictionaryGetValue(ipv4_dict, 459 kIPv4DictService); 460 461 if (isA_CFDictionary(ipv4_service_dict) == NULL) { 462 return NULL; 463 } 464 465 return CFDictionaryGetValue(ipv4_service_dict, kSCPropInterfaceName); 466} 467 468typedef boolean_t GetEntityChangesFunc(CFStringRef serviceID, 469 CFDictionaryRef state_dict, 470 CFDictionaryRef setup_dict, 471 CFDictionaryRef info); 472typedef GetEntityChangesFunc * GetEntityChangesFuncRef; 473 474static GetEntityChangesFunc get_ipv4_changes; 475static GetEntityChangesFunc get_ipv6_changes; 476static GetEntityChangesFunc get_dns_changes; 477static GetEntityChangesFunc get_proxies_changes; 478#if !TARGET_OS_IPHONE 479static GetEntityChangesFunc get_smb_changes; 480#endif /* !TARGET_OS_IPHONE */ 481 482static void 483my_CFRelease(void * t); 484 485static void 486my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new); 487 488static void 489my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key); 490 491static const GetEntityChangesFuncRef entityChangeFunc[ENTITY_TYPES_COUNT] = { 492 get_ipv4_changes, /* 0 */ 493 get_ipv6_changes, /* 1 */ 494 get_dns_changes, /* 2 */ 495 get_proxies_changes,/* 3 */ 496#if !TARGET_OS_IPHONE 497 get_smb_changes, /* 4 */ 498#endif /* !TARGET_OS_IPHONE */ 499}; 500 501/** 502 ** keyChangeList 503 ** - mechanism to do an atomic update of the SCDynamicStore 504 ** when the content needs to be changed across multiple functions 505 **/ 506typedef struct { 507 CFMutableArrayRef notify; 508 CFMutableArrayRef remove; 509 CFMutableDictionaryRef set; 510} keyChangeList, * keyChangeListRef; 511 512static CFStringRef 513my_CFStringCopyComponent(CFStringRef path, CFStringRef separator, 514 CFIndex component_index) 515{ 516 CFArrayRef arr; 517 CFStringRef component = NULL; 518 519 arr = CFStringCreateArrayBySeparatingStrings(NULL, path, separator); 520 if (arr == NULL) { 521 goto done; 522 } 523 if (CFArrayGetCount(arr) <= component_index) { 524 goto done; 525 } 526 component = CFRetain(CFArrayGetValueAtIndex(arr, component_index)); 527 528 done: 529 my_CFRelease(&arr); 530 return (component); 531} 532 533static void 534keyChangeListInit(keyChangeListRef keys) 535{ 536 keys->notify = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 537 keys->remove = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 538 keys->set = CFDictionaryCreateMutable(NULL, 0, 539 &kCFTypeDictionaryKeyCallBacks, 540 &kCFTypeDictionaryValueCallBacks); 541 return; 542} 543 544static void 545keyChangeListFree(keyChangeListRef keys) 546{ 547 my_CFRelease(&keys->notify); 548 my_CFRelease(&keys->remove); 549 my_CFRelease(&keys->set); 550 return; 551} 552 553static Boolean 554keyChangeListActive(keyChangeListRef keys) 555{ 556 return ((CFDictionaryGetCount(keys->set) > 0) || 557 (CFArrayGetCount(keys->remove) > 0) || 558 (CFArrayGetCount(keys->notify) > 0)); 559} 560 561static void 562keyChangeListNotifyKey(keyChangeListRef keys, CFStringRef key) 563{ 564 my_CFArrayAppendUniqueValue(keys->notify, key); 565 return; 566} 567 568static void 569keyChangeListRemoveValue(keyChangeListRef keys, CFStringRef key) 570{ 571 my_CFArrayAppendUniqueValue(keys->remove, key); 572 CFDictionaryRemoveValue(keys->set, key); 573 return; 574} 575 576static void 577keyChangeListSetValue(keyChangeListRef keys, CFStringRef key, CFTypeRef value) 578{ 579 my_CFArrayRemoveValue(keys->remove, key); 580 CFDictionarySetValue(keys->set, key, value); 581 return; 582} 583 584static void 585keyChangeListApplyToStore(keyChangeListRef keys, SCDynamicStoreRef session) 586{ 587 CFArrayRef notify = keys->notify; 588 CFArrayRef remove = keys->remove; 589 CFDictionaryRef set = keys->set; 590 591 if (CFArrayGetCount(notify) == 0) { 592 notify = NULL; 593 } 594 if (CFArrayGetCount(remove) == 0) { 595 remove = NULL; 596 } 597 if (CFDictionaryGetCount(set) == 0) { 598 set = NULL; 599 } 600 if (set == NULL && remove == NULL && notify == NULL) { 601 return; 602 } 603 if (S_IPMonitor_debug & kDebugFlag1) { 604 if (set != NULL) { 605 my_log(LOG_DEBUG, "IPMonitor: Setting:\n%@", set); 606 } 607 if (remove != NULL) { 608 my_log(LOG_DEBUG, "IPMonitor: Removing:\n%@", remove); 609 } 610 if (notify != NULL) { 611 my_log(LOG_DEBUG, "IPMonitor: Notifying:\n%@", notify); 612 } 613 } 614 (void)SCDynamicStoreSetMultiple(session, set, remove, notify); 615 616 return; 617} 618 619static void 620S_nwi_ifstate_dump(nwi_ifstate_t ifstate, int i) 621{ 622 const char * addr_str; 623 void * address; 624 char ntopbuf[INET6_ADDRSTRLEN]; 625 char vpn_ntopbuf[INET6_ADDRSTRLEN]; 626 const struct sockaddr * vpn_addr; 627 628 address = nwi_ifstate_get_address(ifstate); 629 addr_str = inet_ntop(ifstate->af, address, ntopbuf, sizeof(ntopbuf)); 630 vpn_addr = nwi_ifstate_get_vpn_server(ifstate); 631 if (vpn_addr != NULL) { 632 _SC_sockaddr_to_string(nwi_ifstate_get_vpn_server(ifstate), 633 vpn_ntopbuf, 634 sizeof(vpn_ntopbuf)); 635 } 636 my_log(LOG_DEBUG, 637 " [%d]: %s%s%s%s rank 0x%x iaddr: %s%s%s reachability_flags %u", 638 i, ifstate->ifname, 639 ifstate->diff_str != NULL ? ifstate->diff_str : "", 640 (ifstate->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0 641 ? " dns" : "", 642 (ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0 643 ? " never" : "", 644 ifstate->rank, 645 addr_str, 646 (vpn_addr != NULL) ? " vpn_server_addr: " : "", 647 (vpn_addr != NULL) ? vpn_ntopbuf : "", 648 ifstate->reach_flags); 649 return; 650} 651 652static void 653S_nwi_state_dump(nwi_state_t state) 654{ 655 int i; 656 nwi_ifstate_t scan; 657 658 if (state == NULL) { 659 my_log(LOG_DEBUG, "nwi_state = <none>"); 660 return; 661 } 662 my_log(LOG_DEBUG, 663 "nwi_state = { " 664 "gen = %llu size = %u #ipv4 = %u #ipv6 = %u " 665 "reach_flags_v4 = %u reach_flags_v6 %u }", 666 state->generation_count, 667 state->size, 668 state->ipv4_count, 669 state->ipv6_count, 670 nwi_state_get_reachability_flags(state, AF_INET), 671 nwi_state_get_reachability_flags(state, AF_INET6)); 672 if (state->ipv4_count) { 673 my_log(LOG_DEBUG, "IPv4:"); 674 for (i = 0, scan = state->nwi_ifstates; 675 i < state->ipv4_count; i++, scan++) { 676 S_nwi_ifstate_dump(scan, i); 677 } 678 } 679 if (state->ipv6_count) { 680 my_log(LOG_DEBUG, "IPv6:"); 681 for (i = 0, scan = state->nwi_ifstates + state->ipv6_start; 682 i < state->ipv6_count; i++, scan++) { 683 S_nwi_ifstate_dump(scan, i); 684 } 685 } 686 return; 687} 688 689static boolean_t 690S_is_network_boot() 691{ 692 int mib[2]; 693 size_t len; 694 int netboot = 0; 695 696 mib[0] = CTL_KERN; 697 mib[1] = KERN_NETBOOT; 698 len = sizeof(netboot); 699 sysctl(mib, 2, &netboot, &len, NULL, 0); 700 return (netboot); 701} 702 703#if !TARGET_IPHONE_SIMULATOR 704static __inline__ int 705inet6_dgram_socket() 706{ 707 return (socket(AF_INET6, SOCK_DGRAM, 0)); 708} 709 710#ifdef SIOCDRADD_IN6 711static int 712siocdradd_in6(int s, int if_index, const struct in6_addr * addr, u_char flags) 713{ 714 struct in6_defrouter dr; 715 struct sockaddr_in6 * sin6; 716 717 bzero(&dr, sizeof(dr)); 718 sin6 = &dr.rtaddr; 719 sin6->sin6_len = sizeof(struct sockaddr_in6); 720 sin6->sin6_family = AF_INET6; 721 sin6->sin6_addr = *addr; 722 dr.flags = flags; 723 dr.if_index = if_index; 724 return (ioctl(s, SIOCDRADD_IN6, &dr)); 725} 726 727static int 728siocdrdel_in6(int s, int if_index, const struct in6_addr * addr) 729{ 730 struct in6_defrouter dr; 731 struct sockaddr_in6 * sin6; 732 733 bzero(&dr, sizeof(dr)); 734 sin6 = &dr.rtaddr; 735 sin6->sin6_len = sizeof(struct sockaddr_in6); 736 sin6->sin6_family = AF_INET6; 737 sin6->sin6_addr = *addr; 738 dr.if_index = if_index; 739 return (ioctl(s, SIOCDRDEL_IN6, &dr)); 740} 741#endif /* SIOCDRADD_IN6 */ 742#endif /* !TARGET_IPHONE_SIMULATOR */ 743 744#ifdef RTF_IFSCOPE 745static boolean_t 746S_is_scoped_routing_enabled() 747{ 748 int scopedroute = 0; 749 size_t len = sizeof(scopedroute); 750 751 if ((sysctlbyname("net.inet.ip.scopedroute", 752 &scopedroute, &len, 753 NULL, 0) == -1) 754 && (errno != ENOENT)) { 755 my_log(LOG_ERR, "sysctlbyname() failed: %s", strerror(errno)); 756 } 757 return (scopedroute); 758} 759 760static boolean_t 761S_is_scoped_v6_routing_enabled() 762{ 763 int scopedroute_v6 = 0; 764 size_t len = sizeof(scopedroute_v6); 765 766 if ((sysctlbyname("net.inet6.ip6.scopedroute", 767 &scopedroute_v6, &len, 768 NULL, 0) == -1) 769 && (errno != ENOENT)) { 770 my_log(LOG_ERR, "sysctlbyname() failed: %s", strerror(errno)); 771 } 772 return (scopedroute_v6); 773} 774#endif /* RTF_IFSCOPE */ 775 776static void 777my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new) 778{ 779 CFIndex n = CFArrayGetCount(arr); 780 781 if (CFArrayContainsValue(arr, CFRangeMake(0, n), new)) { 782 return; 783 } 784 CFArrayAppendValue(arr, new); 785 return; 786} 787 788static void 789my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key) 790{ 791 CFIndex i; 792 793 i = CFArrayGetFirstIndexOfValue(arr, 794 CFRangeMake(0, CFArrayGetCount(arr)), 795 key); 796 if (i != kCFNotFound) { 797 CFArrayRemoveValueAtIndex(arr, i); 798 } 799 return; 800} 801 802static void 803my_CFRelease(void * t) 804{ 805 void * * obj = (void * *)t; 806 807 if (obj && *obj) { 808 CFRelease(*obj); 809 *obj = NULL; 810 } 811 return; 812} 813 814static CFDictionaryRef 815my_CFDictionaryGetDictionary(CFDictionaryRef dict, CFStringRef key) 816{ 817 if (isA_CFDictionary(dict) == NULL) { 818 return (NULL); 819 } 820 return (isA_CFDictionary(CFDictionaryGetValue(dict, key))); 821} 822 823static CFArrayRef 824my_CFDictionaryGetArray(CFDictionaryRef dict, CFStringRef key) 825{ 826 if (isA_CFDictionary(dict) == NULL) { 827 return (NULL); 828 } 829 return (isA_CFArray(CFDictionaryGetValue(dict, key))); 830} 831 832static boolean_t 833cfstring_to_ipvx(int family, CFStringRef str, void * addr, int addr_size) 834{ 835 char buf[128]; 836 837 if (isA_CFString(str) == NULL) { 838 goto done; 839 } 840 841 switch (family) { 842 case AF_INET: 843 if (addr_size < sizeof(struct in_addr)) { 844 goto done; 845 } 846 break; 847 case AF_INET6: 848 if (addr_size < sizeof(struct in6_addr)) { 849 goto done; 850 } 851 break; 852 default: 853 goto done; 854 } 855 (void)_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII); 856 if (inet_pton(family, buf, addr) == 1) { 857 return (TRUE); 858 } 859 done: 860 bzero(addr, addr_size); 861 return (FALSE); 862} 863 864__private_extern__ 865boolean_t 866cfstring_to_ip(CFStringRef str, struct in_addr * ip_p) 867{ 868 return (cfstring_to_ipvx(AF_INET, str, ip_p, sizeof(*ip_p))); 869} 870 871__private_extern__ 872boolean_t 873cfstring_to_ip6(CFStringRef str, struct in6_addr * ip6_p) 874{ 875 return (cfstring_to_ipvx(AF_INET6, str, ip6_p, sizeof(*ip6_p))); 876} 877 878static CF_RETURNS_RETAINED CFStringRef 879setup_service_key(CFStringRef serviceID, CFStringRef entity) 880{ 881 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 882 kSCDynamicStoreDomainSetup, 883 serviceID, 884 entity)); 885} 886 887static CF_RETURNS_RETAINED CFStringRef 888state_service_key(CFStringRef serviceID, CFStringRef entity) 889{ 890 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 891 kSCDynamicStoreDomainState, 892 serviceID, 893 entity)); 894} 895 896static CFDictionaryRef 897get_service_setup_entity(CFDictionaryRef services_info, CFStringRef serviceID, 898 CFStringRef entity) 899{ 900 CFStringRef setup_key; 901 CFDictionaryRef setup_dict; 902 903 setup_key = setup_service_key(serviceID, entity); 904 setup_dict = my_CFDictionaryGetDictionary(services_info, setup_key); 905 my_CFRelease(&setup_key); 906 return (setup_dict); 907} 908 909static CFDictionaryRef 910get_service_state_entity(CFDictionaryRef services_info, CFStringRef serviceID, 911 CFStringRef entity) 912{ 913 CFStringRef state_key; 914 CFDictionaryRef state_dict; 915 916 state_key = state_service_key(serviceID, entity); 917 state_dict = my_CFDictionaryGetDictionary(services_info, state_key); 918 my_CFRelease(&state_key); 919 return (state_dict); 920} 921 922static boolean_t 923dict_get_first_ip(CFDictionaryRef dict, CFStringRef prop, struct in_addr * ip_p) 924{ 925 CFArrayRef ip_list; 926 927 ip_list = CFDictionaryGetValue(dict, prop); 928 if (isA_CFArray(ip_list) != NULL 929 && CFArrayGetCount(ip_list) > 0 930 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list, 0), ip_p)) { 931 return (TRUE); 932 } 933 return (FALSE); 934} 935 936static boolean_t 937get_override_primary(CFDictionaryRef dict) 938{ 939 CFTypeRef override; 940 941 override = CFDictionaryGetValue(dict, kSCPropNetOverridePrimary); 942 if (isA_CFNumber(override) != NULL) { 943 int val = 0; 944 945 CFNumberGetValue((CFNumberRef)override, kCFNumberIntType, &val); 946 if (val != 0) { 947 return (TRUE); 948 } 949 } 950 else if (isA_CFBoolean(override) != NULL) { 951 if (CFBooleanGetValue(override)) { 952 return (TRUE); 953 } 954 } 955 return (FALSE); 956} 957 958/** 959 ** IPv4Route* 960 **/ 961 962static __inline__ struct in_addr 963subnet_addr(struct in_addr addr, struct in_addr mask) 964{ 965 struct in_addr net; 966 967 net.s_addr = addr.s_addr & mask.s_addr; 968 return (net); 969} 970 971static __inline__ int 972in_addr_cmp(struct in_addr a, struct in_addr b) 973{ 974 return (uint32_cmp(ntohl(a.s_addr), ntohl(b.s_addr))); 975} 976 977static __inline__ int 978RouteFlagsCompare(RouteFlags a, RouteFlags b) 979{ 980 return (uint32_cmp(a, b)); 981} 982 983static void 984IPv4RouteCopyDescriptionWithString(IPv4RouteRef r, CFMutableStringRef str) 985{ 986 Rank rank_assertion = RANK_ASSERTION_MASK(r->rank); 987 988 CFStringAppendFormat(str, NULL, 989 CFSTR("Dest " IP_FORMAT 990 " Mask " IP_FORMAT 991 " Gate " IP_FORMAT 992 " Ifp %s Ifa " IP_FORMAT), 993 IP_LIST(&r->dest), 994 IP_LIST(&r->mask), 995 IP_LIST(&r->gateway), 996 (r->ifname[0] != '\0') ? r->ifname : "<none>", 997 IP_LIST(&r->ifa)); 998 if ((r->flags & kRouteIsNULLFlag) != 0) { 999 CFStringAppend(str, CFSTR(" [null]")); 1000 } 1001 else { 1002 if ((r->flags & kRouteIsNotSubnetLocalFlag) != 0) { 1003 CFStringAppend(str, CFSTR(" [non-local]")); 1004 } 1005 else if ((r->flags & kRouteIsDirectToInterfaceFlag) != 0) { 1006 CFStringAppend(str, CFSTR(" [direct]")); 1007 } 1008 switch (rank_assertion) { 1009 case kRankAssertionFirst: 1010 CFStringAppend(str, CFSTR(" [first]")); 1011 break; 1012 case kRankAssertionLast: 1013 CFStringAppend(str, CFSTR(" [last]")); 1014 break; 1015 case kRankAssertionNever: 1016 CFStringAppend(str, CFSTR(" [never]")); 1017 break; 1018 default: 1019 break; 1020 } 1021 if ((r->flags & kRouteIsScopedFlag) != 0) { 1022 CFStringAppend(str, CFSTR(" [SCOPED]")); 1023 } 1024 } 1025 return; 1026} 1027 1028static CFStringRef 1029IPv4RouteCopyDescription(IPv4RouteRef r) 1030{ 1031 CFMutableStringRef str; 1032 1033 str = CFStringCreateMutable(NULL, 0); 1034 IPv4RouteCopyDescriptionWithString(r, str); 1035 return (str); 1036} 1037 1038static __inline__ void 1039IPv4RoutePrint(IPv4RouteRef route) 1040{ 1041 CFStringRef str = IPv4RouteCopyDescription(route); 1042 1043 SCPrint(TRUE, stdout, CFSTR("%@\n"), str); 1044 CFRelease(str); 1045 return; 1046} 1047 1048static __inline__ void 1049IPv4RouteLog(int level, IPv4RouteRef route) 1050{ 1051 CFStringRef str = IPv4RouteCopyDescription(route); 1052 1053 my_log(level, "%@", str); 1054 CFRelease(str); 1055 return; 1056} 1057 1058static int 1059IPv4RouteCompare(IPv4RouteRef a, Rank a_rank, 1060 IPv4RouteRef b, Rank b_rank, boolean_t * same_dest) 1061{ 1062 int cmp; 1063 1064 *same_dest = FALSE; 1065 cmp = in_addr_cmp(a->dest, b->dest); 1066 if (cmp == 0) { 1067 cmp = in_addr_cmp(a->mask, b->mask); 1068 if (cmp == 0) { 1069 int name_cmp = strcmp(a->ifname, b->ifname); 1070 1071 if (name_cmp == 0) { 1072 cmp = 0; 1073 } 1074 else { 1075 *same_dest = TRUE; 1076 cmp = RankCompare(a_rank, b_rank); 1077 if (cmp == 0) { 1078 cmp = name_cmp; 1079 } 1080 } 1081 } 1082 } 1083 if ((S_IPMonitor_debug & kDebugFlag8) != 0) { 1084 CFStringRef a_str; 1085 CFStringRef b_str; 1086 char ch; 1087 1088 if (cmp < 0) { 1089 ch = '<'; 1090 } 1091 else if (cmp == 0) { 1092 ch = '='; 1093 } 1094 else { 1095 ch = '>'; 1096 } 1097 a_str = IPv4RouteCopyDescription(a); 1098 b_str = IPv4RouteCopyDescription(b); 1099 my_log(LOG_DEBUG, "%@ rank 0x%x %c %@ rank 0x%x", 1100 a_str, a_rank, ch, b_str, b_rank); 1101 CFRelease(a_str); 1102 CFRelease(b_str); 1103 } 1104 return (cmp); 1105} 1106 1107static CFMutableStringRef 1108IPv4RouteListCopyDescription(IPv4RouteListRef routes) 1109{ 1110 int i; 1111 IPv4RouteRef r; 1112 CFMutableStringRef str; 1113 1114 str = CFStringCreateMutable(NULL, 0); 1115 CFStringAppendFormat(str, NULL, CFSTR("<IPv4RouteList[%d]> = {"), 1116 routes->count); 1117 for (i = 0, r = routes->list; i < routes->count; i++, r++) { 1118 CFStringAppendFormat(str, NULL, CFSTR("\n%2d. "), i); 1119 IPv4RouteCopyDescriptionWithString(r, str); 1120 } 1121 CFStringAppend(str, CFSTR("\n}")); 1122 return (str); 1123} 1124 1125static __inline__ void 1126IPv4RouteListPrint(IPv4RouteListRef routes) 1127{ 1128 CFStringRef str = IPv4RouteListCopyDescription(routes); 1129 1130 SCPrint(TRUE, stdout, CFSTR("%@\n"), str); 1131 CFRelease(str); 1132 return; 1133} 1134 1135static __inline__ void 1136IPv4RouteListLog(int level, IPv4RouteListRef routes) 1137{ 1138 CFStringRef str = IPv4RouteListCopyDescription(routes); 1139 1140 my_log(level, "%@", str); 1141 CFRelease(str); 1142 return; 1143} 1144 1145static __inline__ unsigned int 1146IPv4RouteListComputeSize(unsigned int n) 1147{ 1148 return (offsetof(IPv4RouteList, list[n])); 1149} 1150 1151#if !TARGET_IPHONE_SIMULATOR 1152static IPv4RouteRef 1153IPv4RouteListFindRoute(IPv4RouteListRef routes, IPv4RouteRef route) 1154{ 1155 int i; 1156 IPv4RouteRef scan_result = NULL; 1157 IPv4RouteRef scan; 1158 1159 for (i = 0, scan = routes->list; i < routes->count; i++, scan++) { 1160 if ((scan->dest.s_addr == route->dest.s_addr) 1161 && (scan->mask.s_addr == route->mask.s_addr) 1162 && (strcmp(scan->ifname, route->ifname) == 0) 1163 && (scan->ifa.s_addr == route->ifa.s_addr) 1164 && (scan->gateway.s_addr == route->gateway.s_addr) 1165 && (scan->flags == route->flags)) { 1166 scan_result = scan; 1167 break; 1168 } 1169 } 1170 return (scan_result); 1171} 1172 1173static void 1174IPv4RouteListApply(IPv4RouteListRef old_routes, IPv4RouteListRef new_routes, 1175 IPv4RouteListApplyCallBackFuncPtr func, void * arg) 1176{ 1177 int i; 1178 IPv4RouteRef scan; 1179 1180 if (old_routes == new_routes && old_routes == NULL) { 1181 /* both old and new are NULL, so there's nothing to do */ 1182 return; 1183 } 1184 if (old_routes != NULL) { 1185 for (i = 0, scan = old_routes->list; 1186 i < old_routes->count; 1187 i++, scan++) { 1188 IPv4RouteRef new_route = NULL; 1189 1190 if (new_routes != NULL) { 1191 new_route = IPv4RouteListFindRoute(new_routes, scan); 1192 } 1193 if (new_route == NULL) { 1194 if (func != NULL) { 1195 (*func)(kIPv4RouteListRemoveRouteCommand, scan, arg); 1196 } 1197 } 1198 } 1199 } 1200 if (new_routes != NULL) { 1201 for (i = 0, scan = new_routes->list; 1202 i < new_routes->count; 1203 i++, scan++) { 1204 IPv4RouteRef old_route = NULL; 1205 1206 if (old_routes != NULL) { 1207 old_route = IPv4RouteListFindRoute(old_routes, scan); 1208 } 1209 if (old_route == NULL) { 1210 if (func != NULL) { 1211 (*func)(kIPv4RouteListAddRouteCommand, scan, arg); 1212 } 1213 } 1214 } 1215 } 1216 return; 1217} 1218#endif /* !TARGET_IPHONE_SIMULATOR */ 1219 1220/* 1221 * Function: IPv4RouteListAddRoute 1222 * 1223 * Purpose: 1224 * Add the given IPv4Route to the list of routes, eliminating lower-ranked 1225 * duplicates on the same interface, and marking any lower ranked duplicates 1226 * on other interfaces with kRouteIsScopedFlag. 1227 * 1228 * This routine assumes that if routes is not NULL, it is malloc'd memory. 1229 * 1230 * Returns: 1231 * Route list updated with the given route, possibly a different pointer, 1232 * due to using realloc'd memory. 1233 */ 1234 1235enum { 1236 kScopeNone = 0, 1237 kScopeThis = 1, 1238 kScopeNext = 2 1239}; 1240 1241static IPv4RouteListRef 1242IPv4RouteListAddRoute(IPv4RouteListRef routes, int init_size, 1243 IPv4RouteRef this_route, Rank this_rank) 1244{ 1245 int i; 1246 IPv4RouteRef first_scan = NULL; 1247 int scope_which = kScopeNone; 1248 IPv4RouteRef scan; 1249 int where = -1; 1250 1251 if (routes == NULL) { 1252 routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(init_size)); 1253 bzero(routes, sizeof(*routes)); 1254 routes->size = init_size; 1255 routes->count = 0; 1256 } 1257 for (i = 0, scan = routes->list; i < routes->count; 1258 i++, scan++) { 1259 int cmp; 1260 boolean_t same_dest; 1261 1262 cmp = IPv4RouteCompare(this_route, this_rank, scan, scan->rank, &same_dest); 1263 1264 if (same_dest == TRUE && first_scan == NULL) { 1265 first_scan = scan; 1266 } 1267 1268 if (cmp < 0) { 1269 if (where == -1) { 1270 if (same_dest == TRUE 1271 && (first_scan->flags & kRouteIsScopedFlag) == 0) { 1272 if ((scan->flags & kRouteIsScopedFlag) != 0) { 1273 ROUTELIST_DEBUG(("Hit 1: set scope on self\n"), 1274 kDebugFlag8); 1275 scope_which = kScopeThis; 1276 } 1277 else { 1278 ROUTELIST_DEBUG(("Hit 2: set scope on next\n"), 1279 kDebugFlag8); 1280 scope_which = kScopeNext; 1281 } 1282 } 1283 /* remember our insertion point, but keep going to find a dup */ 1284 where = i; 1285 } 1286 } 1287 else if (cmp == 0) { 1288 /* exact match */ 1289 if (where != -1) { 1290 /* this route is a duplicate */ 1291 ROUTELIST_DEBUG(("Hit 3: removing [%d]\n", i), kDebugFlag8); 1292 routes->count--; 1293 if (i == routes->count) { 1294 /* last slot, decrementing gets rid of it */ 1295 } 1296 else { 1297 bcopy(routes->list + i + 1, 1298 routes->list + i, 1299 sizeof(routes->list[0]) * (routes->count - i)); 1300 } 1301 break; 1302 } 1303 /* resolve conflict using rank */ 1304 if (this_rank < scan->rank) { 1305 boolean_t is_scoped = FALSE; 1306 1307 if (scan->flags & kRouteIsScopedFlag) { 1308 is_scoped = TRUE; 1309 } 1310 ROUTELIST_DEBUG(("Hit 4:replacing [%d] rank 0x%x < 0x%x\n", 1311 i, 1312 this_rank, 1313 scan->rank), kDebugFlag8); 1314 *scan = *this_route; 1315 scan->rank = this_rank; 1316 if (is_scoped) { 1317 /* preserve whether route was scoped */ 1318 ROUTELIST_DEBUG(("Hit 5: preserved scope\n"), kDebugFlag8); 1319 scan->flags |= kRouteIsScopedFlag; 1320 } 1321 } 1322 /* we're done */ 1323 goto done; 1324 } 1325 else { 1326 if (same_dest == TRUE) { 1327 if (scope_which == kScopeNone) { 1328 ROUTELIST_DEBUG(("Hit 10: set scope on self\n"), 1329 kDebugFlag8); 1330 scope_which = kScopeThis; 1331 } 1332 } 1333#ifdef TEST_IPV4_ROUTELIST 1334 else if (where != -1) { 1335 /* not possible because we maintain a sorted list */ 1336 ROUTELIST_DEBUG(("Hit 11: moved past routes - can't happen\n"), 1337 kDebugFlag8); 1338 break; 1339 } 1340#endif /* TEST_IPV4_ROUTELIST */ 1341 } 1342 } 1343 if (routes->size == routes->count) { 1344 int how_many; 1345 IPv4RouteListRef new_routes; 1346 int old_size; 1347 1348 /* double the size */ 1349 old_size = routes->size; 1350 how_many = old_size * 2; 1351 new_routes = (IPv4RouteListRef) 1352 realloc(routes, IPv4RouteListComputeSize(how_many)); 1353 if (new_routes == NULL) { 1354 /* no memory */ 1355 goto done; 1356 } 1357 ROUTELIST_DEBUG(("increasing size from %d to %d\n", old_size, 1358 how_many), kDebugFlag8); 1359 new_routes->size = how_many; 1360 routes = new_routes; 1361 } 1362 if (where == -1) { 1363 /* add it to the end */ 1364 where = routes->count; 1365 } 1366 else { 1367 /* insert it at [where] */ 1368 bcopy(routes->list + where, 1369 routes->list + where + 1, 1370 sizeof(routes->list[0]) * (routes->count - where)); 1371 } 1372 /* copy the route */ 1373 routes->list[where] = *this_route; 1374 routes->list[where].rank = this_rank; 1375 if (RANK_ASSERTION_MASK(this_rank) == kRankAssertionNever) { 1376 routes->list[where].flags |= kRouteIsScopedFlag; 1377 } 1378 1379 /* set the scope */ 1380 switch (scope_which) { 1381 case kScopeThis: 1382 routes->list[where].flags |= kRouteIsScopedFlag; 1383 break; 1384 case kScopeNext: 1385 routes->list[where + 1].flags |= kRouteIsScopedFlag; 1386 break; 1387 default: 1388 case kScopeNone: 1389 break; 1390 } 1391 routes->count++; 1392 done: 1393 return (routes); 1394} 1395 1396/* 1397 * Function: IPv4RouteListAddRouteList 1398 * 1399 * Purpose: 1400 * Invoke IPv4RouteListAddRoute for each route in the given list. 1401 * 1402 * Returns: 1403 * See IPv4RouteListAddRoute for more information. 1404 */ 1405static IPv4RouteListRef 1406IPv4RouteListAddRouteList(IPv4RouteListRef routes, int init_size, 1407 IPv4RouteListRef service_routes, Rank rank) 1408{ 1409 int i; 1410 IPv4RouteRef scan; 1411 1412 for (i = 0, scan = service_routes->list; 1413 i < service_routes->count; i++, scan++) { 1414 routes = IPv4RouteListAddRoute(routes, init_size, scan, rank); 1415 } 1416 return (routes); 1417} 1418 1419static boolean_t 1420plist_get_cstring(CFDictionaryRef dict, CFStringRef prop_name, 1421 char * buf, int buf_size) 1422{ 1423 CFStringRef val; 1424 1425 val = CFDictionaryGetValue(dict, prop_name); 1426 if (isA_CFString(val) == NULL) { 1427 return (FALSE); 1428 } 1429 if (CFStringGetCString(val, buf, buf_size, kCFStringEncodingASCII) 1430 == FALSE) { 1431 return (FALSE); 1432 } 1433 return (TRUE); 1434} 1435 1436/* 1437 * Function: IPv4RouteListCreateWithDictionary 1438 * 1439 * Purpose: 1440 * Given the service ipv4 entity dictionary, generate the list of routes. 1441 * Currently, this includes just the default route and subnet route, 1442 * if the service has a subnet mask. 1443 * 1444 * Returns: 1445 * If the passed in route_list is NULL or too small, this routine 1446 * allocates malloc'd memory to hold the routes. 1447 */ 1448static IPv4RouteListRef 1449IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes, 1450 CFDictionaryRef dict, 1451 CFStringRef primaryRank) 1452{ 1453 struct in_addr addr = { 0 }; 1454 boolean_t exclude_from_nwi = FALSE; 1455 RouteFlags flags = 0; 1456 unsigned int ifindex; 1457 char ifn[IFNAMSIZ]; 1458 struct in_addr mask = { 0 }; 1459 int n = 0; 1460 boolean_t add_default = FALSE; 1461 boolean_t add_subnet = FALSE; 1462 IPv4RouteRef r; 1463 struct in_addr subnet = { 0 }; 1464 struct in_addr router = { 0 }; 1465 Rank rank = kRankAssertionDefault; 1466 1467 if (dict == NULL) { 1468 return (NULL); 1469 } 1470 if (plist_get_cstring(dict, kSCPropInterfaceName, ifn, sizeof(ifn)) 1471 == FALSE) { 1472 return (NULL); 1473 } 1474#ifdef TEST_IPV4_ROUTELIST 1475 ifindex = 0; 1476#else /* TEST_IPV4_ROUTELIST */ 1477 ifindex = if_nametoindex(ifn); 1478 if (ifindex == 0) { 1479 /* interface doesn't exist */ 1480 return (NULL); 1481 } 1482#endif /* TEST_IPV4_ROUTELIST */ 1483 if (cfstring_to_ip(CFDictionaryGetValue(dict, kSCPropNetIPv4Router), 1484 &router) == 0) { 1485 (void)dict_get_first_ip(dict, kSCPropNetIPv4DestAddresses, &router); 1486 } 1487 if (dict_get_first_ip(dict, kSCPropNetIPv4Addresses, &addr) 1488 && dict_get_first_ip(dict, kSCPropNetIPv4SubnetMasks, &mask)) { 1489 /* subnet route */ 1490 subnet = subnet_addr(addr, mask); 1491 /* ignore link-local subnets, let IPConfiguration handle them for now */ 1492 if (ntohl(subnet.s_addr) != IN_LINKLOCALNETNUM) { 1493 add_subnet = TRUE; 1494 n++; 1495 } else if (router.s_addr == 0) { 1496 exclude_from_nwi = TRUE; 1497 } 1498 } 1499 if (addr.s_addr == 0) { 1500 /* thanks for playing */ 1501 return (NULL); 1502 } 1503 if (router.s_addr == 0) { 1504 /* 1505 * If no router is configured, demote the rank. If there's already 1506 * a rank assertion that indicates RankNever, use that, otherwise 1507 * use RankLast. 1508 */ 1509 flags |= kRouteIsDirectToInterfaceFlag; 1510 if (primaryRank != NULL 1511 && PrimaryRankGetRankAssertion(primaryRank) == kRankAssertionNever) { 1512 rank = kRankAssertionNever; 1513 } 1514 else { 1515 rank = kRankAssertionLast; 1516 } 1517 } 1518 else { 1519 /* 1520 * If the router address is our address and the subnet mask is 1521 * not 255.255.255.255, assume all routes are local to the interface. 1522 */ 1523 if (addr.s_addr == router.s_addr 1524 && mask.s_addr != INADDR_BROADCAST) { 1525 flags |= kRouteIsDirectToInterfaceFlag; 1526 } 1527 if (primaryRank != NULL) { 1528 rank = PrimaryRankGetRankAssertion(primaryRank); 1529 } else if (get_override_primary(dict)) { 1530 rank = kRankAssertionFirst; 1531 } 1532 } 1533 1534 if (S_dict_get_boolean(dict, kIsNULL, FALSE)) { 1535 exclude_from_nwi = TRUE; 1536 flags |= kRouteIsNULLFlag; 1537 } 1538 1539 if (rank == kRankAssertionNever) { 1540 flags |= kRouteIsScopedFlag; 1541 } 1542 1543 if (add_subnet && (flags & kRouteIsDirectToInterfaceFlag) == 0 1544 && subnet.s_addr != subnet_addr(router, mask).s_addr) { 1545 flags |= kRouteIsNotSubnetLocalFlag; 1546 } 1547 1548 if (strncmp(ifn, "lo0", sizeof(ifn)) != 0) { 1549 add_default = TRUE; 1550 n++; 1551 } 1552 1553 if (routes == NULL || routes->size < n) { 1554 routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(n)); 1555 routes->size = n; 1556 } 1557 bzero(routes, IPv4RouteListComputeSize(n)); 1558 routes->count = n; 1559 routes->exclude_from_nwi = exclude_from_nwi; 1560 1561 /* start at the beginning */ 1562 r = routes->list; 1563 1564 if (add_default) { 1565 /* add the default route */ 1566 r->ifindex = ifindex; 1567 strlcpy(r->ifname, ifn, sizeof(r->ifname)); 1568 r->ifa = addr; 1569 r->flags = flags; 1570 if ((flags & kRouteIsDirectToInterfaceFlag) == 0) { 1571 r->gateway = router; 1572 } 1573 else { 1574 r->gateway = addr; 1575 } 1576 r->rank = rank; 1577 r++; 1578 } 1579 1580 /* add the subnet route */ 1581 if (add_subnet) { 1582 if ((flags & kRouteIsNULLFlag) != 0) { 1583 r->flags |= kRouteIsNULLFlag; 1584 } 1585 r->ifindex = ifindex; 1586 r->gateway = addr; 1587 r->dest = subnet; 1588 r->mask = mask; 1589 strlcpy(r->ifname, ifn, sizeof(r->ifname)); 1590 r->ifa = addr; 1591 r->rank = rank; 1592 } 1593 return (routes); 1594} 1595 1596/* 1597 * Function: parse_component 1598 * Purpose: 1599 * Given a string 'key' and a string prefix 'prefix', 1600 * return the next component in the slash '/' separated 1601 * key. 1602 * 1603 * Examples: 1604 * 1. key = "a/b/c" prefix = "a/" 1605 * returns "b" 1606 * 2. key = "a/b/c" prefix = "a/b/" 1607 * returns "c" 1608 */ 1609static CF_RETURNS_RETAINED CFStringRef 1610parse_component(CFStringRef key, CFStringRef prefix) 1611{ 1612 CFMutableStringRef comp; 1613 CFRange range; 1614 1615 if (CFStringHasPrefix(key, prefix) == FALSE) { 1616 return (NULL); 1617 } 1618 comp = CFStringCreateMutableCopy(NULL, 0, key); 1619 if (comp == NULL) { 1620 return (NULL); 1621 } 1622 CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix))); 1623 range = CFStringFind(comp, CFSTR("/"), 0); 1624 if (range.location == kCFNotFound) { 1625 return (comp); 1626 } 1627 range.length = CFStringGetLength(comp) - range.location; 1628 CFStringDelete(comp, range); 1629 return (comp); 1630} 1631 1632static CFMutableDictionaryRef 1633service_dict_copy(CFStringRef serviceID) 1634{ 1635 CFDictionaryRef d = NULL; 1636 CFMutableDictionaryRef service_dict; 1637 1638 /* create a modifyable dictionary, a copy or a new one */ 1639 d = CFDictionaryGetValue(S_service_state_dict, serviceID); 1640 if (d == NULL) { 1641 service_dict 1642 = CFDictionaryCreateMutable(NULL, 0, 1643 &kCFTypeDictionaryKeyCallBacks, 1644 &kCFTypeDictionaryValueCallBacks); 1645 } 1646 else { 1647 service_dict = CFDictionaryCreateMutableCopy(NULL, 0, d); 1648 } 1649 return (service_dict); 1650} 1651 1652static void 1653log_service_entity(int level, CFStringRef serviceID, CFStringRef entity, 1654 CFStringRef operation, CFTypeRef val) 1655{ 1656 1657 CFDataRef route_list; 1658 CFMutableStringRef this_val = NULL; 1659 1660 if (CFEqual(entity, kSCEntNetIPv4) && isA_CFDictionary(val) != NULL) { 1661 CFDictionaryRef service_dict = NULL; 1662 1663 route_list = CFDictionaryGetValue(val, kIPv4DictRoutes); 1664 if (route_list != NULL) { 1665 /* ALIGN: CF should align to at least 8-byte boundaries */ 1666 this_val = IPv4RouteListCopyDescription((IPv4RouteListRef) 1667 (void *)CFDataGetBytePtr(route_list)); 1668 } 1669 1670 service_dict = CFDictionaryGetValue(val, kIPv4DictService); 1671 1672 if (service_dict != NULL && isA_CFDictionary(service_dict) != NULL) { 1673 if (this_val == NULL) { 1674 this_val = CFStringCreateMutable(NULL, 0); 1675 } 1676 CFStringAppendFormat(this_val, NULL, CFSTR("\n <IPv4Dictionary>: %@"), service_dict); 1677 } 1678 val = this_val; 1679 } 1680 if (val == NULL) { 1681 val = CFSTR("<none>"); 1682 } 1683 my_log(level, "IPMonitor: serviceID %@ %@ %@ value = %@", 1684 serviceID, operation, entity, val); 1685 my_CFRelease(&this_val); 1686 return; 1687} 1688 1689static boolean_t 1690service_dict_set(CFStringRef serviceID, CFStringRef entity, 1691 CFTypeRef new_val) 1692{ 1693 boolean_t changed = FALSE; 1694 CFTypeRef old_val; 1695 CFMutableDictionaryRef service_dict; 1696 1697 service_dict = service_dict_copy(serviceID); 1698 old_val = CFDictionaryGetValue(service_dict, entity); 1699 if (new_val == NULL) { 1700 if (old_val != NULL) { 1701 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 1702 log_service_entity(LOG_DEBUG, serviceID, entity, 1703 CFSTR("Removed:"), old_val); 1704 } 1705 CFDictionaryRemoveValue(service_dict, entity); 1706 changed = TRUE; 1707 } 1708 } 1709 else { 1710 if (old_val == NULL || CFEqual(new_val, old_val) == FALSE) { 1711 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 1712 log_service_entity(LOG_DEBUG, serviceID, entity, 1713 CFSTR("Changed: old"), old_val); 1714 log_service_entity(LOG_DEBUG, serviceID, entity, 1715 CFSTR("Changed: new"), new_val); 1716 } 1717 CFDictionarySetValue(service_dict, entity, new_val); 1718 changed = TRUE; 1719 } 1720 } 1721 if (CFDictionaryGetCount(service_dict) == 0) { 1722 CFDictionaryRemoveValue(S_service_state_dict, serviceID); 1723 } 1724 else { 1725 CFDictionarySetValue(S_service_state_dict, serviceID, service_dict); 1726 } 1727 my_CFRelease(&service_dict); 1728 return (changed); 1729} 1730 1731static CFDictionaryRef 1732service_dict_get(CFStringRef serviceID, CFStringRef entity) 1733{ 1734 CFDictionaryRef service_dict; 1735 1736 service_dict = CFDictionaryGetValue(S_service_state_dict, serviceID); 1737 if (service_dict == NULL) { 1738 return (NULL); 1739 } 1740 return (CFDictionaryGetValue(service_dict, entity)); 1741} 1742 1743#ifndef kSCPropNetHostname 1744#define kSCPropNetHostname CFSTR("Hostname") 1745#endif 1746 1747__private_extern__ 1748CFStringRef 1749copy_dhcp_hostname(CFStringRef serviceID) 1750{ 1751 CFDictionaryRef dict = NULL; 1752 CFStringRef hostname = NULL; 1753 CFDictionaryRef service_dict = NULL; 1754 1755 dict = service_dict_get(serviceID, kSCEntNetIPv4); 1756 1757 if (dict == NULL || isA_CFDictionary(dict) == NULL) { 1758 return (NULL); 1759 } 1760 1761 service_dict = 1762 CFDictionaryGetValue(dict, kIPv4DictService); 1763 1764 if (service_dict == NULL 1765 || isA_CFDictionary(service_dict) == NULL) { 1766 return (NULL); 1767 } 1768 1769 hostname = 1770 CFDictionaryGetValue(service_dict, kSCPropNetHostname); 1771 1772 if (hostname != NULL) { 1773 CFRetain(hostname); 1774 } 1775 1776 return (hostname); 1777} 1778 1779#if !TARGET_IPHONE_SIMULATOR 1780static void 1781ipv6_service_update_router(CFStringRef serviceID, CFDictionaryRef new_val) 1782{ 1783#ifdef SIOCDRADD_IN6 1784 int if_index; 1785 char ifn[IFNAMSIZ]; 1786 CFStringRef new_router = NULL; 1787 char ntopbuf[INET6_ADDRSTRLEN]; 1788 CFDictionaryRef old_val = NULL; 1789 CFStringRef old_router = NULL; 1790 struct in6_addr router_ip; 1791 int s = -1; 1792 1793 ifn[0] = '\0'; 1794 old_val = service_dict_get(serviceID, kSCEntNetIPv6); 1795 if (old_val != NULL) { 1796 plist_get_cstring(old_val, kSCPropInterfaceName, ifn, sizeof(ifn)); 1797 old_router = CFDictionaryGetValue(old_val, kSCPropNetIPv6Router); 1798 } 1799 if (ifn[0] == '\0') { 1800 if (new_val == NULL 1801 || plist_get_cstring(new_val, kSCPropInterfaceName, 1802 ifn, sizeof(ifn)) == FALSE) { 1803 /* no InterfaceName property, ignore it */ 1804 goto done; 1805 } 1806 } 1807 if_index = if_nametoindex(ifn); 1808 if (if_index == 0) { 1809 goto done; 1810 } 1811 s = inet6_dgram_socket(); 1812 if (s < 0) { 1813 my_log(LOG_ERR, 1814 "IPMonitor: ipv6_service_update_router: socket failed, %s", 1815 strerror(errno)); 1816 goto done; 1817 } 1818 if (new_val != NULL) { 1819 new_router = CFDictionaryGetValue(new_val, kSCPropNetIPv6Router); 1820 } 1821 if (S_dict_get_boolean(old_val, kIsNULL, FALSE) == FALSE 1822 && old_router != NULL 1823 && (new_router == NULL || CFEqual(old_router, new_router) == FALSE)) { 1824 /* remove the old Router */ 1825 if (cfstring_to_ip6(old_router, &router_ip)) { 1826 if (IN6_IS_ADDR_LINKLOCAL(&router_ip) || 1827 IN6_IS_ADDR_MC_LINKLOCAL(&router_ip)) { 1828 /* scope it */ 1829 router_ip.__u6_addr.__u6_addr16[1] = htons(if_index); 1830 } 1831 if (siocdrdel_in6(s, if_index, &router_ip) < 0) { 1832 if (errno != EINVAL) { 1833 my_log(LOG_ERR, 1834 "IPMonitor: siocdrdel_in6(%s, %s) failed, %s", 1835 ifn, 1836 inet_ntop(AF_INET6, &router_ip, 1837 ntopbuf, sizeof(ntopbuf)), 1838 strerror(errno)); 1839 } 1840 } 1841 else if (S_IPMonitor_debug & kDebugFlag1) { 1842 my_log(LOG_DEBUG, 1843 "IPMonitor: %s removed default route %s", 1844 ifn, 1845 inet_ntop(AF_INET6, &router_ip, 1846 ntopbuf, sizeof(ntopbuf))); 1847 } 1848 } 1849 } 1850 /* add the new Router */ 1851 if (S_dict_get_boolean(new_val, kIsNULL, FALSE) == FALSE 1852 && cfstring_to_ip6(new_router, &router_ip)) { 1853 if (IN6_IS_ADDR_LINKLOCAL(&router_ip) || 1854 IN6_IS_ADDR_MC_LINKLOCAL(&router_ip)) { 1855 /* scope it */ 1856 router_ip.__u6_addr.__u6_addr16[1] = htons(if_index); 1857 } 1858 if (siocdradd_in6(s, if_index, &router_ip, 0) < 0) { 1859 if (errno != EINVAL) { 1860 my_log(LOG_ERR, 1861 "IPMonitor: siocdradd_in6(%s, %s) failed, %s", 1862 ifn, 1863 inet_ntop(AF_INET6, &router_ip, 1864 ntopbuf, sizeof(ntopbuf)), 1865 strerror(errno)); 1866 } 1867 } 1868 else if (S_IPMonitor_debug & kDebugFlag1) { 1869 my_log(LOG_DEBUG, 1870 "IPMonitor: %s added default route %s", 1871 ifn, 1872 inet_ntop(AF_INET6, &router_ip, 1873 ntopbuf, sizeof(ntopbuf))); 1874 } 1875 } 1876 close(s); 1877 1878 done: 1879#endif /* SIOCDRADD_IN6 */ 1880 return; 1881} 1882#endif /* !TARGET_IPHONE_SIMULATOR */ 1883 1884#define ALLOW_EMPTY_STRING 0x1 1885 1886static CF_RETURNS_RETAINED CFTypeRef 1887sanitize_prop(CFTypeRef val, uint32_t flags) 1888{ 1889 if (val != NULL) { 1890 if (isA_CFString(val)) { 1891 CFMutableStringRef str; 1892 1893 str = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)val); 1894 CFStringTrimWhitespace(str); 1895 if (!(flags & ALLOW_EMPTY_STRING) && (CFStringGetLength(str) == 0)) { 1896 CFRelease(str); 1897 str = NULL; 1898 } 1899 val = str; 1900 } else { 1901 CFRetain(val); 1902 } 1903 } 1904 1905 return val; 1906} 1907 1908static void 1909merge_array_prop(CFMutableDictionaryRef dict, 1910 CFStringRef key, 1911 CFDictionaryRef state_dict, 1912 CFDictionaryRef setup_dict, 1913 uint32_t flags, 1914 Boolean append) 1915{ 1916 CFMutableArrayRef merge_prop; 1917 CFArrayRef setup_prop = NULL; 1918 CFArrayRef state_prop = NULL; 1919 1920 if (setup_dict != NULL) { 1921 setup_prop = isA_CFArray(CFDictionaryGetValue(setup_dict, key)); 1922 } 1923 if (state_dict != NULL) { 1924 state_prop = isA_CFArray(CFDictionaryGetValue(state_dict, key)); 1925 } 1926 1927 if ((setup_prop == NULL) && (state_prop == NULL)) { 1928 return; 1929 } 1930 1931 merge_prop = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1932 if (setup_prop != NULL) { 1933 CFIndex i; 1934 CFIndex n; 1935 1936 n = CFArrayGetCount(setup_prop); 1937 for (i = 0; i < n; i++) { 1938 CFTypeRef val; 1939 1940 val = CFArrayGetValueAtIndex(setup_prop, i); 1941 val = sanitize_prop(val, flags); 1942 if (val != NULL) { 1943 CFArrayAppendValue(merge_prop, val); 1944 CFRelease(val); 1945 } 1946 } 1947 } 1948 if (state_prop != NULL 1949 && (setup_prop == NULL || S_append_state)) { 1950 CFIndex i; 1951 CFIndex n; 1952 CFRange setup_range = CFRangeMake(0, CFArrayGetCount(merge_prop)); 1953 1954 n = CFArrayGetCount(state_prop); 1955 for (i = 0; i < n; i++) { 1956 CFTypeRef val; 1957 1958 val = CFArrayGetValueAtIndex(state_prop, i); 1959 val = sanitize_prop(val, flags); 1960 if (val != NULL) { 1961 if (append || !CFArrayContainsValue(merge_prop, setup_range, val)) { 1962 CFArrayAppendValue(merge_prop, val); 1963 } 1964 CFRelease(val); 1965 } 1966 } 1967 } 1968 if (CFArrayGetCount(merge_prop) > 0) { 1969 CFDictionarySetValue(dict, key, merge_prop); 1970 } 1971 CFRelease(merge_prop); 1972 return; 1973} 1974 1975static void 1976pick_prop(CFMutableDictionaryRef dict, 1977 CFStringRef key, 1978 CFDictionaryRef state_dict, 1979 CFDictionaryRef setup_dict, 1980 uint32_t flags) 1981{ 1982 CFTypeRef val = NULL; 1983 1984 if (setup_dict != NULL) { 1985 val = CFDictionaryGetValue(setup_dict, key); 1986 val = sanitize_prop(val, flags); 1987 } 1988 if (val == NULL && state_dict != NULL) { 1989 val = CFDictionaryGetValue(state_dict, key); 1990 val = sanitize_prop(val, flags); 1991 } 1992 if (val != NULL) { 1993 CFDictionarySetValue(dict, key, val); 1994 CFRelease(val); 1995 } 1996 1997 return; 1998} 1999 2000/** 2001 ** GetEntityChangesFunc functions 2002 **/ 2003static boolean_t 2004get_ipv4_changes(CFStringRef serviceID, CFDictionaryRef state_dict, 2005 CFDictionaryRef setup_dict, CFDictionaryRef info) 2006{ 2007 CFDictionaryRef aggregated_dict = NULL; 2008 boolean_t changed = FALSE; 2009 CFMutableDictionaryRef dict = NULL; 2010 CFStringRef primaryRank = NULL; 2011 IPv4RouteListRef r; 2012#define R_STATIC 3 2013 IPv4RouteListRef routes; 2014 /* ALIGN: force align */ 2015 uint32_t routes_buf[roundup(IPv4RouteListComputeSize(R_STATIC), sizeof(uint32_t))]; 2016 CFDataRef routes_data = NULL; 2017 CFDictionaryRef service_options; 2018 2019 if (state_dict == NULL) { 2020 goto done; 2021 } 2022 service_options = service_dict_get(serviceID, kSCEntNetService); 2023 if (service_options != NULL) { 2024 primaryRank = CFDictionaryGetValue(service_options, kSCPropNetServicePrimaryRank); 2025 } 2026 dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict); 2027 if (setup_dict != NULL) { 2028 CFStringRef router; 2029 struct in_addr router_ip; 2030 2031 router = CFDictionaryGetValue(setup_dict, 2032 kSCPropNetIPv4Router); 2033 if (router != NULL 2034 && cfstring_to_ip(router, &router_ip)) { 2035 CFDictionarySetValue(dict, 2036 kSCPropNetIPv4Router, 2037 router); 2038 } 2039 } 2040 routes = (IPv4RouteListRef)(void *)routes_buf; 2041 routes->size = R_STATIC; 2042 routes->count = 0; 2043 r = IPv4RouteListCreateWithDictionary(routes, dict, primaryRank); 2044 if (r != NULL) { 2045 routes_data = CFDataCreate(NULL, 2046 (const void *)r, 2047 IPv4RouteListComputeSize(r->count)); 2048 if (r != routes) { 2049 free(r); 2050 } 2051 } 2052 else { 2053 my_log(LOG_NOTICE, 2054 "IPMonitor: %@ invalid IPv4 dictionary = %@", 2055 serviceID, 2056 dict); 2057 } 2058 done: 2059 if (routes_data != NULL) { 2060 CFStringRef keys[2]; 2061 CFTypeRef values[2]; 2062 2063 keys[0] = kIPv4DictService; 2064 values[0] = dict; 2065 keys[1] = kIPv4DictRoutes; 2066 values[1] = routes_data; 2067 2068 aggregated_dict = CFDictionaryCreate(NULL, 2069 (const void**)keys, 2070 values, 2071 sizeof(keys)/sizeof(keys[0]), 2072 &kCFTypeDictionaryKeyCallBacks, 2073 &kCFTypeDictionaryValueCallBacks); 2074 2075 } 2076 changed = service_dict_set(serviceID, kSCEntNetIPv4, aggregated_dict); 2077 if (routes_data == NULL) { 2078 /* clean up the rank too */ 2079 CFDictionaryRemoveValue(S_ipv4_service_rank_dict, serviceID); 2080 } 2081 my_CFRelease(&dict); 2082 my_CFRelease(&aggregated_dict); 2083 my_CFRelease(&routes_data); 2084 return (changed); 2085} 2086 2087static boolean_t 2088get_ipv6_changes(CFStringRef serviceID, CFDictionaryRef state_dict, 2089 CFDictionaryRef setup_dict, CFDictionaryRef info) 2090{ 2091 struct in6_addr addr; 2092 CFArrayRef addrs; 2093 boolean_t changed = FALSE; 2094 CFMutableDictionaryRef dict = NULL; 2095 CFDictionaryRef new_dict = NULL; 2096 CFStringRef router = NULL; 2097 struct in6_addr router_ip; 2098 boolean_t valid_ip = FALSE; 2099 2100 if (state_dict == NULL) { 2101 goto done; 2102 } 2103 addrs = isA_CFArray(CFDictionaryGetValue(state_dict, 2104 kSCPropNetIPv6Addresses)); 2105 if (addrs != NULL && CFArrayGetCount(addrs) > 0) { 2106 valid_ip = cfstring_to_ip6(CFArrayGetValueAtIndex(addrs, 0), &addr); 2107 } 2108 if (valid_ip == FALSE) { 2109 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 2110 my_log(LOG_DEBUG, 2111 "IPMonitor: %@ has no valid IPv6 address, ignoring", 2112 serviceID); 2113 } 2114 goto done; 2115 } 2116 dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict); 2117 if (setup_dict != NULL) { 2118 router = CFDictionaryGetValue(setup_dict, 2119 kSCPropNetIPv6Router); 2120 if (router != NULL && cfstring_to_ip6(router, &router_ip)) { 2121 CFDictionarySetValue(dict, 2122 kSCPropNetIPv6Router, 2123 router); 2124 } 2125 } 2126 else { 2127 router = CFDictionaryGetValue(dict, 2128 kSCPropNetIPv6Router); 2129 if (router != NULL 2130 && cfstring_to_ip6(router, &router_ip) == FALSE) { 2131 CFDictionaryRemoveValue(dict, kSCPropNetIPv6Router); 2132 } 2133 } 2134 new_dict = dict; 2135 2136 done: 2137 2138#if !TARGET_IPHONE_SIMULATOR 2139 ipv6_service_update_router(serviceID, new_dict); 2140#endif /* !TARGET_IPHONE_SIMULATOR */ 2141 2142 changed = service_dict_set(serviceID, kSCEntNetIPv6, new_dict); 2143 if (new_dict == NULL) { 2144 /* clean up the rank too */ 2145 CFDictionaryRemoveValue(S_ipv6_service_rank_dict, serviceID); 2146 } 2147 my_CFRelease(&new_dict); 2148 return (changed); 2149} 2150 2151static void 2152accumulate_dns_servers(CFArrayRef in_servers, ProtocolFlags active_protos, 2153 CFMutableArrayRef out_servers, CFStringRef interface) 2154{ 2155 int count; 2156 int i; 2157 2158 count = CFArrayGetCount(in_servers); 2159 for (i = 0; i < count; i++) { 2160 CFStringRef addr; 2161 struct in6_addr ipv6_addr; 2162 struct in_addr ip_addr; 2163 2164 addr = CFArrayGetValueAtIndex(in_servers, i); 2165 assert(addr != NULL); 2166 2167 if (cfstring_to_ip(addr, &ip_addr)) { 2168 /* IPv4 address */ 2169 if ((active_protos & kProtocolFlagsIPv4) == 0 2170 && ntohl(ip_addr.s_addr) != INADDR_LOOPBACK) { 2171 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 2172 my_log(LOG_DEBUG, 2173 "IPMonitor: no IPv4 connectivity, " 2174 "ignoring DNS server address ", IP_FORMAT, 2175 IP_LIST(&ip_addr)); 2176 } 2177 continue; 2178 } 2179 2180 CFRetain(addr); 2181 } 2182 else if (cfstring_to_ip6(addr, &ipv6_addr)) { 2183 /* IPv6 address */ 2184 if ((active_protos & kProtocolFlagsIPv6) == 0 2185 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr)) { 2186 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 2187 char ntopbuf[INET6_ADDRSTRLEN]; 2188 2189 my_log(LOG_DEBUG, 2190 "IPMonitor: no IPv6 connectivity, " 2191 "ignoring DNS server address %s", 2192 inet_ntop(AF_INET6, &ipv6_addr, 2193 ntopbuf, sizeof(ntopbuf))); 2194 } 2195 continue; 2196 } 2197 2198 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr) || 2199 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr)) 2200 && (interface != NULL) 2201 && (CFStringFind(addr, CFSTR("%"), 0).location == kCFNotFound)) { 2202 // append interface name to IPv6 link local address 2203 addr = CFStringCreateWithFormat(NULL, NULL, 2204 CFSTR("%@%%%@"), 2205 addr, 2206 interface); 2207 } else { 2208 CFRetain(addr); 2209 } 2210 } 2211 else { 2212 /* bad IP address */ 2213 my_log(LOG_NOTICE, 2214 "IPMonitor: ignoring bad DNS server address '%@'", 2215 addr); 2216 continue; 2217 } 2218 2219 /* DNS server is valid and one we want */ 2220 CFArrayAppendValue(out_servers, addr); 2221 CFRelease(addr); 2222 } 2223 return; 2224} 2225 2226static void 2227merge_dns_servers(CFMutableDictionaryRef new_dict, 2228 CFArrayRef state_servers, 2229 CFArrayRef setup_servers, 2230 Boolean have_setup, 2231 ProtocolFlags active_protos, 2232 CFStringRef interface) 2233{ 2234 CFMutableArrayRef dns_servers; 2235 Boolean have_dns_setup = FALSE; 2236 2237 if (state_servers == NULL && setup_servers == NULL) { 2238 /* no DNS servers */ 2239 return; 2240 } 2241 dns_servers = CFArrayCreateMutable(NULL, 0, 2242 &kCFTypeArrayCallBacks); 2243 if (setup_servers != NULL) { 2244 accumulate_dns_servers(setup_servers, active_protos, 2245 dns_servers, interface); 2246 if (CFArrayGetCount(dns_servers) > 0) { 2247 have_dns_setup = TRUE; 2248 } 2249 } 2250 if ((CFArrayGetCount(dns_servers) == 0 || S_append_state) 2251 && state_servers != NULL) { 2252 accumulate_dns_servers(state_servers, active_protos, 2253 dns_servers, NULL); 2254 } 2255 2256 /* 2257 * Here, we determine whether or not we want all queries for this DNS 2258 * configuration to be bound to the associated network interface. 2259 * 2260 * For dynamically derived network configurations (i.e. from State:) 2261 * this would be the preferred option using the argument "Hey, the 2262 * server told us to use these servers on this network so let's not 2263 * argue". 2264 * 2265 * But, when a DNS configuration has been provided by the user/admin 2266 * via the Network pref pane (i.e. from Setup:) we opt to not force 2267 * binding of the outbound queries. The simplest example why we take 2268 * this stance is with a multi-homing configuration. Consider a system 2269 * with one network service associated with "en0" and a second service 2270 * associated with "en1". The "en0" service has been set higher in 2271 * the network service order so it would be primary but the user/admin 2272 * wants the DNS queries to go to a server only accessible via "en1". 2273 * Without this exception we would take the DNS server addresses from 2274 * the Network pref pane (for "en0") and have the queries bound to 2275 * "en0" where they'd never reach their intended destination (via 2276 * "en1"). So, our exception to the rule is that we will not bind 2277 * user/admin configurations to any specific network interface. 2278 * 2279 * We also add an exception to the "follow the dynamically derived 2280 * network configuration" path for on-the-fly (no Setup: content) 2281 * network services. 2282 */ 2283 if (CFArrayGetCount(dns_servers) != 0) { 2284 CFDictionarySetValue(new_dict, 2285 kSCPropNetDNSServerAddresses, dns_servers); 2286 if (have_setup && !have_dns_setup) { 2287 CFDictionarySetValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY, kCFBooleanTrue); 2288 } 2289 } 2290 2291 my_CFRelease(&dns_servers); 2292 return; 2293} 2294 2295 2296static boolean_t 2297get_dns_changes(CFStringRef serviceID, CFDictionaryRef state_dict, 2298 CFDictionaryRef setup_dict, CFDictionaryRef info) 2299{ 2300 ProtocolFlags active_protos = kProtocolFlagsNone; 2301 boolean_t changed = FALSE; 2302 CFStringRef domain; 2303 Boolean have_setup = FALSE; 2304 CFStringRef interface = NULL; 2305 CFDictionaryRef ipv4; 2306 CFDictionaryRef ipv6; 2307 int i; 2308 const struct { 2309 CFStringRef key; 2310 uint32_t flags; 2311 Boolean append; 2312 } merge_list[] = { 2313 { kSCPropNetDNSSearchDomains, 0, FALSE }, 2314 { kSCPropNetDNSSortList, 0, FALSE }, 2315 { kSCPropNetDNSSupplementalMatchDomains, ALLOW_EMPTY_STRING, TRUE }, 2316 { kSCPropNetDNSSupplementalMatchOrders, 0, TRUE }, 2317 }; 2318 CFMutableDictionaryRef new_dict = NULL; 2319 const CFStringRef pick_list[] = { 2320 kSCPropNetDNSDomainName, 2321 kSCPropNetDNSOptions, 2322 kSCPropNetDNSSearchOrder, 2323 kSCPropNetDNSServerPort, 2324 kSCPropNetDNSServerTimeout, 2325 kSCPropNetDNSServiceIdentifier, 2326 kSCPropNetDNSSupplementalMatchDomainsNoSearch, 2327 }; 2328 IPv4RouteListRef routes = NULL; 2329 2330 if ((state_dict == NULL) && (setup_dict == NULL)) { 2331 /* there is no DNS content */ 2332 goto done; 2333 } 2334 2335 ipv4 = service_dict_get(serviceID, kSCEntNetIPv4); 2336 routes = ipv4_dict_get_routelist(ipv4); 2337 2338 if (routes != NULL) { 2339 if (get_service_setup_entity(info, serviceID, kSCEntNetIPv4) != NULL) { 2340 have_setup = TRUE; 2341 } 2342 2343 active_protos |= kProtocolFlagsIPv4; 2344 2345 interface = ipv4_dict_get_ifname(ipv4); 2346 } 2347 2348 ipv6 = service_dict_get(serviceID, kSCEntNetIPv6); 2349 if (ipv6 != NULL) { 2350 if (!have_setup && 2351 get_service_setup_entity(info, serviceID, kSCEntNetIPv6) != NULL) { 2352 have_setup = TRUE; 2353 } 2354 2355 active_protos |= kProtocolFlagsIPv6; 2356 2357 if (interface == NULL) { 2358 interface = CFDictionaryGetValue(ipv6, 2359 kSCPropInterfaceName); 2360 } 2361 } 2362 2363 2364 if (active_protos == kProtocolFlagsNone) { 2365 /* there is no IPv4 nor IPv6 */ 2366 if (state_dict == NULL) { 2367 /* ... and no DNS content that we care about */ 2368 goto done; 2369 } 2370 setup_dict = NULL; 2371 } 2372 2373 /* merge DNS configuration */ 2374 new_dict = CFDictionaryCreateMutable(NULL, 0, 2375 &kCFTypeDictionaryKeyCallBacks, 2376 &kCFTypeDictionaryValueCallBacks); 2377 2378 if (active_protos == kProtocolFlagsNone) { 2379 merge_dns_servers(new_dict, 2380 my_CFDictionaryGetArray(state_dict, 2381 kSCPropNetDNSServerAddresses), 2382 NULL, 2383 FALSE, 2384 kProtocolFlagsIPv4 | kProtocolFlagsIPv6, 2385 NULL); 2386 } 2387 else { 2388 merge_dns_servers(new_dict, 2389 my_CFDictionaryGetArray(state_dict, 2390 kSCPropNetDNSServerAddresses), 2391 my_CFDictionaryGetArray(setup_dict, 2392 kSCPropNetDNSServerAddresses), 2393 have_setup, 2394 active_protos, 2395 interface); 2396 } 2397 2398 for (i = 0; i < sizeof(merge_list)/sizeof(merge_list[0]); i++) { 2399 merge_array_prop(new_dict, 2400 merge_list[i].key, 2401 state_dict, 2402 setup_dict, 2403 merge_list[i].flags, 2404 merge_list[i].append); 2405 } 2406 2407 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) { 2408 pick_prop(new_dict, 2409 pick_list[i], 2410 state_dict, 2411 setup_dict, 2412 0); 2413 } 2414 2415 if (active_protos == kProtocolFlagsNone) { 2416 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */ 2417 if (CFDictionaryContainsKey(new_dict, 2418 kSCPropNetDNSSupplementalMatchDomains)) { 2419 /* only keep State: supplemental */ 2420 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSDomainName); 2421 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchDomains); 2422 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchOrder); 2423 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSortList); 2424 2425 if ((interface == NULL) && (setup_dict == NULL) && (state_dict != NULL)) { 2426 /* 2427 * for supplemental-only configurations, add any scoped (or 2428 * wild-card "*") interface 2429 */ 2430 interface = CFDictionaryGetValue(state_dict, kSCPropInterfaceName); 2431 } 2432 } else if (CFDictionaryContainsKey(new_dict, kSCPropNetDNSServiceIdentifier) && 2433 (interface == NULL) && 2434 (state_dict != NULL)) { 2435 interface = CFDictionaryGetValue(state_dict, kSCPropInterfaceName); 2436 } else { 2437 goto done; 2438 } 2439 } 2440 2441 if (CFDictionaryGetCount(new_dict) == 0) { 2442 my_CFRelease(&new_dict); 2443 goto done; 2444 } 2445 2446 if (interface != NULL) { 2447 CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface); 2448 } 2449 2450 if (S_append_state) { 2451 /* 2452 * ensure any specified domain name (e.g. the domain returned by 2453 * a DHCP server) is in the search list. 2454 */ 2455 domain = CFDictionaryGetValue(new_dict, kSCPropNetDNSDomainName); 2456 if (isA_CFString(domain)) { 2457 CFArrayRef search; 2458 2459 search = CFDictionaryGetValue(new_dict, kSCPropNetDNSSearchDomains); 2460 if (isA_CFArray(search) && 2461 !CFArrayContainsValue(search, CFRangeMake(0, CFArrayGetCount(search)), domain)) { 2462 CFMutableArrayRef new_search; 2463 2464 new_search = CFArrayCreateMutableCopy(NULL, 0, search); 2465 CFArrayAppendValue(new_search, domain); 2466 CFDictionarySetValue(new_dict, kSCPropNetDNSSearchDomains, new_search); 2467 my_CFRelease(&new_search); 2468 } 2469 } 2470 } 2471 2472 done: 2473 changed = service_dict_set(serviceID, kSCEntNetDNS, new_dict); 2474 my_CFRelease(&new_dict); 2475 return (changed); 2476} 2477 2478static void 2479merge_dict(const void *key, const void *value, void *context) 2480{ 2481 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)context; 2482 2483 CFDictionarySetValue(dict, key, value); 2484 return; 2485} 2486 2487#define PROXY_AUTO_DISCOVERY_URL 252 2488 2489static CF_RETURNS_RETAINED CFStringRef 2490wpadURL_dhcp(CFDictionaryRef dhcp_options) 2491{ 2492 CFStringRef urlString = NULL; 2493 2494 if (isA_CFDictionary(dhcp_options)) { 2495 CFDataRef data; 2496 2497 data = DHCPInfoGetOptionData(dhcp_options, PROXY_AUTO_DISCOVERY_URL); 2498 if (data != NULL) { 2499 CFURLRef url; 2500 const UInt8 *urlBytes; 2501 CFIndex urlLen; 2502 2503 urlBytes = CFDataGetBytePtr(data); 2504 urlLen = CFDataGetLength(data); 2505 while ((urlLen > 0) && (urlBytes[urlLen - 1] == 0)) { 2506 // remove trailing NUL 2507 urlLen--; 2508 } 2509 2510 if (urlLen <= 0) { 2511 return NULL; 2512 } 2513 2514 url = CFURLCreateWithBytes(NULL, urlBytes, urlLen, kCFStringEncodingUTF8, NULL); 2515 if (url != NULL) { 2516 urlString = CFURLGetString(url); 2517 if (urlString != NULL) { 2518 CFRetain(urlString); 2519 } 2520 CFRelease(url); 2521 } 2522 } 2523 } 2524 2525 return urlString; 2526} 2527 2528static CF_RETURNS_RETAINED CFStringRef 2529wpadURL_dns(void) 2530{ 2531 CFURLRef url; 2532 CFStringRef urlString = NULL; 2533 2534 url = CFURLCreateWithString(NULL, CFSTR("http://wpad/wpad.dat"), NULL); 2535 if (url != NULL) { 2536 urlString = CFURLGetString(url); 2537 if (urlString != NULL) { 2538 CFRetain(urlString); 2539 } 2540 CFRelease(url); 2541 } 2542 2543 return urlString; 2544} 2545 2546static boolean_t 2547get_proxies_changes(CFStringRef serviceID, CFDictionaryRef state_dict, 2548 CFDictionaryRef setup_dict, CFDictionaryRef info) 2549{ 2550 ProtocolFlags active_protos = kProtocolFlagsNone; 2551 boolean_t changed = FALSE; 2552 CFStringRef interface = NULL; 2553 CFDictionaryRef ipv4; 2554 CFDictionaryRef ipv6; 2555 CFMutableDictionaryRef new_dict = NULL; 2556 const struct { 2557 CFStringRef key; 2558 uint32_t flags; 2559 Boolean append; 2560 } merge_list[] = { 2561 { kSCPropNetProxiesSupplementalMatchDomains, ALLOW_EMPTY_STRING, TRUE }, 2562 { kSCPropNetProxiesSupplementalMatchOrders, 0, TRUE }, 2563 }; 2564 const struct { 2565 CFStringRef key1; /* an "enable" key */ 2566 CFStringRef key2; 2567 CFStringRef key3; 2568 } pick_list[] = { 2569 { kSCPropNetProxiesFTPEnable, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort }, 2570 { kSCPropNetProxiesGopherEnable, kSCPropNetProxiesGopherProxy, kSCPropNetProxiesGopherPort }, 2571 { kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort }, 2572 { kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort }, 2573 { kSCPropNetProxiesRTSPEnable, kSCPropNetProxiesRTSPProxy, kSCPropNetProxiesRTSPPort }, 2574 { kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort }, 2575 { kSCPropNetProxiesProxyAutoConfigEnable, 2576 kSCPropNetProxiesProxyAutoConfigURLString, 2577 kSCPropNetProxiesProxyAutoConfigJavaScript, }, 2578 { kSCPropNetProxiesProxyAutoDiscoveryEnable, 2579 NULL, 2580 NULL, } 2581 }; 2582 IPv4RouteListRef routes = NULL; 2583 2584 if ((state_dict == NULL) && (setup_dict == NULL)) { 2585 /* there is no proxy content */ 2586 goto done; 2587 } 2588 2589 ipv4 = service_dict_get(serviceID, kSCEntNetIPv4); 2590 routes = ipv4_dict_get_routelist(ipv4); 2591 2592 if (routes != NULL) { 2593 active_protos |= kProtocolFlagsIPv4; 2594 2595 interface = ipv4_dict_get_ifname(ipv4); 2596 } 2597 2598 ipv6 = service_dict_get(serviceID, kSCEntNetIPv6); 2599 if (ipv6 != NULL) { 2600 active_protos |= kProtocolFlagsIPv6; 2601 2602 if (interface == NULL) { 2603 interface = CFDictionaryGetValue(ipv6, 2604 kSCPropInterfaceName); 2605 } 2606 } 2607 2608 if (active_protos == kProtocolFlagsNone) { 2609 /* there is no IPv4 nor IPv6 */ 2610 if (state_dict == NULL) { 2611 /* ... and no proxy content that we care about */ 2612 goto done; 2613 } 2614 setup_dict = NULL; 2615 } 2616 2617 if ((setup_dict != NULL) && (state_dict != NULL)) { 2618 CFIndex i; 2619 CFMutableDictionaryRef setup_copy; 2620 2621 /* 2622 * Merge the per-service "Setup:" and "State:" proxy information with 2623 * the "Setup:" information always taking precedence. Additionally, 2624 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy, 2625 * Port) is defined than all of the values for that group will be 2626 * used. That is, we don't allow mixing some of the values from 2627 * the "Setup:" keys and others from the "State:" keys. 2628 */ 2629 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict); 2630 2631 for (i = 0; i < sizeof(merge_list)/sizeof(merge_list[0]); i++) { 2632 merge_array_prop(new_dict, 2633 merge_list[i].key, 2634 state_dict, 2635 setup_dict, 2636 merge_list[i].flags, 2637 merge_list[i].append); 2638 } 2639 2640 setup_copy = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict); 2641 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) { 2642 if (CFDictionaryContainsKey(setup_copy, pick_list[i].key1)) { 2643 /* 2644 * if a "Setup:" enabled key has been provided than we want to 2645 * ignore all of the "State:" keys 2646 */ 2647 CFDictionaryRemoveValue(new_dict, pick_list[i].key1); 2648 if (pick_list[i].key2 != NULL) { 2649 CFDictionaryRemoveValue(new_dict, pick_list[i].key2); 2650 } 2651 if (pick_list[i].key3 != NULL) { 2652 CFDictionaryRemoveValue(new_dict, pick_list[i].key3); 2653 } 2654 } else if (CFDictionaryContainsKey(state_dict, pick_list[i].key1) || 2655 ((pick_list[i].key2 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key2)) || 2656 ((pick_list[i].key3 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key3))) { 2657 /* 2658 * if a "Setup:" enabled key has not been provided and we have 2659 * some" "State:" keys than we remove all of of "Setup:" keys 2660 */ 2661 CFDictionaryRemoveValue(setup_copy, pick_list[i].key1); 2662 if (pick_list[i].key2 != NULL) { 2663 CFDictionaryRemoveValue(setup_copy, pick_list[i].key2); 2664 } 2665 if (pick_list[i].key3 != NULL) { 2666 CFDictionaryRemoveValue(setup_copy, pick_list[i].key3); 2667 } 2668 } 2669 } 2670 2671 /* merge the "Setup:" keys */ 2672 CFDictionaryApplyFunction(setup_copy, merge_dict, new_dict); 2673 CFRelease(setup_copy); 2674 } 2675 else if (setup_dict != NULL) { 2676 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict); 2677 } 2678 else if (state_dict != NULL) { 2679 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict); 2680 } 2681 2682 if ((new_dict != NULL) && (CFDictionaryGetCount(new_dict) == 0)) { 2683 CFRelease(new_dict); 2684 new_dict = NULL; 2685 } 2686 2687 if ((new_dict != NULL) && (interface != NULL)) { 2688 CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface); 2689 } 2690 2691 /* process WPAD */ 2692 if (new_dict != NULL) { 2693 CFDictionaryRef dhcp_options; 2694 CFNumberRef num; 2695 CFNumberRef wpad = NULL; 2696 int wpadEnabled = 0; 2697 CFStringRef wpadURL = NULL; 2698 2699 if (CFDictionaryGetValueIfPresent(new_dict, 2700 kSCPropNetProxiesProxyAutoDiscoveryEnable, 2701 (const void **)&num) && 2702 isA_CFNumber(num)) { 2703 /* if we have a WPAD key */ 2704 wpad = num; 2705 if (!CFNumberGetValue(num, kCFNumberIntType, &wpadEnabled)) { 2706 /* if we don't like the enabled key/value */ 2707 wpadEnabled = 0; 2708 } 2709 } 2710 2711 if (wpadEnabled) { 2712 int pacEnabled = 0; 2713 2714 num = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigEnable); 2715 if (!isA_CFNumber(num) || 2716 !CFNumberGetValue(num, kCFNumberIntType, &pacEnabled)) { 2717 /* if we don't like the enabled key/value */ 2718 pacEnabled = 0; 2719 } 2720 2721 if (pacEnabled) { 2722 CFStringRef pacURL; 2723 2724 pacURL = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigURLString); 2725 if (pacURL != NULL) { 2726 if (!isA_CFString(pacURL)) { 2727 /* if we don't like the PAC URL */ 2728 pacEnabled = 0; 2729 } 2730 } else { 2731 CFStringRef pacJS; 2732 2733 pacJS = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigJavaScript); 2734 if (!isA_CFString(pacJS)) { 2735 /* if we don't have (or like) the PAC JavaScript */ 2736 pacEnabled = 0; 2737 } 2738 } 2739 } 2740 2741 if (pacEnabled) { 2742 /* 2743 * we already have a PAC URL so disable WPAD. 2744 */ 2745 wpadEnabled = 0; 2746 goto setWPAD; 2747 } 2748 2749 /* 2750 * if WPAD is enabled and we don't already have a PAC URL then 2751 * we check for a DHCP provided URL. If not available, we use 2752 * a PAC URL pointing to a well-known file (wpad.dat) on a 2753 * well-known host (wpad.<domain>). 2754 */ 2755 dhcp_options = get_service_state_entity(info, serviceID, kSCEntNetDHCP); 2756 wpadURL = wpadURL_dhcp(dhcp_options); 2757 if (wpadURL == NULL) { 2758 wpadURL = wpadURL_dns(); 2759 } 2760 if (wpadURL == NULL) { 2761 wpadEnabled = 0; /* if we don't have a WPAD URL */ 2762 goto setWPAD; 2763 } 2764 2765 pacEnabled = 1; 2766 num = CFNumberCreate(NULL, kCFNumberIntType, &pacEnabled); 2767 CFDictionarySetValue(new_dict, 2768 kSCPropNetProxiesProxyAutoConfigEnable, 2769 num); 2770 CFRelease(num); 2771 CFDictionarySetValue(new_dict, 2772 kSCPropNetProxiesProxyAutoConfigURLString, 2773 wpadURL); 2774 CFRelease(wpadURL); 2775 } 2776 2777 setWPAD: 2778 if (wpad != NULL) { 2779 num = CFNumberCreate(NULL, kCFNumberIntType, &wpadEnabled); 2780 CFDictionarySetValue(new_dict, 2781 kSCPropNetProxiesProxyAutoDiscoveryEnable, 2782 num); 2783 CFRelease(num); 2784 } 2785 } 2786 2787 done: 2788 changed = service_dict_set(serviceID, kSCEntNetProxies, new_dict); 2789 my_CFRelease(&new_dict); 2790 return (changed); 2791} 2792 2793#if !TARGET_OS_IPHONE 2794static boolean_t 2795get_smb_changes(CFStringRef serviceID, CFDictionaryRef state_dict, 2796 CFDictionaryRef setup_dict, CFDictionaryRef info) 2797{ 2798 boolean_t changed = FALSE; 2799 int i; 2800 CFMutableDictionaryRef new_dict = NULL; 2801 const CFStringRef pick_list[] = { 2802 kSCPropNetSMBNetBIOSName, 2803 kSCPropNetSMBNetBIOSNodeType, 2804#ifdef ADD_NETBIOS_SCOPE 2805 kSCPropNetSMBNetBIOSScope, 2806#endif // ADD_NETBIOS_SCOPE 2807 kSCPropNetSMBWorkgroup, 2808 }; 2809 2810 if (service_dict_get(serviceID, kSCEntNetIPv4) == NULL) { 2811 /* there is no IPv4 */ 2812 goto done; 2813 } 2814 2815 if (state_dict == NULL && setup_dict == NULL) { 2816 /* there is no SMB */ 2817 goto done; 2818 } 2819 2820 /* merge SMB configuration */ 2821 new_dict = CFDictionaryCreateMutable(NULL, 0, 2822 &kCFTypeDictionaryKeyCallBacks, 2823 &kCFTypeDictionaryValueCallBacks); 2824 2825 merge_array_prop(new_dict, 2826 kSCPropNetSMBWINSAddresses, 2827 state_dict, 2828 setup_dict, 2829 0, 2830 FALSE); 2831 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) { 2832 pick_prop(new_dict, 2833 pick_list[i], 2834 state_dict, 2835 setup_dict, 2836 0); 2837 } 2838 2839 if (CFDictionaryGetCount(new_dict) == 0) { 2840 my_CFRelease(&new_dict); 2841 goto done; 2842 } 2843 2844 done: 2845 changed = service_dict_set(serviceID, kSCEntNetSMB, new_dict); 2846 my_CFRelease(&new_dict); 2847 return (changed); 2848} 2849#endif /* !TARGET_OS_IPHONE */ 2850 2851static CFStringRef 2852services_info_get_interface(CFDictionaryRef services_info, 2853 CFStringRef serviceID) 2854{ 2855 CFStringRef interface = NULL; 2856 CFDictionaryRef ipv4_dict; 2857 2858 ipv4_dict = get_service_state_entity(services_info, serviceID, 2859 kSCEntNetIPv4); 2860 if (isA_CFDictionary(ipv4_dict) != NULL) { 2861 interface = CFDictionaryGetValue(ipv4_dict, kSCPropInterfaceName); 2862 } 2863 else { 2864 CFDictionaryRef ipv6_dict; 2865 2866 ipv6_dict = get_service_state_entity(services_info, serviceID, 2867 kSCEntNetIPv6); 2868 if (isA_CFDictionary(ipv6_dict) != NULL) { 2869 interface = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName); 2870 } 2871 } 2872 return (interface); 2873} 2874 2875 2876 2877static const CFStringRef *statusEntityNames[] = { 2878 &kSCEntNetIPSec, 2879 &kSCEntNetPPP, 2880 &kSCEntNetVPN, 2881}; 2882 2883static Boolean 2884get_transient_service_changes(CFStringRef serviceID, CFDictionaryRef services_info) 2885{ 2886 boolean_t changed = FALSE; 2887 int i; 2888 2889 static const struct { 2890 const CFStringRef *entityName; 2891 const CFStringRef *statusKey; 2892 } transientServiceInfo[] = { 2893 { &kSCEntNetIPSec, &kSCPropNetIPSecStatus }, 2894 { &kSCEntNetPPP, &kSCPropNetPPPStatus }, 2895 { &kSCEntNetVPN, &kSCPropNetVPNStatus }, 2896 }; 2897 2898 for (i = 0; i < sizeof(transientServiceInfo)/sizeof(transientServiceInfo[0]); i++) { 2899 CFDictionaryRef dict; 2900 CFNumberRef status = NULL; 2901 CFMutableDictionaryRef ts_dict = NULL; 2902 2903 dict = get_service_state_entity(services_info, serviceID, *transientServiceInfo[i].entityName); 2904 2905 if (isA_CFDictionary(dict) != NULL) { 2906 status = CFDictionaryGetValue(dict, *transientServiceInfo[i].statusKey); 2907 } 2908 2909 if (isA_CFNumber(status) != NULL) { 2910 ts_dict = CFDictionaryCreateMutable(NULL, 2911 0, 2912 &kCFTypeDictionaryKeyCallBacks, 2913 &kCFTypeDictionaryValueCallBacks); 2914 CFDictionaryAddValue(ts_dict, 2915 *transientServiceInfo[i].statusKey, 2916 status); 2917 } 2918 2919 if (service_dict_set(serviceID, *transientServiceInfo[i].entityName, ts_dict)) { 2920 changed = TRUE; 2921 } 2922 2923 if (ts_dict != NULL) { 2924 CFRelease(ts_dict); 2925 } 2926 } 2927 return (changed); 2928} 2929 2930static boolean_t 2931get_rank_changes(CFStringRef serviceID, CFDictionaryRef state_options, 2932 CFDictionaryRef setup_options, CFDictionaryRef services_info) 2933{ 2934 boolean_t changed = FALSE; 2935 CFBooleanRef ip_is_coupled = NULL; 2936 CFMutableDictionaryRef new_dict = NULL; 2937 CFStringRef new_rank = NULL; 2938 CFStringRef setup_rank = NULL; 2939 CFStringRef state_rank = NULL; 2940 2941 2942 /* 2943 * Check "PrimaryRank" setting 2944 * 2945 * Note 1: Rank setting in setup/state option overwrites the 2946 * Rank setting in interface 2947 * Within each rank setting, the following precedence is defined: 2948 * 2949 * Note 2: Rank Never > Rank Last > Rank First > Rank None 2950 */ 2951 if (isA_CFDictionary(state_options)) { 2952 CFBooleanRef coupled; 2953 2954 state_rank 2955 = CFDictionaryGetValue(state_options, kSCPropNetServicePrimaryRank); 2956 state_rank = isA_CFString(state_rank); 2957 coupled = CFDictionaryGetValue(state_options, kIPIsCoupled); 2958 if (isA_CFBoolean(coupled) != NULL) { 2959 ip_is_coupled = coupled; 2960 } 2961 } 2962 if (isA_CFDictionary(setup_options)) { 2963 CFBooleanRef coupled; 2964 2965 setup_rank 2966 = CFDictionaryGetValue(setup_options, kSCPropNetServicePrimaryRank); 2967 setup_rank = isA_CFString(setup_rank); 2968 2969 coupled = CFDictionaryGetValue(setup_options, kIPIsCoupled); 2970 if (isA_CFBoolean(coupled) != NULL) { 2971 ip_is_coupled = coupled; 2972 } 2973 } 2974 2975 if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankNever)) || 2976 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankNever))) { 2977 new_rank = kSCValNetServicePrimaryRankNever; 2978 } 2979 else if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankLast)) || 2980 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankLast))) { 2981 new_rank = kSCValNetServicePrimaryRankLast; 2982 } 2983 else if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankFirst)) || 2984 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankFirst))) { 2985 new_rank = kSCValNetServicePrimaryRankFirst; 2986 } 2987 2988 /* This corresponds to Note 1 */ 2989 if (setup_rank == NULL && state_rank == NULL) { 2990 /* Fetch the interface associated with the service */ 2991 CFStringRef interface; 2992 2993 interface = services_info_get_interface(services_info, serviceID); 2994 2995 /* Get the rank on that interface */ 2996 if (interface != NULL) { 2997 new_rank = CFDictionaryGetValue(S_if_rank_dict, interface); 2998 if (S_IPMonitor_debug & kDebugFlag1) { 2999 my_log(LOG_DEBUG, 3000 "serviceID %@ interface %@ rank = %@", 3001 serviceID, interface, 3002 (new_rank != NULL) ? new_rank : CFSTR("<none>")); 3003 } 3004 } 3005 } 3006 3007 3008 if (ip_is_coupled != NULL && CFBooleanGetValue(ip_is_coupled) == FALSE) { 3009 /* don't bother setting a value if it's the default */ 3010 ip_is_coupled = NULL; 3011 } 3012 if (new_rank != NULL || ip_is_coupled != NULL) { 3013 new_dict = CFDictionaryCreateMutable(NULL, 0, 3014 &kCFTypeDictionaryKeyCallBacks, 3015 &kCFTypeDictionaryValueCallBacks); 3016 if (new_rank != NULL) { 3017 CFDictionarySetValue(new_dict, kSCPropNetServicePrimaryRank, 3018 new_rank); 3019 } 3020 if (ip_is_coupled != NULL) { 3021 CFDictionarySetValue(new_dict, kIPIsCoupled, kCFBooleanTrue); 3022 } 3023 } 3024 changed = service_dict_set(serviceID, kSCEntNetService, new_dict); 3025 my_CFRelease(&new_dict); 3026 return (changed); 3027} 3028 3029static CFStringRef 3030if_rank_key_copy(CFStringRef ifname) 3031{ 3032 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, 3033 kSCDynamicStoreDomainState, 3034 ifname, 3035 kSCEntNetService)); 3036} 3037 3038static void 3039if_rank_set(CFStringRef ifname, CFDictionaryRef rank_dict) 3040{ 3041 CFStringRef rank = NULL; 3042 3043 if (isA_CFDictionary(rank_dict) != NULL) { 3044 rank = CFDictionaryGetValue(rank_dict, kSCPropNetServicePrimaryRank); 3045 rank = isA_CFString(rank); 3046 } 3047 3048 /* specific rank is asserted */ 3049 if (rank != NULL) { 3050 if (S_IPMonitor_debug & kDebugFlag1) { 3051 my_log(LOG_DEBUG, "Interface %@ asserted rank %@", 3052 ifname, rank); 3053 } 3054 CFDictionarySetValue(S_if_rank_dict, ifname, rank); 3055 } else { 3056 if (S_IPMonitor_debug & kDebugFlag1) { 3057 my_log(LOG_DEBUG, "Interface %@ removed rank", 3058 ifname); 3059 } 3060 CFDictionaryRemoveValue(S_if_rank_dict, ifname); 3061 } 3062 return; 3063} 3064 3065static void 3066if_rank_apply(const void * key, const void * value, void * context) 3067{ 3068 CFStringRef ifname; 3069 CFDictionaryRef rank_dict = (CFDictionaryRef)value; 3070 3071 /* State:/Network/Interface/<ifname>/Service, <ifname> is at index 3 */ 3072 ifname = my_CFStringCopyComponent(key, CFSTR("/"), 3); 3073 if (ifname != NULL) { 3074 if_rank_set(ifname, rank_dict); 3075 CFRelease(ifname); 3076 } 3077 return; 3078} 3079 3080static void 3081if_rank_dict_init(void) 3082{ 3083 CFDictionaryRef info; 3084 CFStringRef pattern; 3085 CFArrayRef patterns; 3086 3087 S_if_rank_dict 3088 = CFDictionaryCreateMutable(NULL, 0, 3089 &kCFTypeDictionaryKeyCallBacks, 3090 &kCFTypeDictionaryValueCallBacks); 3091 pattern = if_rank_key_copy(kSCCompAnyRegex); 3092 patterns = CFArrayCreate(NULL, 3093 (const void **)&pattern, 1, 3094 &kCFTypeArrayCallBacks); 3095 CFRelease(pattern); 3096 info = SCDynamicStoreCopyMultiple(S_session, NULL, patterns); 3097 CFRelease(patterns); 3098 if (info != NULL) { 3099 CFDictionaryApplyFunction(info, if_rank_apply, NULL); 3100 CFRelease(info); 3101 } 3102 return; 3103 3104} 3105 3106static void 3107add_service_keys(CFStringRef serviceID, CFMutableArrayRef keys, CFMutableArrayRef patterns) 3108{ 3109 int i; 3110 CFStringRef key; 3111 3112 if (CFEqual(serviceID, kSCCompAnyRegex)) { 3113 keys = patterns; 3114 } 3115 3116 for (i = 0; i < ENTITY_TYPES_COUNT; i++) { 3117 key = setup_service_key(serviceID, *entityTypeNames[i]); 3118 CFArrayAppendValue(keys, key); 3119 CFRelease(key); 3120 key = state_service_key(serviceID, *entityTypeNames[i]); 3121 CFArrayAppendValue(keys, key); 3122 CFRelease(key); 3123 } 3124 3125 key = state_service_key(serviceID, kSCEntNetDHCP); 3126 CFArrayAppendValue(patterns, key); 3127 CFRelease(key); 3128 3129 key = setup_service_key(serviceID, NULL); 3130 CFArrayAppendValue(patterns, key); 3131 CFRelease(key); 3132 key = state_service_key(serviceID, NULL); 3133 CFArrayAppendValue(patterns, key); 3134 CFRelease(key); 3135 3136 return; 3137} 3138 3139static void 3140add_status_keys(CFStringRef service_id, CFMutableArrayRef patterns) 3141{ 3142 int i; 3143 3144 for (i = 0; i < sizeof(statusEntityNames)/sizeof(statusEntityNames[0]); i++) { 3145 CFStringRef pattern; 3146 3147 pattern = state_service_key(service_id, *statusEntityNames[i]); 3148 CFArrayAppendValue(patterns, pattern); 3149 CFRelease(pattern); 3150 } 3151 3152 return; 3153} 3154 3155static const CFStringRef *reachabilitySetupKeys[] = { 3156 &kSCEntNetPPP, 3157 &kSCEntNetInterface, 3158 &kSCEntNetIPv4, 3159 &kSCEntNetIPv6, 3160}; 3161 3162 3163static void 3164add_reachability_keys(CFMutableArrayRef patterns) 3165{ 3166 int i; 3167 3168 for (i = 0; i < sizeof(reachabilitySetupKeys)/(sizeof(reachabilitySetupKeys[0])); i++) 3169 { 3170 CFStringRef pattern; 3171 pattern = setup_service_key(kSCCompAnyRegex, *reachabilitySetupKeys[i]); 3172 CFArrayAppendValue(patterns, pattern); 3173 CFRelease(pattern); 3174 } 3175} 3176 3177 3178static void 3179add_vpn_keys(CFMutableArrayRef patterns) 3180{ 3181 CFStringRef pattern; 3182 3183 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN); 3184 CFArrayAppendValue(patterns, pattern); 3185 CFRelease(pattern); 3186} 3187 3188 3189static CFDictionaryRef 3190services_info_copy(SCDynamicStoreRef session, CFArrayRef service_list, 3191 CFArrayRef if_rank_list) 3192{ 3193 int count; 3194 CFMutableArrayRef get_keys; 3195 CFMutableArrayRef get_patterns; 3196 int if_count; 3197 CFDictionaryRef info; 3198 int s; 3199 3200 count = CFArrayGetCount(service_list); 3201 get_keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 3202 get_patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 3203 3204 CFArrayAppendValue(get_keys, S_setup_global_ipv4); 3205 CFArrayAppendValue(get_keys, S_multicast_resolvers); 3206 CFArrayAppendValue(get_keys, S_private_resolvers); 3207 3208 for (s = 0; s < count; s++) { 3209 CFStringRef serviceID = CFArrayGetValueAtIndex(service_list, s); 3210 3211 add_service_keys(serviceID, get_keys, get_patterns); 3212 add_status_keys(serviceID, get_keys); 3213 } 3214 3215 add_reachability_keys(get_patterns); 3216 3217 add_vpn_keys(get_patterns); 3218 3219 if_count = (if_rank_list != NULL) 3220 ? CFArrayGetCount(if_rank_list) : 0; 3221 for (s = 0; s < if_count; s++) { 3222 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_list, s); 3223 CFStringRef key; 3224 3225 key = if_rank_key_copy(ifname); 3226 CFArrayAppendValue(get_keys, key); 3227 CFRelease(key); 3228 } 3229 3230 info = SCDynamicStoreCopyMultiple(session, get_keys, get_patterns); 3231 my_CFRelease(&get_keys); 3232 my_CFRelease(&get_patterns); 3233 return (info); 3234} 3235 3236#if !TARGET_IPHONE_SIMULATOR 3237static int rtm_seq = 0; 3238#endif /* !TARGET_IPHONE_SIMULATOR */ 3239 3240#if !TARGET_IPHONE_SIMULATOR 3241static int 3242route_open_socket(void) 3243{ 3244 int sockfd; 3245 3246 if ((sockfd = socket(PF_ROUTE, SOCK_RAW, PF_ROUTE)) == -1) { 3247 my_log(LOG_NOTICE, 3248 "IPMonitor: route_open_socket: socket failed, %s", 3249 strerror(errno)); 3250 } 3251 return (sockfd); 3252} 3253#endif /* !TARGET_IPHONE_SIMULATOR */ 3254 3255/* 3256 * Define: ROUTE_MSG_ADDRS_SPACE 3257 * Purpose: 3258 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for 3259 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case 3260 * someone changes the code and doesn't think to modify this. 3261 */ 3262#define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \ 3263 + 2 * sizeof(struct sockaddr_dl) \ 3264 + 128) 3265typedef struct { 3266 struct rt_msghdr hdr; 3267 char addrs[ROUTE_MSG_ADDRS_SPACE]; 3268} route_msg; 3269 3270#if !TARGET_IPHONE_SIMULATOR 3271static int 3272ipv4_route(int sockfd, 3273 int cmd, struct in_addr gateway, struct in_addr netaddr, 3274 struct in_addr netmask, char * ifname, unsigned int ifindex, 3275 struct in_addr ifa, RouteFlags flags) 3276{ 3277 boolean_t default_route = (netaddr.s_addr == 0); 3278 int len; 3279 int ret = 0; 3280 route_msg rtmsg; 3281 union { 3282 struct sockaddr_in * in_p; 3283 struct sockaddr_dl * dl_p; 3284 void * ptr; 3285 } rtaddr; 3286 3287 if (default_route && S_netboot) { 3288 return (0); 3289 } 3290 3291 if (ifname == NULL) { 3292 /* this should not happen, but rather than crash, return an error */ 3293 my_log(LOG_NOTICE, 3294 "IPMonitor: ipv4_route ifname is NULL on network address %s", 3295 inet_ntoa(netaddr)); 3296 return (EBADF); 3297 } 3298 if ((flags & kRouteIsNULLFlag) != 0) { 3299 my_log(LOG_DEBUG, "IPMonitor: ignoring route %s on %s", 3300 inet_ntoa(netaddr), ifname); 3301 return (0); 3302 } 3303 memset(&rtmsg, 0, sizeof(rtmsg)); 3304 rtmsg.hdr.rtm_type = cmd; 3305 rtmsg.hdr.rtm_version = RTM_VERSION; 3306 rtmsg.hdr.rtm_seq = ++rtm_seq; 3307 rtmsg.hdr.rtm_addrs 3308 = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP | RTA_IFA; 3309 if (default_route 3310 && (flags & kRouteIsDirectToInterfaceFlag) == 0) { 3311 rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; 3312 } 3313 else { 3314 rtmsg.hdr.rtm_flags = RTF_UP | RTF_CLONING | RTF_STATIC; 3315 } 3316 if ((flags & kRouteIsScopedFlag) != 0) { 3317#ifdef RTF_IFSCOPE 3318 if (!S_scopedroute) { 3319 return (0); 3320 } 3321 if (ifindex == 0) { 3322 /* specifically asked for a scoped route, yet no index supplied */ 3323 my_log(LOG_NOTICE, 3324 "IPMonitor: ipv4_route index is 0 on %s-scoped route %s", 3325 ifname, inet_ntoa(netaddr)); 3326 return (EBADF); 3327 } 3328 rtmsg.hdr.rtm_index = ifindex; 3329 rtmsg.hdr.rtm_flags |= RTF_IFSCOPE; 3330#else /* RTF_IFSCOPE */ 3331 return (0); 3332#endif /* RTF_IFSCOPE */ 3333 } 3334 3335 rtaddr.ptr = rtmsg.addrs; 3336 3337 /* dest */ 3338 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p); 3339 rtaddr.in_p->sin_family = AF_INET; 3340 rtaddr.in_p->sin_addr = netaddr; 3341 rtaddr.ptr += sizeof(*rtaddr.in_p); 3342 3343 /* gateway */ 3344 if ((rtmsg.hdr.rtm_flags & RTF_GATEWAY) != 0) { 3345 /* gateway is an IP address */ 3346 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p); 3347 rtaddr.in_p->sin_family = AF_INET; 3348 rtaddr.in_p->sin_addr = gateway; 3349 rtaddr.ptr += sizeof(*rtaddr.in_p); 3350 } 3351 else { 3352 /* gateway is the interface itself */ 3353 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p); 3354 rtaddr.dl_p->sdl_family = AF_LINK; 3355 rtaddr.dl_p->sdl_nlen = strlen(ifname); 3356 bcopy(ifname, rtaddr.dl_p->sdl_data, rtaddr.dl_p->sdl_nlen); 3357 rtaddr.ptr += sizeof(*rtaddr.dl_p); 3358 } 3359 3360 /* mask */ 3361 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p); 3362 rtaddr.in_p->sin_family = AF_INET; 3363 rtaddr.in_p->sin_addr = netmask; 3364 rtaddr.ptr += sizeof(*rtaddr.in_p); 3365 3366 /* interface name */ 3367 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p); 3368 rtaddr.dl_p->sdl_family = AF_LINK; 3369 rtaddr.dl_p->sdl_nlen = strlen(ifname); 3370 bcopy(ifname, rtaddr.dl_p->sdl_data, rtaddr.dl_p->sdl_nlen); 3371 rtaddr.ptr += sizeof(*rtaddr.dl_p); 3372 3373 /* interface address */ 3374 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p); 3375 rtaddr.in_p->sin_family = AF_INET; 3376 rtaddr.in_p->sin_addr = ifa; 3377 rtaddr.ptr += sizeof(*rtaddr.in_p); 3378 3379 len = sizeof(rtmsg.hdr) + (rtaddr.ptr - (void *)rtmsg.addrs); 3380 rtmsg.hdr.rtm_msglen = len; 3381 if (write(sockfd, &rtmsg, len) == -1) { 3382 ret = errno; 3383 } 3384 return (ret); 3385} 3386#endif /* !TARGET_IPHONE_SIMULATOR */ 3387 3388#if !TARGET_IPHONE_SIMULATOR 3389static boolean_t 3390ipv6_route(int cmd, struct in6_addr gateway, struct in6_addr netaddr, 3391 struct in6_addr netmask, char * ifname, boolean_t is_direct) 3392{ 3393 boolean_t default_route; 3394 int len; 3395 boolean_t ret = TRUE; 3396 struct { 3397 struct rt_msghdr hdr; 3398 struct sockaddr_in6 dst; 3399 struct sockaddr_in6 gway; 3400 struct sockaddr_in6 mask; 3401 struct sockaddr_dl ifp; 3402 } rtmsg; 3403 int sockfd = -1; 3404 struct in6_addr zeroes = IN6ADDR_ANY_INIT; 3405 3406 default_route = (bcmp(&zeroes, &netaddr, sizeof(netaddr)) == 0); 3407 3408 if ((IN6_IS_ADDR_LINKLOCAL(&gateway) || 3409 IN6_IS_ADDR_MC_LINKLOCAL(&gateway)) && 3410 (ifname != NULL)) { 3411 unsigned int index = if_nametoindex(ifname); 3412 3413 /* add the scope id to the link local address */ 3414 gateway.__u6_addr.__u6_addr16[1] = (uint16_t)htons(index); 3415 } 3416 sockfd = route_open_socket(); 3417 if (sockfd == -1) { 3418 return (FALSE); 3419 } 3420 memset(&rtmsg, 0, sizeof(rtmsg)); 3421 rtmsg.hdr.rtm_type = cmd; 3422 if (default_route) { 3423 if (is_direct) { 3424 /* if router is directly reachable, don't set the gateway flag */ 3425 rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC; 3426 } 3427 else { 3428 rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; 3429 } 3430 } 3431 else { 3432 rtmsg.hdr.rtm_flags = RTF_UP | RTF_CLONING | RTF_STATIC; 3433 } 3434 rtmsg.hdr.rtm_version = RTM_VERSION; 3435 rtmsg.hdr.rtm_seq = ++rtm_seq; 3436 rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; 3437 rtmsg.dst.sin6_len = sizeof(rtmsg.dst); 3438 rtmsg.dst.sin6_family = AF_INET6; 3439 rtmsg.dst.sin6_addr = netaddr; 3440 rtmsg.gway.sin6_len = sizeof(rtmsg.gway); 3441 rtmsg.gway.sin6_family = AF_INET6; 3442 rtmsg.gway.sin6_addr = gateway; 3443 rtmsg.mask.sin6_len = sizeof(rtmsg.mask); 3444 rtmsg.mask.sin6_family = AF_INET6; 3445 rtmsg.mask.sin6_addr = netmask; 3446 3447 len = sizeof(rtmsg); 3448 if (ifname) { 3449 rtmsg.ifp.sdl_len = sizeof(rtmsg.ifp); 3450 rtmsg.ifp.sdl_family = AF_LINK; 3451 rtmsg.ifp.sdl_nlen = strlen(ifname); 3452 rtmsg.hdr.rtm_addrs |= RTA_IFP; 3453 bcopy(ifname, rtmsg.ifp.sdl_data, rtmsg.ifp.sdl_nlen); 3454 } 3455 else { 3456 /* no ifp information */ 3457 len -= sizeof(rtmsg.ifp); 3458 } 3459 rtmsg.hdr.rtm_msglen = len; 3460 if (write(sockfd, &rtmsg, len) == -1) { 3461 if ((cmd == RTM_ADD) && (errno == EEXIST)) { 3462 /* no sense complaining about a route that already exists */ 3463 } 3464 else if ((cmd == RTM_DELETE) && (errno == ESRCH)) { 3465 /* no sense complaining about a route that isn't there */ 3466 } 3467 else { 3468 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 3469 my_log(LOG_DEBUG, 3470 "IPMonitor ipv6_route: write routing" 3471 " socket failed, %s", strerror(errno)); 3472 } 3473 ret = FALSE; 3474 } 3475 } 3476 3477 close(sockfd); 3478 return (ret); 3479} 3480 3481static boolean_t 3482ipv6_default_route_delete(void) 3483{ 3484 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 3485 my_log(LOG_DEBUG, "IPMonitor: IPv6 route delete default"); 3486 } 3487 return (ipv6_route(RTM_DELETE, S_ip6_zeros, S_ip6_zeros, S_ip6_zeros, 3488 NULL, FALSE)); 3489} 3490 3491static boolean_t 3492ipv6_default_route_add(struct in6_addr router, char * ifname, 3493 boolean_t is_direct) 3494{ 3495 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 3496 char ntopbuf[INET6_ADDRSTRLEN]; 3497 3498 my_log(LOG_DEBUG, 3499 "IPMonitor: IPv6 route add default" 3500 " %s interface %s direct %d", 3501 inet_ntop(AF_INET6, &router, ntopbuf, sizeof(ntopbuf)), 3502 ifname, is_direct); 3503 } 3504 return (ipv6_route(RTM_ADD, router, S_ip6_zeros, S_ip6_zeros, 3505 ifname, is_direct)); 3506} 3507#endif /* !TARGET_IPHONE_SIMULATOR */ 3508 3509 3510#if !TARGET_IPHONE_SIMULATOR 3511static int 3512multicast_route_delete(int sockfd) 3513{ 3514 struct in_addr gateway = { htonl(INADDR_LOOPBACK) }; 3515 struct in_addr netaddr = { htonl(INADDR_UNSPEC_GROUP) }; 3516 struct in_addr netmask = { htonl(IN_CLASSD_NET) }; 3517 3518 return (ipv4_route(sockfd, RTM_DELETE, gateway, netaddr, netmask, "lo0", 0, 3519 gateway, 0)); 3520} 3521 3522static int 3523multicast_route_add(int sockfd) 3524{ 3525 struct in_addr gateway = { htonl(INADDR_LOOPBACK) }; 3526 struct in_addr netaddr = { htonl(INADDR_UNSPEC_GROUP) }; 3527 struct in_addr netmask = { htonl(IN_CLASSD_NET) }; 3528 3529 return (ipv4_route(sockfd, RTM_ADD, gateway, netaddr, netmask, "lo0", 0, 3530 gateway, 0)); 3531} 3532#endif /* !TARGET_IPHONE_SIMULATOR */ 3533 3534#if !TARGET_IPHONE_SIMULATOR 3535#ifdef RTF_IFSCOPE 3536static void 3537set_ipv6_default_interface(char * ifname) 3538{ 3539 struct in6_ndifreq ndifreq; 3540 int sock; 3541 3542 bzero((char *)&ndifreq, sizeof(ndifreq)); 3543 if (ifname != NULL) { 3544 strlcpy(ndifreq.ifname, ifname, sizeof(ndifreq.ifname)); 3545 ndifreq.ifindex = if_nametoindex(ifname); 3546 } else { 3547 strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); 3548 ndifreq.ifindex = 0; 3549 } 3550 3551 sock = inet6_dgram_socket(); 3552 if (sock == -1) { 3553 my_log(LOG_ERR, 3554 "IPMonitor: set_ipv6_default_interface: socket failed, %s", 3555 strerror(errno)); 3556 return; 3557 } 3558 if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) == -1) { 3559 my_log(LOG_ERR, 3560 "IPMonitor: set_ipv6_default_interface: ioctl(SIOCSDEFIFACE_IN6) failed, %s", 3561 strerror(errno)); 3562 } 3563 close(sock); 3564 return; 3565} 3566#endif /* RTF_IFSCOPE */ 3567 3568static void 3569set_ipv6_router(struct in6_addr * router, char * ifname, boolean_t is_direct) 3570{ 3571 /* assign the new default route, ensure local multicast route available */ 3572 (void)ipv6_default_route_delete(); 3573 if (router != NULL) { 3574 (void)ipv6_default_route_add(*router, ifname, is_direct); 3575 } 3576 return; 3577} 3578#endif /* !TARGET_IPHONE_SIMULATOR */ 3579 3580#if !TARGET_OS_IPHONE 3581static __inline__ void 3582empty_dns() 3583{ 3584 (void)unlink(VAR_RUN_RESOLV_CONF); 3585} 3586 3587static void 3588set_dns(CFArrayRef val_search_domains, 3589 CFStringRef val_domain_name, 3590 CFArrayRef val_servers, 3591 CFArrayRef val_sortlist) 3592{ 3593 FILE * f = fopen(VAR_RUN_RESOLV_CONF "-", "w"); 3594 3595 /* publish new resolv.conf */ 3596 if (f) { 3597 CFIndex i; 3598 CFIndex n; 3599 3600 SCPrint(TRUE, f, CFSTR("#\n")); 3601 SCPrint(TRUE, f, CFSTR("# Mac OS X Notice\n")); 3602 SCPrint(TRUE, f, CFSTR("#\n")); 3603 SCPrint(TRUE, f, CFSTR("# This file is not used by the host name and address resolution\n")); 3604 SCPrint(TRUE, f, CFSTR("# or the DNS query routing mechanisms used by most processes on\n")); 3605 SCPrint(TRUE, f, CFSTR("# this Mac OS X system.\n")); 3606 SCPrint(TRUE, f, CFSTR("#\n")); 3607 SCPrint(TRUE, f, CFSTR("# This file is automatically generated.\n")); 3608 SCPrint(TRUE, f, CFSTR("#\n")); 3609 3610 if (isA_CFArray(val_search_domains)) { 3611 SCPrint(TRUE, f, CFSTR("search")); 3612 n = CFArrayGetCount(val_search_domains); 3613 for (i = 0; i < n; i++) { 3614 CFStringRef domain; 3615 3616 domain = CFArrayGetValueAtIndex(val_search_domains, i); 3617 if (isA_CFString(domain)) { 3618 SCPrint(TRUE, f, CFSTR(" %@"), domain); 3619 } 3620 } 3621 SCPrint(TRUE, f, CFSTR("\n")); 3622 } 3623 else if (isA_CFString(val_domain_name)) { 3624 SCPrint(TRUE, f, CFSTR("domain %@\n"), val_domain_name); 3625 } 3626 3627 if (isA_CFArray(val_servers)) { 3628 n = CFArrayGetCount(val_servers); 3629 for (i = 0; i < n; i++) { 3630 CFStringRef nameserver; 3631 3632 nameserver = CFArrayGetValueAtIndex(val_servers, i); 3633 if (isA_CFString(nameserver)) { 3634 SCPrint(TRUE, f, CFSTR("nameserver %@\n"), nameserver); 3635 } 3636 } 3637 } 3638 3639 if (isA_CFArray(val_sortlist)) { 3640 SCPrint(TRUE, f, CFSTR("sortlist")); 3641 n = CFArrayGetCount(val_sortlist); 3642 for (i = 0; i < n; i++) { 3643 CFStringRef address; 3644 3645 address = CFArrayGetValueAtIndex(val_sortlist, i); 3646 if (isA_CFString(address)) { 3647 SCPrint(TRUE, f, CFSTR(" %@"), address); 3648 } 3649 } 3650 SCPrint(TRUE, f, CFSTR("\n")); 3651 } 3652 3653 fclose(f); 3654 rename(VAR_RUN_RESOLV_CONF "-", VAR_RUN_RESOLV_CONF); 3655 } 3656 return; 3657} 3658#endif /* !TARGET_OS_IPHONE */ 3659 3660#if !TARGET_IPHONE_SIMULATOR 3661static boolean_t 3662router_is_our_ipv6_address(CFStringRef router, CFArrayRef addr_list) 3663{ 3664 CFIndex i; 3665 CFIndex n = CFArrayGetCount(addr_list); 3666 struct in6_addr r; 3667 3668 (void)cfstring_to_ip6(router, &r); 3669 for (i = 0; i < n; i++) { 3670 struct in6_addr ip; 3671 3672 if (cfstring_to_ip6(CFArrayGetValueAtIndex(addr_list, i), &ip) 3673 && bcmp(&r, &ip, sizeof(r)) == 0) { 3674 return (TRUE); 3675 } 3676 } 3677 return (FALSE); 3678} 3679#endif /* !TARGET_IPHONE_SIMULATOR */ 3680 3681static IPv4RouteListRef 3682service_dict_get_ipv4_routelist(CFDictionaryRef service_dict) 3683{ 3684 CFDictionaryRef dict; 3685 3686 dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4); 3687 3688 return (ipv4_dict_get_routelist(dict)); 3689} 3690 3691static CFStringRef 3692service_dict_get_ipv4_ifname(CFDictionaryRef service_dict) 3693{ 3694 CFDictionaryRef dict; 3695 3696 dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4); 3697 return (ipv4_dict_get_ifname(dict)); 3698} 3699 3700static boolean_t 3701service_get_ip_is_coupled(CFStringRef serviceID) 3702{ 3703 CFDictionaryRef dict; 3704 boolean_t ip_is_coupled = FALSE; 3705 3706 dict = service_dict_get(serviceID, kSCEntNetService); 3707 if (dict != NULL) { 3708 if (CFDictionaryContainsKey(dict, kIPIsCoupled)) { 3709 ip_is_coupled = TRUE; 3710 } 3711 } 3712 return (ip_is_coupled); 3713} 3714 3715#if !TARGET_IPHONE_SIMULATOR 3716 3717typedef struct apply_ipv4_route_context { 3718 IPv4RouteListRef old; 3719 IPv4RouteListRef new; 3720 int sockfd; 3721} apply_ipv4_route_context_t; 3722 3723/* add/remove a router/32 subnet */ 3724static int 3725ipv4_route_gateway(int sockfd, int cmd, char * ifn_p, 3726 IPv4RouteRef def_route) 3727{ 3728 struct in_addr mask; 3729 3730 mask.s_addr = htonl(INADDR_BROADCAST); 3731 return (ipv4_route(sockfd, cmd, def_route->ifa, 3732 def_route->gateway, mask, ifn_p, def_route->ifindex, 3733 def_route->ifa, def_route->flags)); 3734} 3735 3736/* 3737 * Function: apply_ipv4_route 3738 * Purpose: 3739 * Callback function that adds/removes the specified route. 3740 */ 3741static void 3742apply_ipv4_route(IPv4RouteListApplyCommand cmd, IPv4RouteRef route, void * arg) 3743{ 3744 apply_ipv4_route_context_t *context = (apply_ipv4_route_context_t *)arg; 3745 char * ifn_p; 3746 int retval; 3747 3748 ifn_p = route->ifname; 3749 switch (cmd) { 3750 case kIPv4RouteListAddRouteCommand: 3751 if ((route->flags & kRouteIsNotSubnetLocalFlag) != 0) { 3752 retval = ipv4_route_gateway(context->sockfd, RTM_ADD, 3753 ifn_p, route); 3754 if (retval == EEXIST) { 3755 /* delete and add again */ 3756 (void)ipv4_route_gateway(context->sockfd, RTM_DELETE, 3757 ifn_p, route); 3758 retval = ipv4_route_gateway(context->sockfd, RTM_ADD, 3759 ifn_p, route); 3760 } 3761 if (retval != 0) { 3762 my_log(LOG_NOTICE, 3763 "IPMonitor apply_ipv4_route failed to add" 3764 " %s/32 route, %s", 3765 inet_ntoa(route->gateway), strerror(retval)); 3766 } 3767 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 3768 my_log(LOG_DEBUG, "Added IPv4 Route %s/32", 3769 inet_ntoa(route->gateway)); 3770 } 3771 } 3772 retval = ipv4_route(context->sockfd, 3773 RTM_ADD, route->gateway, 3774 route->dest, route->mask, ifn_p, route->ifindex, 3775 route->ifa, route->flags); 3776 if (retval == EEXIST) { 3777 /* delete and add again */ 3778 (void)ipv4_route(context->sockfd, 3779 RTM_DELETE, route->gateway, 3780 route->dest, route->mask, ifn_p, route->ifindex, 3781 route->ifa, route->flags); 3782 retval = ipv4_route(context->sockfd, 3783 RTM_ADD, route->gateway, 3784 route->dest, route->mask, 3785 ifn_p, route->ifindex, 3786 route->ifa, route->flags); 3787 } 3788 if (retval != 0) { 3789 my_log(LOG_NOTICE, 3790 "IPMonitor apply_ipv4_route failed to add" 3791 " route, %s:", strerror(retval)); 3792 IPv4RouteLog(LOG_NOTICE, route); 3793 } 3794 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 3795 my_log(LOG_DEBUG, 3796 "Added IPv4 route new[%d] = ", 3797 route - context->new->list); 3798 IPv4RouteLog(LOG_DEBUG, route); 3799 } 3800 break; 3801 case kIPv4RouteListRemoveRouteCommand: 3802 retval = ipv4_route(context->sockfd, 3803 RTM_DELETE, route->gateway, 3804 route->dest, route->mask, ifn_p, route->ifindex, 3805 route->ifa, route->flags); 3806 if (retval != 0) { 3807 if (retval != ESRCH) { 3808 my_log(LOG_NOTICE, 3809 "IPMonitor apply_ipv4_route failed to remove" 3810 " route, %s: ", strerror(retval)); 3811 IPv4RouteLog(LOG_NOTICE, route); 3812 } 3813 } 3814 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 3815 my_log(LOG_DEBUG, 3816 "Removed IPv4 route old[%d] = ", 3817 route - context->old->list); 3818 IPv4RouteLog(LOG_DEBUG, route); 3819 } 3820 if ((route->flags & kRouteIsNotSubnetLocalFlag) != 0) { 3821 retval = ipv4_route_gateway(context->sockfd, RTM_DELETE, 3822 ifn_p, route); 3823 if (retval != 0) { 3824 my_log(LOG_NOTICE, 3825 "IPMonitor apply_ipv4_route failed to remove" 3826 " %s/32 route, %s: ", 3827 inet_ntoa(route->gateway), strerror(retval)); 3828 } 3829 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 3830 my_log(LOG_DEBUG, "Removed IPv4 Route %s/32", 3831 inet_ntoa(route->gateway)); 3832 } 3833 } 3834 break; 3835 default: 3836 break; 3837 } 3838 return; 3839} 3840#endif /* !TARGET_IPHONE_SIMULATOR */ 3841 3842/* 3843 * Function: update_ipv4 3844 * 3845 * Purpose: 3846 * Update the IPv4 configuration based on the latest information. 3847 * Publish the State:/Network/Global/IPv4 information, and update the 3848 * IPv4 routing table. IPv4RouteListApply() invokes our callback, 3849 * apply_ipv4_route(), to install/remove the routes. 3850 */ 3851static void 3852update_ipv4(CFStringRef primary, 3853 IPv4RouteListRef new_routelist, 3854 keyChangeListRef keys) 3855{ 3856#if !TARGET_IPHONE_SIMULATOR 3857 apply_ipv4_route_context_t context; 3858#endif /* !TARGET_IPHONE_SIMULATOR */ 3859 3860 if (keys != NULL) { 3861 if (new_routelist != NULL && primary != NULL) { 3862 char * ifn_p = NULL; 3863 IPv4RouteRef r; 3864 CFMutableDictionaryRef dict = NULL; 3865 3866 dict = CFDictionaryCreateMutable(NULL, 0, 3867 &kCFTypeDictionaryKeyCallBacks, 3868 &kCFTypeDictionaryValueCallBacks); 3869 /* the first entry is the default route */ 3870 r = new_routelist->list; 3871 if (r->gateway.s_addr != 0) { 3872 CFStringRef router; 3873 3874 router = CFStringCreateWithCString(NULL, 3875 inet_ntoa(r->gateway), 3876 kCFStringEncodingASCII); 3877 if (router != NULL) { 3878 CFDictionarySetValue(dict, kSCPropNetIPv4Router, router); 3879 CFRelease(router); 3880 } 3881 } 3882 if (r->ifname[0] != '\0') { 3883 ifn_p = r->ifname; 3884 } 3885 if (ifn_p != NULL) { 3886 CFStringRef ifname_cf; 3887 3888 ifname_cf = CFStringCreateWithCString(NULL, 3889 ifn_p, 3890 kCFStringEncodingASCII); 3891 if (ifname_cf != NULL) { 3892 CFDictionarySetValue(dict, 3893 kSCDynamicStorePropNetPrimaryInterface, 3894 ifname_cf); 3895 CFRelease(ifname_cf); 3896 } 3897 } 3898 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService, 3899 primary); 3900 keyChangeListSetValue(keys, S_state_global_ipv4, dict); 3901 CFRelease(dict); 3902 } 3903 else { 3904 keyChangeListRemoveValue(keys, S_state_global_ipv4); 3905 } 3906 } 3907 3908#if !TARGET_IPHONE_SIMULATOR 3909 bzero(&context, sizeof(context)); 3910 context.sockfd = route_open_socket(); 3911 if (context.sockfd != -1) { 3912 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 3913 if (S_ipv4_routelist == NULL) { 3914 my_log(LOG_DEBUG, "Old Routes = <none>"); 3915 } 3916 else { 3917 my_log(LOG_DEBUG, "Old Routes = "); 3918 IPv4RouteListLog(LOG_DEBUG, S_ipv4_routelist); 3919 } 3920 if (new_routelist == NULL) { 3921 my_log(LOG_DEBUG, "New Routes = <none>"); 3922 } 3923 else { 3924 my_log(LOG_DEBUG, "New Routes = "); 3925 IPv4RouteListLog(LOG_DEBUG, new_routelist); 3926 } 3927 } 3928 context.old = S_ipv4_routelist; 3929 context.new = new_routelist; 3930 IPv4RouteListApply(S_ipv4_routelist, new_routelist, 3931 &apply_ipv4_route, (void *)&context); 3932 if (new_routelist != NULL) { 3933 (void)multicast_route_delete(context.sockfd); 3934 } 3935 else { 3936 (void)multicast_route_add(context.sockfd); 3937 } 3938 close(context.sockfd); 3939 } 3940 if (S_ipv4_routelist != NULL) { 3941 free(S_ipv4_routelist); 3942 } 3943 S_ipv4_routelist = new_routelist; 3944#endif /* !TARGET_IPHONE_SIMULATOR */ 3945 3946 return; 3947} 3948 3949static void 3950update_ipv6(CFStringRef primary, 3951 keyChangeListRef keys) 3952{ 3953 CFDictionaryRef ipv6_dict = NULL; 3954 3955 if (primary != NULL) { 3956 ipv6_dict = service_dict_get(primary, kSCEntNetIPv6); 3957 } 3958 if (ipv6_dict != NULL) { 3959#if !TARGET_IPHONE_SIMULATOR 3960 CFArrayRef addrs; 3961#endif /* !TARGET_IPHONE_SIMULATOR */ 3962 CFMutableDictionaryRef dict = NULL; 3963 CFStringRef if_name = NULL; 3964#if !TARGET_IPHONE_SIMULATOR 3965 char ifn[IFNAMSIZ] = { '\0' }; 3966 char * ifn_p = NULL; 3967 boolean_t is_direct = FALSE; 3968#endif /* !TARGET_IPHONE_SIMULATOR */ 3969 CFStringRef val_router = NULL; 3970 3971 dict = CFDictionaryCreateMutable(NULL, 0, 3972 &kCFTypeDictionaryKeyCallBacks, 3973 &kCFTypeDictionaryValueCallBacks); 3974 3975#if !TARGET_IPHONE_SIMULATOR 3976 addrs = CFDictionaryGetValue(ipv6_dict, 3977 kSCPropNetIPv6Addresses); 3978#endif /* !TARGET_IPHONE_SIMULATOR */ 3979 3980 val_router = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Router); 3981 if (val_router != NULL) { 3982#if !TARGET_IPHONE_SIMULATOR 3983 is_direct = router_is_our_ipv6_address(val_router, addrs); 3984#endif /* !TARGET_IPHONE_SIMULATOR */ 3985 /* no router if router is one of our IP addresses */ 3986 CFDictionarySetValue(dict, kSCPropNetIPv6Router, 3987 val_router); 3988 } 3989#if !TARGET_IPHONE_SIMULATOR 3990 else { 3991 val_router = CFArrayGetValueAtIndex(addrs, 0); 3992 is_direct = TRUE; 3993 } 3994#endif /* !TARGET_IPHONE_SIMULATOR */ 3995 if_name = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName); 3996 if (if_name) { 3997 CFDictionarySetValue(dict, 3998 kSCDynamicStorePropNetPrimaryInterface, 3999 if_name); 4000#if !TARGET_IPHONE_SIMULATOR 4001 if (CFStringGetCString(if_name, ifn, sizeof(ifn), 4002 kCFStringEncodingASCII)) { 4003 ifn_p = ifn; 4004 } 4005#endif /* !TARGET_IPHONE_SIMULATOR */ 4006 } 4007 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService, 4008 primary); 4009 keyChangeListSetValue(keys, S_state_global_ipv6, dict); 4010 CFRelease(dict); 4011 4012#if !TARGET_IPHONE_SIMULATOR 4013#ifdef RTF_IFSCOPE 4014 if (S_scopedroute_v6) { 4015 set_ipv6_default_interface(ifn_p); 4016 } else 4017#endif /* RTF_IFSCOPE */ 4018 { /* route add default ... */ 4019 struct in6_addr router; 4020 4021 (void)cfstring_to_ip6(val_router, &router); 4022 set_ipv6_router(&router, ifn_p, is_direct); 4023 } 4024#endif /* !TARGET_IPHONE_SIMULATOR */ 4025 } 4026 else { 4027 keyChangeListRemoveValue(keys, S_state_global_ipv6); 4028#if !TARGET_IPHONE_SIMULATOR 4029#ifdef RTF_IFSCOPE 4030 if (S_scopedroute_v6) { 4031 set_ipv6_default_interface(NULL); 4032 } else 4033#endif /* RTF_IFSCOPE */ 4034 { /* route delete default ... */ 4035 set_ipv6_router(NULL, NULL, FALSE); 4036 } 4037#endif /* !TARGET_IPHONE_SIMULATOR */ 4038 } 4039 return; 4040} 4041 4042static Boolean 4043update_dns(CFDictionaryRef services_info, 4044 CFStringRef primary, 4045 keyChangeListRef keys) 4046{ 4047 Boolean changed = FALSE; 4048 CFDictionaryRef dict = NULL; 4049 4050 if (primary != NULL) { 4051 CFDictionaryRef service_dict; 4052 4053 service_dict = CFDictionaryGetValue(S_service_state_dict, primary); 4054 if (service_dict != NULL) { 4055 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS); 4056 } 4057 } 4058 4059 if (!_SC_CFEqual(S_dns_dict, dict)) { 4060 if (dict == NULL) { 4061#if !TARGET_OS_IPHONE 4062 empty_dns(); 4063#endif /* !TARGET_OS_IPHONE */ 4064 keyChangeListRemoveValue(keys, S_state_global_dns); 4065 } else { 4066 CFMutableDictionaryRef new_dict; 4067 4068#if !TARGET_OS_IPHONE 4069 set_dns(CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains), 4070 CFDictionaryGetValue(dict, kSCPropNetDNSDomainName), 4071 CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses), 4072 CFDictionaryGetValue(dict, kSCPropNetDNSSortList)); 4073#endif /* !TARGET_OS_IPHONE */ 4074 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict); 4075 CFDictionaryRemoveValue(new_dict, kSCPropInterfaceName); 4076 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchDomains); 4077 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchOrders); 4078 CFDictionaryRemoveValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY); 4079 keyChangeListSetValue(keys, S_state_global_dns, new_dict); 4080 CFRelease(new_dict); 4081 } 4082 changed = TRUE; 4083 } 4084 4085 if (dict != NULL) CFRetain(dict); 4086 if (S_dns_dict != NULL) CFRelease(S_dns_dict); 4087 S_dns_dict = dict; 4088 4089 return changed; 4090} 4091 4092static Boolean 4093update_dnsinfo(CFDictionaryRef services_info, 4094 CFStringRef primary, 4095 keyChangeListRef keys, 4096 CFArrayRef service_order) 4097{ 4098 Boolean changed; 4099 CFDictionaryRef dict = NULL; 4100 CFArrayRef multicastResolvers; 4101 CFArrayRef privateResolvers; 4102 4103 multicastResolvers = CFDictionaryGetValue(services_info, S_multicast_resolvers); 4104 privateResolvers = CFDictionaryGetValue(services_info, S_private_resolvers); 4105 4106 if (primary != NULL) { 4107 CFDictionaryRef service_dict; 4108 4109 service_dict = CFDictionaryGetValue(S_service_state_dict, primary); 4110 if (service_dict != NULL) { 4111 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS); 4112 } 4113 } 4114 4115 changed = dns_configuration_set(dict, 4116 S_service_state_dict, 4117 service_order, 4118 multicastResolvers, 4119 privateResolvers); 4120 if (changed) { 4121 keyChangeListNotifyKey(keys, S_state_global_dns); 4122 } 4123 return changed; 4124} 4125 4126static Boolean 4127update_nwi(nwi_state_t state) 4128{ 4129 unsigned char signature[CC_SHA1_DIGEST_LENGTH]; 4130 static unsigned char signature_last[CC_SHA1_DIGEST_LENGTH]; 4131 4132 _nwi_state_signature(state, signature, sizeof(signature)); 4133 if (bcmp(signature, signature_last, sizeof(signature)) == 0) { 4134 return FALSE; 4135 } 4136 4137 // save [new] signature 4138 bcopy(signature, signature_last, sizeof(signature)); 4139 4140 // save [new] configuration 4141 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 4142 my_log(LOG_DEBUG, "Updating network information"); 4143 S_nwi_state_dump(state); 4144 } 4145 if (_nwi_state_store(state) == FALSE) { 4146 my_log(LOG_ERR, "Notifying nwi_state_store failed"); 4147 } 4148 4149 return TRUE; 4150} 4151 4152static Boolean 4153update_proxies(CFDictionaryRef services_info, 4154 CFStringRef primary, 4155 keyChangeListRef keys, 4156 CFArrayRef service_order) 4157{ 4158 Boolean changed = FALSE; 4159 CFDictionaryRef dict = NULL; 4160 CFDictionaryRef new_dict; 4161 4162 if (primary != NULL) { 4163 CFDictionaryRef service_dict; 4164 4165 service_dict = CFDictionaryGetValue(S_service_state_dict, primary); 4166 if (service_dict != NULL) { 4167 dict = CFDictionaryGetValue(service_dict, kSCEntNetProxies); 4168 } 4169 } 4170 4171 new_dict = proxy_configuration_update(dict, 4172 S_service_state_dict, 4173 service_order, 4174 services_info); 4175 if (!_SC_CFEqual(S_proxies_dict, new_dict)) { 4176 if (new_dict == NULL) { 4177 keyChangeListRemoveValue(keys, S_state_global_proxies); 4178 } else { 4179 keyChangeListSetValue(keys, S_state_global_proxies, new_dict); 4180 } 4181 changed = TRUE; 4182 } 4183 4184 if (S_proxies_dict != NULL) CFRelease(S_proxies_dict); 4185 S_proxies_dict = new_dict; 4186 4187 return changed; 4188} 4189 4190#if !TARGET_OS_IPHONE 4191static Boolean 4192update_smb(CFDictionaryRef services_info, 4193 CFStringRef primary, 4194 keyChangeListRef keys) 4195{ 4196 Boolean changed = FALSE; 4197 CFDictionaryRef dict = NULL; 4198 4199 if (primary != NULL) { 4200 CFDictionaryRef service_dict; 4201 4202 service_dict = CFDictionaryGetValue(S_service_state_dict, primary); 4203 if (service_dict != NULL) { 4204 dict = CFDictionaryGetValue(service_dict, kSCEntNetSMB); 4205 } 4206 } 4207 4208 if (!_SC_CFEqual(S_smb_dict, dict)) { 4209 if (dict == NULL) { 4210 keyChangeListRemoveValue(keys, S_state_global_smb); 4211 } else { 4212 keyChangeListSetValue(keys, S_state_global_smb, dict); 4213 } 4214 changed = TRUE; 4215 } 4216 4217 if (dict != NULL) CFRetain(dict); 4218 if (S_smb_dict != NULL) CFRelease(S_smb_dict); 4219 S_smb_dict = dict; 4220 4221 return changed; 4222} 4223#endif /* !TARGET_OS_IPHONE */ 4224 4225static Rank 4226get_service_rank(CFArrayRef order, int n_order, CFStringRef serviceID) 4227{ 4228 CFIndex i; 4229 Rank rank = kRankIndexMask; 4230 4231 if (serviceID != NULL && order != NULL && n_order > 0) { 4232 for (i = 0; i < n_order; i++) { 4233 CFStringRef s = isA_CFString(CFArrayGetValueAtIndex(order, i)); 4234 4235 if (s == NULL) { 4236 continue; 4237 } 4238 if (CFEqual(serviceID, s)) { 4239 rank = i + 1; 4240 break; 4241 } 4242 } 4243 } 4244 return (rank); 4245} 4246 4247/** 4248 ** Service election: 4249 **/ 4250/* 4251 * Function: rank_dict_get_service_rank 4252 * Purpose: 4253 * Retrieve the service rank in the given dictionary. 4254 */ 4255static Rank 4256rank_dict_get_service_rank(CFDictionaryRef rank_dict, CFStringRef serviceID) 4257{ 4258 CFNumberRef rank; 4259 Rank rank_val = RankMake(kRankIndexMask, kRankAssertionDefault); 4260 4261 rank = CFDictionaryGetValue(rank_dict, serviceID); 4262 if (rank != NULL) { 4263 CFNumberGetValue(rank, kCFNumberSInt32Type, &rank_val); 4264 } 4265 return (rank_val); 4266} 4267 4268/* 4269 * Function: rank_dict_set_service_rank 4270 * Purpose: 4271 * Save the results of ranking the service so we can look it up later without 4272 * repeating all of the ranking code. 4273 */ 4274static void 4275rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict, 4276 CFStringRef serviceID, Rank rank_val) 4277{ 4278 CFNumberRef rank; 4279 4280 rank = CFNumberCreate(NULL, kCFNumberSInt32Type, (const void *)&rank_val); 4281 if (rank != NULL) { 4282 CFDictionarySetValue(rank_dict, serviceID, rank); 4283 CFRelease(rank); 4284 } 4285 return; 4286} 4287 4288static const CFStringRef *transientInterfaceEntityNames[] = { 4289 &kSCEntNetPPP, 4290}; 4291 4292 4293static void 4294CollectTransientServices(const void * key, 4295 const void * value, 4296 void * context) 4297{ 4298 int i; 4299 CFStringRef service = key; 4300 CFMutableArrayRef vif_setup_keys = context; 4301 4302 /* This service is either a vpn type service or a comm center service */ 4303 if (!CFStringHasPrefix(service, kSCDynamicStoreDomainSetup)) { 4304 return; 4305 } 4306 4307 for (i = 0; i < sizeof(transientInterfaceEntityNames)/sizeof(transientInterfaceEntityNames[0]); i++) { 4308 if (!CFStringHasSuffix(service, *transientInterfaceEntityNames[i])) { 4309 continue; 4310 } 4311 4312 CFArrayAppendValue(vif_setup_keys, service); 4313 } 4314 return; 4315} 4316 4317 4318static SCNetworkReachabilityFlags 4319GetReachabilityFlagsFromVPN(CFDictionaryRef services_info, 4320 CFStringRef service_id, 4321 CFStringRef entity, 4322 CFStringRef vpn_setup_key) 4323{ 4324 CFStringRef key; 4325 CFDictionaryRef dict; 4326 SCNetworkReachabilityFlags flags = 0; 4327 4328 4329 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 4330 kSCDynamicStoreDomainSetup, 4331 service_id, 4332 kSCEntNetInterface); 4333 dict = CFDictionaryGetValue(services_info, key); 4334 CFRelease(key); 4335 4336 if (isA_CFDictionary(dict) 4337 && CFDictionaryContainsKey(dict, kSCPropNetInterfaceDeviceName)) { 4338 4339 flags = (kSCNetworkReachabilityFlagsReachable 4340 | kSCNetworkReachabilityFlagsTransientConnection 4341 | kSCNetworkReachabilityFlagsConnectionRequired); 4342 4343 if (CFEqual(entity, kSCEntNetPPP)) { 4344 CFNumberRef num; 4345 CFDictionaryRef p_dict = CFDictionaryGetValue(services_info, vpn_setup_key); 4346 4347 if (!isA_CFDictionary(p_dict)) { 4348 return (flags); 4349 } 4350 4351 // get PPP dial-on-traffic status 4352 num = CFDictionaryGetValue(p_dict, kSCPropNetPPPDialOnDemand); 4353 if (isA_CFNumber(num)) { 4354 int32_t ppp_demand; 4355 4356 if (CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand)) { 4357 if (ppp_demand) { 4358 flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic; 4359 } 4360 } 4361 } 4362 } 4363 } 4364 return (flags); 4365} 4366 4367static Boolean 4368S_dict_get_boolean(CFDictionaryRef dict, CFStringRef key, Boolean def_value) 4369{ 4370 Boolean ret = def_value; 4371 4372 if (dict != NULL) { 4373 CFBooleanRef val; 4374 4375 val = CFDictionaryGetValue(dict, key); 4376 if (isA_CFBoolean(val) != NULL) { 4377 ret = CFBooleanGetValue(val); 4378 } 4379 } 4380 return (ret); 4381} 4382 4383 4384static void 4385GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info, 4386 SCNetworkReachabilityFlags *reach_flags_v4, 4387 SCNetworkReachabilityFlags *reach_flags_v6) 4388{ 4389 int i; 4390 int count; 4391 CFMutableArrayRef vif_setup_keys; 4392 4393 vif_setup_keys = CFArrayCreateMutable(NULL, 4394 0, 4395 &kCFTypeArrayCallBacks); 4396 4397 CFDictionaryApplyFunction(services_info, CollectTransientServices, vif_setup_keys); 4398 4399 count = CFArrayGetCount(vif_setup_keys); 4400 4401 if (count != 0) { 4402 my_log(LOG_DEBUG, "Collected the following VIF Setup Keys: %@", vif_setup_keys); 4403 } 4404 4405 for (i = 0; i < count; i++) { 4406 CFArrayRef components = NULL; 4407 CFStringRef entity; 4408 CFStringRef service_id; 4409 CFStringRef vif_setup_key; 4410 4411 vif_setup_key = CFArrayGetValueAtIndex(vif_setup_keys, i); 4412 4413 /* 4414 * setup key in the following format: 4415 * Setup:/Network/Service/<Service ID>/<Entity> 4416 */ 4417 components = CFStringCreateArrayBySeparatingStrings(NULL, vif_setup_key, CFSTR("/")); 4418 4419 if (CFArrayGetCount(components) != 5) { 4420 my_log(LOG_ERR, "Invalid Setup Key encountered: %@", vif_setup_key); 4421 goto skip; 4422 } 4423 4424 /* service id is the 3rd element */ 4425 service_id = CFArrayGetValueAtIndex(components, 3); 4426 4427 /* entity id is the 4th element */ 4428 entity = CFArrayGetValueAtIndex(components, 4); 4429 4430 my_log(LOG_DEBUG, "Service %@ is a %@ Entity", service_id, entity); 4431 4432 4433 if (CFEqual(entity, kSCEntNetPPP)) { 4434 SCNetworkReachabilityFlags flags; 4435 CFStringRef key; 4436 4437 flags = GetReachabilityFlagsFromVPN(services_info, 4438 service_id, 4439 entity, 4440 vif_setup_key); 4441 4442 /* Check for the v4 reachability flags */ 4443 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 4444 kSCDynamicStoreDomainSetup, 4445 service_id, 4446 kSCEntNetIPv4); 4447 4448 if (CFDictionaryContainsKey(services_info, key)) { 4449 *reach_flags_v4 |= flags; 4450 my_log(LOG_DEBUG,"Service %@ setting ipv4 reach flags: %d", service_id, *reach_flags_v4); 4451 } 4452 4453 CFRelease(key); 4454 4455 /* Check for the v6 reachability flags */ 4456 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 4457 kSCDynamicStoreDomainSetup, 4458 service_id, 4459 kSCEntNetIPv6); 4460 4461 if (CFDictionaryContainsKey(services_info, key)) { 4462 *reach_flags_v6 |= flags; 4463 my_log(LOG_DEBUG,"Service %@ setting ipv6 reach flags: %d", service_id, *reach_flags_v6); 4464 } 4465 CFRelease(key); 4466 4467 if (flags != 0) { 4468 if (components != NULL) { 4469 CFRelease(components); 4470 } 4471 goto done; 4472 } 4473 } 4474skip: 4475 if (components != NULL) { 4476 CFRelease(components); 4477 } 4478 } 4479done: 4480 CFRelease(vif_setup_keys); 4481 return; 4482} 4483 4484static SCNetworkReachabilityFlags 4485GetReachFlagsFromStatus(CFStringRef entity, int status) 4486{ 4487 SCNetworkReachabilityFlags flags = 0; 4488 4489 if (CFEqual(entity, kSCEntNetPPP)) { 4490 switch (status) { 4491 case PPP_RUNNING : 4492 /* if we're really UP and RUNNING */ 4493 break; 4494 case PPP_ONHOLD : 4495 /* if we're effectively UP and RUNNING */ 4496 break; 4497 case PPP_IDLE : 4498 /* if we're not connected at all */ 4499 my_log(LOG_INFO, "PPP link idle"); 4500 flags |= kSCNetworkReachabilityFlagsConnectionRequired; 4501 break; 4502 case PPP_STATERESERVED : 4503 // if we're not connected at all 4504 my_log(LOG_INFO, "PPP link idle, dial-on-traffic to connect"); 4505 flags |= kSCNetworkReachabilityFlagsConnectionRequired; 4506 break; 4507 default : 4508 /* if we're in the process of [dis]connecting */ 4509 my_log(LOG_INFO, "PPP link, connection in progress"); 4510 flags |= kSCNetworkReachabilityFlagsConnectionRequired; 4511 break; 4512 } 4513 } 4514#ifdef HAVE_IPSEC_STATUS 4515 else if (CFEqual(entity, kSCEntNetIPSec)) { 4516 switch (status) { 4517 case IPSEC_RUNNING : 4518 /* if we're really UP and RUNNING */ 4519 break; 4520 case IPSEC_IDLE : 4521 /* if we're not connected at all */ 4522 my_log(LOG_INFO, "IPSec link idle"); 4523 flags |= kSCNetworkReachabilityFlagsConnectionRequired; 4524 break; 4525 default : 4526 /* if we're in the process of [dis]connecting */ 4527 my_log(LOG_INFO, "IPSec link, connection in progress"); 4528 flags |= kSCNetworkReachabilityFlagsConnectionRequired; 4529 break; 4530 } 4531 } 4532#endif // HAVE_IPSEC_STATUS 4533#ifdef HAVE_VPN_STATUS 4534 else if (CFEqual(entity, kSCEntNetVPN)) { 4535 switch (status) { 4536 case VPN_RUNNING : 4537 /* if we're really UP and RUNNING */ 4538 break; 4539 case VPN_IDLE : 4540 case VPN_LOADING : 4541 case VPN_LOADED : 4542 case VPN_UNLOADING : 4543 /* if we're not connected at all */ 4544 my_log(LOG_INFO, "%s VPN link idle"); 4545 flags |= kSCNetworkReachabilityFlagsConnectionRequired; 4546 break; 4547 default : 4548 /* if we're in the process of [dis]connecting */ 4549 my_log(LOG_INFO, "VPN link, connection in progress"); 4550 flags |= kSCNetworkReachabilityFlagsConnectionRequired; 4551 break; 4552 } 4553 } 4554#endif // HAVE_VPN_STATUS 4555 return (flags); 4556} 4557 4558static void 4559VPNAttributesGet(CFStringRef service_id, 4560 CFDictionaryRef services_info, 4561 SCNetworkReachabilityFlags *flags, 4562 CFStringRef *server_address, 4563 int af) 4564{ 4565 int i; 4566 CFDictionaryRef entity_dict; 4567 boolean_t found = FALSE; 4568 CFNumberRef num; 4569 CFDictionaryRef p_state = NULL; 4570 int status = 0; 4571 4572 /* if the IPv[4/6] exist */ 4573 entity_dict = service_dict_get(service_id, (af == AF_INET) ? kSCEntNetIPv4 : kSCEntNetIPv6); 4574 if (!isA_CFDictionary(entity_dict)) { 4575 return; 4576 } 4577 4578 if (af == AF_INET) { 4579 entity_dict = CFDictionaryGetValue(entity_dict, kIPv4DictService); 4580 if (!isA_CFDictionary(entity_dict)) { 4581 return; 4582 } 4583 } 4584 4585 for (i = 0; i < sizeof(statusEntityNames)/sizeof(statusEntityNames[0]); i++) { 4586 p_state = service_dict_get(service_id, *statusEntityNames[i]); 4587 /* ensure that this is a VPN Type service */ 4588 if (isA_CFDictionary(p_state)) { 4589 found = TRUE; 4590 break; 4591 } 4592 } 4593 4594 /* Did we find a vpn type service? If not, we are done.*/ 4595 if (!found) { 4596 return; 4597 } 4598 4599 *flags |= (kSCNetworkReachabilityFlagsReachable| kSCNetworkReachabilityFlagsTransientConnection); 4600 4601 /* Get the Server Address */ 4602 if (server_address != NULL) { 4603 *server_address = CFDictionaryGetValue(entity_dict, CFSTR("ServerAddress")); 4604 *server_address = isA_CFString(*server_address); 4605 if (*server_address != NULL) { 4606 CFRetain(*server_address); 4607 } 4608 } 4609 4610 /* get status */ 4611 if (!CFDictionaryGetValueIfPresent(p_state, 4612 kSCPropNetVPNStatus, 4613 (const void **)&num) || 4614 !isA_CFNumber(num) || 4615 !CFNumberGetValue(num, kCFNumberSInt32Type, &status)) { 4616 return; 4617 } 4618 4619 *flags |= GetReachFlagsFromStatus(*statusEntityNames[i], status); 4620 4621 if (CFEqual(*statusEntityNames[i], kSCEntNetPPP)) { 4622 CFStringRef key; 4623 CFDictionaryRef p_setup; 4624 int ppp_demand; 4625 4626 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 4627 kSCDynamicStoreDomainSetup, 4628 service_id, 4629 kSCEntNetPPP); 4630 p_setup = CFDictionaryGetValue(services_info, key); 4631 CFRelease(key); 4632 4633 /* get dial-on-traffic status */ 4634 if (isA_CFDictionary(p_setup) && 4635 CFDictionaryGetValueIfPresent(p_setup, 4636 kSCPropNetPPPDialOnDemand, 4637 (const void **)&num) && 4638 isA_CFNumber(num) && 4639 CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand) && 4640 (ppp_demand != 0)) { 4641 *flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic; 4642 if (status == PPP_IDLE) { 4643 *flags |= kSCNetworkReachabilityFlagsInterventionRequired; 4644 } 4645 } 4646 } 4647 return; 4648} 4649 4650 4651typedef struct ElectionInfo { 4652 int n_services; 4653 CFArrayRef order; 4654 int n_order; 4655 ElectionResultsRef results; 4656} ElectionInfo, * ElectionInfoRef; 4657 4658typedef CFDictionaryApplierFunction ElectionFuncRef; 4659 4660static void 4661CandidateRelease(CandidateRef candidate) 4662{ 4663 my_CFRelease(&candidate->serviceID); 4664 my_CFRelease(&candidate->if_name); 4665 my_CFRelease(&candidate->signature); 4666 return; 4667} 4668 4669static void 4670CandidateCopy(CandidateRef dest, CandidateRef src) 4671{ 4672 *dest = *src; 4673 if (dest->serviceID) { 4674 CFRetain(dest->serviceID); 4675 } 4676 if (dest->if_name) { 4677 CFRetain(dest->if_name); 4678 } 4679 if(dest->signature) { 4680 CFRetain(dest->signature); 4681 } 4682 return; 4683} 4684 4685static ElectionResultsRef 4686ElectionResultsAlloc(int size) 4687{ 4688 ElectionResultsRef results; 4689 4690 results = (ElectionResultsRef)malloc(ElectionResultsComputeSize(size)); 4691 results->count = 0; 4692 results->size = size; 4693 return (results); 4694} 4695 4696static void 4697ElectionResultsRelease(ElectionResultsRef results) 4698{ 4699 int i; 4700 CandidateRef scan; 4701 4702 for (i = 0, scan = results->candidates; 4703 i < results->count; 4704 i++, scan++) { 4705 CandidateRelease(scan); 4706 } 4707 free(results); 4708 return; 4709} 4710 4711static void 4712ElectionResultsLog(int level, ElectionResultsRef results, const char * prefix) 4713{ 4714 int i; 4715 CandidateRef scan; 4716 4717 if (results == NULL) { 4718 my_log(level, "%s: no candidates", prefix); 4719 return; 4720 } 4721 my_log(level, "%s: %d candidates", prefix, results->count); 4722 for (i = 0, scan = results->candidates; 4723 i < results->count; 4724 i++, scan++) { 4725 my_log(level, "%d. %@ Rank=0x%x serviceID=%@", i, scan->if_name, 4726 scan->rank, scan->serviceID); 4727 } 4728 return; 4729} 4730 4731/* 4732 * Function: ElectionResultsAddCandidate 4733 * Purpose: 4734 * Add the candidate into the election results. Find the insertion point 4735 * by comparing the rank of the candidate with existing entries. 4736 */ 4737static void 4738ElectionResultsAddCandidate(ElectionResultsRef results, CandidateRef candidate) 4739{ 4740 int i; 4741 int where; 4742 4743#define BAD_INDEX (-1) 4744 if (results->count == results->size) { 4745 /* this should not happen */ 4746 my_log(LOG_NOTICE, "can't fit another candidate"); 4747 return; 4748 } 4749 4750 /* find the insertion point */ 4751 where = BAD_INDEX; 4752 for (i = 0; i < results->count; i++) { 4753 CandidateRef this_candidate = results->candidates + i; 4754 4755 if (candidate->rank < this_candidate->rank) { 4756 where = i; 4757 break; 4758 } 4759 } 4760 /* add it to the end */ 4761 if (where == BAD_INDEX) { 4762 CandidateCopy(results->candidates + results->count, candidate); 4763 results->count++; 4764 return; 4765 } 4766 /* slide existing entries over */ 4767 for (i = results->count; i > where; i--) { 4768 results->candidates[i] = results->candidates[i - 1]; 4769 } 4770 /* insert element */ 4771 CandidateCopy(results->candidates + where, candidate); 4772 results->count++; 4773 return; 4774} 4775 4776/* 4777 * Function: ElectionResultsCopy 4778 * Purpose: 4779 * Visit all of the services and invoke the protocol-specific election 4780 * function. Return the results of the election. 4781 */ 4782static ElectionResultsRef 4783ElectionResultsCopy(ElectionFuncRef elect_func, CFArrayRef order, int n_order) 4784{ 4785 int count; 4786 ElectionInfo info; 4787 4788 count = CFDictionaryGetCount(S_service_state_dict); 4789 if (count == 0) { 4790 return (NULL); 4791 } 4792 info.results = ElectionResultsAlloc(count); 4793 info.n_services = count; 4794 info.order = order; 4795 info.n_order = n_order; 4796 CFDictionaryApplyFunction(S_service_state_dict, elect_func, (void *)&info); 4797 if (info.results->count == 0) { 4798 ElectionResultsRelease(info.results); 4799 info.results = NULL; 4800 } 4801 return (info.results); 4802} 4803 4804/* 4805 * Function: ElectionResultsCandidateNeedsDemotion 4806 * Purpose: 4807 * Check whether the given candidate requires demotion. A candidate 4808 * might need to be demoted if its IPv4 and IPv6 services must be coupled 4809 * but a higher ranked service has IPv4 or IPv6. 4810 */ 4811static Boolean 4812ElectionResultsCandidateNeedsDemotion(ElectionResultsRef other_results, 4813 CandidateRef candidate) 4814{ 4815 CandidateRef other_candidate; 4816 Boolean ret = FALSE; 4817 4818 if (other_results == NULL 4819 || candidate->ip_is_coupled == FALSE 4820 || RANK_ASSERTION_MASK(candidate->rank) == kRankAssertionNever) { 4821 goto done; 4822 } 4823 other_candidate = other_results->candidates; 4824 if (CFEqual(other_candidate->if_name, candidate->if_name)) { 4825 /* they are over the same interface, no need to demote */ 4826 goto done; 4827 } 4828 if (CFStringHasPrefix(other_candidate->if_name, CFSTR("stf"))) { 4829 /* avoid creating a feedback loop */ 4830 goto done; 4831 } 4832 if (RANK_ASSERTION_MASK(other_candidate->rank) == kRankAssertionNever) { 4833 /* the other candidate isn't eligible to become primary, ignore */ 4834 goto done; 4835 } 4836 if (candidate->rank < other_candidate->rank) { 4837 /* we're higher ranked than the other candidate, ignore */ 4838 goto done; 4839 } 4840 ret = TRUE; 4841 4842 done: 4843 return (ret); 4844 4845} 4846 4847 4848static void 4849get_signature_sha1(CFStringRef signature, 4850 unsigned char * sha1) 4851{ 4852 CC_SHA1_CTX ctx; 4853 CFDataRef signature_data; 4854 4855 signature_data = CFStringCreateExternalRepresentation(NULL, 4856 signature, 4857 kCFStringEncodingUTF8, 4858 0); 4859 4860 CC_SHA1_Init(&ctx); 4861 CC_SHA1_Update(&ctx, 4862 signature_data, 4863 CFDataGetLength(signature_data)); 4864 CC_SHA1_Final(sha1, &ctx); 4865 4866 CFRelease(signature_data); 4867 4868 return; 4869} 4870 4871 4872static void 4873add_candidate_to_nwi_state(nwi_state_t nwi_state, int af, 4874 CandidateRef candidate, Rank rank) 4875{ 4876 uint64_t flags = 0; 4877 char ifname[IFNAMSIZ]; 4878 nwi_ifstate_t ifstate; 4879 4880 if (nwi_state == NULL) { 4881 /* can't happen */ 4882 return; 4883 } 4884 if (RANK_ASSERTION_MASK(rank) == kRankAssertionNever) { 4885 flags |= NWI_IFSTATE_FLAGS_NOT_IN_LIST; 4886 } 4887 if (service_dict_get(candidate->serviceID, kSCEntNetDNS) != NULL) { 4888 flags |= NWI_IFSTATE_FLAGS_HAS_DNS; 4889 } 4890 CFStringGetCString(candidate->if_name, ifname, sizeof(ifname), 4891 kCFStringEncodingASCII); 4892 if ((S_IPMonitor_debug & kDebugFlag2) != 0) { 4893 my_log(LOG_DEBUG, 4894 "Inserting IPv%c [%s] with flags 0x%x primary_rank 0x%x reach_flags %d", 4895 ipvx_char(af), ifname, rank, candidate->reachability_flags); 4896 } 4897 ifstate = nwi_insert_ifstate(nwi_state, ifname, af, flags, rank, 4898 (void *)&candidate->addr, 4899 (void *)&candidate->vpn_server_addr, 4900 candidate->reachability_flags); 4901 if (ifstate != NULL && candidate->signature) { 4902 uint8_t hash[CC_SHA1_DIGEST_LENGTH]; 4903 4904 get_signature_sha1(candidate->signature, hash); 4905 nwi_ifstate_set_signature(ifstate, hash); 4906 } 4907 return; 4908} 4909 4910 4911static void 4912add_reachability_flags_to_candidate(CandidateRef candidate, CFDictionaryRef services_info, int af) 4913{ 4914 SCNetworkReachabilityFlags flags = kSCNetworkReachabilityFlagsReachable; 4915 CFStringRef vpn_server_address = NULL; 4916 4917 VPNAttributesGet(candidate->serviceID, 4918 services_info, 4919 &flags, 4920 &vpn_server_address, 4921 af); 4922 4923 candidate->reachability_flags = flags; 4924 4925 if (vpn_server_address == NULL) { 4926 bzero(&candidate->vpn_server_addr, sizeof(candidate->vpn_server_addr)); 4927 } else { 4928 char buf[128]; 4929 CFStringGetCString(vpn_server_address, buf, sizeof(buf), kCFStringEncodingASCII); 4930 4931 _SC_string_to_sockaddr(buf, 4932 AF_UNSPEC, 4933 (void *)&candidate->vpn_server_addr, 4934 sizeof(candidate->vpn_server_addr)); 4935 4936 CFRelease(vpn_server_address); 4937 } 4938 return; 4939} 4940/* 4941 * Function: ElectionResultsCopyPrimary 4942 * Purpose: 4943 * Use the results of the current protocol and the other protocol to 4944 * determine which service should become primary. 4945 * 4946 * At the same time, generate the nwi_state for the protocol. 4947 * 4948 * For IPv4, also generate the IPv4 routing table. 4949 */ 4950static CFStringRef 4951ElectionResultsCopyPrimary(ElectionResultsRef results, 4952 ElectionResultsRef other_results, 4953 nwi_state_t nwi_state, int af, 4954 IPv4RouteListRef * ret_routes, 4955 CFDictionaryRef services_info) 4956{ 4957 CFStringRef primary = NULL; 4958 Boolean primary_is_null = FALSE; 4959 IPv4RouteListRef routes = NULL; 4960 4961 if (nwi_state != NULL) { 4962 nwi_state_clear(nwi_state, af); 4963 } 4964 if (results != NULL) { 4965 CandidateRef deferred[results->count]; 4966 int deferred_count; 4967 int i; 4968 CandidateRef scan; 4969 4970 deferred_count = 0; 4971 for (i = 0, scan = results->candidates; 4972 i < results->count; 4973 i++, scan++) { 4974 Boolean is_primary = FALSE; 4975 Rank rank = scan->rank; 4976 Boolean skip = FALSE; 4977 4978 if (primary == NULL 4979 && RANK_ASSERTION_MASK(rank) != kRankAssertionNever) { 4980 if (ElectionResultsCandidateNeedsDemotion(other_results, 4981 scan)) { 4982 /* demote to RankNever */ 4983 my_log(LOG_NOTICE, 4984 "IPv%c over %@ demoted: not primary for IPv%c", 4985 ipvx_char(af), scan->if_name, ipvx_other_char(af)); 4986 rank = RankMake(rank, kRankAssertionNever); 4987 deferred[deferred_count++] = scan; 4988 skip = TRUE; 4989 } 4990 else { 4991 primary = CFRetain(scan->serviceID); 4992 is_primary = TRUE; 4993 } 4994 } 4995 if (af == AF_INET) { 4996 /* generate the routing table for IPv4 */ 4997 CFDictionaryRef service_dict; 4998 IPv4RouteListRef service_routes; 4999 5000 service_dict 5001 = service_dict_get(scan->serviceID, kSCEntNetIPv4); 5002 service_routes = ipv4_dict_get_routelist(service_dict); 5003 if (service_routes != NULL) { 5004 routes = IPv4RouteListAddRouteList(routes, 5005 results->count * 3, 5006 service_routes, 5007 rank); 5008 if (service_routes->exclude_from_nwi) { 5009 skip = TRUE; 5010 } 5011 } 5012 else { 5013 skip = TRUE; 5014 } 5015 } 5016 else { 5017 /* a NULL service must be excluded from nwi */ 5018 CFDictionaryRef ipv6_dict; 5019 5020 ipv6_dict = service_dict_get(scan->serviceID, kSCEntNetIPv6); 5021 5022 if (S_dict_get_boolean(ipv6_dict, kIsNULL, FALSE)) { 5023 skip = TRUE; 5024 } 5025 } 5026 if (skip) { 5027 /* if we're skipping the primary, it's NULL */ 5028 if (is_primary) { 5029 primary_is_null = TRUE; 5030 } 5031 } 5032 else { 5033 if (primary_is_null) { 5034 /* everything after the primary must be Never */ 5035 rank = RankMake(rank, kRankAssertionNever); 5036 } 5037 add_reachability_flags_to_candidate(scan, services_info, af); 5038 add_candidate_to_nwi_state(nwi_state, af, scan, rank); 5039 } 5040 } 5041 for (i = 0; i < deferred_count; i++) { 5042 CandidateRef candidate = deferred[i]; 5043 Rank rank; 5044 5045 /* demote to RankNever */ 5046 rank = RankMake(candidate->rank, kRankAssertionNever); 5047 add_reachability_flags_to_candidate(candidate, services_info, af); 5048 add_candidate_to_nwi_state(nwi_state, af, candidate, rank); 5049 } 5050 } 5051 if (nwi_state != NULL) { 5052 nwi_state_set_last(nwi_state, af); 5053 } 5054 if (ret_routes != NULL) { 5055 *ret_routes = routes; 5056 } 5057 else if (routes != NULL) { 5058 free(routes); 5059 } 5060 if (primary_is_null) { 5061 my_CFRelease(&primary); 5062 } 5063 return (primary); 5064} 5065 5066 5067static inline 5068CFStringRef 5069service_dict_get_signature(CFDictionaryRef service_dict) 5070{ 5071 return (CFDictionaryGetValue(service_dict, kStoreKeyNetworkSignature)); 5072} 5073 5074 5075/* 5076 * Function: elect_ipv4 5077 * Purpose: 5078 * Evaluate the service and determine what rank the service should have. 5079 * If it's a suitable candidate, add it to the election results. 5080 */ 5081static void 5082elect_ipv4(const void * key, const void * value, void * context) 5083{ 5084 Candidate candidate; 5085 CFStringRef if_name; 5086 ElectionInfoRef info; 5087 Rank primary_rank; 5088 CFDictionaryRef service_dict = (CFDictionaryRef)value; 5089 IPv4RouteListRef service_routes; 5090 CFDictionaryRef v4_dict; 5091 CFDictionaryRef v4_service_dict; 5092 5093 service_routes = service_dict_get_ipv4_routelist(service_dict); 5094 if (service_routes == NULL) { 5095 /* no service routes, no service */ 5096 return; 5097 } 5098 if_name = service_dict_get_ipv4_ifname(service_dict); 5099 if (if_name == NULL) { 5100 /* need an interface name */ 5101 return; 5102 } 5103 if (CFEqual(if_name, CFSTR("lo0"))) { 5104 /* don't ever elect loopback */ 5105 return; 5106 } 5107 info = (ElectionInfoRef)context; 5108 bzero(&candidate, sizeof(candidate)); 5109 candidate.serviceID = (CFStringRef)key; 5110 candidate.rank = get_service_rank(info->order, info->n_order, 5111 candidate.serviceID); 5112 primary_rank = RANK_ASSERTION_MASK(service_routes->list->rank); 5113 if (S_ppp_override_primary 5114 && (strncmp(PPP_PREFIX, service_routes->list->ifname, 5115 sizeof(PPP_PREFIX) - 1) == 0)) { 5116 /* PPP override: make ppp* look the best */ 5117 /* Hack: should use interface type, not interface name */ 5118 primary_rank = kRankAssertionFirst; 5119 } 5120 candidate.rank = RankMake(candidate.rank, primary_rank); 5121 candidate.ip_is_coupled = service_get_ip_is_coupled(candidate.serviceID); 5122 candidate.if_name = if_name; 5123 candidate.addr.v4 = service_routes->list->ifa; 5124 rank_dict_set_service_rank(S_ipv4_service_rank_dict, 5125 candidate.serviceID, candidate.rank); 5126 v4_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4); 5127 v4_service_dict = CFDictionaryGetValue(v4_dict, kIPv4DictService); 5128 candidate.signature = service_dict_get_signature(v4_service_dict); 5129 ElectionResultsAddCandidate(info->results, &candidate); 5130 return; 5131} 5132 5133 5134/* 5135 * Function: elect_ipv6 5136 * Purpose: 5137 * Evaluate the service and determine what rank the service should have. 5138 * If it's a suitable candidate, add it to the election results. 5139 */ 5140static void 5141elect_ipv6(const void * key, const void * value, void * context) 5142{ 5143 CFArrayRef addrs; 5144 Candidate candidate; 5145 CFStringRef if_name; 5146 ElectionInfoRef info; 5147 Rank primary_rank = kRankAssertionDefault; 5148 CFDictionaryRef ipv6_dict; 5149 CFStringRef router; 5150 CFDictionaryRef service_dict = (CFDictionaryRef)value; 5151 CFDictionaryRef service_options; 5152 5153 5154 ipv6_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6); 5155 if (ipv6_dict == NULL) { 5156 /* no IPv6 */ 5157 return; 5158 } 5159 if_name = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName); 5160 if (if_name == NULL) { 5161 /* need an interface name */ 5162 return; 5163 } 5164 if (CFEqual(if_name, CFSTR("lo0"))) { 5165 /* don't ever elect loopback */ 5166 return; 5167 } 5168 router = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Router); 5169 if (router == NULL) { 5170 /* don't care about services without a router */ 5171 return; 5172 } 5173 info = (ElectionInfoRef)context; 5174 bzero(&candidate, sizeof(candidate)); 5175 candidate.serviceID = (CFStringRef)key; 5176 candidate.if_name = if_name; 5177 addrs = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Addresses); 5178 (void)cfstring_to_ip6(CFArrayGetValueAtIndex(addrs, 0), 5179 &candidate.addr.v6); 5180 candidate.rank = get_service_rank(info->order, info->n_order, 5181 candidate.serviceID); 5182 service_options 5183 = service_dict_get(candidate.serviceID, kSCEntNetService); 5184 if (service_options != NULL) { 5185 CFStringRef primaryRankStr = NULL; 5186 5187 primaryRankStr = CFDictionaryGetValue(service_options, 5188 kSCPropNetServicePrimaryRank); 5189 if (primaryRankStr != NULL) { 5190 primary_rank = PrimaryRankGetRankAssertion(primaryRankStr); 5191 } 5192 candidate.ip_is_coupled 5193 = CFDictionaryContainsKey(service_options, kIPIsCoupled); 5194 } 5195 if (primary_rank != kRankAssertionNever) { 5196 if (get_override_primary(ipv6_dict)) { 5197 primary_rank = kRankAssertionFirst; 5198 } 5199 else if (S_ppp_override_primary 5200 && CFStringHasPrefix(if_name, CFSTR(PPP_PREFIX))) { 5201 /* PPP override: make ppp* look the best */ 5202 /* Hack: should use interface type, not interface name */ 5203 primary_rank = kRankAssertionFirst; 5204 } 5205 } 5206 candidate.rank = RankMake(candidate.rank, primary_rank); 5207 rank_dict_set_service_rank(S_ipv6_service_rank_dict, 5208 candidate.serviceID, candidate.rank); 5209 candidate.signature = service_dict_get_signature(ipv6_dict); 5210 ElectionResultsAddCandidate(info->results, &candidate); 5211 return; 5212} 5213 5214static uint32_t 5215service_changed(CFDictionaryRef services_info, CFStringRef serviceID) 5216{ 5217 uint32_t changed = 0; 5218 int i; 5219 5220 /* update service options first (e.g. rank) */ 5221 if (get_rank_changes(serviceID, 5222 get_service_state_entity(services_info, serviceID, 5223 NULL), 5224 get_service_setup_entity(services_info, serviceID, 5225 NULL), 5226 services_info)) { 5227 changed |= (1 << kEntityTypeServiceOptions); 5228 } 5229 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */ 5230 for (i = 0; i < ENTITY_TYPES_COUNT; i++) { 5231 GetEntityChangesFuncRef func = entityChangeFunc[i]; 5232 if ((*func)(serviceID, 5233 get_service_state_entity(services_info, serviceID, 5234 *entityTypeNames[i]), 5235 get_service_setup_entity(services_info, serviceID, 5236 *entityTypeNames[i]), 5237 services_info)) { 5238 changed |= (1 << i); 5239 } 5240 } 5241 5242 if (get_transient_service_changes(serviceID, services_info)) { 5243 changed |= (1 << kEntityTypeVPNStatus); 5244 } 5245 5246 return (changed); 5247} 5248 5249static CFArrayRef 5250service_order_get(CFDictionaryRef services_info) 5251{ 5252 CFArrayRef order = NULL; 5253 CFDictionaryRef ipv4_dict; 5254 5255 ipv4_dict = my_CFDictionaryGetDictionary(services_info, 5256 S_setup_global_ipv4); 5257 if (ipv4_dict != NULL) { 5258 CFNumberRef ppp_override; 5259 int ppp_val = 0; 5260 5261 order = CFDictionaryGetValue(ipv4_dict, kSCPropNetServiceOrder); 5262 order = isA_CFArray(order); 5263 5264 /* get ppp override primary */ 5265 ppp_override = CFDictionaryGetValue(ipv4_dict, 5266 kSCPropNetPPPOverridePrimary); 5267 ppp_override = isA_CFNumber(ppp_override); 5268 if (ppp_override != NULL) { 5269 CFNumberGetValue(ppp_override, kCFNumberIntType, &ppp_val); 5270 } 5271 S_ppp_override_primary = (ppp_val != 0) ? TRUE : FALSE; 5272 } 5273 else { 5274 S_ppp_override_primary = FALSE; 5275 } 5276 return (order); 5277} 5278 5279static boolean_t 5280set_new_primary(CFStringRef * primary_p, CFStringRef new_primary, 5281 const char * entity) 5282{ 5283 boolean_t changed = FALSE; 5284 CFStringRef primary = *primary_p; 5285 5286 if (new_primary != NULL) { 5287 if (primary != NULL && CFEqual(new_primary, primary)) { 5288 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 5289 my_log(LOG_DEBUG, 5290 "IPMonitor: %@ is still primary %s", 5291 new_primary, entity); 5292 } 5293 } 5294 else { 5295 my_CFRelease(primary_p); 5296 *primary_p = CFRetain(new_primary); 5297 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 5298 my_log(LOG_DEBUG, 5299 "IPMonitor: %@ is the new primary %s", 5300 new_primary, entity); 5301 } 5302 changed = TRUE; 5303 } 5304 } 5305 else if (primary != NULL) { 5306 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 5307 my_log(LOG_DEBUG, 5308 "IPMonitor: %@ is no longer primary %s", 5309 primary, entity); 5310 } 5311 my_CFRelease(primary_p); 5312 changed = TRUE; 5313 } 5314 return (changed); 5315} 5316 5317static Rank 5318rank_service_entity(CFDictionaryRef rank_dict, CFStringRef serviceID, 5319 CFStringRef entity) 5320{ 5321 if (service_dict_get(serviceID, entity) == NULL) { 5322 return (RankMake(kRankIndexMask, kRankAssertionDefault)); 5323 } 5324 return (rank_dict_get_service_rank(rank_dict, serviceID)); 5325} 5326 5327static void 5328update_interface_rank(CFDictionaryRef services_info, CFStringRef ifname) 5329{ 5330 CFStringRef if_rank_key; 5331 CFDictionaryRef rank_dict; 5332 5333 if_rank_key = if_rank_key_copy(ifname); 5334 rank_dict = CFDictionaryGetValue(services_info, if_rank_key); 5335 CFRelease(if_rank_key); 5336 if_rank_set(ifname, rank_dict); 5337 return; 5338} 5339 5340static void 5341append_serviceIDs_for_interface(CFMutableArrayRef services_changed, 5342 CFStringRef ifname) 5343{ 5344 int count; 5345 int i; 5346 void * * keys; 5347#define N_KEYS_VALUES_STATIC 10 5348 void * keys_values_buf[N_KEYS_VALUES_STATIC * 2]; 5349 void * * values; 5350 5351 count = CFDictionaryGetCount(S_service_state_dict); 5352 if (count <= N_KEYS_VALUES_STATIC) { 5353 keys = keys_values_buf; 5354 } else { 5355 keys = (void * *)malloc(sizeof(*keys) * count * 2); 5356 } 5357 values = keys + count; 5358 CFDictionaryGetKeysAndValues(S_service_state_dict, 5359 (const void * *)keys, 5360 (const void * *)values); 5361 5362 for (i = 0; i < count; i++) { 5363 CFDictionaryRef ipv4 = NULL; 5364 CFStringRef interface = NULL; 5365 CFStringRef serviceID; 5366 CFDictionaryRef service_dict; 5367 5368 serviceID = (CFStringRef)keys[i]; 5369 service_dict = (CFDictionaryRef)values[i]; 5370 5371 /* check if this is a ipv4 dictionary */ 5372 ipv4 = CFDictionaryGetValue(service_dict, kSCEntNetIPv4); 5373 if (ipv4 != NULL) { 5374 interface = ipv4_dict_get_ifname(ipv4); 5375 if (interface != NULL && CFEqual(interface, ifname)) { 5376 if (S_IPMonitor_debug & kDebugFlag1) { 5377 my_log(LOG_DEBUG, 5378 "Found ipv4 service %@ on interface %@.", 5379 serviceID, ifname); 5380 } 5381 5382 my_CFArrayAppendUniqueValue(services_changed, serviceID); 5383 } 5384 } else { 5385 CFDictionaryRef proto_dict; 5386 5387 /* check if this is a ipv6 dictionary */ 5388 proto_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6); 5389 if (proto_dict == NULL) { 5390 continue; 5391 } 5392 interface = CFDictionaryGetValue(proto_dict, kSCPropInterfaceName); 5393 if (interface != NULL && CFEqual(interface, ifname)) { 5394 if (S_IPMonitor_debug & kDebugFlag1) { 5395 my_log(LOG_DEBUG, "Found ipv6 service %@ on interface %@.", 5396 serviceID, ifname); 5397 } 5398 5399 my_CFArrayAppendUniqueValue(services_changed, serviceID); 5400 } 5401 } 5402 } 5403 5404 if (keys != keys_values_buf) { 5405 free(keys); 5406 } 5407} 5408 5409static __inline__ const char * 5410get_changed_str(CFStringRef serviceID, CFStringRef entity, CFDictionaryRef old_dict) 5411{ 5412 CFDictionaryRef new_dict = NULL; 5413 5414 if (serviceID != NULL) { 5415 new_dict = service_dict_get(serviceID, entity); 5416 } 5417 5418 if (old_dict == NULL) { 5419 if (new_dict != NULL) { 5420 return "+"; 5421 } 5422 } else { 5423 if (new_dict == NULL) { 5424 return "-"; 5425 } else if (!CFEqual(old_dict, new_dict)) { 5426 return "!"; 5427 } 5428 } 5429 return ""; 5430} 5431 5432static CF_RETURNS_RETAINED CFStringRef 5433generate_log_changes(nwi_state_t changes_state, 5434 boolean_t dns_changed, 5435 boolean_t dnsinfo_changed, 5436 CFDictionaryRef old_primary_dns, 5437 boolean_t proxy_changed, 5438 CFDictionaryRef old_primary_proxy, 5439 boolean_t smb_changed, 5440 CFDictionaryRef old_primary_smb 5441 ) 5442{ 5443 int idx; 5444 CFMutableStringRef log_output; 5445 nwi_ifstate_t scan; 5446 5447 log_output = CFStringCreateMutable(NULL, 0); 5448 5449 if (changes_state != NULL) { 5450 for (idx = 0; idx < sizeof(nwi_af_list)/sizeof(nwi_af_list[0]); idx++) { 5451 CFMutableStringRef changes = NULL; 5452 CFMutableStringRef primary_str = NULL; 5453 5454 scan = nwi_state_get_first_ifstate(changes_state, nwi_af_list[idx]); 5455 5456 while (scan != NULL) { 5457 const char * changed_str; 5458 5459 changed_str = nwi_ifstate_get_diff_str(scan); 5460 if (changed_str != NULL) { 5461 void * address; 5462 const char * addr_str; 5463 char ntopbuf[INET6_ADDRSTRLEN]; 5464 5465 address = (void *)nwi_ifstate_get_address(scan); 5466 addr_str = inet_ntop(scan->af, address, 5467 ntopbuf, sizeof(ntopbuf)); 5468 5469 if (primary_str == NULL) { 5470 primary_str = CFStringCreateMutable(NULL, 0); 5471 CFStringAppendFormat(primary_str, NULL, CFSTR("%s%s:%s"), 5472 nwi_ifstate_get_ifname(scan), 5473 changed_str, addr_str); 5474 } else { 5475 if (changes == NULL) { 5476 changes = CFStringCreateMutable(NULL, 0); 5477 } 5478 CFStringAppendFormat(changes, NULL, CFSTR(", %s"), 5479 nwi_ifstate_get_ifname(scan)); 5480 if (strcmp(changed_str, "") != 0) { 5481 CFStringAppendFormat(changes, NULL, CFSTR("%s:%s"), 5482 changed_str, addr_str); 5483 } 5484 } 5485 } 5486 scan = nwi_ifstate_get_next(scan, scan->af); 5487 } 5488 5489 if (primary_str != NULL) { 5490 CFStringAppendFormat(log_output, NULL, CFSTR(" %s(%@"), 5491 nwi_af_list[idx] == AF_INET ? "v4" : "v6", 5492 primary_str); 5493 5494 if (changes != NULL && CFStringGetLength(changes) != 0) { 5495 CFStringAppendFormat(log_output, NULL, CFSTR("%@"), 5496 changes); 5497 } 5498 CFStringAppendFormat(log_output, NULL, CFSTR(")")); 5499 5500 my_CFRelease(&primary_str); 5501 my_CFRelease(&changes); 5502 } 5503 } 5504 } 5505 5506 if (dns_changed || dnsinfo_changed) { 5507 const char *str; 5508 5509 str = get_changed_str(S_primary_dns, kSCEntNetDNS, old_primary_dns); 5510 if ((strcmp(str, "") == 0) && dnsinfo_changed) { 5511 str = "*"; // dnsinfo change w/no change to primary 5512 } 5513 CFStringAppendFormat(log_output, NULL, CFSTR(" DNS%s"), str); 5514 } else if (S_primary_dns != NULL) { 5515 CFStringAppendFormat(log_output, NULL, CFSTR(" DNS")); 5516 } 5517 5518 if (proxy_changed) { 5519 const char *str; 5520 5521 str = get_changed_str(S_primary_proxies, kSCEntNetProxies, old_primary_proxy); 5522 CFStringAppendFormat(log_output, NULL, CFSTR(" Proxy%s"), str); 5523 } else if (S_primary_proxies != NULL) { 5524 CFStringAppendFormat(log_output, NULL, CFSTR(" Proxy")); 5525 } 5526 5527#if !TARGET_OS_IPHONE 5528 if (smb_changed) { 5529 const char *str; 5530 5531 str = get_changed_str(S_primary_smb, kSCEntNetSMB, old_primary_smb); 5532 CFStringAppendFormat(log_output, NULL, CFSTR(" SMB%s"), str); 5533 } else if (S_primary_smb != NULL) { 5534 CFStringAppendFormat(log_output, NULL, CFSTR(" SMB")); 5535 } 5536#endif // !TARGET_OS_IPHONE 5537 5538 return log_output; 5539} 5540 5541#pragma mark - 5542#pragma mark Network changed notification 5543 5544static dispatch_queue_t 5545__network_change_queue() 5546{ 5547 static dispatch_once_t once; 5548 static dispatch_queue_t q; 5549 5550 dispatch_once(&once, ^{ 5551 q = dispatch_queue_create("network change queue", NULL); 5552 }); 5553 5554 return q; 5555} 5556 5557// Note: must run on __network_change_queue() 5558static void 5559post_network_change_when_ready() 5560{ 5561 int status; 5562 5563 if (S_network_change_needed == 0) { 5564 return; 5565 } 5566 5567 if (!S_network_change_timeout && 5568 (!S_dnsinfo_synced || !S_nwi_synced)) { 5569 // if we [still] need to wait for the DNS configuration 5570 // or network information changes to be ack'd 5571 5572 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 5573 my_log(LOG_DEBUG, 5574 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s, %s)", 5575 S_dnsinfo_synced ? "DNS" : "!DNS", 5576 S_nwi_synced ? "nwi" : "!nwi"); 5577 } 5578 return; 5579 } 5580 5581 // cancel any running timer 5582 if (S_network_change_timer != NULL) { 5583 dispatch_source_cancel(S_network_change_timer); 5584 dispatch_release(S_network_change_timer); 5585 S_network_change_timer = NULL; 5586 S_network_change_timeout = FALSE; 5587 } 5588 5589 // set (and log?) the post time 5590 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 5591 struct timeval elapsed; 5592 struct timeval end; 5593 5594 (void) gettimeofday(&end, NULL); 5595 timersub(&end, &S_network_change_start, &elapsed); 5596 5597#define QUERY_TIME__FMT "%d.%6.6d" 5598#define QUERY_TIME__DIV 1 5599 5600 my_log(LOG_DEBUG, 5601 "Post \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s: " QUERY_TIME__FMT ": 0x%x)", 5602 S_network_change_timeout ? "timeout" : "delayed", 5603 elapsed.tv_sec, 5604 elapsed.tv_usec / QUERY_TIME__DIV, 5605 S_network_change_needed); 5606 } 5607 5608 if ((S_network_change_needed & NETWORK_CHANGE_NET) != 0) { 5609 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI); 5610 if (status != NOTIFY_STATUS_OK) { 5611 my_log(LOG_ERR, 5612 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI ") failed: error=%ld", status); 5613 } 5614 } 5615 5616 if ((S_network_change_needed & NETWORK_CHANGE_DNS) != 0) { 5617 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS); 5618 if (status != NOTIFY_STATUS_OK) { 5619 my_log(LOG_ERR, 5620 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS ") failed: error=%ld", status); 5621 } 5622 } 5623 5624 if ((S_network_change_needed & NETWORK_CHANGE_PROXY) != 0) { 5625 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY); 5626 if (status != NOTIFY_STATUS_OK) { 5627 my_log(LOG_ERR, 5628 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY ") failed: error=%ld", status); 5629 } 5630 } 5631 5632 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE); 5633 if (status != NOTIFY_STATUS_OK) { 5634 my_log(LOG_ERR, 5635 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE ") failed: error=%ld", status); 5636 } 5637 5638 S_network_change_needed = 0; 5639 return; 5640} 5641 5642#define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s 5643 5644// Note: must run on __network_change_queue() 5645static void 5646post_network_change(uint32_t change) 5647{ 5648 if (S_network_change_needed == 0) { 5649 // set the start time 5650 (void) gettimeofday(&S_network_change_start, NULL); 5651 } 5652 5653 // indicate that we need to post a change for ... 5654 S_network_change_needed |= change; 5655 5656 // cancel any running timer 5657 if (S_network_change_timer != NULL) { 5658 dispatch_source_cancel(S_network_change_timer); 5659 dispatch_release(S_network_change_timer); 5660 S_network_change_timer = NULL; 5661 S_network_change_timeout = FALSE; 5662 } 5663 5664 // if needed, start new timer 5665 if (!S_dnsinfo_synced || !S_nwi_synced) { 5666 S_network_change_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 5667 0, 5668 0, 5669 __network_change_queue()); 5670 dispatch_source_set_event_handler(S_network_change_timer, ^{ 5671 S_network_change_timeout = TRUE; 5672 post_network_change_when_ready(); 5673 }); 5674 dispatch_source_set_timer(S_network_change_timer, 5675 dispatch_time(DISPATCH_TIME_NOW, 5676 TRAILING_EDGE_TIMEOUT_NSEC), // start 5677 0, // interval 5678 10 * NSEC_PER_MSEC); // leeway 5679 dispatch_resume(S_network_change_timer); 5680 } 5681 5682 post_network_change_when_ready(); 5683 5684 return; 5685} 5686 5687#pragma mark - 5688#pragma mark Process network (SCDynamicStore) changes 5689 5690static void 5691IPMonitorNotify(SCDynamicStoreRef session, CFArrayRef changed_keys, 5692 void * not_used) 5693{ 5694 CFIndex count; 5695 uint32_t changes = 0; 5696 nwi_state_t changes_state = NULL; 5697 boolean_t dns_changed = FALSE; 5698 boolean_t dnsinfo_changed = FALSE; 5699 boolean_t global_ipv4_changed = FALSE; 5700 boolean_t global_ipv6_changed = FALSE; 5701 int i; 5702 CFMutableArrayRef if_rank_changes = NULL; 5703 keyChangeList keys; 5704 CFIndex n; 5705 CFStringRef network_change_msg = NULL; 5706 int n_services; 5707 int n_service_order = 0; 5708 nwi_state_t old_nwi_state = NULL; 5709 CFDictionaryRef old_primary_dns = NULL; 5710 CFDictionaryRef old_primary_proxy = NULL; 5711#if !TARGET_OS_IPHONE 5712 CFDictionaryRef old_primary_smb = NULL; 5713#endif // !TARGET_OS_IPHONE 5714 boolean_t proxies_changed = FALSE; 5715 boolean_t reachability_changed = FALSE; 5716 CFArrayRef service_order; 5717 CFMutableArrayRef service_changes = NULL; 5718 CFDictionaryRef services_info = NULL; 5719#if !TARGET_OS_IPHONE 5720 boolean_t smb_changed = FALSE; 5721#endif // !TARGET_OS_IPHONE 5722 5723 count = CFArrayGetCount(changed_keys); 5724 if (count == 0) { 5725 return; 5726 } 5727 5728 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 5729 my_log(LOG_DEBUG, 5730 "IPMonitor: changes %@ (%d)", changed_keys, count); 5731 } 5732 5733 if (S_primary_dns != NULL) { 5734 old_primary_dns = service_dict_get(S_primary_dns, kSCEntNetDNS); 5735 if (old_primary_dns != NULL) { 5736 old_primary_dns = CFDictionaryCreateCopy(NULL, old_primary_dns); 5737 } 5738 } 5739 5740 if (S_primary_proxies != NULL) { 5741 old_primary_proxy = service_dict_get(S_primary_proxies, kSCEntNetProxies); 5742 if (old_primary_proxy != NULL) { 5743 old_primary_proxy = CFDictionaryCreateCopy(NULL, old_primary_proxy); 5744 } 5745 } 5746 5747#if !TARGET_OS_IPHONE 5748 if (S_primary_smb != NULL) { 5749 old_primary_smb = service_dict_get(S_primary_smb, kSCEntNetSMB); 5750 if (old_primary_smb != NULL) { 5751 old_primary_smb = CFDictionaryCreateCopy(NULL, old_primary_smb); 5752 } 5753 } 5754#endif // !TARGET_OS_IPHONE 5755 5756 keyChangeListInit(&keys); 5757 service_changes = CFArrayCreateMutable(NULL, 0, 5758 &kCFTypeArrayCallBacks); 5759 5760 for (i = 0; i < count; i++) { 5761 CFStringRef change = CFArrayGetValueAtIndex(changed_keys, i); 5762 if (CFEqual(change, S_setup_global_ipv4)) { 5763 global_ipv4_changed = TRUE; 5764 global_ipv6_changed = TRUE; 5765 } 5766 else if (CFEqual(change, S_multicast_resolvers)) { 5767 dnsinfo_changed = TRUE; 5768 } 5769 else if (CFEqual(change, S_private_resolvers)) { 5770 dnsinfo_changed = TRUE; 5771 } 5772#if !TARGET_OS_IPHONE 5773 else if (CFEqual(change, CFSTR(_PATH_RESOLVER_DIR))) { 5774 dnsinfo_changed = TRUE; 5775 } 5776#endif /* !TARGET_OS_IPHONE */ 5777 else if (CFStringHasPrefix(change, S_state_service_prefix)) { 5778 int i; 5779 CFStringRef serviceID; 5780 5781 for (i = 0; i < sizeof(statusEntityNames)/sizeof(statusEntityNames[0]); i++) { 5782 if (CFStringHasSuffix(change, *statusEntityNames[i])) { 5783 dnsinfo_changed = TRUE; 5784 break; 5785 } 5786 } 5787 5788 serviceID = parse_component(change, S_state_service_prefix); 5789 if (serviceID) { 5790 my_CFArrayAppendUniqueValue(service_changes, serviceID); 5791 CFRelease(serviceID); 5792 } 5793 } 5794 else if (CFStringHasPrefix(change, S_setup_service_prefix)) { 5795 int j; 5796 5797 CFStringRef serviceID = parse_component(change, 5798 S_setup_service_prefix); 5799 if (serviceID) { 5800 my_CFArrayAppendUniqueValue(service_changes, serviceID); 5801 CFRelease(serviceID); 5802 } 5803 5804 for (j = 0; j < sizeof(transientInterfaceEntityNames)/sizeof(transientInterfaceEntityNames[0]); j++) { 5805 if (CFStringHasSuffix(change, *transientInterfaceEntityNames[j])) { 5806 reachability_changed = TRUE; 5807 break; 5808 } 5809 } 5810 5811 if (CFStringHasSuffix(change, kSCEntNetInterface)) { 5812 reachability_changed = TRUE; 5813 } 5814 5815 5816 } 5817 else if (CFStringHasSuffix(change, kSCEntNetService)) { 5818 CFStringRef ifname = my_CFStringCopyComponent(change, CFSTR("/"), 3); 5819 5820 if (ifname != NULL) { 5821 if (if_rank_changes == NULL) { 5822 if_rank_changes = CFArrayCreateMutable(NULL, 0, 5823 &kCFTypeArrayCallBacks); 5824 } 5825 my_CFArrayAppendUniqueValue(if_rank_changes, ifname); 5826 CFRelease(ifname); 5827 } 5828 } 5829 } 5830 5831 /* determine which serviceIDs are impacted by the interface rank changes */ 5832 if (if_rank_changes != NULL) { 5833 n = CFArrayGetCount(if_rank_changes); 5834 for (i = 0; i < n; i++) { 5835 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_changes, i); 5836 5837 if (S_IPMonitor_debug & kDebugFlag1) { 5838 my_log(LOG_DEBUG, "Interface rank changed %@", 5839 ifname); 5840 } 5841 append_serviceIDs_for_interface(service_changes, ifname); 5842 } 5843 } 5844 5845 /* grab a snapshot of everything we need */ 5846 services_info = services_info_copy(session, service_changes, 5847 if_rank_changes); 5848 service_order = service_order_get(services_info); 5849 if (service_order != NULL) { 5850 n_service_order = CFArrayGetCount(service_order); 5851 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 5852 my_log(LOG_DEBUG, 5853 "IPMonitor: service_order %@ ", service_order); 5854 } 5855 } 5856 5857 if (if_rank_changes != NULL) { 5858 for (i = 0; i < n; i++) { 5859 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_changes, i); 5860 update_interface_rank(services_info, ifname); 5861 } 5862 } 5863 5864 n = CFArrayGetCount(service_changes); 5865 for (i = 0; i < n; i++) { 5866 uint32_t changes; 5867 CFStringRef serviceID; 5868 5869 serviceID = CFArrayGetValueAtIndex(service_changes, i); 5870 changes = service_changed(services_info, serviceID); 5871 if ((changes & (1 << kEntityTypeServiceOptions)) != 0) { 5872 /* if __Service__ (e.g. PrimaryRank) changed */ 5873 global_ipv4_changed = TRUE; 5874 global_ipv6_changed = TRUE; 5875 } 5876 else { 5877 if ((changes & (1 << kEntityTypeIPv4)) != 0) { 5878 global_ipv4_changed = TRUE; 5879 dnsinfo_changed = TRUE; 5880 proxies_changed = TRUE; 5881 } 5882 if ((changes & (1 << kEntityTypeIPv6)) != 0) { 5883 global_ipv6_changed = TRUE; 5884 dnsinfo_changed = TRUE; 5885 proxies_changed = TRUE; 5886 } 5887 } 5888 if ((changes & (1 << kEntityTypeDNS)) != 0) { 5889 if (S_primary_dns != NULL && CFEqual(S_primary_dns, serviceID)) { 5890 dns_changed = TRUE; 5891 } 5892 dnsinfo_changed = TRUE; 5893 } 5894 if ((changes & (1 << kEntityTypeProxies)) != 0) { 5895 proxies_changed = TRUE; 5896 } 5897#if !TARGET_OS_IPHONE 5898 if ((changes & (1 << kEntityTypeSMB)) != 0) { 5899 if (S_primary_smb != NULL && CFEqual(S_primary_smb, serviceID)) { 5900 smb_changed = TRUE; 5901 } 5902 } 5903#endif 5904 } 5905 5906 if ((changes & (1 <<kEntityTypeVPNStatus)) != 0) { 5907 global_ipv4_changed = TRUE; 5908 global_ipv6_changed = TRUE; 5909 } 5910 5911 /* ensure S_nwi_state can hold as many services as we have currently */ 5912 n_services = CFDictionaryGetCount(S_service_state_dict); 5913 old_nwi_state = nwi_state_copy_priv(S_nwi_state); 5914 S_nwi_state = nwi_state_new(S_nwi_state, n_services); 5915 5916 if (global_ipv4_changed) { 5917 if (S_ipv4_results != NULL) { 5918 ElectionResultsRelease(S_ipv4_results); 5919 } 5920 S_ipv4_results 5921 = ElectionResultsCopy(elect_ipv4, service_order, n_service_order); 5922 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 5923 ElectionResultsLog(LOG_DEBUG, S_ipv4_results, "IPv4"); 5924 } 5925 } 5926 if (global_ipv6_changed) { 5927 if (S_ipv6_results != NULL) { 5928 ElectionResultsRelease(S_ipv6_results); 5929 } 5930 S_ipv6_results 5931 = ElectionResultsCopy(elect_ipv6, service_order, n_service_order); 5932 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 5933 ElectionResultsLog(LOG_DEBUG, S_ipv6_results, "IPv6"); 5934 } 5935 } 5936 if (global_ipv4_changed || global_ipv6_changed || dnsinfo_changed) { 5937 CFStringRef new_primary; 5938 IPv4RouteListRef new_routelist = NULL; 5939 5940 /* IPv4 */ 5941 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 5942 my_log(LOG_DEBUG, 5943 "IPMonitor: electing IPv4 primary"); 5944 } 5945 new_primary = ElectionResultsCopyPrimary(S_ipv4_results, 5946 S_ipv6_results, 5947 S_nwi_state, AF_INET, 5948 &new_routelist, 5949 services_info); 5950 (void)set_new_primary(&S_primary_ipv4, new_primary, "IPv4"); 5951 update_ipv4(S_primary_ipv4, new_routelist, &keys); 5952 my_CFRelease(&new_primary); 5953 5954 /* IPv6 */ 5955 if ((S_IPMonitor_debug & kDebugFlag1) != 0) { 5956 my_log(LOG_DEBUG, 5957 "IPMonitor: electing IPv6 primary"); 5958 } 5959 new_primary = ElectionResultsCopyPrimary(S_ipv6_results, 5960 S_ipv4_results, 5961 S_nwi_state, AF_INET6, 5962 NULL, 5963 services_info); 5964 (void)set_new_primary(&S_primary_ipv6, new_primary, "IPv6"); 5965 update_ipv6(S_primary_ipv6, &keys); 5966 my_CFRelease(&new_primary); 5967 } 5968 5969 if (global_ipv4_changed || global_ipv6_changed) { 5970 CFStringRef new_primary_dns = NULL; 5971 CFStringRef new_primary_proxies = NULL; 5972#if !TARGET_OS_IPHONE 5973 CFStringRef new_primary_smb = NULL; 5974#endif /* !TARGET_OS_IPHONE */ 5975 5976 if (S_primary_ipv4 != NULL && S_primary_ipv6 != NULL) { 5977 /* decide between IPv4 and IPv6 */ 5978 if (rank_service_entity(S_ipv4_service_rank_dict, 5979 S_primary_ipv4, kSCEntNetDNS) 5980 <= rank_service_entity(S_ipv6_service_rank_dict, 5981 S_primary_ipv6, kSCEntNetDNS)) { 5982 new_primary_dns = S_primary_ipv4; 5983 } 5984 else { 5985 new_primary_dns = S_primary_ipv6; 5986 } 5987 if (rank_service_entity(S_ipv4_service_rank_dict, 5988 S_primary_ipv4, kSCEntNetProxies) 5989 <= rank_service_entity(S_ipv6_service_rank_dict, 5990 S_primary_ipv6, kSCEntNetProxies)) { 5991 new_primary_proxies = S_primary_ipv4; 5992 } 5993 else { 5994 new_primary_proxies = S_primary_ipv6; 5995 } 5996#if !TARGET_OS_IPHONE 5997 if (rank_service_entity(S_ipv4_service_rank_dict, 5998 S_primary_ipv4, kSCEntNetSMB) 5999 <= rank_service_entity(S_ipv6_service_rank_dict, 6000 S_primary_ipv6, kSCEntNetSMB)) { 6001 new_primary_smb = S_primary_ipv4; 6002 } 6003 else { 6004 new_primary_smb = S_primary_ipv6; 6005 } 6006#endif /* !TARGET_OS_IPHONE */ 6007 6008 } 6009 else if (S_primary_ipv6 != NULL) { 6010 new_primary_dns = S_primary_ipv6; 6011 new_primary_proxies = S_primary_ipv6; 6012#if !TARGET_OS_IPHONE 6013 new_primary_smb = S_primary_ipv6; 6014#endif /* !TARGET_OS_IPHONE */ 6015 } 6016 else if (S_primary_ipv4 != NULL) { 6017 new_primary_dns = S_primary_ipv4; 6018 new_primary_proxies = S_primary_ipv4; 6019#if !TARGET_OS_IPHONE 6020 new_primary_smb = S_primary_ipv4; 6021#endif /* !TARGET_OS_IPHONE */ 6022 } 6023 6024 if (set_new_primary(&S_primary_dns, new_primary_dns, "DNS")) { 6025 dns_changed = TRUE; 6026 dnsinfo_changed = TRUE; 6027 } 6028 if (set_new_primary(&S_primary_proxies, new_primary_proxies, "Proxies")) { 6029 proxies_changed = TRUE; 6030 } 6031#if !TARGET_OS_IPHONE 6032 if (set_new_primary(&S_primary_smb, new_primary_smb, "SMB")) { 6033 smb_changed = TRUE; 6034 } 6035#endif /* !TARGET_OS_IPHONE */ 6036 } 6037 6038 if (!proxies_changed && dnsinfo_changed && 6039 ((G_supplemental_proxies_follow_dns != NULL) && CFBooleanGetValue(G_supplemental_proxies_follow_dns))) { 6040 proxies_changed = TRUE; 6041 } 6042 6043 changes_state = nwi_state_diff(old_nwi_state, S_nwi_state); 6044 6045 if (global_ipv4_changed || global_ipv6_changed || dnsinfo_changed || reachability_changed) { 6046 if (S_nwi_state != NULL) { 6047 S_nwi_state->generation_count = mach_absolute_time(); 6048 if (global_ipv4_changed || global_ipv6_changed || reachability_changed) { 6049 SCNetworkReachabilityFlags reach_flags_v4 = 0; 6050 SCNetworkReachabilityFlags reach_flags_v6 = 0; 6051 6052 GetReachabilityFlagsFromTransientServices(services_info, 6053 &reach_flags_v4, 6054 &reach_flags_v6); 6055 6056 _nwi_state_set_reachability_flags(S_nwi_state, reach_flags_v4, reach_flags_v6); 6057 } 6058 6059 /* Update the per-interface generation count */ 6060 _nwi_state_update_interface_generations(old_nwi_state, S_nwi_state, changes_state); 6061 } 6062 6063 if (update_nwi(S_nwi_state)) { 6064 changes |= NETWORK_CHANGE_NET; 6065 6066 /* 6067 * the DNS configuration includes per-resolver configuration 6068 * reachability flags that are based on the nwi state. Let's 6069 * make sure that we check for changes 6070 */ 6071 dnsinfo_changed = TRUE; 6072 } 6073 } 6074 if (dns_changed) { 6075 if (update_dns(services_info, S_primary_dns, &keys)) { 6076 changes |= NETWORK_CHANGE_DNS; 6077 dnsinfo_changed = TRUE; 6078 } else { 6079 dns_changed = FALSE; 6080 } 6081 } 6082 if (dnsinfo_changed) { 6083 if (update_dnsinfo(services_info, S_primary_dns, &keys, service_order)) { 6084 changes |= NETWORK_CHANGE_DNS; 6085 } else { 6086 dnsinfo_changed = FALSE; 6087 } 6088 } 6089 if (proxies_changed) { 6090 // if proxy change OR supplemental Proxies follow supplemental DNS 6091 if (update_proxies(services_info, S_primary_proxies, &keys, service_order)) { 6092 changes |= NETWORK_CHANGE_PROXY; 6093 } else { 6094 proxies_changed = FALSE; 6095 } 6096 } 6097#if !TARGET_OS_IPHONE 6098 if (smb_changed) { 6099 if (update_smb(services_info, S_primary_smb, &keys)) { 6100 changes |= NETWORK_CHANGE_SMB; 6101 } else { 6102 smb_changed = FALSE; 6103 } 6104 } 6105#endif /* !TARGET_OS_IPHONE */ 6106 my_CFRelease(&service_changes); 6107 my_CFRelease(&services_info); 6108 my_CFRelease(&if_rank_changes); 6109 6110 if (changes != 0) { 6111 network_change_msg = 6112 generate_log_changes(changes_state, 6113 dns_changed, 6114 dnsinfo_changed, 6115 old_primary_dns, 6116 proxies_changed, 6117 old_primary_proxy, 6118#if !TARGET_OS_IPHONE 6119 smb_changed, 6120 old_primary_smb 6121#else // !TARGET_OS_IPHONE 6122 FALSE, // smb_changed 6123 NULL // old_primary_smb 6124#endif // !TARGET_OS_IPHONE 6125 ); 6126 } 6127 6128 keyChangeListApplyToStore(&keys, session); 6129 my_CFRelease(&old_primary_dns); 6130 my_CFRelease(&old_primary_proxy); 6131#if !TARGET_OS_IPHONE 6132 my_CFRelease(&old_primary_smb); 6133#endif // !TARGET_OS_IPHONE 6134 6135 if (changes != 0) { 6136 dispatch_async(__network_change_queue(), ^{ 6137 post_network_change(changes); 6138 }); 6139 } 6140 6141 if ((network_change_msg != NULL) && (CFStringGetLength(network_change_msg) != 0)) { 6142 my_log(LOG_NOTICE, "network changed:%@", network_change_msg); 6143 } else if (keyChangeListActive(&keys)) { 6144 my_log(LOG_NOTICE, "network changed."); 6145 } else { 6146 my_log(LOG_DEBUG, "network event w/no changes"); 6147 } 6148 6149 my_CFRelease(&network_change_msg); 6150 6151 if (changes_state != NULL) { 6152 nwi_state_release(changes_state); 6153 } 6154 if (old_nwi_state != NULL) { 6155 nwi_state_release(old_nwi_state); 6156 } 6157 keyChangeListFree(&keys); 6158 return; 6159} 6160 6161static void 6162watch_proxies() 6163{ 6164#if !TARGET_OS_IPHONE 6165 const _scprefs_observer_type type = scprefs_observer_type_mcx; 6166#else 6167 const _scprefs_observer_type type = scprefs_observer_type_global; 6168#endif 6169 static dispatch_queue_t proxy_cb_queue; 6170 6171 proxy_cb_queue = dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL); 6172 _scprefs_observer_watch(type, 6173 "com.apple.SystemConfiguration.plist", 6174 proxy_cb_queue, 6175 ^{ 6176 SCDynamicStoreNotifyValue(NULL, S_state_global_proxies); 6177 notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY); 6178 my_log(LOG_DEBUG, "IPMonitor: Notifying:\n%@", 6179 S_state_global_proxies); 6180 }); 6181 return; 6182} 6183 6184#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) 6185#include "IPMonitorControlPrefs.h" 6186 6187__private_extern__ SCLoggerRef 6188my_log_get_logger() 6189{ 6190 return (S_IPMonitor_logger); 6191} 6192 6193static void 6194prefs_changed(__unused SCPreferencesRef prefs) 6195{ 6196 if (S_bundle_logging_verbose || IPMonitorControlPrefsIsVerbose()) { 6197 S_IPMonitor_debug = kDebugFlagDefault; 6198 S_IPMonitor_verbose = TRUE; 6199 SCLoggerSetFlags(S_IPMonitor_logger, kSCLoggerFlagsFile | kSCLoggerFlagsDefault); 6200 my_log(LOG_DEBUG, "IPMonitor: Setting logging verbose mode on."); 6201 } else { 6202 my_log(LOG_DEBUG, "IPMonitor: Setting logging verbose mode off."); 6203 S_IPMonitor_debug = 0; 6204 S_IPMonitor_verbose = FALSE; 6205 SCLoggerSetFlags(S_IPMonitor_logger, kSCLoggerFlagsDefault); 6206 } 6207 return; 6208} 6209 6210#define LOGGER_ID CFSTR("com.apple.networking.IPMonitor") 6211static void 6212my_log_init() 6213{ 6214 if (S_IPMonitor_logger != NULL) { 6215 return; 6216 } 6217 S_IPMonitor_logger = SCLoggerCreate(LOGGER_ID); 6218 return; 6219 6220} 6221 6222#else // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) 6223 6224static void 6225my_log_init() 6226{ 6227 return; 6228} 6229 6230#endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) 6231 6232 6233static void 6234ip_plugin_init() 6235{ 6236 CFMutableArrayRef keys = NULL; 6237 CFStringRef pattern; 6238 CFMutableArrayRef patterns = NULL; 6239 CFRunLoopSourceRef rls = NULL; 6240 6241 if (S_is_network_boot() != 0) { 6242 S_netboot = TRUE; 6243 } 6244 6245#ifdef RTF_IFSCOPE 6246 if (S_is_scoped_routing_enabled() != 0) { 6247 S_scopedroute = TRUE; 6248 } 6249 6250 if (S_is_scoped_v6_routing_enabled() != 0) { 6251 S_scopedroute_v6 = TRUE; 6252 } 6253#endif /* RTF_IFSCOPE */ 6254 6255 S_session = SCDynamicStoreCreate(NULL, CFSTR("IPMonitor"), 6256 IPMonitorNotify, NULL); 6257 if (S_session == NULL) { 6258 my_log(LOG_ERR, 6259 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s", 6260 SCErrorString(SCError())); 6261 return; 6262 } 6263 S_state_global_ipv4 6264 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 6265 kSCDynamicStoreDomainState, 6266 kSCEntNetIPv4); 6267 S_state_global_ipv6 6268 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 6269 kSCDynamicStoreDomainState, 6270 kSCEntNetIPv6); 6271 S_state_global_dns 6272 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 6273 kSCDynamicStoreDomainState, 6274 kSCEntNetDNS); 6275 S_state_global_proxies 6276 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 6277 kSCDynamicStoreDomainState, 6278 kSCEntNetProxies); 6279#if !TARGET_OS_IPHONE 6280 S_state_global_smb 6281 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 6282 kSCDynamicStoreDomainState, 6283 kSCEntNetSMB); 6284#endif /* !TARGET_OS_IPHONE */ 6285 S_setup_global_ipv4 6286 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 6287 kSCDynamicStoreDomainSetup, 6288 kSCEntNetIPv4); 6289 S_state_service_prefix 6290 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 6291 kSCDynamicStoreDomainState, 6292 CFSTR(""), 6293 NULL); 6294 S_setup_service_prefix 6295 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 6296 kSCDynamicStoreDomainSetup, 6297 CFSTR(""), 6298 NULL); 6299 S_service_state_dict 6300 = CFDictionaryCreateMutable(NULL, 0, 6301 &kCFTypeDictionaryKeyCallBacks, 6302 &kCFTypeDictionaryValueCallBacks); 6303 6304 S_ipv4_service_rank_dict 6305 = CFDictionaryCreateMutable(NULL, 0, 6306 &kCFTypeDictionaryKeyCallBacks, 6307 &kCFTypeDictionaryValueCallBacks); 6308 6309 S_ipv6_service_rank_dict 6310 = CFDictionaryCreateMutable(NULL, 0, 6311 &kCFTypeDictionaryKeyCallBacks, 6312 &kCFTypeDictionaryValueCallBacks); 6313 6314 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 6315 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 6316 6317 /* register for State: and Setup: per-service notifications */ 6318 add_service_keys(kSCCompAnyRegex, keys, patterns); 6319 6320 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetPPP); 6321 CFArrayAppendValue(patterns, pattern); 6322 CFRelease(pattern); 6323 6324 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN); 6325 CFArrayAppendValue(patterns, pattern); 6326 CFRelease(pattern); 6327 6328 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetInterface); 6329 CFArrayAppendValue(patterns, pattern); 6330 CFRelease(pattern); 6331 6332 /* register for State: per-service PPP/VPN/IPSec status notifications */ 6333 add_status_keys(kSCCompAnyRegex, patterns); 6334 6335 /* register for interface rank notifications */ 6336 pattern = if_rank_key_copy(kSCCompAnyRegex); 6337 CFArrayAppendValue(patterns, pattern); 6338 CFRelease(pattern); 6339 6340 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */ 6341 CFArrayAppendValue(keys, S_setup_global_ipv4); 6342 6343 /* add notifier for multicast DNS configuration (Bonjour/.local) */ 6344 S_multicast_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"), 6345 kSCDynamicStoreDomainState, 6346 kSCCompNetwork, 6347 CFSTR(kDNSServiceCompMulticastDNS)); 6348 CFArrayAppendValue(keys, S_multicast_resolvers); 6349 6350 /* add notifier for private DNS configuration (Back to My Mac) */ 6351 S_private_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"), 6352 kSCDynamicStoreDomainState, 6353 kSCCompNetwork, 6354 CFSTR(kDNSServiceCompPrivateDNS)); 6355 CFArrayAppendValue(keys, S_private_resolvers); 6356 6357 if (!SCDynamicStoreSetNotificationKeys(S_session, keys, patterns)) { 6358 my_log(LOG_ERR, 6359 "IPMonitor ip_plugin_init " 6360 "SCDynamicStoreSetNotificationKeys failed: %s", 6361 SCErrorString(SCError())); 6362 goto done; 6363 } 6364 6365 rls = SCDynamicStoreCreateRunLoopSource(NULL, S_session, 0); 6366 if (rls == NULL) { 6367 my_log(LOG_ERR, 6368 "IPMonitor ip_plugin_init " 6369 "SCDynamicStoreCreateRunLoopSource failed: %s", 6370 SCErrorString(SCError())); 6371 goto done; 6372 } 6373 6374 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 6375 CFRelease(rls); 6376 6377 /* initialize dns configuration */ 6378 (void)dns_configuration_set(NULL, NULL, NULL, NULL, NULL); 6379#if !TARGET_OS_IPHONE 6380 empty_dns(); 6381#endif /* !TARGET_OS_IPHONE */ 6382 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_dns); 6383 6384#if !TARGET_OS_IPHONE 6385 /* initialize SMB configuration */ 6386 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_smb); 6387#endif /* !TARGET_OS_IPHONE */ 6388 6389 if_rank_dict_init(); 6390 watch_proxies(); 6391 6392 done: 6393 my_CFRelease(&keys); 6394 my_CFRelease(&patterns); 6395 return; 6396} 6397 6398__private_extern__ 6399void 6400prime_IPMonitor() 6401{ 6402 /* initialize multicast route */ 6403 update_ipv4(NULL, NULL, NULL); 6404 return; 6405} 6406 6407static boolean_t 6408S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key, 6409 boolean_t def) 6410{ 6411 CFBooleanRef b; 6412 boolean_t ret = def; 6413 6414 b = isA_CFBoolean(CFDictionaryGetValue(plist, key)); 6415 if (b != NULL) { 6416 ret = CFBooleanGetValue(b); 6417 } 6418 return (ret); 6419} 6420 6421__private_extern__ 6422void 6423load_IPMonitor(CFBundleRef bundle, Boolean bundleVerbose) 6424{ 6425 CFDictionaryRef info_dict; 6426 6427 info_dict = CFBundleGetInfoDictionary(bundle); 6428 6429 if (info_dict != NULL) { 6430 S_append_state 6431 = S_get_plist_boolean(info_dict, 6432 CFSTR("AppendStateArrayToSetupArray"), 6433 FALSE); 6434 } 6435 if (bundleVerbose) { 6436 S_IPMonitor_debug = kDebugFlagDefault; 6437 S_bundle_logging_verbose = bundleVerbose; 6438 S_IPMonitor_verbose = TRUE; 6439 } 6440 6441 my_log_init(); 6442 6443#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) 6444 /* register to receive changes to verbose and read the initial setting */ 6445 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed); 6446 prefs_changed(NULL); 6447 6448#endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) 6449 6450 load_DNSConfiguration(bundle, // bundle 6451 S_IPMonitor_logger, // SCLogger 6452 &S_IPMonitor_verbose, // bundleVerbose 6453 ^(Boolean inSync) { // syncHandler 6454 dispatch_async(__network_change_queue(), ^{ 6455 S_dnsinfo_synced = inSync; 6456 6457 if (inSync && 6458 ((S_network_change_needed & NETWORK_CHANGE_DNS) == 0)) { 6459 // all of the mDNSResponder ack's should result 6460 // in a [new] network change being posted 6461 post_network_change(NETWORK_CHANGE_DNS); 6462 } else { 6463 post_network_change_when_ready(); 6464 } 6465 }); 6466 }); 6467 6468 load_NetworkInformation(bundle, // bundle 6469 S_IPMonitor_logger, // SCLogger 6470 &S_IPMonitor_verbose, // bundleVerbose 6471 ^(Boolean inSync) { // syncHandler 6472 dispatch_async(__network_change_queue(), ^{ 6473 S_nwi_synced = inSync; 6474 post_network_change_when_ready(); 6475 }); 6476 }); 6477 6478 dns_configuration_init(bundle); 6479 6480 proxy_configuration_init(bundle); 6481 6482 ip_plugin_init(); 6483 6484#if !TARGET_OS_IPHONE 6485 if (S_session != NULL) { 6486 dns_configuration_monitor(S_session, IPMonitorNotify); 6487 } 6488#endif /* !TARGET_OS_IPHONE */ 6489 6490#if !TARGET_IPHONE_SIMULATOR 6491 load_hostname((S_IPMonitor_debug & kDebugFlag1) != 0); 6492#endif /* !TARGET_IPHONE_SIMULATOR */ 6493 6494#if !TARGET_OS_IPHONE 6495 load_smb_configuration((S_IPMonitor_debug & kDebugFlag1) != 0); 6496#endif /* !TARGET_OS_IPHONE */ 6497 6498 return; 6499} 6500 6501 6502#pragma mark - 6503#pragma mark Standalone test code 6504 6505 6506#ifdef TEST_IPMONITOR 6507 6508#include "dns-configuration.c" 6509 6510#if !TARGET_IPHONE_SIMULATOR 6511#include "set-hostname.c" 6512#endif /* !TARGET_IPHONE_SIMULATOR */ 6513 6514int 6515main(int argc, char **argv) 6516{ 6517 _sc_log = FALSE; 6518 6519 S_IPMonitor_debug = kDebugFlag1; 6520 if (argc > 1) { 6521 S_IPMonitor_debug = strtoul(argv[1], NULL, 0); 6522 } 6523 6524 load_IPMonitor(CFBundleGetMainBundle(), FALSE); 6525 prime_IPMonitor(); 6526 S_IPMonitor_debug = kDebugFlag1; 6527 CFRunLoopRun(); 6528 /* not reached */ 6529 exit(0); 6530 return 0; 6531} 6532#endif /* TEST_IPMONITOR */ 6533 6534#ifdef TEST_IPV4_ROUTELIST 6535 6536#include "dns-configuration.c" 6537 6538#if !TARGET_IPHONE_SIMULATOR 6539#include "set-hostname.c" 6540#endif /* !TARGET_IPHONE_SIMULATOR */ 6541 6542struct ipv4_service_contents { 6543 const char * addr; 6544 const char * mask; 6545 const char * dest; 6546 const char * router; 6547 const char * ifname; 6548 Rank rank; 6549 const CFStringRef *primaryRank; 6550}; 6551 6552/* 6553 * addr mask dest router ifname pri primaryRank 6554 */ 6555struct ipv4_service_contents en0_10 = { 6556 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, NULL 6557}; 6558 6559struct ipv4_service_contents en0_15 = { 6560 "10.0.0.19", "255.255.255.0", NULL, "10.0.0.1", "en0", 15, NULL 6561}; 6562 6563struct ipv4_service_contents en0_30 = { 6564 "10.0.0.11", "255.255.255.0", NULL, "10.0.0.1", "en0", 30, NULL 6565}; 6566 6567struct ipv4_service_contents en0_40 = { 6568 "10.0.0.12", "255.255.255.0", NULL, "10.0.0.1", "en0", 40, NULL 6569}; 6570 6571struct ipv4_service_contents en0_50 = { 6572 "10.0.0.13", "255.255.255.0", NULL, "10.0.0.1", "en0", 50, NULL 6573}; 6574 6575struct ipv4_service_contents en0_110 = { 6576 "192.168.2.10", "255.255.255.0", NULL, "192.168.2.1", "en0", 110, NULL 6577}; 6578 6579struct ipv4_service_contents en0_1 = { 6580 "17.202.40.191", "255.255.252.0", NULL, "17.202.20.1", "en0", 1, NULL 6581}; 6582 6583struct ipv4_service_contents en1_20 = { 6584 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, NULL 6585}; 6586 6587struct ipv4_service_contents en1_2 = { 6588 "17.202.42.24", "255.255.252.0", NULL, "17.202.20.1", "en1", 2, NULL 6589}; 6590 6591struct ipv4_service_contents en1_125 = { 6592 "192.168.2.20", "255.255.255.0", NULL, "192.168.2.1", "en1", 125, NULL 6593}; 6594 6595struct ipv4_service_contents fw0_25 = { 6596 "192.168.2.30", "255.255.255.0", NULL, "192.168.2.1", "fw0", 25, NULL 6597}; 6598 6599struct ipv4_service_contents fw0_21 = { 6600 "192.168.3.30", "255.255.255.0", NULL, "192.168.3.1", "fw0", 21, NULL 6601}; 6602 6603struct ipv4_service_contents ppp0_0_1 = { 6604 "17.219.156.22", NULL, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL 6605}; 6606 6607struct ipv4_service_contents en0_test6 = { 6608 "17.202.42.113", "255.255.252.0", NULL, "17.202.40.1", "en0", 2, NULL 6609}; 6610struct ipv4_service_contents en1_test6 = { 6611 "17.202.42.111", "255.255.252.0", NULL, "17.202.40.1", "en1", 3, NULL 6612}; 6613struct ipv4_service_contents en2_test6 = { 6614 "17.255.98.164", "255.255.240.0", NULL, "17.255.96.1", "en2", 1, NULL 6615}; 6616 6617struct ipv4_service_contents en0_test7 = { 6618 "17.202.42.113", "255.255.252.0", NULL, "17.202.40.1", "en0", 3, NULL 6619}; 6620struct ipv4_service_contents en1_test7 = { 6621 "17.202.42.111", "255.255.252.0", NULL, "17.202.40.1", "en1", 2, NULL 6622}; 6623struct ipv4_service_contents en2_test7 = { 6624 "17.255.98.164", "255.255.240.0", NULL, "17.255.96.1", "en2", 1, NULL 6625}; 6626struct ipv4_service_contents fw0_test6_and_7 = { 6627 "169.254.11.33", "255.255.0.0", NULL, NULL, "fw0", 0x0ffffff, NULL 6628}; 6629 6630struct ipv4_service_contents en0_10_last = { 6631 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast 6632}; 6633 6634struct ipv4_service_contents en0_10_never = { 6635 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever 6636}; 6637 6638struct ipv4_service_contents en1_20_first = { 6639 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst 6640}; 6641 6642struct ipv4_service_contents en1_20_never = { 6643 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever 6644}; 6645 6646struct ipv4_service_contents en0_linklocal = { 6647 "169.254.22.44", "255.255.0.0", NULL, NULL, "en0", 0xfffff, NULL 6648}; 6649 6650struct ipv4_service_contents * test1[] = { 6651 &en0_40, 6652 &en0_15, 6653 &fw0_25, 6654 &en0_30, 6655 &en1_20, 6656 &en0_50, 6657 &en0_10, 6658 NULL 6659}; 6660 6661struct ipv4_service_contents * test2[] = { 6662 &en0_40, 6663 &fw0_25, 6664 &en0_30, 6665 &en1_20, 6666 &en0_50, 6667 &en0_10, 6668 NULL 6669}; 6670 6671struct ipv4_service_contents * test3[] = { 6672 &en0_40, 6673 &en1_20, 6674 &en0_50, 6675 &en0_10, 6676 &en0_110, 6677 &en1_125, 6678 &fw0_25, 6679 &fw0_21, 6680 &en0_40, 6681 &en0_30, 6682 NULL 6683}; 6684 6685struct ipv4_service_contents * test4[] = { 6686 &en0_1, 6687 &en0_40, 6688 &en0_30, 6689 &en1_20, 6690 &en1_2, 6691 NULL 6692}; 6693 6694struct ipv4_service_contents * test5[] = { 6695 &ppp0_0_1, 6696 &en0_1, 6697 &en0_40, 6698 &en0_30, 6699 &en1_20, 6700 &en1_2, 6701 NULL 6702}; 6703 6704struct ipv4_service_contents * test6[] = { 6705 &en0_test6, 6706 &en1_test6, 6707 &en2_test6, 6708 &fw0_test6_and_7, 6709 NULL 6710}; 6711 6712struct ipv4_service_contents * test7[] = { 6713 &en0_test7, 6714 &en1_test7, 6715 &en2_test7, 6716 &fw0_test6_and_7, 6717 NULL 6718}; 6719 6720struct ipv4_service_contents * test8[] = { 6721 &en0_10, 6722 &en1_20, 6723 NULL 6724}; 6725 6726struct ipv4_service_contents * test9[] = { 6727 &en0_10, 6728 &en1_20_first, 6729 &fw0_25, 6730 NULL 6731}; 6732 6733struct ipv4_service_contents * test10[] = { 6734 &en0_10_last, 6735 &en1_20, 6736 &fw0_25, 6737 NULL 6738}; 6739 6740struct ipv4_service_contents * test11[] = { 6741 &en0_10_never, 6742 &en1_20, 6743 &fw0_25, 6744 NULL 6745}; 6746 6747struct ipv4_service_contents * test12[] = { 6748 &en0_10, 6749 &en1_20, 6750 NULL 6751}; 6752 6753struct ipv4_service_contents * test13[] = { 6754 &en0_10, 6755 &en1_20_never, 6756 NULL 6757}; 6758 6759struct ipv4_service_contents * test14[] = { 6760 &en1_20_never, 6761 NULL 6762}; 6763 6764struct ipv4_service_contents * test15[] = { 6765 &en0_linklocal, 6766 NULL 6767}; 6768 6769static void 6770dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name, 6771 const char * str) 6772{ 6773 CFStringRef prop_val; 6774 6775 if (str == NULL) { 6776 return; 6777 } 6778 prop_val = CFStringCreateWithCString(NULL, 6779 str, 6780 kCFStringEncodingASCII); 6781 CFDictionarySetValue(dict, prop_name, prop_val); 6782 CFRelease(prop_val); 6783 return; 6784} 6785 6786static void 6787dict_add_string_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name, 6788 const char * str) 6789{ 6790 CFArrayRef array; 6791 CFStringRef prop_val; 6792 6793 if (str == NULL) { 6794 return; 6795 } 6796 prop_val = CFStringCreateWithCString(NULL, 6797 str, 6798 kCFStringEncodingASCII); 6799 array = CFArrayCreate(NULL, 6800 (const void **)&prop_val, 1, 6801 &kCFTypeArrayCallBacks); 6802 CFRelease(prop_val); 6803 CFDictionarySetValue(dict, prop_name, array); 6804 CFRelease(array); 6805 return; 6806} 6807 6808static CFDictionaryRef 6809make_IPv4_dict(struct ipv4_service_contents * t) 6810{ 6811 CFMutableDictionaryRef dict; 6812 6813 dict = CFDictionaryCreateMutable(NULL, 0, 6814 &kCFTypeDictionaryKeyCallBacks, 6815 &kCFTypeDictionaryValueCallBacks); 6816 dict_add_string_as_array(dict, kSCPropNetIPv4Addresses, t->addr); 6817 dict_add_string_as_array(dict, kSCPropNetIPv4SubnetMasks, t->mask); 6818 dict_add_string_as_array(dict, kSCPropNetIPv4DestAddresses, t->dest); 6819 dict_add_string(dict, kSCPropNetIPv4Router, t->router); 6820 dict_add_string(dict, kSCPropInterfaceName, t->ifname); 6821 return (dict); 6822} 6823 6824static IPv4RouteListRef 6825make_IPv4RouteList(struct ipv4_service_contents * * this_test) 6826{ 6827 IPv4RouteListRef r; 6828 IPv4RouteListRef routes; 6829 char routes_buf[IPv4RouteListComputeSize(R_STATIC)]; 6830 IPv4RouteListRef ret = NULL; 6831 struct ipv4_service_contents * * scan_test; 6832 6833 for (scan_test = this_test; *scan_test != NULL; scan_test++) { 6834 CFDictionaryRef dict; 6835 6836 dict = make_IPv4_dict(*scan_test); 6837 if (dict == NULL) { 6838 fprintf(stderr, "make_IPv4_dict failed\n"); 6839 exit(1); 6840 } 6841 routes = (IPv4RouteListRef)routes_buf; 6842 routes->size = R_STATIC; 6843 routes->count = 0; 6844 r = IPv4RouteListCreateWithDictionary(routes, dict, 6845 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL); 6846 if (r == NULL) { 6847 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n"); 6848 exit(1); 6849 } 6850 6851 (*scan_test)->rank = RankMake((*scan_test)->rank, kRankAssertionDefault); 6852 6853 if ((*scan_test)->primaryRank != NULL) { 6854 (*scan_test)->rank = RankMake((*scan_test)->rank, 6855 PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank)); 6856 } 6857 6858 if ((*scan_test)->router == NULL) { 6859 (*scan_test)->rank = RankMake((*scan_test)->rank, 6860 PrimaryRankGetRankAssertion(kSCValNetServicePrimaryRankLast)); 6861 } 6862 6863 ret = IPv4RouteListAddRouteList(ret, 1, r, (*scan_test)->rank); 6864 if (r != routes) { 6865 free(r); 6866 } 6867 CFRelease(dict); 6868 } 6869 return (ret); 6870} 6871 6872/* 6873 * Function: run_test 6874 * Purpose: 6875 * Runs through the given set of routes first in the forward direction, 6876 * then again backwards. We should end up with exactly the same set of 6877 * routes at the end. 6878 */ 6879static boolean_t 6880run_test(const char * name, struct ipv4_service_contents * * this_test) 6881{ 6882 CFStringRef descr; 6883 boolean_t ret = FALSE; 6884 IPv4RouteListRef r; 6885 IPv4RouteListRef routes; 6886 char routes_buf[IPv4RouteListComputeSize(R_STATIC)]; 6887 IPv4RouteListRef routes1 = NULL, routes2 = NULL; 6888 struct ipv4_service_contents * * scan_test; 6889 6890 printf("\nStarting test %s\n", name); 6891 for (scan_test = this_test; *scan_test != NULL; scan_test++) { 6892 CFDictionaryRef dict; 6893 6894 dict = make_IPv4_dict(*scan_test); 6895 if (dict == NULL) { 6896 fprintf(stderr, "make_IPv4_dict failed\n"); 6897 exit(1); 6898 } 6899 routes = (IPv4RouteListRef)routes_buf; 6900 routes->size = R_STATIC; 6901 routes->count = 0; 6902 r = IPv4RouteListCreateWithDictionary(routes, dict, 6903 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL); 6904 if (r == NULL) { 6905 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n"); 6906 exit(1); 6907 } 6908 if ((S_IPMonitor_debug & kDebugFlag4) != 0) { 6909 descr = IPv4RouteListCopyDescription(r); 6910 SCPrint(TRUE, stdout, CFSTR("test: Adding %@\n"), descr); 6911 CFRelease(descr); 6912 } 6913 6914 (*scan_test)->rank = RankMake((*scan_test)->rank, kRankAssertionDefault); 6915 6916 if ((*scan_test)->primaryRank != NULL) { 6917 (*scan_test)->rank = RankMake((*scan_test)->rank, 6918 PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank)); 6919 } 6920 6921 routes1 = IPv4RouteListAddRouteList(routes1, 1, r, (*scan_test)->rank); 6922 if (r != routes) { 6923 free(r); 6924 } 6925 CFRelease(dict); 6926 } 6927 if ((S_IPMonitor_debug & kDebugFlag4) != 0) { 6928 if (routes1 != NULL) { 6929 descr = IPv4RouteListCopyDescription(routes1); 6930 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr); 6931 CFRelease(descr); 6932 } 6933 } 6934 for (scan_test--; scan_test >= this_test; scan_test--) { 6935 CFDictionaryRef dict; 6936 6937 dict = make_IPv4_dict(*scan_test); 6938 if (dict == NULL) { 6939 fprintf(stderr, "make_IPv4_dict failed\n"); 6940 exit(1); 6941 } 6942 routes = (IPv4RouteListRef)routes_buf; 6943 routes->size = R_STATIC; 6944 routes->count = 0; 6945 r = IPv4RouteListCreateWithDictionary(routes, dict, 6946 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL); 6947 if (r == NULL) { 6948 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n"); 6949 exit(1); 6950 } 6951 if ((S_IPMonitor_debug & kDebugFlag4) != 0) { 6952 descr = IPv4RouteListCopyDescription(r); 6953 SCPrint(TRUE, stdout, CFSTR("test: Adding %@\n"), descr); 6954 CFRelease(descr); 6955 } 6956 if ((*scan_test)->primaryRank != NULL) { 6957 (*scan_test)->rank = RankMake((*scan_test)->rank, 6958 PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank)); 6959 } 6960 6961 routes2 = IPv4RouteListAddRouteList(routes2, 1, r, (*scan_test)->rank); 6962 if (r != routes) { 6963 free(r); 6964 } 6965 CFRelease(dict); 6966 } 6967 if ((S_IPMonitor_debug & kDebugFlag4) != 0) { 6968 if (routes2 != NULL) { 6969 descr = IPv4RouteListCopyDescription(routes2); 6970 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr); 6971 CFRelease(descr); 6972 } 6973 } 6974 if ((routes1 != NULL && routes2 == NULL) 6975 || (routes1 == NULL && routes2 != NULL)) { 6976 fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n", 6977 (routes1 != NULL) ? "not " : "", 6978 (routes2 != NULL) ? "not " : ""); 6979 } 6980 else if (routes1 != NULL && routes2 != NULL) { 6981 /* check if they are different */ 6982 if (routes1->count != routes2->count) { 6983 fprintf(stderr, "routes1 count %d != routes 2 count %d\n", 6984 routes1->count, routes2->count); 6985 } 6986 else if (bcmp(routes1, routes2, 6987 IPv4RouteListComputeSize(routes1->count)) != 0) { 6988 fprintf(stderr, "routes1 and routes2 are different\n"); 6989 } 6990 else { 6991 printf("routes1 and routes2 are the same\n"); 6992 ret = TRUE; 6993 } 6994 } 6995 if (routes1 != NULL) { 6996 free(routes1); 6997 } 6998 if (routes2 != NULL) { 6999 free(routes2); 7000 } 7001 return (ret); 7002} 7003 7004typedef struct compare_context { 7005 IPv4RouteListRef old; 7006 IPv4RouteListRef new; 7007} compare_context_t; 7008 7009static void 7010compare_callback(IPv4RouteListApplyCommand cmd, IPv4RouteRef route, void * arg) 7011{ 7012 compare_context_t * context = (compare_context_t *)arg; 7013 7014 switch (cmd) { 7015 case kIPv4RouteListAddRouteCommand: 7016 printf("Add new[%ld] = ", route - context->new->list); 7017 IPv4RoutePrint(route); 7018 break; 7019 case kIPv4RouteListRemoveRouteCommand: 7020 printf("Remove old[%ld] = ", route - context->old->list); 7021 IPv4RoutePrint(route); 7022 break; 7023 default: 7024 break; 7025 } 7026 return; 7027} 7028 7029static void 7030compare_tests(struct ipv4_service_contents * * old_test, 7031 struct ipv4_service_contents * * new_test) 7032{ 7033 IPv4RouteListRef new_routes; 7034 IPv4RouteListRef old_routes; 7035 compare_context_t context; 7036 7037 old_routes = make_IPv4RouteList(old_test); 7038 new_routes = make_IPv4RouteList(new_test); 7039 7040 if (old_routes == NULL) { 7041 printf("No Old Routes\n"); 7042 } 7043 else { 7044 printf("Old Routes = "); 7045 IPv4RouteListPrint(old_routes); 7046 } 7047 if (new_routes == NULL) { 7048 printf("No New Routes\n"); 7049 } 7050 else { 7051 printf("New Routes = "); 7052 IPv4RouteListPrint(new_routes); 7053 } 7054 context.old = old_routes; 7055 context.new = new_routes; 7056 IPv4RouteListApply(old_routes, new_routes, compare_callback, &context); 7057 if (old_routes != NULL) { 7058 free(old_routes); 7059 } 7060 if (new_routes != NULL) { 7061 free(new_routes); 7062 } 7063 7064 return; 7065} 7066 7067int 7068main(int argc, char **argv) 7069{ 7070 _sc_log = FALSE; 7071 _sc_verbose = (argc > 1) ? TRUE : FALSE; 7072 7073 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4; 7074 if (argc > 1) { 7075 S_IPMonitor_debug = strtoul(argv[1], NULL, 0); 7076 } 7077 7078 if (run_test("test1", test1) == FALSE) { 7079 fprintf(stderr, "test1 failed\n"); 7080 exit(1); 7081 } 7082 if (run_test("test2", test2) == FALSE) { 7083 fprintf(stderr, "test2 failed\n"); 7084 exit(1); 7085 } 7086 if (run_test("test3", test4) == FALSE) { 7087 fprintf(stderr, "test3 failed\n"); 7088 exit(1); 7089 } 7090 if (run_test("test4", test4) == FALSE) { 7091 fprintf(stderr, "test4 failed\n"); 7092 exit(1); 7093 } 7094 if (run_test("test5", test5) == FALSE) { 7095 fprintf(stderr, "test5 failed\n"); 7096 exit(1); 7097 } 7098 if (run_test("test15", test15) == FALSE) { 7099 fprintf(stderr, "test15 failed\n"); 7100 exit(1); 7101 } 7102 7103 7104 printf("\nCompare 1 to 2:\n"); 7105 compare_tests(test1, test2); 7106 7107 printf("\nCompare 2 to 1:\n"); 7108 compare_tests(test2, test1); 7109 7110 printf("\nCompare 1 to 1:\n"); 7111 compare_tests(test1, test1); 7112 7113 printf("\nCompare 1 to 3:\n"); 7114 compare_tests(test1, test3); 7115 7116 printf("\nCompare 3 to 1:\n"); 7117 compare_tests(test3, test1); 7118 7119 printf("\nCompare 2 to 3:\n"); 7120 compare_tests(test2, test3); 7121 7122 printf("\nCompare 3 to 2:\n"); 7123 compare_tests(test3, test2); 7124 7125 printf("\nCompare 3 to 4:\n"); 7126 compare_tests(test3, test4); 7127 7128 printf("\nCompare 5 to 4:\n"); 7129 compare_tests(test5, test4); 7130 7131 printf("\nCompare 6 to 7:\n"); 7132 compare_tests(test6, test7); 7133 7134 printf("\nCompare 7 to 6:\n"); 7135 compare_tests(test7, test6); 7136 7137 printf("\nCompare 8 to 9:\n"); 7138 compare_tests(test8, test9); 7139 7140 printf("\nCompare 8 to 10:\n"); 7141 compare_tests(test8, test10); 7142 7143 printf("\nCompare 8 to 11:\n"); 7144 compare_tests(test8, test11); 7145 7146 printf("\nCompare 12 to 13:\n"); 7147 compare_tests(test12, test13); 7148 7149 printf("\nCompare 13 to 14:\n"); 7150 compare_tests(test13, test14); 7151 7152 printf("\nChecking for leaks\n"); 7153 char cmd[128]; 7154 sprintf(cmd, "leaks %d 2>&1", getpid()); 7155 fflush(stdout); 7156 (void)system(cmd); 7157 7158 exit(0); 7159 return (0); 7160} 7161 7162#endif /* TEST_IPV4_ROUTELIST */ 7163 7164