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 * rtadv.c
26 * - IPv6 Router Advertisement
27 * - sends router solicitation requests, and waits for responses
28 * - reads router advertisement messages, and from it, gleans the source
29 *   IPv6 address to use as the Router
30 */
31
32/*
33 * Modification History
34 *
35 * October 6, 2009		Dieter Siegmund (dieter@apple.com)
36 * - added support for DHCPv6
37 */
38#include <stdlib.h>
39#include <unistd.h>
40#include <string.h>
41#include <sys/types.h>
42#include <sys/socket.h>
43#include <sys/ioctl.h>
44#include <sys/sockio.h>
45#include <sys/uio.h>
46#include <sys/time.h>
47#include <sys/param.h>
48#include <ctype.h>
49#include <net/if.h>
50#include <net/if_dl.h>
51#include <net/route.h>
52#include <netinet/in.h>
53#include <netinet/ip6.h>
54#include <netinet6/ip6_var.h>
55#define KERNEL_PRIVATE
56#include <netinet6/in6_var.h>
57#undef KERNEL_PRIVATE
58#include <netinet/icmp6.h>
59#include <netinet6/nd6.h>
60#include <arpa/inet.h>
61#include <syslog.h>
62#include <CoreFoundation/CFSocket.h>
63#include <SystemConfiguration/SystemConfiguration.h>
64
65#include "cfutil.h"
66#include "ipconfigd_threads.h"
67#include "FDSet.h"
68#include "globals.h"
69#include "timer.h"
70#include "ifutil.h"
71#include "util.h"
72#include "symbol_scope.h"
73#include "DHCPv6Client.h"
74#include "RTADVSocket.h"
75
76typedef struct {
77    timer_callout_t *		timer;
78    int				try;
79    Boolean			data_received;
80    struct in6_addr		our_router;
81    uint8_t			our_router_hwaddr[MAX_LINK_ADDR_LEN];
82    int				our_router_hwaddr_len;
83    RTADVSocketRef		sock;
84    boolean_t			lladdr_ok; /* ok to send link-layer address */
85    DHCPv6ClientRef		dhcp_client;
86    struct in6_addr *		dns_servers;
87    int				dns_servers_count;
88} Service_rtadv_t;
89
90
91STATIC void
92rtadv_set_dns_servers(Service_rtadv_t * rtadv,
93		      const struct in6_addr * dns_servers,
94		      int dns_servers_count)
95{
96    if (rtadv->dns_servers != NULL) {
97	free(rtadv->dns_servers);
98	rtadv->dns_servers = NULL;
99    }
100    if (dns_servers != NULL) {
101	rtadv->dns_servers = (struct in6_addr *)
102	    malloc(sizeof(*dns_servers) * dns_servers_count);
103	bcopy(dns_servers, rtadv->dns_servers,
104	      sizeof(*dns_servers) * dns_servers_count);
105	rtadv->dns_servers_count = dns_servers_count;
106    }
107    return;
108}
109
110STATIC void
111rtadv_cancel_pending_events(ServiceRef service_p)
112{
113    Service_rtadv_t *	rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p);
114
115    timer_cancel(rtadv->timer);
116    RTADVSocketDisableReceive(rtadv->sock);
117    return;
118}
119
120STATIC void
121rtadv_failed(ServiceRef service_p, ipconfig_status_t status)
122{
123    Service_rtadv_t *	rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p);
124
125    rtadv->try = 0;
126    rtadv_cancel_pending_events(service_p);
127    inet6_rtadv_disable(if_name(service_interface(service_p)));
128    rtadv_set_dns_servers(rtadv, NULL, 0);
129    service_publish_failure(service_p, status);
130    return;
131}
132
133STATIC void
134rtadv_inactive(ServiceRef service_p)
135{
136    interface_t *	if_p = service_interface(service_p);
137
138    inet6_flush_prefixes(if_name(if_p));
139    inet6_flush_routes(if_name(if_p));
140    rtadv_failed(service_p, ipconfig_status_media_inactive_e);
141    return;
142}
143
144STATIC void
145rtadv_start(ServiceRef service_p, IFEventID_t event_id, void * event_data)
146{
147    RTADVSocketReceiveDataRef 	data;
148    int				error;
149    interface_t *		if_p = service_interface(service_p);
150    char 			ntopbuf[INET6_ADDRSTRLEN];
151    Service_rtadv_t *		rtadv;
152    struct timeval		tv;
153
154    rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p);
155    switch (event_id) {
156    case IFEventID_start_e:
157	my_log(LOG_DEBUG, "RTADV: start %s", if_name(if_p));
158	rtadv_cancel_pending_events(service_p);
159	RTADVSocketEnableReceive(rtadv->sock,
160				 (RTADVSocketReceiveFuncPtr)rtadv_start,
161				 service_p, (void *)IFEventID_data_e);
162	if (inet6_rtadv_enable(if_name(if_p)) != 0) {
163	    rtadv_failed(service_p, ipconfig_status_internal_error_e);
164	    return;
165	}
166	bzero(&rtadv->our_router, sizeof(rtadv->our_router));
167	rtadv_set_dns_servers(rtadv, NULL, 0);
168	rtadv->our_router_hwaddr_len = 0;
169	rtadv->try = 0;
170	rtadv->data_received = FALSE;
171
172	/* FALL THROUGH */
173
174    case IFEventID_timeout_e:
175	rtadv->try++;
176	if (rtadv->try > 1) {
177	    link_status_t	link_status = service_link_status(service_p);
178
179	    if (link_status.valid == TRUE
180		&& link_status.active == FALSE) {
181		rtadv_inactive(service_p);
182		return;
183	    }
184	}
185	if (rtadv->try > MAX_RTR_SOLICITATIONS) {
186	    /* now we just wait to see if something comes in */
187	    return;
188	}
189	my_log(LOG_DEBUG,
190	       "RTADV %s: sending Router Solicitation (%d of %d)",
191	       if_name(if_p), rtadv->try, MAX_RTR_SOLICITATIONS);
192	error = RTADVSocketSendSolicitation(rtadv->sock,
193					    rtadv->lladdr_ok);
194	switch (error) {
195	case 0:
196	case ENXIO:
197	case ENETDOWN:
198	case EADDRNOTAVAIL:
199	    break;
200	default:
201	    my_log(LOG_ERR, "RTADV %s: send Router Solicitation: failed, %s",
202		   if_name(if_p), strerror(error));
203	    break;
204	}
205
206	/* set timer values and wait for responses */
207	tv.tv_sec = RTR_SOLICITATION_INTERVAL;
208	tv.tv_usec = random_range(0, USECS_PER_SEC - 1);
209	timer_set_relative(rtadv->timer, tv,
210			   (timer_func_t *)rtadv_start,
211			   service_p, (void *)IFEventID_timeout_e, NULL);
212	break;
213
214    case IFEventID_data_e:
215	data = (RTADVSocketReceiveDataRef)event_data;
216	/* save the router and flags, and start DHCPv6 if necessary */
217	if (G_IPConfiguration_verbose) {
218	    char		link_addr_buf[MAX_LINK_ADDR_LEN * 3 + 1];
219
220	    link_addr_buf[0] = '\0';
221	    if (data->router_hwaddr != NULL) {
222		if (data->router_hwaddr_len == ETHER_ADDR_LEN) {
223		    snprintf(link_addr_buf, sizeof(link_addr_buf),
224			     " (" EA_FORMAT ")",
225			     EA_LIST(data->router_hwaddr));
226		}
227		else if (data->router_hwaddr_len == 8) {
228		    snprintf(link_addr_buf, sizeof(link_addr_buf),
229			     " (" FWA_FORMAT ")",
230			     FWA_LIST(data->router_hwaddr));
231		}
232	    }
233
234	    my_log(LOG_DEBUG,
235		   "RTADV %s: Received RA from %s%s%s%s",
236		   if_name(if_p),
237		   inet_ntop(AF_INET6, &data->router,
238			     ntopbuf, sizeof(ntopbuf)),
239		   link_addr_buf,
240		   data->managed_bit ? " [Managed]" : "",
241		   data->other_bit ? " [OtherConfig]" : "");
242	    if (data->dns_servers != NULL) {
243		int		i;
244
245		for (i = 0; i < data->dns_servers_count; i++) {
246		    my_log(LOG_DEBUG,
247			   "RTADV %s: DNS Server %s",
248			   if_name(if_p),
249			   inet_ntop(AF_INET6, data->dns_servers + i,
250				     ntopbuf, sizeof(ntopbuf)));
251		}
252	    }
253	}
254	rtadv->data_received = TRUE;
255	rtadv_cancel_pending_events(service_p);
256	rtadv->our_router = data->router;
257	if (data->router_hwaddr != NULL) {
258	    int		len;
259
260	    len = data->router_hwaddr_len;
261	    if (len > sizeof(rtadv->our_router_hwaddr)) {
262		len = sizeof(rtadv->our_router_hwaddr);
263	    }
264	    bcopy(data->router_hwaddr, rtadv->our_router_hwaddr, len);
265	    rtadv->our_router_hwaddr_len = len;
266	}
267	rtadv_set_dns_servers(rtadv, data->dns_servers,
268			      data->dns_servers_count);
269	if (rtadv->dhcp_client != NULL) {
270	    if (data->managed_bit || data->other_bit) {
271		DHCPv6ClientStart(rtadv->dhcp_client,
272				  (G_dhcpv6_stateful_enabled
273				   && data->managed_bit));
274	    }
275	    else {
276		DHCPv6ClientStop(rtadv->dhcp_client, FALSE);
277	    }
278	}
279	break;
280    default:
281	break;
282    }
283    return;
284}
285
286STATIC CFStringRef
287rtadv_create_signature(ServiceRef service_p,
288		       inet6_addrinfo_t * list_p, int list_count)
289{
290    struct in6_addr	netaddr;
291    char 		ntopbuf[INET6_ADDRSTRLEN];
292    Service_rtadv_t *	rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p);
293    CFMutableStringRef	sig_str;
294
295    if (list_p == NULL || list_count == 0
296	|| rtadv->our_router_hwaddr_len == 0) {
297	return (NULL);
298    }
299    netaddr = list_p[0].addr;
300    in6_netaddr(&netaddr, list_p[0].prefix_length);
301    sig_str = CFStringCreateMutable(NULL, 0);
302    CFStringAppendFormat(sig_str, NULL,
303			 CFSTR("IPv6.Prefix=%s/%d;IPv6.RouterHardwareAddress="),
304			 inet_ntop(AF_INET6, &netaddr,
305				   ntopbuf, sizeof(ntopbuf)),
306			 list_p[0].prefix_length);
307    my_CFStringAppendBytesAsHex(sig_str, rtadv->our_router_hwaddr,
308				rtadv->our_router_hwaddr_len, ':');
309    return (sig_str);
310}
311
312STATIC void
313rtadv_address_changed(ServiceRef service_p,
314		      inet6_addrlist_t * addr_list_p)
315{
316    interface_t *	if_p = service_interface(service_p);
317    inet6_addrinfo_t *	linklocal;
318    Service_rtadv_t *	rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p);
319
320    linklocal = inet6_addrlist_get_linklocal(addr_list_p);
321    if (linklocal == NULL) {
322	/* no linklocal address assigned, nothing to do */
323	my_log(LOG_DEBUG,
324	       "RTADV %s: link-local address not present",
325	       if_name(if_p));
326	return;
327    }
328    if ((linklocal->addr_flags & IN6_IFF_NOTREADY) != 0) {
329	/* linklocal address isn't ready */
330	my_log(LOG_DEBUG,
331	       "RTADV %s: link-local address is not ready",
332	       if_name(if_p));
333	return;
334    }
335#ifdef IN6_IFF_DADPROGRESS
336    rtadv->lladdr_ok = (linklocal->addr_flags & IN6_IFF_DADPROGRESS) == 0;
337    my_log(LOG_DEBUG,
338	   "RTADV %s: link-layer option in Router Solicitation is %sOK",
339	   if_name(if_p), rtadv->lladdr_ok ? "" : "not ");
340#else /* IN6_IFF_DADPROGRESS */
341    rtadv->lladdr_ok = TRUE;
342#endif /* IN6_IFF_DADPROGRESS */
343
344    if (rtadv->try == 0) {
345	link_status_t	link_status = service_link_status(service_p);
346
347	if (link_status.valid == FALSE
348	    || link_status.active == TRUE) {
349	    my_log(LOG_DEBUG,
350		   "RTADV %s: link-local address is ready, starting",
351		   if_name(if_p));
352	    rtadv_start(service_p, IFEventID_start_e, NULL);
353	    return;
354	}
355    }
356    else {
357	int			count;
358	inet6_addrlist_t	dhcp_addr_list;
359	int			i;
360	dhcpv6_info_t		info;
361	dhcpv6_info_t *		info_p = NULL;
362	inet6_addrinfo_t *	scan;
363	inet6_addrinfo_t	list[addr_list_p->count];
364	struct in6_addr *	router = NULL;
365	int			router_count = 0;
366	CFStringRef		signature = NULL;
367
368	inet6_addrlist_init(&dhcp_addr_list);
369	if (rtadv->dhcp_client != NULL) {
370	    DHCPv6ClientCopyAddresses(rtadv->dhcp_client, &dhcp_addr_list);
371	}
372
373	/* only copy autoconf and DHCP addresses */
374	for (i = 0, count = 0, scan = addr_list_p->list;
375	     i < addr_list_p->count; i++, scan++) {
376	    if ((scan->addr_flags & IN6_IFF_NOTREADY) != 0) {
377		continue;
378	    }
379	    if ((scan->addr_flags & IN6_IFF_AUTOCONF) != 0
380		|| inet6_addrlist_contains_address(&dhcp_addr_list, scan)) {
381		list[count++] = *scan;
382	    }
383	}
384	inet6_addrlist_free(&dhcp_addr_list);
385	if (count == 0) {
386	    return;
387	}
388	if (IN6_IS_ADDR_UNSPECIFIED(&rtadv->our_router) == FALSE) {
389	    router = &rtadv->our_router;
390	    router_count = 1;
391	}
392	if (rtadv->dhcp_client != NULL
393	    && DHCPv6ClientGetInfo(rtadv->dhcp_client, &info)) {
394	    info_p = &info;
395	}
396	if (rtadv->dns_servers != NULL) {
397	    info.dns_servers = rtadv->dns_servers;
398	    info.dns_servers_count = rtadv->dns_servers_count;
399	    info_p = &info;
400	}
401	signature = rtadv_create_signature(service_p, list, count);
402	ServicePublishSuccessIPv6(service_p, list, count, router, router_count,
403				  info_p, signature);
404	my_CFRelease(&signature);
405    }
406    return;
407}
408
409STATIC void
410rtadv_dhcp_callback(void * callback_arg, DHCPv6ClientRef client)
411{
412    inet6_addrlist_t	addrs;
413    ServiceRef		service_p = (ServiceRef)callback_arg;
414
415    inet6_addrlist_copy(&addrs, if_link_index(service_interface(service_p)));
416    rtadv_address_changed(service_p, &addrs);
417    inet6_addrlist_free(&addrs);
418    return;
419}
420
421STATIC void
422rtadv_init(ServiceRef service_p)
423{
424    inet6_addrlist_t	addrs;
425    Service_rtadv_t *	rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p);
426
427    rtadv->try = 0;
428    inet6_addrlist_copy(&addrs,
429			if_link_index(service_interface(service_p)));
430    rtadv_address_changed(service_p, &addrs);
431    inet6_addrlist_free(&addrs);
432    return;
433}
434
435PRIVATE_EXTERN ipconfig_status_t
436rtadv_thread(ServiceRef service_p, IFEventID_t evid, void * event_data)
437{
438    interface_t *	if_p = service_interface(service_p);
439    ipconfig_status_t	status = ipconfig_status_success_e;
440    Service_rtadv_t *	rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p);
441
442    switch (evid) {
443    case IFEventID_start_e:
444	if (if_flags(if_p) & IFF_LOOPBACK) {
445	    status = ipconfig_status_invalid_operation_e;
446	    break;
447	}
448	if (rtadv != NULL) {
449	    my_log(LOG_DEBUG, "RTADV %s: re-entering start state",
450		   if_name(if_p));
451	    status = ipconfig_status_internal_error_e;
452	    break;
453	}
454	rtadv = malloc(sizeof(*rtadv));
455	if (rtadv == NULL) {
456	    my_log(LOG_ERR, "RTADV %s: malloc failed", if_name(if_p));
457	    status = ipconfig_status_allocation_failed_e;
458	    break;
459	}
460	bzero(rtadv, sizeof(*rtadv));
461	ServiceSetPrivate(service_p, rtadv);
462	rtadv->timer = timer_callout_init();
463	if (rtadv->timer == NULL) {
464	    my_log(LOG_ERR, "RTADV %s: timer_callout_init failed",
465		   if_name(if_p));
466	    status = ipconfig_status_allocation_failed_e;
467	    goto stop;
468	}
469	rtadv->sock = RTADVSocketCreate(if_p);
470	if (rtadv->sock == NULL) {
471	    my_log(LOG_ERR, "RTADV %s: RTADVSocketCreate failed",
472		   if_name(if_p));
473	    status = ipconfig_status_allocation_failed_e;
474	    goto stop;
475	}
476	if (G_dhcpv6_enabled) {
477	    rtadv->dhcp_client = DHCPv6ClientCreate(if_p);
478	    if (rtadv->dhcp_client == NULL) {
479		my_log(LOG_ERR, "RTADV %s: DHCPv6ClientCreate failed",
480		       if_name(if_p));
481		status = ipconfig_status_allocation_failed_e;
482		goto stop;
483	    }
484	    DHCPv6ClientSetNotificationCallBack(rtadv->dhcp_client,
485						rtadv_dhcp_callback,
486						service_p);
487	}
488	rtadv_init(service_p);
489	break;
490
491    stop:
492    case IFEventID_stop_e:
493	if (rtadv == NULL) {
494	    my_log(LOG_DEBUG, "RTADV %s: already stopped",
495		   if_name(if_p));
496	    status = ipconfig_status_internal_error_e;
497	    break;
498	}
499	my_log(LOG_DEBUG, "RTADV %s: stop", if_name(if_p));
500
501	/* close/release the RTADV socket */
502	RTADVSocketRelease(&rtadv->sock);
503
504	/* stop DHCPv6 client */
505	DHCPv6ClientRelease(&rtadv->dhcp_client);
506
507	/* this flushes the addresses */
508	(void)inet6_rtadv_disable(if_name(if_p));
509
510	/* clean-up resources */
511	if (rtadv->timer) {
512	    timer_callout_free(&rtadv->timer);
513	}
514	rtadv_set_dns_servers(rtadv, NULL, 0);
515	inet6_flush_prefixes(if_name(if_p));
516	inet6_flush_routes(if_name(if_p));
517	ServiceSetPrivate(service_p, NULL);
518	free(rtadv);
519	break;
520
521    case IFEventID_ipv6_address_changed_e:
522	if (rtadv == NULL) {
523	    my_log(LOG_DEBUG, "RTADV %s: private data is NULL",
524		   if_name(if_p));
525	    status = ipconfig_status_internal_error_e;
526	    break;
527	}
528	if (rtadv->dhcp_client != NULL) {
529	    DHCPv6ClientAddressChanged(rtadv->dhcp_client, event_data);
530	}
531	rtadv_address_changed(service_p, event_data);
532	break;
533    case IFEventID_renew_e:
534    case IFEventID_link_status_changed_e: {
535	link_status_t	link_status;
536	void *		network_changed = event_data;
537
538	if (rtadv == NULL) {
539	    return (ipconfig_status_internal_error_e);
540	}
541	link_status = service_link_status(service_p);
542	if (link_status.valid == FALSE
543	    || link_status.active == TRUE) {
544	    if (network_changed != NULL) {
545		inet6_flush_prefixes(if_name(if_p));
546		inet6_flush_routes(if_name(if_p));
547		inet6_rtadv_disable(if_name(if_p));
548		if (rtadv->dhcp_client != NULL) {
549		    DHCPv6ClientStop(rtadv->dhcp_client, TRUE);
550		}
551		service_publish_failure(service_p,
552					ipconfig_status_network_changed_e);
553	    }
554	    else if (evid == IFEventID_link_status_changed_e
555		     && rtadv->try == 1
556		     && rtadv->data_received == FALSE) {
557		/* we're already on it */
558		break;
559	    }
560	    rtadv_init(service_p);
561	}
562	else {
563	    rtadv->try = 0;
564	}
565	break;
566    }
567    case IFEventID_link_timer_expired_e:
568	rtadv_inactive(service_p);
569	if (rtadv->dhcp_client != NULL) {
570	    DHCPv6ClientStop(rtadv->dhcp_client, FALSE);
571	}
572	break;
573
574    case IFEventID_wake_e:
575	break;
576
577    case IFEventID_get_dhcpv6_info_e: {
578	dhcpv6_info_t *		info_p = (dhcpv6_info_t *)event_data;
579
580	if (rtadv->dhcp_client != NULL) {
581	    (void)DHCPv6ClientGetInfo(rtadv->dhcp_client, info_p);
582	}
583	if (rtadv->dns_servers != NULL) {
584	    info_p->dns_servers = rtadv->dns_servers;
585	    info_p->dns_servers_count = rtadv->dns_servers_count;
586	}
587	break;
588    }
589    default:
590	break;
591    } /* switch */
592
593    return (status);
594}
595