/* * Copyright (c) 2003-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "configthreads_common.h" #include "globals.h" #include "ip6config_utils.h" typedef struct { SCNetworkReachabilityRef target; struct in6_addr our_6to4_addr; int our_prefixLen; int addr_flags; struct in6_addr our_relay; struct in6_addr prefixmask; } Service_6to4_t; /* * Taken from RFC 1918. Private IP address ranges are: * 10.0.0.0 - 10.255.255.255 (10/8 prefix) * 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) * 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) */ #define IN_PRIVATE_10 ((u_int32_t)0x0a000000) #define IN_PRIVATE_10_NET ((u_int32_t)IN_CLASSA_NET) #define IN_PRIVATE_172_16 ((u_int32_t)0xac100000) #define IN_PRIVATE_172_16_NET ((u_int32_t)0xfff00000) #define IN_PRIVATE_192_168 ((u_int32_t)0xc0a80000) #define IN_PRIVATE_192_168_NET ((u_int32_t)IN_CLASSB_NET) static __inline__ boolean_t ip_is_private(struct in_addr iaddr) { u_int32_t val = ntohl(iaddr.s_addr); if ((val & IN_PRIVATE_10_NET) == IN_PRIVATE_10 || (val & IN_PRIVATE_172_16_NET) == IN_PRIVATE_172_16 || (val & IN_PRIVATE_192_168_NET) == IN_PRIVATE_192_168) { return (TRUE); } return (FALSE); } static __inline__ boolean_t ip_is_linklocal(struct in_addr iaddr) { u_int32_t val = ntohl(iaddr.s_addr); return (IN_LINKLOCAL(val)); } static boolean_t stf_get_valid_ip4(ip6config_method_data_t * method_data, struct in_addr * ip4_addr) { int count; int i; struct in_addr * scan; scan = method_data->stf_data.ip4_addrs_list; count = method_data->stf_data.n_ip4; if (scan == NULL || count == 0) { goto done; } for (i = 0; i < count; i++, scan++) { if (ip_is_linklocal(*scan) || ip_is_private(*scan)) { continue; } *ip4_addr = *scan; return (TRUE); } done: return (FALSE); } static void stf_construct_6to4_address(struct in_addr * ip4_addr, struct in6_addr * ip6_addr, boolean_t relay) { char str[64]; uint32_t tmp_addr = ntohl((uint32_t)ip4_addr->s_addr); /* Constructing 6to4 address: * - start with 2002 prefix * - add each byte of ip4 addr like: * %02x%02x:%02x%02x 0102:0304 (1.2.3.4) * - append v6_net (1) if not relay * - append hostbits6 (::1) if not relay * So, for example: * ip4 addr: 1.2.3.4 * ip6 addr: 2002:0102:0304:0001:0000:0000:0000:0001 */ ip6_addr->s6_addr[0] = 0x20; ip6_addr->s6_addr[1] = 0x02; ip6_addr->s6_addr[2] = ((tmp_addr & 0xff000000) >> 24); ip6_addr->s6_addr[3] = ((tmp_addr & 0x00ff0000) >> 16); ip6_addr->s6_addr[4] = ((tmp_addr & 0x0000ff00) >> 8); ip6_addr->s6_addr[5] = (tmp_addr & 0x000000ff); ip6_addr->s6_addr[6] = 0x00; if (relay == FALSE) { ip6_addr->s6_addr[7] = 0x01; } else { ip6_addr->s6_addr[7] = 0x00; } ip6_addr->s6_addr[8] = 0x00; ip6_addr->s6_addr[9] = 0x00; ip6_addr->s6_addr[10] = 0x00; ip6_addr->s6_addr[11] = 0x00; ip6_addr->s6_addr[12] = 0x00; ip6_addr->s6_addr[13] = 0x00; ip6_addr->s6_addr[14] = 0x00; if (relay == FALSE) { ip6_addr->s6_addr[15] = 0x01; } else { ip6_addr->s6_addr[15] = 0x00; } inet_ntop(AF_INET6, (const void *)ip6_addr, str, sizeof(str)); my_log(LOG_DEBUG, "6to4: constructed address: %s", str); return; } #define DEFAULT_6TO4_PREFIXLEN 16 static void stf_configure_address(Service_t * service_p, void * event_data) { Service_6to4_t * stf = (Service_6to4_t *)service_p->private; start_event_data_t * evdata = ((start_event_data_t *)event_data); ip6config_method_data_t * stf_config_data = evdata->config.data; struct in_addr ip4_addr; /* get first routable address */ if (stf_get_valid_ip4(stf_config_data, &ip4_addr) == FALSE) { my_log(LOG_ERR, "ip6config: bad or no ip4 address for 6to4"); return; } /* create 6to4 address */ stf_construct_6to4_address(&ip4_addr, &stf->our_6to4_addr, FALSE); stf->our_prefixLen = DEFAULT_6TO4_PREFIXLEN; return; } static void stf_resolve_callback(SCNetworkReachabilityRef target, SCNetworkConnectionFlags flags, void *info) { Service_t * service_p = (Service_t *)info; Service_6to4_t * stf = service_p->private; CFArrayRef relay_addrs = NULL; int i, n, err, found = 0; if ((flags & kSCNetworkFlagsReachable) && !(flags & kSCNetworkFlagsConnectionRequired)) { relay_addrs = SCNetworkReachabilityCopyResolvedAddress(target, &err); } if (relay_addrs == NULL) { my_log(LOG_DEBUG, "STF_RESOLVE: relay address not reachable."); return; } SCLog(G_verbose, LOG_INFO, CFSTR("STF_RESOLVE: relays: %@"), relay_addrs); n = CFArrayGetCount(relay_addrs); /* First try finding any v6 addresses. * Only try finding v4 if there are no v6 */ for (i = 0; i < n; i++) { CFDataRef data = NULL; struct sockaddr *sa; data = CFArrayGetValueAtIndex(relay_addrs, i); if (data == NULL) { my_log(LOG_DEBUG, "STF_RESOLVE: array member %d contains no data", i); continue; } sa = (struct sockaddr *)CFDataGetBytePtr(data); if (sa->sa_family == AF_INET6) { char buf[64]; struct sockaddr_in6 * sa6 = (struct sockaddr_in6 *)sa; /* this is a v6 address, use it */ inet_ntop(AF_INET6, &sa6->sin6_addr, buf, sizeof(buf)); my_log(LOG_DEBUG, "STF_RESOLVE: returned addr is IPv6: %s", buf); memcpy(&stf->our_relay, &sa6->sin6_addr, sizeof(struct in6_addr)); found = 1; break; } } if (!found) { for (i = 0; i < n; i++) { CFDataRef data = NULL; struct sockaddr *sa; struct in6_addr tmp6_addr = IN6ADDR_ANY_INIT; data = CFArrayGetValueAtIndex(relay_addrs, i); if (data == NULL) { my_log(LOG_DEBUG, "STF_RESOLVE: array member %d contains no data", i); continue; } sa = (struct sockaddr *)CFDataGetBytePtr(data); if (sa->sa_family == AF_INET) { struct sockaddr_in * sa4 = (struct sockaddr_in *)sa; char buf1[64], buf2[64]; /* this is a v4 address, convert it */ stf_construct_6to4_address(&sa4->sin_addr, &tmp6_addr, TRUE); inet_ntop(AF_INET, &sa4->sin_addr, buf1, sizeof(buf1)); inet_ntop(AF_INET6, &tmp6_addr, buf2, sizeof(buf2)); my_log(LOG_DEBUG, "STF_RESOLVE: returned addr is IPv4: %s %s", buf1, buf2); memcpy(&stf->our_relay, &tmp6_addr, sizeof(struct in6_addr)); found = 1; break; } } } if (!found) { my_log(LOG_DEBUG, "stf_resolve_callback: no v6 or v4 addresses found!!!"); } else { if (!IN6_IS_ADDR_UNSPECIFIED(&stf->our_6to4_addr)) { service_publish_clear(service_p); (void)service_set_address(service_p, &stf->our_6to4_addr, stf->our_prefixLen, stf->addr_flags); memcpy(&service_p->info.router, &stf->our_relay, sizeof(struct in6_addr)); service_publish_success(service_p); } } if (relay_addrs) { my_CFRelease(&relay_addrs); } SCNetworkReachabilityUnscheduleFromRunLoop(stf->target, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); my_CFRelease(&stf->target); return; } static void stf_resolve_hostname(char * address, Service_t * service_p) { Service_6to4_t * stf = service_p->private; SCNetworkReachabilityRef target = NULL; SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL }; if (address == NULL) { my_log(LOG_DEBUG, "stf_resolve_hostname: address == NULL"); return; } context.info = service_p; target = SCNetworkReachabilityCreateWithName(NULL, address); if (target == NULL) { my_log(LOG_DEBUG, "stf_resolve_hostname: error creating target"); return; } if (SCNetworkReachabilitySetCallback(target, stf_resolve_callback, &context)) { SCNetworkReachabilityScheduleWithRunLoop(target, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); } stf->target = target; return; } static void stf_get_relay_address(Service_t * service_p, void * event_data) { Service_6to4_t * stf = (Service_6to4_t *)service_p->private; start_event_data_t * evdata = ((start_event_data_t *)event_data); ip6config_method_data_t * stf_config_data = evdata->config.data; relay_address_t * relay_info = &stf_config_data->stf_data.relay_address; my_log(LOG_DEBUG, "stf_get_relay_address: relay address type is %s", relay_address_type_string(relay_info->addr_type)); /* only change the relay address if there is a new address */ switch(relay_info->addr_type) { case relay_address_type_ipv6_e: { if (!IN6_IS_ADDR_UNSPECIFIED(&relay_info->relay_address_u.ip6_relay_addr) && !IN6_ARE_ADDR_EQUAL(&stf->our_relay, &relay_info->relay_address_u.ip6_relay_addr)) { memcpy(&stf->our_relay, &relay_info->relay_address_u.ip6_relay_addr, sizeof(struct in6_addr)); } break; } case relay_address_type_ipv4_e: { if (relay_info->relay_address_u.ip4_relay_addr.s_addr != INADDR_ANY) { bzero(&stf->our_relay, sizeof(struct in6_addr)); stf_construct_6to4_address(&relay_info->relay_address_u.ip4_relay_addr, &stf->our_relay, TRUE); } break; } case relay_address_type_dns_e: { if (relay_info->relay_address_u.dns_relay_addr) { my_log(LOG_DEBUG, "stf_get_relay_address: resolving hostname"); stf_resolve_hostname(relay_info->relay_address_u.dns_relay_addr, service_p); } break; } default: { my_log(LOG_DEBUG, "stf_get_relay_address: no relay address type given"); break; } } return; } __private_extern__ ip6config_status_t stf_thread(Service_t * service_p, IFEventID_t evid, void * event_data) { interface_t * if_p = service_interface(service_p); Service_6to4_t * stf = (Service_6to4_t *)service_p->private; ip6config_status_t status = ip6config_status_success_e; switch (evid) { case IFEventID_start_e: { if (if_flags(if_p) & IFF_LOOPBACK) { status = ip6config_status_invalid_operation_e; break; } my_log(LOG_DEBUG, "STF_THREAD %s: STARTING", if_name(if_p)); if (stf) { my_log(LOG_DEBUG, "STF_THREAD %s: re-entering start state", if_name(if_p)); status = ip6config_status_internal_error_e; break; } stf = calloc(1, sizeof(*stf)); if (stf == NULL) { my_log(LOG_ERR, "STF_THREAD %s: calloc failed", if_name(if_p)); status = ip6config_status_allocation_failed_e; break; } service_p->private = stf; stf_configure_address(service_p, event_data); stf_get_relay_address(service_p, event_data); if (!IN6_IS_ADDR_UNSPECIFIED(&stf->our_6to4_addr) && !IN6_IS_ADDR_UNSPECIFIED(&stf->our_relay)) { (void)service_set_address(service_p, &stf->our_6to4_addr, stf->our_prefixLen, stf->addr_flags); memcpy(&service_p->info.router, &stf->our_relay, sizeof(struct in6_addr)); service_publish_success(service_p); } break; } case IFEventID_stop_e: { /* Remove address in kernel * Remove route in kernel */ my_log(LOG_DEBUG, "STF_THREAD %s: STOPPING", if_name(if_p)); if (stf == NULL) { my_log(LOG_DEBUG, "STF_THREAD %s: private data is NULL", if_name(if_p)); status = ip6config_status_internal_error_e; break; } /* remove 6to4 address and route */ service_remove_addresses(service_p); service_publish_clear(service_p); if (stf->target) { SCNetworkReachabilityUnscheduleFromRunLoop(stf->target, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); my_CFRelease(&stf->target); } free(stf); service_p->private = NULL; break; } case IFEventID_change_e: { /* Change event */ change_event_data_t * evdata = ((change_event_data_t *)event_data); my_log(LOG_DEBUG, "STF_THREAD %s: CHANGE", if_name(if_p)); if (stf == NULL) { my_log(LOG_DEBUG, "STF_THREAD %s: private data is NULL", if_name(if_p)); status = ip6config_status_internal_error_e; break; } if (stf->target) { SCNetworkReachabilityUnscheduleFromRunLoop(stf->target, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); my_CFRelease(&stf->target); } service_publish_clear(service_p); /* get relay address in case that changed */ stf_get_relay_address(service_p, event_data); /* Check both the address and the relay before publishing */ if (!IN6_IS_ADDR_UNSPECIFIED(&stf->our_relay) && !IN6_IS_ADDR_UNSPECIFIED(&stf->our_6to4_addr)) { /* set route and publish */ memcpy(&service_p->info.router, &stf->our_relay, sizeof(struct in6_addr)); service_publish_success(service_p); } else { /* The addresses aren't ready. Remove the existing * address and wait till we have what we need. */ service_remove_addresses(service_p); } evdata->needs_stop = FALSE; break; } case IFEventID_state_change_e: { int i; ip6_addrinfo_list_t * ip6_addrs = ((ip6_addrinfo_list_t *)event_data); my_log(LOG_DEBUG, "STF_THREAD %s: STATE_CHANGE", if_name(if_p)); if (stf == NULL) { my_log(LOG_DEBUG, "STF_THREAD %s: private data is NULL", if_name(if_p)); status = ip6config_status_internal_error_e; break; } /* go through the address list; if addr is not autoconf and * not linklocal then deal with it; there is only one address * possible here so stop after the first hit */ for (i = 0; i < ip6_addrs->n_addrs; i++) { ip6_addrinfo_t *new_addr = ip6_addrs->addr_list + i; if (!IN6_IS_ADDR_LINKLOCAL(&new_addr->addr) && !(new_addr->flags & IN6_IFF_AUTOCONF)) { if (service_p->info.addrs.addr_list) free(service_p->info.addrs.addr_list); service_p->info.addrs.addr_list = malloc(sizeof(ip6_addrinfo_t)); if (!service_p->info.addrs.addr_list) { my_log(LOG_ERR, "STF_THREAD: error allocating memory for addresses"); status = ip6config_status_allocation_failed_e; break; } memcpy(&service_p->info.addrs.addr_list[0].addr, &new_addr->addr, sizeof(struct in6_addr)); service_p->info.addrs.addr_list[0].prefixlen = new_addr->prefixlen; service_p->info.addrs.addr_list[0].flags = new_addr->flags; prefixLen2mask(&service_p->info.addrs.addr_list[0].prefixmask, service_p->info.addrs.addr_list[0].prefixlen); /* update private data */ memcpy(&stf->our_6to4_addr, &service_p->info.addrs.addr_list[0].addr, sizeof(struct in6_addr)); stf->our_prefixLen = service_p->info.addrs.addr_list[0].prefixlen; stf->addr_flags = service_p->info.addrs.addr_list[0].flags; memcpy(&stf->prefixmask, &service_p->info.addrs.addr_list[0].prefixmask, sizeof(struct in6_addr)); /* set route */ /* only publish if the router has been set */ if (!IN6_IS_ADDR_UNSPECIFIED(&stf->our_relay)) { memcpy(&service_p->info.router, &stf->our_relay, sizeof(struct in6_addr)); service_publish_success(service_p); } break; } } break; } case IFEventID_ipv4_primary_change_e: { struct in6_addr old_addr; /* Primary service has changed */ my_log(LOG_DEBUG, "STF_THREAD: ipv4_primary_change"); memcpy(&old_addr, &stf->our_6to4_addr, sizeof(struct in6_addr)); bzero( &stf->our_6to4_addr, sizeof(struct in6_addr)); stf_configure_address(service_p, event_data); if (!IN6_ARE_ADDR_EQUAL(&old_addr, &stf->our_6to4_addr)) { /* remove 6to4 address */ service_remove_addresses(service_p); service_publish_clear(service_p); /* if the 6to4 address is not specified then don't set it, and * if the relay address is not set then either we're still * waiting for it to resolve or it's not valid, so don't * set the address */ if (!IN6_IS_ADDR_UNSPECIFIED(&stf->our_6to4_addr) && !IN6_IS_ADDR_UNSPECIFIED(&stf->our_relay)) { char buf1[64], buf2[64]; my_log(LOG_DEBUG, "STF_THREAD: ipv4_primary_change: addr: %s", inet_ntop(AF_INET6, &stf->our_6to4_addr, buf1, sizeof(buf1))); (void)service_set_address(service_p, &stf->our_6to4_addr, stf->our_prefixLen, stf->addr_flags); my_log(LOG_DEBUG, "STF_THREAD: ipv4_primary_change: relay: %s", inet_ntop(AF_INET6, &stf->our_relay, buf2, sizeof(buf2))); memcpy(&service_p->info.router, &stf->our_relay, sizeof(struct in6_addr)); service_publish_success(service_p); } } break; } default: { break; } } /* switch */ return (status); }