1/* 2 * Copyright (c) 2011-2014 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 * IPConfigurationService.c 26 * - API to communicate with IPConfiguration to instantiate services 27 */ 28/* 29 * Modification History 30 * 31 * April 14, 2011 Dieter Siegmund (dieter@apple.com) 32 * - initial revision 33 */ 34 35#include <stdio.h> 36#include <stdlib.h> 37#include <unistd.h> 38#include <sys/types.h> 39#include <string.h> 40#include <CoreFoundation/CFRuntime.h> 41#include <SystemConfiguration/SystemConfiguration.h> 42#include <SystemConfiguration/SCValidation.h> 43#include <pthread.h> 44#include <mach/mach_error.h> 45#include <mach/mach_port.h> 46#include "ipconfig_types.h" 47#include "ipconfig_ext.h" 48#include "symbol_scope.h" 49#include "cfutil.h" 50#include "ipconfig.h" 51#include "IPConfigurationLog.h" 52#include "IPConfigurationServiceInternal.h" 53#include "IPConfigurationService.h" 54 55const CFStringRef 56kIPConfigurationServiceOptionMTU = _kIPConfigurationServiceOptionMTU; 57 58const CFStringRef 59kIPConfigurationServiceOptionPerformNUD = _kIPConfigurationServiceOptionPerformNUD; 60 61const CFStringRef 62kIPConfigurationServiceOptionIPv6Entity = _kIPConfigurationServiceOptionIPv6Entity; 63 64/** 65 ** IPConfigurationService 66 **/ 67struct __IPConfigurationService { 68 CFRuntimeBase cf_base; 69 70 mach_port_t server; 71 if_name_t ifname; 72 CFDataRef serviceID_data; 73 CFStringRef store_key; 74}; 75 76 77/** 78 ** CF object glue code 79 **/ 80STATIC CFStringRef __IPConfigurationServiceCopyDebugDesc(CFTypeRef cf); 81STATIC void __IPConfigurationServiceDeallocate(CFTypeRef cf); 82 83STATIC CFTypeID __kIPConfigurationServiceTypeID = _kCFRuntimeNotATypeID; 84 85STATIC const CFRuntimeClass __IPConfigurationServiceClass = { 86 0, /* version */ 87 "IPConfigurationService", /* className */ 88 NULL, /* init */ 89 NULL, /* copy */ 90 __IPConfigurationServiceDeallocate, /* deallocate */ 91 NULL, /* equal */ 92 NULL, /* hash */ 93 NULL, /* copyFormattingDesc */ 94 __IPConfigurationServiceCopyDebugDesc /* copyDebugDesc */ 95}; 96 97STATIC CFStringRef 98__IPConfigurationServiceCopyDebugDesc(CFTypeRef cf) 99{ 100 CFAllocatorRef allocator = CFGetAllocator(cf); 101 CFMutableStringRef result; 102 IPConfigurationServiceRef service = (IPConfigurationServiceRef)cf; 103 104 result = CFStringCreateMutable(allocator, 0); 105 CFStringAppendFormat(result, NULL, 106 CFSTR("<IPConfigurationService %p [%p]> {"), 107 cf, allocator); 108 CFStringAppendFormat(result, NULL, CFSTR("ifname = %s, serviceID = %.*s"), 109 service->ifname, 110 (int)CFDataGetLength(service->serviceID_data), 111 CFDataGetBytePtr(service->serviceID_data)); 112 CFStringAppend(result, CFSTR("}")); 113 return (result); 114} 115 116STATIC void 117__IPConfigurationServiceDeallocate(CFTypeRef cf) 118{ 119 kern_return_t kret; 120 IPConfigurationServiceRef service = (IPConfigurationServiceRef)cf; 121 inline_data_t service_id; 122 int service_id_len; 123 ipconfig_status_t status; 124 125 service_id_len = (int)CFDataGetLength(service->serviceID_data); 126 127 bcopy(CFDataGetBytePtr(service->serviceID_data), &service_id, 128 service_id_len); 129 kret = ipconfig_remove_service_on_interface(service->server, 130 service->ifname, 131 service_id, service_id_len, 132 &status); 133 if (kret != KERN_SUCCESS) { 134 IPConfigLogFL(LOG_NOTICE, 135 "ipconfig_remove_service_on_interface(%s %.*s) " 136 "failed, %s", 137 service->ifname, service_id_len, service_id, 138 mach_error_string(kret)); 139 } 140 else if (status != ipconfig_status_success_e) { 141 IPConfigLogFL(LOG_NOTICE, 142 "ipconfig_remove_service_on_interface(%s %.*s)" 143 " failed: %s", 144 service->ifname, service_id_len, service_id, 145 ipconfig_status_string(status)); 146 } 147 mach_port_deallocate(mach_task_self(), service->server); 148 service->server = MACH_PORT_NULL; 149 my_CFRelease(&service->serviceID_data); 150 my_CFRelease(&service->store_key); 151 return; 152} 153 154STATIC void 155__IPConfigurationServiceInitialize(void) 156{ 157 /* initialize runtime */ 158 __kIPConfigurationServiceTypeID 159 = _CFRuntimeRegisterClass(&__IPConfigurationServiceClass); 160 return; 161} 162 163STATIC void 164__IPConfigurationServiceRegisterClass(void) 165{ 166 STATIC pthread_once_t initialized = PTHREAD_ONCE_INIT; 167 168 pthread_once(&initialized, __IPConfigurationServiceInitialize); 169 return; 170} 171 172STATIC IPConfigurationServiceRef 173__IPConfigurationServiceAllocate(CFAllocatorRef allocator) 174{ 175 IPConfigurationServiceRef service; 176 int size; 177 178 __IPConfigurationServiceRegisterClass(); 179 180 size = sizeof(*service) - sizeof(CFRuntimeBase); 181 service = (IPConfigurationServiceRef) 182 _CFRuntimeCreateInstance(allocator, 183 __kIPConfigurationServiceTypeID, size, NULL); 184 bzero(((void *)service) + sizeof(CFRuntimeBase), size); 185 return (service); 186} 187 188STATIC CFDictionaryRef 189config_dict_create(pid_t pid, CFDictionaryRef requested_ipv6_config, 190 CFNumberRef mtu, CFBooleanRef perform_nud) 191{ 192 int count; 193 CFDictionaryRef config_dict; 194 CFDictionaryRef ipv6_dict; 195 const void * keys[4]; 196 CFDictionaryRef options; 197 const void * values[4]; 198 199 /* monitor pid */ 200 count = 0; 201 keys[count] = _kIPConfigurationServiceOptionMonitorPID; 202 values[count] = kCFBooleanTrue; 203 count++; 204 205 /* no publish */ 206 keys[count] = _kIPConfigurationServiceOptionNoPublish; 207 values[count] = kCFBooleanTrue; 208 count++; 209 210 /* mtu */ 211 if (mtu != NULL) { 212 keys[count] = kIPConfigurationServiceOptionMTU; 213 values[count] = mtu; 214 count++; 215 } 216 217 /* perform NUD */ 218 if (perform_nud != NULL) { 219 keys[count] = kIPConfigurationServiceOptionPerformNUD; 220 values[count] = perform_nud; 221 count++; 222 } 223 224 options 225 = CFDictionaryCreate(NULL, keys, values, count, 226 &kCFTypeDictionaryKeyCallBacks, 227 &kCFTypeDictionaryValueCallBacks); 228 229 if (requested_ipv6_config != NULL) { 230 ipv6_dict = requested_ipv6_config; 231 } 232 else { 233 /* create the default IPv6 dictionary */ 234 ipv6_dict 235 = CFDictionaryCreate(NULL, 236 (const void * *)&kSCPropNetIPv6ConfigMethod, 237 (const void * *)&kSCValNetIPv6ConfigMethodAutomatic, 238 1, 239 &kCFTypeDictionaryKeyCallBacks, 240 &kCFTypeDictionaryValueCallBacks); 241 } 242 keys[0] = kIPConfigurationServiceOptions; 243 values[0] = options; 244 keys[1] = kSCEntNetIPv6; 245 values[1] = ipv6_dict; 246 config_dict 247 = CFDictionaryCreate(NULL, keys, values, 2, 248 &kCFTypeDictionaryKeyCallBacks, 249 &kCFTypeDictionaryValueCallBacks); 250 CFRelease(options); 251 if (ipv6_dict != requested_ipv6_config) { 252 CFRelease(ipv6_dict); 253 } 254 return (config_dict); 255} 256 257static Boolean 258ipv6_config_is_valid(CFDictionaryRef config) 259{ 260 CFStringRef config_method; 261 Boolean is_valid = FALSE; 262 263 config_method = CFDictionaryGetValue(config, kSCPropNetIPv6ConfigMethod); 264 if (isA_CFString(config_method) == NULL) { 265 goto done; 266 } 267 if (CFEqual(config_method, kSCValNetIPv6ConfigMethodManual)) { 268 CFArrayRef addresses; 269 CFIndex count; 270 CFArrayRef prefix_lengths; 271 272 /* must contain Addresses, PrefixLength */ 273 addresses = CFDictionaryGetValue(config, kSCPropNetIPv6Addresses); 274 prefix_lengths = CFDictionaryGetValue(config, 275 kSCPropNetIPv6PrefixLength); 276 if (isA_CFArray(addresses) == NULL 277 || isA_CFArray(prefix_lengths) == NULL) { 278 goto done; 279 } 280 count = CFArrayGetCount(addresses); 281 if (count == 0 || count != CFArrayGetCount(prefix_lengths)) { 282 goto done; 283 } 284 } 285 else if (CFEqual(config_method, kSCValNetIPv6ConfigMethodAutomatic) 286 || CFEqual(config_method, kSCValNetIPv6ConfigMethodLinkLocal)) { 287 /* no other parameters are required */ 288 } 289 else { 290 goto done; 291 } 292 is_valid = TRUE; 293 294 done: 295 return (is_valid); 296} 297 298/** 299 ** IPConfigurationService APIs 300 **/ 301 302PRIVATE_EXTERN CFTypeID 303IPConfigurationServiceGetTypeID(void) 304{ 305 __IPConfigurationServiceRegisterClass(); 306 return (__kIPConfigurationServiceTypeID); 307} 308 309IPConfigurationServiceRef 310IPConfigurationServiceCreate(CFStringRef interface_name, 311 CFDictionaryRef options) 312{ 313 CFDictionaryRef config_dict; 314 CFDataRef data = NULL; 315 if_name_t if_name; 316 kern_return_t kret; 317 CFNumberRef mtu = NULL; 318 CFBooleanRef perform_nud = NULL; 319 CFDictionaryRef requested_ipv6_config = NULL; 320 mach_port_t server = MACH_PORT_NULL; 321 IPConfigurationServiceRef service = NULL; 322 CFStringRef serviceID; 323 inline_data_t service_id; 324 unsigned int service_id_len; 325 ipconfig_status_t status = ipconfig_status_success_e; 326 boolean_t tried_to_delete = FALSE; 327 void * xml_data_ptr = NULL; 328 int xml_data_len = 0; 329 330 if (options != NULL) { 331 mtu = CFDictionaryGetValue(options, 332 kIPConfigurationServiceOptionMTU); 333 if (mtu != NULL && isA_CFNumber(mtu) == NULL) { 334 IPConfigLogFL(LOG_NOTICE, "ignoring invalid '%@' option", 335 kIPConfigurationServiceOptionMTU); 336 mtu = NULL; 337 } 338 perform_nud 339 = CFDictionaryGetValue(options, 340 kIPConfigurationServiceOptionPerformNUD); 341 if (perform_nud != NULL && isA_CFBoolean(perform_nud) == NULL) { 342 IPConfigLogFL(LOG_NOTICE, "ignoring invalid '%@' option", 343 kIPConfigurationServiceOptionPerformNUD); 344 perform_nud = NULL; 345 } 346 requested_ipv6_config 347 = CFDictionaryGetValue(options, 348 kIPConfigurationServiceOptionIPv6Entity); 349 if (requested_ipv6_config != NULL 350 && ipv6_config_is_valid(requested_ipv6_config) == FALSE) { 351 IPConfigLogFL(LOG_NOTICE, "ignoring invalid '%@' option", 352 kIPConfigurationServiceOptionIPv6Entity); 353 requested_ipv6_config = NULL; 354 } 355 356 } 357 kret = ipconfig_server_port(&server); 358 if (kret != BOOTSTRAP_SUCCESS) { 359 IPConfigLogFL(LOG_NOTICE, 360 "ipconfig_server_port, %s", 361 mach_error_string(kret)); 362 return (NULL); 363 } 364 config_dict = config_dict_create(getpid(), requested_ipv6_config, 365 mtu, perform_nud); 366 data = CFPropertyListCreateData(NULL, 367 config_dict, 368 kCFPropertyListBinaryFormat_v1_0, 369 0, 370 NULL); 371 CFRelease(config_dict); 372 xml_data_ptr = (void *)CFDataGetBytePtr(data); 373 xml_data_len = (int)CFDataGetLength(data); 374 my_CFStringToCStringAndLength(interface_name, if_name, 375 sizeof(if_name)); 376 while (1) { 377 kret = ipconfig_add_service(server, if_name, 378 xml_data_ptr, xml_data_len, 379 service_id, &service_id_len, 380 &status); 381 if (kret != KERN_SUCCESS) { 382 IPConfigLogFL(LOG_NOTICE, 383 "ipconfig_add_service(%s) failed, %s", 384 if_name, mach_error_string(kret)); 385 goto done; 386 } 387 if (status != ipconfig_status_duplicate_service_e) { 388 break; 389 } 390 if (tried_to_delete) { 391 /* already tried once */ 392 break; 393 } 394 tried_to_delete = TRUE; 395 (void)ipconfig_remove_service(server, if_name, 396 xml_data_ptr, xml_data_len, 397 &status); 398 } 399 if (status != ipconfig_status_success_e) { 400 IPConfigLogFL(LOG_NOTICE, 401 "ipconfig_add_service(%s) failed: %s", 402 if_name, ipconfig_status_string(status)); 403 goto done; 404 } 405 406 /* allocate/return an IPConfigurationServiceRef */ 407 service = __IPConfigurationServiceAllocate(NULL); 408 if (service == NULL) { 409 goto done; 410 } 411 bcopy(if_name, service->ifname, sizeof(service->ifname)); 412 service->serviceID_data = CFDataCreate(NULL, (const UInt8 *)service_id, 413 service_id_len); 414 service->server = server; 415 serviceID 416 = CFStringCreateWithBytes(NULL, 417 CFDataGetBytePtr(service->serviceID_data), 418 CFDataGetLength(service->serviceID_data), 419 kCFStringEncodingASCII, FALSE); 420 service->store_key = IPConfigurationServiceKey(serviceID); 421 CFRelease(serviceID); 422 server = MACH_PORT_NULL; 423 424 done: 425 if (server != MACH_PORT_NULL) { 426 mach_port_deallocate(mach_task_self(), server); 427 } 428 my_CFRelease(&data); 429 return (service); 430} 431 432/* 433 * Function: IPConfigurationServiceGetNotificationKey 434 * 435 * Purpose: 436 * Return the SCDynamicStoreKeyRef used to monitor the service using 437 * SCDynamicStoreSetNotificationKeys(). 438 * 439 * Parameters: 440 * service : the service to monitor 441 */ 442CFStringRef 443IPConfigurationServiceGetNotificationKey(IPConfigurationServiceRef service) 444{ 445 return (service->store_key); 446} 447 448CFDictionaryRef 449IPConfigurationServiceCopyInformation(IPConfigurationServiceRef service) 450{ 451 CFDictionaryRef info; 452 SCDynamicStoreRef store; 453 454 store = SCDynamicStoreCreate(NULL, CFSTR("IPConfigurationService"), 455 NULL, NULL); 456 if (store == NULL) { 457 return (NULL); 458 } 459 info = SCDynamicStoreCopyValue(store, service->store_key); 460 if (info != NULL 461 && isA_CFDictionary(info) == NULL) { 462 my_CFRelease(&info); 463 } 464 CFRelease(store); 465 return (info); 466} 467 468void 469IPConfigurationServiceRefreshConfiguration(IPConfigurationServiceRef service) 470{ 471 kern_return_t kret; 472 inline_data_t service_id; 473 int service_id_len; 474 ipconfig_status_t status; 475 476 service_id_len = (int)CFDataGetLength(service->serviceID_data); 477 bcopy(CFDataGetBytePtr(service->serviceID_data), &service_id, 478 service_id_len); 479 kret = ipconfig_refresh_service(service->server, 480 service->ifname, 481 service_id, service_id_len, 482 &status); 483 if (kret != KERN_SUCCESS) { 484 IPConfigLogFL(LOG_NOTICE, 485 "ipconfig_refresh_service(%s %.*s) failed, %s", 486 service->ifname, service_id_len, service_id, 487 mach_error_string(kret)); 488 } 489 else if (status != ipconfig_status_success_e) { 490 IPConfigLogFL(LOG_NOTICE, 491 "ipconfig_refresh_service(%s %.*s) failed: %s", 492 service->ifname, service_id_len, service_id, 493 ipconfig_status_string(status)); 494 } 495 return; 496} 497