1/*
2 * Copyright (c) 1999-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 * linklocal.c
25 * - link-local address configuration thread
26 * - contains linklocal_thread()
27 */
28/*
29 * Modification History
30 *
31 * September 27, 2001	Dieter Siegmund (dieter@apple.com)
32 * - moved ad-hoc processing into its own service
33 *
34 * January 8, 2002	Dieter Siegmund (dieter@apple.com)
35 * - added pseudo-link-local service support i.e. configure the
36 *   subnet, but don't configure a link-local address
37 *
38 * April 13, 2005	Dieter Siegmund (dieter@apple.com)
39 * - for the pseudo-link-local service support, check whether an ARP for
40 *   169.254.255.255 (link-local subnet-specific broadcast) is received.
41 *   If it is, we can assume that a router is configured for proxy ARP, and
42 *   thus link-local to routable communication is not possible, so disable
43 *   link-local ARP on this interface.
44 */
45
46#include <stdlib.h>
47#include <unistd.h>
48#include <string.h>
49#include <stdio.h>
50#include <sys/types.h>
51#include <sys/wait.h>
52#include <sys/errno.h>
53#include <sys/socket.h>
54#include <sys/ioctl.h>
55#include <sys/sockio.h>
56#include <ctype.h>
57#include <net/if.h>
58#include <net/ethernet.h>
59#include <netinet/in.h>
60#include <netinet/udp.h>
61#include <netinet/in_systm.h>
62#include <netinet/ip.h>
63#include <netinet/bootp.h>
64#include <arpa/inet.h>
65#include <syslog.h>
66#include <net/if_types.h>
67
68#include "dhcp_options.h"
69#include "dhcp.h"
70#include "interfaces.h"
71#include "util.h"
72#include "host_identifier.h"
73#include "dhcplib.h"
74
75#include "ipconfigd_threads.h"
76
77#include "dprintf.h"
78
79#define LINKLOCAL_RANGE_START	IN_LINKLOCALNETNUM
80#define LINKLOCAL_RANGE_END	((u_int32_t)0xa9feffff) /* 169.254.255.255 */
81#define LINKLOCAL_FIRST_USEABLE	(LINKLOCAL_RANGE_START + 256) /* 169.254.1.0 */
82#define LINKLOCAL_LAST_USEABLE	(LINKLOCAL_RANGE_END - 256) /* 169.254.254.255 */
83#define LINKLOCAL_MASK		IN_CLASSB_NET
84#define LINKLOCAL_RANGE		((u_int32_t)(LINKLOCAL_LAST_USEABLE + 1) \
85				 - LINKLOCAL_FIRST_USEABLE)
86
87#define	MAX_LINKLOCAL_INITIAL_TRIES	10
88
89/*
90 * LINKLOCAL_RETRY_TIME_SECS
91 *   After we probe for MAX_LINKLOCAL_INITIAL_TRIES addresses and fail,
92 *   wait this amount of time before trying the next one.  This avoids
93 *   overwhelming the network with ARP probes in the worst case scenario.
94 */
95#define LINKLOCAL_RETRY_TIME_SECS	30
96
97typedef struct {
98    arp_client_t *	arp;
99    timer_callout_t *	timer;
100    int			current;
101    struct in_addr	our_ip;
102    struct in_addr	probe;
103    boolean_t		allocate;
104    boolean_t		enable_arp_collision_detection;
105} Service_linklocal_t;
106
107static int
108siocarpipll(int s, const char * name, int val)
109{
110    struct ifreq	ifr;
111
112    bzero(&ifr, sizeof(ifr));
113    ifr.ifr_intval = val;
114    strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
115    return (ioctl(s, SIOCARPIPLL, &ifr));
116}
117
118static void
119set_arp_linklocal(const char * name, int val)
120{
121    int	s;
122    s = socket(AF_INET, SOCK_DGRAM, 0);
123    if (s == -1) {
124	my_log(LOG_NOTICE, "set_arp_linklocal(%s) socket() failed, %s",
125	       name, strerror(errno));
126	return;
127    }
128    if (siocarpipll(s, name, val) < 0) {
129	if (errno != ENXIO) {
130	    my_log(LOG_NOTICE,
131		   "set_arp_linklocal(%s) SIOCARPIPLL %d failed, %s",
132		   name, val, strerror(errno));
133	}
134    }
135    close(s);
136}
137
138static __inline__ void
139arp_linklocal_disable(const char * name)
140{
141    set_arp_linklocal(name, 0);
142}
143
144static __inline__ void
145arp_linklocal_enable(const char * name)
146{
147    set_arp_linklocal(name, 1);
148}
149
150static boolean_t
151parent_service_ip_address(ServiceRef service_p, struct in_addr * ret_ip)
152{
153    struct in_addr	addr;
154    ServiceRef 		parent_service_p = service_parent_service(service_p);
155
156    if (parent_service_p == NULL) {
157	return (FALSE);
158    }
159    addr = ServiceGetActiveIPAddress(parent_service_p);
160    if (addr.s_addr == 0) {
161	return (FALSE);
162    }
163    *ret_ip = addr;
164    return (TRUE);
165}
166
167struct in_addr
168S_find_linklocal_address(ServiceRef service_p)
169{
170    int				count;
171    int				i;
172    interface_t *		if_p;
173    struct in_addr		ll_addr;
174
175    ll_addr = linklocal_get_address(service_p);
176    if (ll_addr.s_addr != 0) {
177	return (ll_addr);
178    }
179    if_p = service_interface(service_p);
180    count = if_inet_count(if_p);
181    for (i = 0; i < count; i++) {
182	inet_addrinfo_t * 	info = if_inet_addr_at(if_p, i);
183
184	if (ip_is_linklocal(info->addr)) {
185	    my_log(LOG_DEBUG, "LINKLOCAL %s: found address " IP_FORMAT,
186		   if_name(if_p), IP_LIST(&info->addr));
187	    return (info->addr);
188	}
189    }
190    return (G_ip_zeroes);
191}
192
193static void
194linklocal_cancel_pending_events(ServiceRef service_p)
195{
196    Service_linklocal_t * 	linklocal;
197
198    linklocal = (Service_linklocal_t *)ServiceGetPrivate(service_p);
199    if (linklocal == NULL)
200	return;
201    if (linklocal->timer) {
202	timer_cancel(linklocal->timer);
203    }
204    if (linklocal->arp) {
205	arp_client_cancel(linklocal->arp);
206    }
207    return;
208}
209
210
211static void
212linklocal_failed(ServiceRef service_p, ipconfig_status_t status)
213{
214    Service_linklocal_t * linklocal;
215
216    linklocal = (Service_linklocal_t *)ServiceGetPrivate(service_p);
217    linklocal->enable_arp_collision_detection = FALSE;
218    linklocal_cancel_pending_events(service_p);
219    arp_linklocal_disable(if_name(service_interface(service_p)));
220    service_remove_address(service_p);
221    if (status != ipconfig_status_media_inactive_e) {
222	linklocal->our_ip = G_ip_zeroes;
223    }
224    service_publish_failure(service_p, status);
225    return;
226}
227
228static void
229linklocal_inactive(ServiceRef service_p)
230{
231    linklocal_failed(service_p, ipconfig_status_media_inactive_e);
232    return;
233}
234
235static void
236linklocal_detect_proxy_arp(ServiceRef service_p, IFEventID_t event_id,
237			   void * event_data)
238{
239    interface_t *	  if_p = service_interface(service_p);
240    Service_linklocal_t * linklocal;
241
242    linklocal = (Service_linklocal_t *)ServiceGetPrivate(service_p);
243    switch (event_id) {
244      case IFEventID_start_e: {
245	  struct in_addr	iaddr;
246	  struct in_addr	llbroadcast;
247
248	  arp_linklocal_disable(if_name(if_p));
249	  llbroadcast.s_addr = htonl(LINKLOCAL_RANGE_END);
250	  /* clean-up anything that might have come before */
251	  linklocal_cancel_pending_events(service_p);
252	  if (parent_service_ip_address(service_p, &iaddr) == FALSE) {
253	      my_log(LOG_NOTICE, "LINKLOCAL %s: parent has no IP",
254		     if_name(if_p));
255	      break;
256	  }
257	  my_log(LOG_DEBUG,
258		 "LINKLOCAL %s: ARP Request: Source " IP_FORMAT
259		 " Target 169.254.255.255", if_name(if_p), IP_LIST(&iaddr));
260	  arp_client_probe(linklocal->arp,
261			   (arp_result_func_t *)linklocal_detect_proxy_arp,
262			   service_p, (void *)IFEventID_arp_e, iaddr,
263			   llbroadcast);
264	  /* wait for the results */
265	  break;
266      }
267      case IFEventID_arp_e: {
268	  link_status_t		link_status;
269	  arp_result_t *	result = (arp_result_t *)event_data;
270
271	  if (result->error) {
272	      my_log(LOG_DEBUG, "LINKLOCAL %s: ARP probe failed, %s",
273		     if_name(if_p),
274		     arp_client_errmsg(linklocal->arp));
275	      break;
276	  }
277	  linklocal_set_needs_attention();
278	  if (result->in_use) {
279	      my_log(LOG_DEBUG,
280		     "LINKLOCAL %s: ARP response received for 169.254.255.255"
281		     " from " EA_FORMAT,
282		     if_name(if_p),
283		     EA_LIST(result->addr.target_hardware));
284	      service_publish_failure(service_p,
285				      ipconfig_status_address_in_use_e);
286	      break;
287	  }
288	  link_status = service_link_status(service_p);
289	  if (link_status.valid == TRUE
290	      && link_status.active == FALSE) {
291	      linklocal_failed(service_p,
292			       ipconfig_status_media_inactive_e);
293	      break;
294	  }
295	  arp_linklocal_enable(if_name(if_p));
296	  service_publish_failure(service_p,
297				  ipconfig_status_success_e);
298	  break;
299      }
300      default: {
301	  break;
302      }
303    }
304    return;
305}
306
307static void
308linklocal_allocate(ServiceRef service_p, IFEventID_t event_id,
309		   void * event_data)
310{
311    interface_t *	  if_p = service_interface(service_p);
312    Service_linklocal_t * linklocal;
313
314    linklocal = (Service_linklocal_t *)ServiceGetPrivate(service_p);
315    switch (event_id) {
316      case IFEventID_start_e: {
317	  linklocal->enable_arp_collision_detection = FALSE;
318
319	  arp_linklocal_disable(if_name(if_p));
320
321	  /* clean-up anything that might have come before */
322	  linklocal_cancel_pending_events(service_p);
323
324	  linklocal->current = 1;
325	  if (linklocal->our_ip.s_addr) {
326	      /* try to keep the same address */
327	      linklocal->probe = linklocal->our_ip;
328	  }
329	  else {
330	      linklocal->probe.s_addr
331		  = htonl(LINKLOCAL_FIRST_USEABLE
332	            + random_range(0, LINKLOCAL_RANGE));
333	  }
334	  my_log(LOG_DEBUG, "LINKLOCAL %s: probing " IP_FORMAT,
335		 if_name(if_p), IP_LIST(&linklocal->probe));
336	  arp_client_probe(linklocal->arp,
337			   (arp_result_func_t *)linklocal_allocate, service_p,
338			   (void *)IFEventID_arp_e, G_ip_zeroes,
339			   linklocal->probe);
340	  /* wait for the results */
341	  return;
342      }
343      case IFEventID_arp_e: {
344	  arp_result_t *	result = (arp_result_t *)event_data;
345
346	  if (result->error) {
347	      my_log(LOG_DEBUG, "LINKLOCAL %s: ARP probe failed, %s",
348		     if_name(if_p),
349		     arp_client_errmsg(linklocal->arp));
350	      break;
351	  }
352	  if (result->in_use
353	      || service_is_using_ip(service_p, linklocal->probe)) {
354	      if (result->in_use) {
355	          my_log(LOG_DEBUG, "LINKLOCAL %s: IP address "
356		         IP_FORMAT " is in use by " EA_FORMAT,
357		         if_name(if_p),
358		         IP_LIST(&linklocal->probe),
359		         EA_LIST(result->addr.target_hardware));
360	      }
361	      else {
362		  my_log(LOG_DEBUG, "LINKLOCAL %s: IP address "
363			 IP_FORMAT " is no longer unique",
364			 if_name(if_p));
365	      }
366	      if (linklocal->our_ip.s_addr == linklocal->probe.s_addr) {
367		  linklocal->our_ip = G_ip_zeroes;
368		  (void)service_remove_address(service_p);
369		  service_publish_failure(service_p,
370					  ipconfig_status_address_in_use_e);
371	      }
372	  }
373	  else {
374	      link_status_t	   link_status = service_link_status(service_p);
375	      const struct in_addr linklocal_mask = { htonl(LINKLOCAL_MASK) };
376
377	      if (link_status.valid == TRUE
378		  && link_status.active == FALSE) {
379		  linklocal_failed(service_p,
380				   ipconfig_status_media_inactive_e);
381		  break;
382	      }
383
384	      /* ad-hoc IP address is not in use, so use it */
385	      (void)service_set_address(service_p, linklocal->probe,
386					linklocal_mask, G_ip_zeroes);
387	      linklocal_set_address(service_p, linklocal->probe);
388	      arp_linklocal_enable(if_name(if_p));
389	      linklocal_cancel_pending_events(service_p);
390	      linklocal->our_ip = linklocal->probe;
391	      ServicePublishSuccessIPv4(service_p, NULL);
392	      linklocal->enable_arp_collision_detection = TRUE;
393	      /* we're done */
394	      break; /* out of switch */
395	  }
396	  if (linklocal->current >= MAX_LINKLOCAL_INITIAL_TRIES) {
397	      struct timeval tv;
398	      /* initial tries threshold reached, try again after a timeout */
399	      tv.tv_sec = LINKLOCAL_RETRY_TIME_SECS;
400	      tv.tv_usec = 0;
401	      timer_set_relative(linklocal->timer, tv,
402				 (timer_func_t *)linklocal_allocate,
403				 service_p, (void *)IFEventID_timeout_e, NULL);
404	      /* don't fall through, wait for timer */
405	      break;
406	  }
407	  linklocal->current++;
408	  /* FALL THROUGH */
409      case IFEventID_timeout_e:
410	  /* try the next address */
411	  linklocal->probe.s_addr
412	      = htonl(LINKLOCAL_FIRST_USEABLE
413		+ random_range(0, LINKLOCAL_RANGE));
414	  arp_client_probe(linklocal->arp,
415			   (arp_result_func_t *)linklocal_allocate, service_p,
416			   (void *)IFEventID_arp_e, G_ip_zeroes,
417			   linklocal->probe);
418	  my_log(LOG_DEBUG, "LINKLOCAL %s probing " IP_FORMAT,
419		 if_name(if_p), IP_LIST(&linklocal->probe));
420	  /* wait for the results */
421	  break;
422      }
423      default:
424	  break;
425    }
426
427    return;
428}
429
430static void
431linklocal_start(ServiceRef service_p)
432{
433    Service_linklocal_t *	linklocal;
434
435    linklocal = (Service_linklocal_t *)ServiceGetPrivate(service_p);
436    if (linklocal->allocate) {
437	linklocal_allocate(service_p, IFEventID_start_e, NULL);
438    }
439    else {
440	linklocal_detect_proxy_arp(service_p,
441				   IFEventID_start_e, NULL);
442    }
443    return;
444}
445
446ipconfig_status_t
447linklocal_thread(ServiceRef service_p, IFEventID_t event_id, void * event_data)
448{
449    interface_t *		if_p = service_interface(service_p);
450    Service_linklocal_t *	linklocal;
451    ipconfig_status_t		status = ipconfig_status_success_e;
452
453    linklocal = (Service_linklocal_t *)ServiceGetPrivate(service_p);
454    switch (event_id) {
455      case IFEventID_start_e: {
456	  ipconfig_method_data_t * method_data;
457
458	  if (if_flags(if_p) & IFF_LOOPBACK) {
459	      status = ipconfig_status_invalid_operation_e;
460	      break;
461	  }
462	  if (linklocal != NULL) {
463	      my_log(LOG_ERR, "LINKLOCAL %s: re-entering start state",
464		     if_name(if_p));
465	      status = ipconfig_status_internal_error_e;
466	      break;
467	  }
468	  my_log(LOG_DEBUG, "LINKLOCAL %s: start", if_name(if_p));
469	  linklocal = malloc(sizeof(*linklocal));
470	  if (linklocal == NULL) {
471	      my_log(LOG_ERR, "LINKLOCAL %s: malloc failed",
472		     if_name(if_p));
473	      status = ipconfig_status_allocation_failed_e;
474	      break;
475	  }
476	  bzero(linklocal, sizeof(*linklocal));
477	  ServiceSetPrivate(service_p, linklocal);
478
479	  linklocal->timer = timer_callout_init();
480	  if (linklocal->timer == NULL) {
481	      my_log(LOG_ERR, "LINKLOCAL %s: timer_callout_init failed",
482		     if_name(if_p));
483	      status = ipconfig_status_allocation_failed_e;
484	      goto stop;
485	  }
486	  linklocal->arp = arp_client_init(G_arp_session, if_p);
487	  if (linklocal->arp == NULL) {
488	      my_log(LOG_ERR, "LINKLOCAL %s: arp_client_init failed",
489		     if_name(if_p));
490	      status = ipconfig_status_allocation_failed_e;
491	      goto stop;
492	  }
493	  /* ARP probes count as collisions for link-local address allocation */
494	  arp_client_set_probes_are_collisions(linklocal->arp, TRUE);
495	  linklocal->allocate = TRUE;
496
497	  method_data = (ipconfig_method_data_t *)event_data;
498	  if (method_data != NULL
499	      && method_data->linklocal.allocate == FALSE) {
500	      /* don't allocate an IP address, just set the subnet */
501	      linklocal->allocate = FALSE;
502	      linklocal_detect_proxy_arp(service_p, IFEventID_start_e, NULL);
503	      break;
504	  }
505	  linklocal->our_ip = S_find_linklocal_address(service_p);
506	  linklocal_allocate(service_p, IFEventID_start_e, NULL);
507	  break;
508      }
509      case IFEventID_stop_e: {
510      stop:
511	  my_log(LOG_DEBUG, "LINKLOCAL %s: stop", if_name(if_p));
512	  if (linklocal == NULL) {
513	      my_log(LOG_DEBUG, "LINKLOCAL %s: already stopped",
514		     if_name(if_p));
515	      status = ipconfig_status_internal_error_e; /* shouldn't happen */
516	      break;
517	  }
518
519	  /* remove IP address */
520	  arp_linklocal_disable(if_name(if_p));
521	  service_remove_address(service_p);
522
523	  /* clean-up the published state */
524	  service_publish_failure(service_p,
525				  ipconfig_status_success_e);
526
527	  /* clean-up resources */
528	  if (linklocal->timer) {
529	      timer_callout_free(&linklocal->timer);
530	  }
531	  if (linklocal->arp) {
532	      arp_client_free(&linklocal->arp);
533	  }
534	  if (linklocal) {
535	      free(linklocal);
536	  }
537	  ServiceSetPrivate(service_p, NULL);
538	  break;
539      }
540      case IFEventID_change_e: {
541	  boolean_t		  	allocate = TRUE;
542	  change_event_data_t *   	change_event;
543	  ipconfig_method_data_t * 	method_data;
544
545	  change_event = (change_event_data_t *)event_data;
546	  method_data = change_event->method_data;
547	  if (method_data != NULL
548	      && method_data->linklocal.allocate == FALSE) {
549	      /* don't allocate an IP address, just set the subnet */
550	      allocate = FALSE;
551	  }
552	  if (linklocal->allocate != allocate) {
553	      linklocal->allocate = allocate;
554	      if (allocate) {
555		  linklocal->our_ip = S_find_linklocal_address(service_p);
556		  linklocal_allocate(service_p, IFEventID_start_e, NULL);
557	      }
558	      else {
559		  linklocal_failed(service_p, ipconfig_status_success_e);
560		  linklocal_detect_proxy_arp(service_p,
561					     IFEventID_start_e, NULL);
562	      }
563	  }
564	  break;
565      }
566      case IFEventID_arp_collision_e: {
567	  arp_collision_data_t *	arpc;
568
569	  arpc = (arp_collision_data_t *)event_data;
570	  if (linklocal == NULL) {
571	      return (ipconfig_status_internal_error_e);
572	  }
573	  if (linklocal->allocate == FALSE) {
574	      break;
575	  }
576	  if (linklocal->enable_arp_collision_detection == FALSE
577	      || arpc->ip_addr.s_addr != linklocal->our_ip.s_addr) {
578	      break;
579	  }
580	  linklocal->our_ip = G_ip_zeroes;
581	  (void)service_remove_address(service_p);
582	  service_publish_failure(service_p,
583				  ipconfig_status_address_in_use_e);
584	  linklocal_allocate(service_p, IFEventID_start_e, NULL);
585	  break;
586      }
587      case IFEventID_link_status_changed_e: {
588	  link_status_t	   link_status;
589
590	  if (linklocal == NULL) {
591	      return (ipconfig_status_internal_error_e);
592	  }
593	  link_status = service_link_status(service_p);
594	  if (link_status.valid == TRUE) {
595	      linklocal_cancel_pending_events(service_p);
596	      if (link_status.active == TRUE) {
597		  linklocal_start(service_p);
598	      }
599	      else {
600		  linklocal->enable_arp_collision_detection = FALSE;
601	      }
602	  }
603	  break;
604      }
605      case IFEventID_link_timer_expired_e:
606	  linklocal_inactive(service_p);
607	  break;
608
609      case IFEventID_renew_e: {
610	  break;
611      }
612      default:
613	  break;
614    } /* switch (event_id) */
615    return (status);
616}
617