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