1/* 2 * Copyright (c) 2003-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * manual_v6.c 26 * - manual IPv6 configuration thread manual_v6_thread() 27 * - assigns the address to the interface, waits for the address to appear 28 * on the interface; once it appears, if it's a duplicated IP, report the 29 * conflict; otherwise publish success 30 */ 31 32#include <stdlib.h> 33#include <unistd.h> 34#include <string.h> 35#include <sys/types.h> 36#include <sys/socket.h> 37#include <sys/ioctl.h> 38#include <sys/sockio.h> 39#include <sys/uio.h> 40#include <sys/time.h> 41#include <sys/param.h> 42#include <ctype.h> 43#include <net/if.h> 44#include <net/if_dl.h> 45#include <net/route.h> 46#include <netinet/in.h> 47#include <netinet/ip6.h> 48#include <netinet6/ip6_var.h> 49#define KERNEL_PRIVATE 50#include <netinet6/in6_var.h> 51#undef KERNEL_PRIVATE 52#include <netinet/icmp6.h> 53#include <netinet6/nd6.h> 54#include <arpa/inet.h> 55#include <syslog.h> 56#include <CoreFoundation/CFSocket.h> 57#include <SystemConfiguration/SystemConfiguration.h> 58#include <SystemConfiguration/SCPrivate.h> 59#include <SystemConfiguration/SCValidation.h> 60 61#include "ipconfigd_threads.h" 62#include "FDSet.h" 63#include "globals.h" 64#include "timer.h" 65#include "ifutil.h" 66#include "sysconfig.h" 67#include "util.h" 68#include "cfutil.h" 69#include "symbol_scope.h" 70 71static void 72manual_v6_publish(ServiceRef service_p) 73{ 74 inet6_addrinfo_t info; 75 76 /* publish our address */ 77 info.addr_flags = 0; 78 ServiceGetRequestedIPv6Address(service_p, &info.addr, &info.prefix_length); 79 ServicePublishSuccessIPv6(service_p, &info, 1, NULL, 0, NULL, NULL); 80 return; 81} 82 83static void 84manual_v6_inactive(ServiceRef service_p) 85{ 86 struct in6_addr addr; 87 int prefix_length; 88 89 ServiceGetRequestedIPv6Address(service_p, 90 &addr, &prefix_length); 91 ServiceRemoveIPv6Address(service_p, &addr, prefix_length); 92 service_publish_failure(service_p, 93 ipconfig_status_media_inactive_e); 94 return; 95} 96 97 98static void 99manual_v6_set_address(ServiceRef service_p) 100{ 101 struct in6_addr addr; 102 u_int32_t flags; 103 interface_t * if_p = service_interface(service_p); 104 int prefix_length; 105 106 /* get the requested IP/prefix */ 107 ServiceGetRequestedIPv6Address(service_p, &addr, &prefix_length); 108 109 /* set the new address */ 110 flags = (if_ift_type(if_p) == IFT_CELLULAR) ? IN6_IFF_OPTIMISTIC : 0; 111 ServiceSetIPv6Address(service_p, &addr, prefix_length, flags, 112 ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME); 113 return; 114} 115 116static void 117manual_v6_start(ServiceRef service_p) 118{ 119 link_status_t link_status; 120 interface_t * if_p = service_interface(service_p); 121 122 if (if_ift_type(if_p) == IFT_LOOP) { 123 manual_v6_set_address(service_p); 124 manual_v6_publish(service_p); 125 return; 126 } 127 link_status = service_link_status(service_p); 128 if (link_status.valid == TRUE && link_status.active == FALSE) { 129 manual_v6_inactive(service_p); 130 } 131 else { 132 manual_v6_set_address(service_p); 133 /* wait to publish once the address flags say DaD has completed */ 134 } 135 return; 136} 137 138static void 139manual_v6_address_changed(ServiceRef service_p, 140 inet6_addrlist_t * addr_list_p) 141{ 142 struct in6_addr addr; 143 int i; 144 interface_t * if_p = service_interface(service_p); 145 int prefix_length; 146 inet6_addrinfo_t * scan; 147 148 if (addr_list_p == NULL || addr_list_p->count == 0) { 149 /* no addresses configured, nothing to do */ 150 return; 151 } 152 153 /* get our address */ 154 ServiceGetRequestedIPv6Address(service_p, &addr, &prefix_length); 155 156 /* find it in the list of IP addresses */ 157 for (i = 0, scan = addr_list_p->list; i < addr_list_p->count; i++, scan++) { 158 if ((scan->addr_flags & IN6_IFF_AUTOCONF) != 0 159 || IN6_ARE_ADDR_EQUAL(&scan->addr, &addr) == FALSE) { 160 continue; 161 } 162 /* found a match */ 163 if ((scan->addr_flags & IN6_IFF_DUPLICATED) != 0) { 164 char ntopbuf[INET6_ADDRSTRLEN]; 165 /* DaD found a conflict, report it */ 166 my_log(LOG_ERR, 167 "%s %s: IPv6 address %s is in use by another host", 168 ServiceGetMethodString(service_p), 169 if_name(if_p), 170 inet_ntop(AF_INET6, &addr, ntopbuf, sizeof(ntopbuf))); 171 ServiceReportIPv6AddressConflict(service_p, &addr); 172 ServiceRemoveIPv6Address(service_p, &addr, prefix_length); 173 service_publish_failure(service_p, 174 ipconfig_status_address_in_use_e); 175 } 176 else if ((scan->addr_flags & IN6_IFF_TENTATIVE) == 0) { 177 /* DaD complete */ 178 manual_v6_publish(service_p); 179 } 180 break; 181 } 182 return; 183} 184 185STATIC void 186manual_v6_simulate_address_changed(ServiceRef service_p) 187{ 188 inet6_addrlist_t addrs; 189 190 inet6_addrlist_copy(&addrs, if_link_index(service_interface(service_p))); 191 manual_v6_address_changed(service_p, &addrs); 192 inet6_addrlist_free(&addrs); 193 return; 194} 195 196PRIVATE_EXTERN ipconfig_status_t 197manual_v6_thread(ServiceRef service_p, IFEventID_t evid, void * event_data) 198{ 199 interface_t * if_p = service_interface(service_p); 200 ipconfig_status_t status = ipconfig_status_success_e; 201 202 switch (evid) { 203 case IFEventID_start_e: { 204 ipconfig_method_data_t * method_data; 205 206 my_log(LOG_DEBUG, "%s %s: starting", ServiceGetMethodString(service_p), 207 if_name(if_p)); 208 method_data = (ipconfig_method_data_t *)event_data; 209 ServiceSetRequestedIPv6Address(service_p, 210 &method_data->manual_v6.addr, 211 method_data->manual_v6.prefix_length); 212 manual_v6_start(service_p); 213 manual_v6_simulate_address_changed(service_p); 214 break; 215 } 216 case IFEventID_stop_e: { 217 struct in6_addr addr; 218 int prefix_length; 219 220 ServiceGetRequestedIPv6Address(service_p, &addr, &prefix_length); 221 my_log(LOG_DEBUG, "%s %s: stop", ServiceGetMethodString(service_p), 222 if_name(if_p)); 223 ServiceRemoveIPv6Address(service_p, &addr, prefix_length); 224 break; 225 } 226 case IFEventID_change_e: { 227 struct in6_addr addr; 228 change_event_data_t * change; 229 int prefix_length; 230 ipconfig_method_data_t *method_data; 231 232 ServiceGetRequestedIPv6Address(service_p, &addr, &prefix_length); 233 change = ((change_event_data_t *)event_data); 234 method_data = change->method_data; 235 if (IN6_ARE_ADDR_EQUAL(&method_data->manual_v6.addr, &addr) == FALSE 236 || method_data->manual_v6.prefix_length != prefix_length) { 237 change->needs_stop = TRUE; 238 } 239 break; 240 } 241 case IFEventID_renew_e: 242 case IFEventID_link_status_changed_e: { 243 link_status_t link_status; 244 245 link_status = service_link_status(service_p); 246 if (link_status.valid == TRUE) { 247 if (link_status.active == TRUE) { 248 manual_v6_start(service_p); 249 } 250 } 251 break; 252 } 253 case IFEventID_link_timer_expired_e: 254 manual_v6_inactive(service_p); 255 break; 256 257 case IFEventID_ipv6_address_changed_e: 258 manual_v6_address_changed(service_p, event_data); 259 break; 260 261 default: 262 break; 263 } /* switch */ 264 265 return (status); 266} 267