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