1/* 2 * Copyright (c) 2003-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 * stf.c 26 * - Six to Four (6to4) configuration 27 * - monitors the Primary IPv4 address, and it it's globally routable, 28 * maps it to the 6to4 address and assigns it to the stf0 interface 29 */ 30 31#include <stdlib.h> 32#include <unistd.h> 33#include <string.h> 34#include <sys/types.h> 35#include <sys/socket.h> 36#include <sys/ioctl.h> 37#include <sys/sockio.h> 38#include <sys/uio.h> 39#include <sys/time.h> 40#include <sys/param.h> 41#include <ctype.h> 42#include <net/if.h> 43#include <net/if_dl.h> 44#include <net/route.h> 45#include <netinet/in.h> 46#include <netinet/ip6.h> 47#include <netinet6/ip6_var.h> 48#define KERNEL_PRIVATE 49#include <netinet6/in6_var.h> 50#undef KERNEL_PRIVATE 51#include <netinet/icmp6.h> 52#include <netinet6/nd6.h> 53#include <arpa/inet.h> 54#include <syslog.h> 55#include <CoreFoundation/CFSocket.h> 56#include <SystemConfiguration/SystemConfiguration.h> 57#include <SystemConfiguration/SCPrivate.h> 58#include <SystemConfiguration/SCValidation.h> 59 60#include "ipconfigd_threads.h" 61#include "FDSet.h" 62#include "globals.h" 63#include "timer.h" 64#include "ifutil.h" 65#include "sysconfig.h" 66#include "util.h" 67#include "cfutil.h" 68#include "symbol_scope.h" 69 70typedef struct { 71 struct in_addr local_ip; /* IPv4 address we're mapping */ 72 struct in6_addr relay; /* relay we're using */ 73 char * relay_hostname; /* DNS hostname of relay */ 74 SCNetworkReachabilityRef reach; /* resolves DNS name */ 75 CFStringRef signature; /* signature of IPv4 service */ 76 SCDynamicStoreRef store; /* notify on primary changes */ 77 CFRunLoopSourceRef store_rls; 78} Service_stf_t; 79 80 81#define STF_PREFIX_LENGTH 16 82 83static const struct in6_addr stf_anycast_relay = { /* RFC3068 */ 84 { 85 { 0x20, 0x02, /* 2002/16 prefix = 6to4 */ 86 0xc0, 0x58, 0x63, 0x01, /* 192.88.99.1 */ 87 0x00, 0x00, /* 16-bit subnet */ 88 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 64-bit host */ 89 } 90 } 91}; 92 93typedef struct { 94 uint8_t prefix[2]; /* 16 bits = { 0x20, 0x02 } */ 95 uint8_t ipv4_address[4];/* 32 bits */ 96 uint8_t net[2]; /* 16 bits */ 97 uint8_t host[8]; /* 64 bits */ 98} in6_6to4_addr_t; 99 100static void 101make_6to4_addr(struct in_addr ip_addr, struct in6_addr * ip6_addr, 102 boolean_t is_host) 103{ 104 in6_6to4_addr_t * addr = (in6_6to4_addr_t *)ip6_addr; 105 106 bzero(ip6_addr, sizeof(*ip6_addr)); 107 addr->prefix[0] = 0x20; 108 addr->prefix[1] = 0x02; 109 bcopy((void *)&ip_addr.s_addr, (void *)&addr->ipv4_address, 110 sizeof(ip_addr.s_addr)); 111 if (is_host) { 112 /* for host address, set the network and host values to 1 */ 113 addr->net[1] = 0x01; /* net = 0x0001 */ 114 addr->host[7] = 0x01; /* host = 0x0000000000000001 */ 115 } 116 return; 117} 118 119static void 120stf_publish(ServiceRef service_p) 121{ 122 inet6_addrinfo_t info; 123 CFStringRef our_signature = NULL; 124 Service_stf_t * stf = (Service_stf_t *)ServiceGetPrivate(service_p); 125 126 if (IN6_IS_ADDR_UNSPECIFIED(&stf->relay) 127 || stf->local_ip.s_addr == 0) { 128 /* don't have both the Relay and the IPv4 address */ 129 return; 130 } 131 if (stf->signature != NULL) { 132 our_signature = CFStringCreateWithFormat(NULL, NULL, 133 CFSTR("IPv6.6to4=(%@)"), 134 stf->signature); 135 } 136 make_6to4_addr(stf->local_ip, &info.addr, TRUE); 137 info.addr_flags = 0; 138 info.prefix_length = STF_PREFIX_LENGTH; 139 ServicePublishSuccessIPv6(service_p, &info, 1, &stf->relay, 1, NULL, 140 our_signature); 141 my_CFRelease(&our_signature); 142 return; 143} 144 145static void 146stf_reachability_callback(SCNetworkReachabilityRef target, 147 SCNetworkReachabilityFlags flags, 148 void * info) 149{ 150 CFIndex count; 151 int error; 152 int i; 153 interface_t * if_p; 154 ServiceRef service_p = (ServiceRef)info; 155 CFArrayRef relay_addrs = NULL; 156 Service_stf_t * stf = (Service_stf_t *)ServiceGetPrivate(service_p); 157 158 if_p = service_interface(service_p); 159 if ((flags & kSCNetworkReachabilityFlagsReachable) == 0 160 || (flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0) { 161 /* relay is not yet reachable */ 162 my_log(LOG_NOTICE, "6TO4 %s: can't resolve %s", 163 if_name(if_p), stf->relay_hostname); 164 return; 165 } 166 relay_addrs = SCNetworkReachabilityCopyResolvedAddress(stf->reach, &error); 167 if (relay_addrs == NULL) { 168 return; 169 } 170 count = CFArrayGetCount(relay_addrs); 171 for (i = 0; i < count; i++) { 172 CFDataRef data = CFArrayGetValueAtIndex(relay_addrs, i); 173 struct sockaddr_in * sin; 174 175 /* ALIGN: CFDataGetBytePtr returns alignment to at 176 * least sizeof(uint64) bytes */ 177 sin = (struct sockaddr_in *)(void *)CFDataGetBytePtr(data); 178 if (sin->sin_family == AF_INET && sin->sin_addr.s_addr != 0) { 179 struct in6_addr relay; 180 181 if (G_IPConfiguration_verbose) { 182 my_log(LOG_DEBUG, "6TO4 %s: resolved %s to " IP_FORMAT, 183 if_name(if_p), stf->relay_hostname, 184 IP_LIST(&sin->sin_addr)); 185 } 186 /* don't need the reachability any longer */ 187 SCNetworkReachabilityUnscheduleFromRunLoop(stf->reach, 188 CFRunLoopGetCurrent(), 189 kCFRunLoopDefaultMode); 190 my_CFRelease(&stf->reach); 191 make_6to4_addr(sin->sin_addr, &relay, FALSE); 192 if (IN6_ARE_ADDR_EQUAL(&stf->relay, &relay) == FALSE) { 193 stf->relay = relay; 194 stf_publish(service_p); 195 } 196 break; 197 } 198 } 199 CFRelease(relay_addrs); 200 return; 201} 202 203static void 204stf_set_relay_hostname(ServiceRef service_p, const char * relay_hostname) 205{ 206 SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL }; 207 interface_t * if_p = service_interface(service_p); 208 Service_stf_t * stf = (Service_stf_t *)ServiceGetPrivate(service_p); 209 210 if (stf->relay_hostname != NULL) { 211 free(stf->relay_hostname); 212 stf->relay_hostname = NULL; 213 } 214 if (stf->reach != NULL) { 215 SCNetworkReachabilityUnscheduleFromRunLoop(stf->reach, 216 CFRunLoopGetCurrent(), 217 kCFRunLoopDefaultMode); 218 my_CFRelease(&stf->reach); 219 } 220 if (relay_hostname == NULL) { 221 /* no more relay hostname to resolve */ 222 return; 223 } 224 stf->reach = SCNetworkReachabilityCreateWithName(NULL, relay_hostname); 225 if (stf->reach == NULL) { 226 my_log(LOG_ERR, 227 "6TO4 %s:SCNetworkReachabilityCreateWithName failed, %s", 228 if_name(if_p), 229 SCErrorString(SCError())); 230 return; 231 } 232 context.info = service_p; 233 if (SCNetworkReachabilitySetCallback(stf->reach, 234 stf_reachability_callback, 235 &context) == FALSE) { 236 my_log(LOG_ERR, 237 "6TO4 %s: SCNetworkReachabilitySetCallback failed, %s", 238 if_name(if_p), 239 SCErrorString(SCError())); 240 my_CFRelease(&stf->reach); 241 242 } 243 SCNetworkReachabilityScheduleWithRunLoop(stf->reach, 244 CFRunLoopGetCurrent(), 245 kCFRunLoopDefaultMode); 246 if (G_IPConfiguration_verbose) { 247 my_log(LOG_DEBUG, "6TO4 %s: resolving %s", if_name(if_p), 248 relay_hostname); 249 } 250 stf->relay_hostname = strdup(relay_hostname); 251 return; 252} 253 254static CFStringRef 255copy_state_global_ipv4_key(void) 256{ 257 return (SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 258 kSCDynamicStoreDomainState, 259 kSCEntNetIPv4)); 260} 261 262static struct in_addr 263copy_primary_routable_ipv4_info(CFDictionaryRef info, 264 CFStringRef ipv4_global_key, 265 CFStringRef * ret_signature) 266{ 267 CFArrayRef addresses; 268 CFDictionaryRef dict = NULL; 269 CFDictionaryRef ipv4_global_dict = NULL; 270 struct in_addr ret_ip; 271 CFStringRef serviceID = NULL; 272 CFStringRef signature = NULL; 273 274 if (info != NULL) { 275 ipv4_global_dict = CFDictionaryGetValue(info, ipv4_global_key); 276 ipv4_global_dict = isA_CFDictionary(ipv4_global_dict); 277 } 278 279 ret_ip.s_addr = 0; 280 if (ipv4_global_dict != NULL) { 281 serviceID = CFDictionaryGetValue(ipv4_global_dict, 282 kSCDynamicStorePropNetPrimaryService); 283 } 284 if (isA_CFString(serviceID) == NULL) { 285 goto done; 286 } 287 if (info != NULL) { 288 CFStringRef key; 289 290 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 291 kSCDynamicStoreDomainState, 292 serviceID, 293 kSCEntNetIPv4); 294 dict = CFDictionaryGetValue(info, key); 295 dict = isA_CFDictionary(dict); 296 CFRelease(key); 297 } 298 if (dict == NULL) { 299 goto done; 300 } 301 signature = CFDictionaryGetValue(dict, kNetworkSignature); 302 signature = isA_CFString(signature); 303 addresses = CFDictionaryGetValue(dict, kSCPropNetIPv4Addresses); 304 if (isA_CFArray(addresses) != NULL && CFArrayGetCount(addresses) > 0) { 305 CFStringRef addr = CFArrayGetValueAtIndex(addresses, 0); 306 struct in_addr ip; 307 308 if (my_CFStringToIPAddress(addr, &ip) 309 && ip.s_addr != 0 310 && ip_is_linklocal(ip) == FALSE 311 && ip_is_private(ip) == FALSE) { 312 if (ret_signature != NULL && signature != NULL) { 313 *ret_signature = CFRetain(signature); 314 } 315 ret_ip = ip; 316 } 317 } 318 319 done: 320 return (ret_ip); 321} 322 323static void 324stf_remove_all_addresses(ServiceRef service_p) 325{ 326 int i; 327 interface_t * if_p = service_interface(service_p); 328 inet6_addrlist_t list; 329 int s; 330 331 inet6_addrlist_copy(&list, if_link_index(if_p)); 332 if (list.count == 0) { 333 return; 334 } 335 s = inet6_dgram_socket(); 336 if (s < 0) { 337 goto done; 338 } 339 for (i = 0; i < list.count; i++) { 340 if (G_IPConfiguration_verbose) { 341 char ntopbuf[INET6_ADDRSTRLEN]; 342 343 my_log(LOG_DEBUG, "6TO4 %s: removing %s/%d", 344 if_name(if_p), 345 inet_ntop(AF_INET6, &list.list[i].addr, 346 ntopbuf, sizeof(ntopbuf)), 347 list.list[i].prefix_length); 348 } 349 inet6_difaddr(s, if_name(if_p), &list.list[i].addr); 350 } 351 close(s); 352 done: 353 inet6_addrlist_free(&list); 354 return; 355} 356 357static void 358stf_update_address(ServiceRef service_p, CFDictionaryRef info, 359 CFStringRef ipv4_global_key) 360{ 361 struct in_addr local_ip; 362 CFStringRef signature = NULL; 363 Service_stf_t * stf = (Service_stf_t *)ServiceGetPrivate(service_p); 364 365 local_ip = copy_primary_routable_ipv4_info(info, 366 ipv4_global_key, 367 &signature); 368 369 /* if there is no primary IPv4 address, or it has changed since last time */ 370 if (local_ip.s_addr == 0 371 || local_ip.s_addr != stf->local_ip.s_addr) { 372 struct in6_addr local_ip6; 373 374 if (G_IPConfiguration_verbose) { 375 interface_t * if_p; 376 377 if_p = service_interface(service_p); 378 if (local_ip.s_addr == 0) { 379 my_log(LOG_DEBUG, "6TO4 %s: no primary IPv4 address", 380 if_name(if_p)); 381 } 382 else { 383 my_log(LOG_DEBUG, 384 "6TO4 %s: primary IPv4 address changed to " IP_FORMAT, 385 if_name(if_p), IP_LIST(&local_ip)); 386 } 387 } 388 389 /* clear the old address */ 390 if (stf->local_ip.s_addr != 0) { 391 /* remove the address */ 392 make_6to4_addr(stf->local_ip, &local_ip6, TRUE); 393 ServiceRemoveIPv6Address(service_p, &local_ip6, STF_PREFIX_LENGTH); 394 } 395 396 /* clear the old signature */ 397 my_CFRelease(&stf->signature); 398 399 stf->local_ip = local_ip; 400 if (local_ip.s_addr != 0) { 401 /* set the new address and publish */ 402 make_6to4_addr(local_ip, &local_ip6, TRUE); 403 ServiceSetIPv6Address(service_p, &local_ip6, STF_PREFIX_LENGTH, 0, 404 ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME); 405 if (isA_CFString(signature) != NULL) { 406 stf->signature = CFRetain(signature); 407 } 408 stf_publish(service_p); 409 } 410 else { 411 /* unpublish */ 412 service_publish_failure(service_p, 413 ipconfig_status_resource_unavailable_e); 414 } 415 } 416 my_CFRelease(&signature); 417 return; 418} 419 420static CFDictionaryRef 421copy_service_information(SCDynamicStoreRef store, 422 CFStringRef global_ipv4_key) 423{ 424 CFStringRef all_ipv4_services_pattern; 425 CFArrayRef keys; 426 CFArrayRef patterns; 427 CFDictionaryRef info; 428 429 keys = CFArrayCreate(NULL, 430 (const void **)&global_ipv4_key, 1, 431 &kCFTypeArrayCallBacks); 432 all_ipv4_services_pattern 433 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 434 kSCDynamicStoreDomainState, 435 kSCCompAnyRegex, 436 kSCEntNetIPv4); 437 patterns = CFArrayCreate(NULL, 438 (const void **)&all_ipv4_services_pattern, 1, 439 &kCFTypeArrayCallBacks); 440 CFRelease(all_ipv4_services_pattern); 441 info = SCDynamicStoreCopyMultiple(store, keys, patterns); 442 CFRelease(keys); 443 CFRelease(patterns); 444 445 return (info); 446} 447 448static void 449stf_global_ipv4_changed(SCDynamicStoreRef store, 450 CFArrayRef changes, 451 void * info) 452{ 453 CFDictionaryRef dict; 454 CFStringRef key; 455 456 if (changes == NULL || CFArrayGetCount(changes) == 0) { 457 return; 458 } 459 key = CFArrayGetValueAtIndex(changes, 0); 460 dict = copy_service_information(store, key); 461 stf_update_address((ServiceRef)info, dict, key); 462 my_CFRelease(&dict); 463 return; 464} 465 466 467/* 468 * Function: stf_configure_address 469 * Purpose: 470 * Called once to install the notification source and to configure the 471 * address of the stf0 interface. 472 */ 473static void 474stf_configure_address(ServiceRef service_p) 475{ 476 CFArrayRef array; 477 CFDictionaryRef dict; 478 SCDynamicStoreContext context = { 0, NULL, NULL, NULL, NULL }; 479 CFStringRef key; 480 Service_stf_t * stf = (Service_stf_t *)ServiceGetPrivate(service_p); 481 482 /* create the notification source */ 483 context.info = service_p; 484 stf->store = SCDynamicStoreCreate(NULL, 485 CFSTR("IPConfiguration:STF"), 486 stf_global_ipv4_changed, &context); 487 key = copy_state_global_ipv4_key(); 488 array = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks); 489 SCDynamicStoreSetNotificationKeys(stf->store, array, NULL); 490 CFRelease(array); 491 stf->store_rls = SCDynamicStoreCreateRunLoopSource(NULL, stf->store, 0); 492 CFRunLoopAddSource(CFRunLoopGetCurrent(), stf->store_rls, 493 kCFRunLoopDefaultMode); 494 495 /* grab the current value */ 496 dict = copy_service_information(stf->store, key); 497 stf_update_address(service_p, dict, key); 498 CFRelease(key); 499 my_CFRelease(&dict); 500 return; 501} 502 503static void 504stf_set_relay(ServiceRef service_p, ipconfig_method_data_stf_t * method_data) 505{ 506 interface_t * if_p; 507 Boolean publish_new = FALSE; 508 address_type_t relay_type = address_type_none_e; 509 Service_stf_t * stf = (Service_stf_t *)ServiceGetPrivate(service_p); 510 511 if_p = service_interface(service_p); 512 if (method_data != NULL) { 513 relay_type = method_data->relay_addr_type; 514 } 515 switch (relay_type) { 516 case address_type_none_e: 517 stf_set_relay_hostname(service_p, NULL); 518 if (IN6_ARE_ADDR_EQUAL(&stf->relay, &stf_anycast_relay)) { 519 /* we're currently using the anycast relay, nothing to do */ 520 return; 521 } 522 if (G_IPConfiguration_verbose) { 523 my_log(LOG_DEBUG, "6TO4 %s: using default anycast relay", 524 if_name(if_p)); 525 } 526 stf->relay = stf_anycast_relay; 527 publish_new = TRUE; 528 break; 529 530 case address_type_dns_e: 531 if (stf->relay_hostname != NULL 532 && strcmp(stf->relay_hostname, 533 method_data->relay_addr.dns) == 0) { 534 /* the same DNS server address, nothing to do */ 535 return; 536 } 537 if (G_IPConfiguration_verbose) { 538 my_log(LOG_DEBUG, "6TO4 %s: specified DNS relay %s", 539 if_name(if_p), method_data->relay_addr.dns); 540 } 541 stf_set_relay_hostname(service_p, method_data->relay_addr.dns); 542 break; 543 544 case address_type_ipv4_e: { 545 struct in6_addr requested_ip; 546 547 make_6to4_addr(method_data->relay_addr.v4, &requested_ip, FALSE); 548 stf_set_relay_hostname(service_p, NULL); 549 if (IN6_ARE_ADDR_EQUAL(&requested_ip, &stf->relay)) { 550 /* new relay same as old, nothing to do */ 551 return; 552 } 553 if (G_IPConfiguration_verbose) { 554 my_log(LOG_DEBUG, "6TO4 %s: specified IPv4 relay " IP_FORMAT, 555 if_name(if_p), IP_LIST(&method_data->relay_addr.v4)); 556 } 557 stf->relay = requested_ip; 558 publish_new = TRUE; 559 break; 560 } 561 case address_type_ipv6_e: 562 stf_set_relay_hostname(service_p, NULL); 563 if (IN6_ARE_ADDR_EQUAL(&method_data->relay_addr.v6, &stf->relay)) { 564 /* new relay same as old */ 565 return; 566 } 567 if (G_IPConfiguration_verbose) { 568 char ntopbuf[INET6_ADDRSTRLEN]; 569 570 my_log(LOG_DEBUG, "6TO4 %s: specified IPv6 relay %s", 571 if_name(if_p), 572 inet_ntop(AF_INET6, &method_data->relay_addr.v6, 573 ntopbuf, sizeof(ntopbuf))); 574 } 575 stf->relay = method_data->relay_addr.v6; 576 publish_new = TRUE; 577 break; 578 default: 579 my_log(LOG_ERR, "6TO4 %s: specified unknown relay type %d", 580 if_name(if_p), relay_type); 581 return; 582 } 583 if (publish_new) { 584 stf_publish(service_p); 585 } 586 return; 587} 588 589PRIVATE_EXTERN ipconfig_status_t 590stf_thread(ServiceRef service_p, IFEventID_t evid, void * event_data) 591{ 592 interface_t * if_p = service_interface(service_p); 593 ipconfig_status_t status = ipconfig_status_success_e; 594 Service_stf_t * stf = (Service_stf_t *)ServiceGetPrivate(service_p); 595 596 switch (evid) { 597 case IFEventID_start_e: 598 if (if_flags(if_p) & IFF_LOOPBACK) { 599 status = ipconfig_status_invalid_operation_e; 600 break; 601 } 602 if (stf != NULL) { 603 my_log(LOG_DEBUG, "6TO4 %s: re-entering start state", 604 if_name(if_p)); 605 status = ipconfig_status_internal_error_e; 606 break; 607 } 608 stf = malloc(sizeof(*stf)); 609 if (stf == NULL) { 610 my_log(LOG_ERR, "6TO4 %s: malloc failed", if_name(if_p)); 611 status = ipconfig_status_allocation_failed_e; 612 break; 613 } 614 bzero(stf, sizeof(*stf)); 615 ServiceSetPrivate(service_p, stf); 616 /* scrub all IP addresses - in case we crashed */ 617 stf_remove_all_addresses(service_p); 618 stf_configure_address(service_p); 619 stf_set_relay(service_p, (ipconfig_method_data_stf_t *)event_data); 620 break; 621 case IFEventID_change_e: { 622 change_event_data_t * change_event; 623 624 change_event = ((change_event_data_t *)event_data); 625 stf_set_relay(service_p, &change_event->method_data->stf); 626 break; 627 } 628 case IFEventID_stop_e: { 629 struct in6_addr local_ip6; 630 631 if (stf == NULL) { 632 my_log(LOG_DEBUG, "6TO4 %s: already stopped", 633 if_name(if_p)); 634 status = ipconfig_status_internal_error_e; 635 break; 636 } 637 my_log(LOG_DEBUG, "6TO4 %s: stop", if_name(if_p)); 638 stf_set_relay_hostname(service_p, NULL); 639 if (stf->store_rls != NULL) { 640 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), stf->store_rls, 641 kCFRunLoopDefaultMode); 642 my_CFRelease(&stf->store_rls); 643 } 644 if (stf->local_ip.s_addr != 0) { 645 /* remove the address */ 646 make_6to4_addr(stf->local_ip, &local_ip6, TRUE); 647 ServiceRemoveIPv6Address(service_p, &local_ip6, STF_PREFIX_LENGTH); 648 } 649 my_CFRelease(&stf->store); 650 my_CFRelease(&stf->signature); 651 ServiceSetPrivate(service_p, NULL); 652 free(stf); 653 break; 654 } 655 default: 656 break; 657 } /* switch */ 658 659 return (status); 660} 661