/* * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This 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 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 "configthreads_common.h" #include "ip6config_utils.h" #include "globals.h" #include "config_method.h" #include "interfaces.h" static ip6config_func_t * lookup_func(ip6config_method_t method) { switch (method) { case ip6config_method_automatic_e: case ip6config_method_rtadv_e: { return rtadv_thread; break; } case ip6config_method_manual_e: { return manual_thread; break; } case ip6config_method_6to4_e: { return stf_thread; break; } case ip6config_method_linklocal_e: { return linklocal_thread; break; } default: { break; } } return (NULL); } __private_extern__ ip6config_status_t config_method_start(Service_t * service_p, ip6config_method_t method, ip6config_method_data_t * data) { start_event_data_t start_data; ip6config_func_t * func; interface_t * if_p = service_interface(service_p); /* This is checking to make sure that we can perform some basic functions * over the given interface. For everything but 6to4 we need to be able * to multicast, and we need to filter out loopback except on manual configs. */ if (!(if_flags(if_p) & IFF_MULTICAST)) { switch (method) { case ip6config_method_6to4_e: { break; } default: { if (if_ift_type(if_p) != IFT_L2VLAN) { return (ip6config_status_invalid_operation_e); } } } } if (if_flags(if_p) & IFF_LOOPBACK) { switch (method) { case ip6config_method_automatic_e: case ip6config_method_rtadv_e: case ip6config_method_6to4_e: { my_log(LOG_ERR, "CONFIG_METHOD_START %s: invalid config method for loopback", if_name(if_p)); return (ip6config_status_invalid_operation_e); break; } default: break; } } func = lookup_func(method); if (func == NULL) { return (ip6config_status_operation_not_supported_e); } start_data.config.data = data; return (*func)(service_p, IFEventID_start_e, &start_data); } __private_extern__ ip6config_status_t config_method_state_change(Service_t * service_p, ip6_addrinfo_list_t * ip6_addrs) { ip6config_func_t * func; ip6config_status_t status; func = lookup_func(service_p->method); if (func == NULL) { return (ip6config_status_operation_not_supported_e); } status = (*func)(service_p, IFEventID_state_change_e, ip6_addrs); return (status); } __private_extern__ ip6config_status_t config_method_change(Service_t * service_p, ip6config_method_t method, ip6config_method_data_t * data, boolean_t * needs_stop) { change_event_data_t change_data; ip6config_func_t * func; ip6config_status_t status; *needs_stop = FALSE; func = lookup_func(method); if (func == NULL) { return (ip6config_status_operation_not_supported_e); } change_data.config.data = data; change_data.needs_stop = FALSE; status = (*func)(service_p, IFEventID_change_e, &change_data); *needs_stop = change_data.needs_stop; return (status); } __private_extern__ ip6config_status_t config_method_ipv4_primary_change(Service_t * service_p, ip6config_method_t method, ip6config_method_data_t * data) { start_event_data_t start_data; ip6config_func_t * func; func = lookup_func(method); if (func == NULL) { return (ip6config_status_operation_not_supported_e); } start_data.config.data = data; return (*func)(service_p, IFEventID_ipv4_primary_change_e, &start_data); } static ip6config_status_t config_method_event(Service_t * service_p, IFEventID_t event) { ip6config_status_t status = ip6config_status_success_e; ip6config_func_t * func; ip6config_method_t method = service_p->method; func = lookup_func(method); if (func == NULL) { SCLog(TRUE, LOG_INFO, CFSTR("config_method_event(%s): lookup_func(%d) failed"), IFEventID_names(event), method); status = ip6config_status_internal_error_e; goto done; } (*func)(service_p, event, NULL); done: return (status); } __private_extern__ ip6config_status_t config_method_stop(Service_t * service_p) { return (config_method_event(service_p, IFEventID_stop_e)); } __private_extern__ ip6config_status_t config_method_media(Service_t * service_p) { return (config_method_event(service_p, IFEventID_media_e)); } static boolean_t ip6config_method_from_cfstring(CFStringRef m, ip6config_method_t * method) { if (CFEqual(m, kSCValNetIPv6ConfigMethodManual)) { *method = ip6config_method_manual_e; } else if (CFEqual(m, kSCValNetIPv6ConfigMethodAutomatic) || CFEqual(m, kSCValNetIPv6ConfigMethodRouterAdvertisement)) { *method = ip6config_method_rtadv_e; } else if (CFEqual(m, kSCValNetIPv6ConfigMethod6to4)) { *method = ip6config_method_6to4_e; } else { return (FALSE); } return (TRUE); } static void ip6config_get_ip4_addresses(CFArrayRef addresses, stf_method_data_t * stf_data) { int count; int i; count = CFArrayGetCount(addresses); if (count == 0) { my_log(LOG_ERR, "ip6config: 6TO4 - ipv4 address array empty"); stf_data->ip4_addrs_list = NULL; return; } stf_data->ip4_addrs_list = (struct in_addr *) calloc(count, sizeof(struct in_addr)); if (stf_data->ip4_addrs_list == NULL) { my_log(LOG_ERR, "ip6config: malloc stf_data->ip4_addrs_list failed"); return; } stf_data->n_ip4 = count; for (i = 0; i < count; i++) { struct in_addr tmp_addr = { 0 }; cfstring_to_numeric(AF_INET, CFArrayGetValueAtIndex(addresses, i), &tmp_addr); memcpy(&(stf_data->ip4_addrs_list[i]), &tmp_addr, sizeof(struct in_addr)); } return; } __private_extern__ int ip6config_get_6to4_address_data(SCDynamicStoreRef session, ip6config_method_data_t * method_data) { CFStringRef ipv4_global_key = NULL; CFDictionaryRef ipv4_global_dict = NULL; CFStringRef primary_service = NULL; CFStringRef service_key = NULL; CFDictionaryRef ipv4_service_dict = NULL; CFArrayRef addresses = NULL; int error = -1; /* get State:/Network/Global/IPv4 */ ipv4_global_key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); if (ipv4_global_key == NULL) { goto done; } ipv4_global_dict = my_SCDynamicStoreCopyValue(session, ipv4_global_key); if (ipv4_global_dict == NULL) { goto done; } /* get primary IPv4 service */ primary_service = CFDictionaryGetValue(ipv4_global_dict, kSCDynamicStorePropNetPrimaryService); if (primary_service == NULL) { goto done; } /* get State:/Network/Service//IPv4 */ service_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, primary_service, kSCEntNetIPv4); if (service_key == NULL) { goto done; } ipv4_service_dict = my_SCDynamicStoreCopyValue(session, service_key); if (ipv4_service_dict == NULL) { goto done; } addresses = CFDictionaryGetValue(ipv4_service_dict, kSCPropNetIPv4Addresses); if (addresses == NULL || isA_CFArray(addresses) == NULL) { goto done; } ip6config_get_ip4_addresses(addresses, &method_data->stf_data); if (method_data->stf_data.ip4_addrs_list != NULL) { error = 0; } done: my_CFRelease(&ipv4_global_key); my_CFRelease(&ipv4_global_dict); my_CFRelease(&service_key); my_CFRelease(&ipv4_service_dict); return (error); } static int ip6config_6to4_relay_is_numeric_address(CFStringRef address, relay_address_t * relay_address) { struct in6_addr tmp6_addr; struct in_addr tmp4_addr; int err; if ((err = cfstring_to_numeric(AF_INET6, address, &tmp6_addr)) == 0) { relay_address->addr_type = relay_address_type_ipv6_e; memcpy(&relay_address->relay_address_u.ip6_relay_addr, &tmp6_addr, sizeof(struct in6_addr)); } else { my_log(LOG_DEBUG, "ip6config_6to4_relay_is_numeric_address: NOT IP6 ADDRESS"); if ((err = cfstring_to_numeric(AF_INET, address, &tmp4_addr)) == 0) { relay_address->addr_type = relay_address_type_ipv4_e; relay_address->relay_address_u.ip4_relay_addr = tmp4_addr; } else { my_log(LOG_DEBUG, "ip6config_6to4_relay_is_numeric_address: NOT IP4 ADDRESS"); } } return (err); } static int ip6config_get_6to4_relay_address(CFStringRef address, relay_address_t * relay_address) { if (isA_CFString(address) == NULL) { my_log(LOG_DEBUG, "ip6config_get_6to4_relay_address: address not CFString"); return (-1); } if (ip6config_6to4_relay_is_numeric_address(address, relay_address) != 0) { int len; char buf[256]; len = cfstring_to_cstring(address, buf, sizeof(buf)); if (len == 0) { my_log(LOG_DEBUG, "ip6config_get_6to4_relay_address: length of address is 0"); return (-1); } relay_address->relay_address_u.dns_relay_addr = malloc(len + 1); if (relay_address->relay_address_u.dns_relay_addr == NULL) { my_log(LOG_DEBUG, "ip6config_get_6to4_relay_address: malloc relay failed"); return (-1); } relay_address->addr_type = relay_address_type_dns_e; memcpy(relay_address->relay_address_u.dns_relay_addr, buf, (len + 1)); } return (0); } #define DEFAULT_PREFIX_LENGTH 64 /* from RFC3068 */ #define DEFAULT_6TO4_RELAY_ADDRESS_INIT \ {{{ 0x20, 0x02, 0xc0, 0x58, 0x63, 0x01, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}} __private_extern__ ip6config_method_data_t* ip6config_method_data_from_dict(CFDictionaryRef dict, ip6config_method_t * method) { CFStringRef config_method; int count = 0; int i; CFArrayRef addresses = NULL; CFArrayRef prefixLens = NULL; CFStringRef relay_address = NULL; ip6config_method_data_t * method_data = NULL; int method_data_len = 0; config_method = CFDictionaryGetValue(dict, kSCPropNetIPv6ConfigMethod); if (config_method == NULL || ip6config_method_from_cfstring(config_method, method) == FALSE) { my_log(LOG_ERR, "ip6config: configuration method is missing/invalid"); goto error; } if (*method == ip6config_method_6to4_e) { method_data_len = sizeof(*method_data) + (count * sizeof(method_data->ip6[0])); method_data = (ip6config_method_data_t *) malloc(method_data_len); if (method_data == NULL) { my_log(LOG_ERR, "ip6config: malloc method_data failed"); goto error; } bzero(method_data, method_data_len); method_data->n_ip6 = count; relay_address = CFDictionaryGetValue(dict, kSCPropNet6to4Relay); if (relay_address == NULL) { /* by default, we use the 6to4 anycast address */ struct in6_addr tmp6_addr = DEFAULT_6TO4_RELAY_ADDRESS_INIT; method_data->stf_data.relay_address.addr_type = relay_address_type_ipv6_e; memcpy(&method_data->stf_data.relay_address.relay_address_u.ip6_relay_addr, &tmp6_addr, sizeof(struct in6_addr)); } else if (ip6config_get_6to4_relay_address(relay_address, &method_data->stf_data.relay_address) != 0) { my_log(LOG_INFO, "ip6config: bad 6to4 relay address"); goto error; } } else { addresses = CFDictionaryGetValue(dict, kSCPropNetIPv6Addresses); prefixLens = CFDictionaryGetValue(dict, kSCPropNetIPv6PrefixLength); /* All must be same size */ if (addresses) { if (isA_CFArray(addresses)) { count = CFArrayGetCount(addresses); if (count == 0) { my_log(LOG_ERR, "ip6configd: address array empty"); goto error; } } if (prefixLens) { if (isA_CFArray(prefixLens)) { if (count != CFArrayGetCount(prefixLens)) { my_log(LOG_ERR, "ip6configd: address/prefix arrays not same size"); goto error; } } else { prefixLens = NULL; } } } if (*method == ip6config_method_manual_e && addresses == NULL) { my_log(LOG_ERR, "ip6configd: manual method requires an address"); goto error; } method_data_len = sizeof(*method_data) + (count * sizeof(method_data->ip6[0])); method_data = (ip6config_method_data_t *) malloc(method_data_len); if (method_data == NULL) { my_log(LOG_ERR, "ip6config: malloc method_data failed"); goto error; } bzero(method_data, method_data_len); method_data->n_ip6 = count; for (i = 0; i < count; i++) { struct in6_addr tmp_addr = IN6ADDR_ANY_INIT; cfstring_to_numeric(AF_INET6, CFArrayGetValueAtIndex(addresses, i), &tmp_addr); memcpy(&(method_data->ip6[i].addr), &tmp_addr, sizeof(struct in6_addr)); if (prefixLens) { CFNumberRef val = isA_CFNumber(CFArrayGetValueAtIndex(prefixLens, i)); if (val) { CFNumberGetValue(val, kCFNumberIntType, &(method_data->ip6[i].prefixLen)); } else { my_log(LOG_ERR, "ip6config: error getting prefixlen"); goto error; } } else { /* we use the default if none was specified - this is a * workaround for Radar 3227716. */ method_data->ip6[i].prefixLen = DEFAULT_PREFIX_LENGTH; } } } return (method_data); error: if (method_data) free(method_data); return (NULL); } __private_extern__ int ip6config_address_data_from_state(CFDictionaryRef dict, ip6_addrinfo_list_t * ip6_addrs) { int i, count; CFArrayRef addresses = NULL; CFArrayRef prefixLens = NULL; CFArrayRef flags = NULL; addresses = CFDictionaryGetValue(dict, kSCPropNetIPv6Addresses); prefixLens = CFDictionaryGetValue(dict, kSCPropNetIPv6PrefixLength); flags = CFDictionaryGetValue(dict, kSCPropNetIPv6Flags); /* All must be same size */ if (!addresses || !prefixLens || !flags) { my_log(LOG_DEBUG, "ip6config_address_data_from_state: no addresses/prefixlens/flags in dictionary"); return (-1); } count = CFArrayGetCount(addresses); if (count == 0) { my_log(LOG_ERR, "ip6config_address_data_from_state: address array empty"); return (-1); } if (count != CFArrayGetCount(prefixLens)) { my_log(LOG_ERR, "ip6config_address_data_from_state: address/prefixlen arrays not same size"); return (-1); } if (count != CFArrayGetCount(flags)) { my_log(LOG_ERR, "ip6config_address_data_from_state: address/flags arrays not same size"); return (-1); } ip6_addrs->addr_list = calloc(count, sizeof(*ip6_addrs->addr_list)); if (ip6_addrs->addr_list == NULL) { my_log(LOG_DEBUG, "ip6config_address_data_from_state: error allocating addr_list"); return (-1); } for (i = 0; i < count; i++) { CFNumberRef val; struct in6_addr tmp_addr = IN6ADDR_ANY_INIT; cfstring_to_numeric(AF_INET6, CFArrayGetValueAtIndex(addresses, i), &tmp_addr); memcpy(&ip6_addrs->addr_list[i].addr, &tmp_addr, sizeof(struct in6_addr)); val = isA_CFNumber(CFArrayGetValueAtIndex(prefixLens, i)); if (val) { CFNumberGetValue(val, kCFNumberIntType, &ip6_addrs->addr_list[i].prefixlen); } val = isA_CFNumber(CFArrayGetValueAtIndex(flags, i)); if (val) { CFNumberGetValue(val, kCFNumberIntType, &ip6_addrs->addr_list[i].flags); } } ip6_addrs->n_addrs = count; return (0); }