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