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