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