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/* 25 * bootp.c 26 * - BOOTP configuration thread 27 * - contains bootp_thread() 28 * - configures an interface's address using BOOTP 29 * - once the address is retrieved, the client probes the address using 30 * ARP to ensure that another client isn't already using the address 31 */ 32/* 33 * Modification History 34 * 35 * May 9, 2000 Dieter Siegmund (dieter@apple.com) 36 * - reworked to fit within the new event-driven framework 37 * 38 * October 4, 2000 Dieter Siegmund (dieter@apple.com) 39 * - added code to unpublish interface state if the link goes 40 * down and stays down for more than 4 seconds 41 */ 42 43#include <stdlib.h> 44#include <unistd.h> 45#include <string.h> 46#include <stdio.h> 47#include <sys/types.h> 48#include <sys/wait.h> 49#include <sys/errno.h> 50#include <sys/socket.h> 51#include <sys/ioctl.h> 52#include <sys/sockio.h> 53#include <ctype.h> 54#include <net/if.h> 55#include <net/ethernet.h> 56#include <netinet/in.h> 57#include <netinet/udp.h> 58#include <netinet/in_systm.h> 59#include <netinet/ip.h> 60#include <netinet/bootp.h> 61#include <arpa/inet.h> 62#include <net/if_types.h> 63#include <syslog.h> 64 65#include "rfc_options.h" 66#include "dhcp_options.h" 67#include "dhcp.h" 68#include "interfaces.h" 69#include "util.h" 70#include "host_identifier.h" 71#include "dhcplib.h" 72#include "ipconfigd_threads.h" 73 74typedef struct { 75 boolean_t gathering; 76 struct bootp request; 77 struct saved_pkt saved; 78 long start_secs; 79 int try; 80 int wait_secs; 81 u_int32_t xid; 82 timer_callout_t * timer; 83 arp_client_t * arp; 84 bootp_client_t * client; 85 boolean_t user_warned; 86 boolean_t enable_arp_collision_detection; 87 boolean_t resolve_router_timed_out; 88} Service_bootp_t; 89 90/* tags_search: these are the tags we look for using BOOTP */ 91static const uint8_t bootp_params[] = { 92 dhcptag_host_name_e, 93 dhcptag_subnet_mask_e, 94 dhcptag_router_e, 95 dhcptag_domain_name_server_e, 96 dhcptag_domain_name_e, 97}; 98#define N_BOOTP_PARAMS (sizeof(bootp_params) / sizeof(bootp_params[0])) 99#define IDEAL_RATING N_BOOTP_PARAMS 100 101static void 102bootp_request(ServiceRef service_p, IFEventID_t evid, void * event_data); 103 104/* 105 * Function: make_bootp_request 106 * Purpose: 107 * Create a "blank" bootp packet. 108 */ 109static void 110make_bootp_request(struct bootp * pkt, 111 uint8_t * hwaddr, uint8_t hwtype, uint8_t hwlen) 112{ 113 bzero(pkt, sizeof (*pkt)); 114 pkt->bp_op = BOOTREQUEST; 115 pkt->bp_htype = hwtype; 116 pkt->bp_hlen = hwlen; 117 if (G_must_broadcast) 118 pkt->bp_unused = htons(DHCP_FLAGS_BROADCAST); 119 bcopy(hwaddr, pkt->bp_chaddr, hwlen); 120 bcopy(G_rfc_magic, pkt->bp_vend, sizeof(G_rfc_magic)); 121 pkt->bp_vend[4] = dhcptag_end_e; 122 return; 123} 124 125static void 126bootp_set_dhcp_info(Service_bootp_t * bootp, dhcp_info_t * dhcp_info_p) 127{ 128 dhcp_info_p->pkt = (uint8_t *)bootp->saved.pkt; 129 dhcp_info_p->pkt_size = bootp->saved.pkt_size; 130 dhcp_info_p->options = &bootp->saved.options; 131 dhcp_info_p->lease_start = 0; 132 dhcp_info_p->lease_expiration = 0; 133 return; 134} 135 136static void 137bootp_publish_success(ServiceRef service_p) 138{ 139 Service_bootp_t * bootp = (Service_bootp_t *)ServiceGetPrivate(service_p); 140 dhcp_info_t dhcp_info; 141 142 bootp_set_dhcp_info(bootp, &dhcp_info); 143 ServicePublishSuccessIPv4(service_p, &dhcp_info); 144 return; 145} 146 147static void 148S_cancel_pending_events(ServiceRef service_p) 149{ 150 Service_bootp_t * bootp = (Service_bootp_t *)ServiceGetPrivate(service_p); 151 152 if (bootp == NULL) 153 return; 154 if (bootp->timer) { 155 timer_cancel(bootp->timer); 156 } 157 if (bootp->client) { 158 bootp_client_disable_receive(bootp->client); 159 } 160 if (bootp->arp) { 161 arp_client_cancel(bootp->arp); 162 } 163 return; 164} 165 166static void 167bootp_resolve_router_callback(ServiceRef service_p, 168 router_arp_status_t status); 169 170static void 171bootp_resolve_router_retry(void * arg0, void * arg1, void * arg2) 172{ 173 Service_bootp_t * bootp; 174 ServiceRef service_p = (ServiceRef)arg0; 175 176 bootp = (Service_bootp_t *)ServiceGetPrivate(service_p); 177 service_resolve_router(service_p, bootp->arp, 178 bootp_resolve_router_callback, 179 bootp->saved.our_ip); 180 return; 181} 182 183static void 184bootp_resolve_router_callback(ServiceRef service_p, 185 router_arp_status_t status) 186{ 187 Service_bootp_t * bootp = (Service_bootp_t *)ServiceGetPrivate(service_p); 188 struct timeval tv; 189 190 switch (status) { 191 case router_arp_status_no_response_e: 192 /* try again in 60 seconds */ 193 tv.tv_sec = 60; 194 tv.tv_usec = 0; 195 timer_set_relative(bootp->timer, tv, 196 (timer_func_t *)bootp_resolve_router_retry, 197 service_p, NULL, NULL); 198 if (bootp->resolve_router_timed_out) { 199 break; 200 } 201 /* publish what we have so far */ 202 bootp->resolve_router_timed_out = TRUE; 203 bootp_publish_success(service_p); 204 break; 205 case router_arp_status_success_e: 206 bootp->resolve_router_timed_out = FALSE; 207 bootp_publish_success(service_p); 208 break; 209 default: 210 case router_arp_status_failed_e: 211 break; 212 } 213} 214 215static void 216bootp_success(ServiceRef service_p) 217{ 218 Service_bootp_t * bootp = (Service_bootp_t *)ServiceGetPrivate(service_p); 219 struct in_addr mask = {0}; 220 void * option; 221 /* ALIGN: saved.pkt is uint32_t aligned, cast ok */ 222 struct bootp * reply = (struct bootp *)(void *)bootp->saved.pkt; 223 224 S_cancel_pending_events(service_p); 225 option = dhcpol_find_with_length(&bootp->saved.options, 226 dhcptag_subnet_mask_e, 227 sizeof(mask)); 228 if (option != NULL) { 229 mask = *((struct in_addr *)option); 230 } 231 if (bootp->saved.our_ip.s_addr 232 && reply->bp_yiaddr.s_addr != bootp->saved.our_ip.s_addr) { 233 (void)service_remove_address(service_p); 234 } 235 bootp->try = 0; 236 bootp->enable_arp_collision_detection = TRUE; 237 bootp->saved.our_ip = reply->bp_yiaddr; 238 (void)service_set_address(service_p, bootp->saved.our_ip, 239 mask, G_ip_zeroes); 240 bootp->resolve_router_timed_out = FALSE; 241 if (service_update_router_address(service_p, &bootp->saved.options, 242 bootp->saved.our_ip) 243 && service_resolve_router(service_p, bootp->arp, 244 bootp_resolve_router_callback, 245 bootp->saved.our_ip)) { 246 /* router resolution started */ 247 } 248 else { 249 bootp_publish_success(service_p); 250 } 251 return; 252} 253 254static void 255bootp_failed(ServiceRef service_p, ipconfig_status_t status, char * msg) 256{ 257 Service_bootp_t * bootp = (Service_bootp_t *)ServiceGetPrivate(service_p); 258 struct timeval tv; 259 260 S_cancel_pending_events(service_p); 261 dhcpol_free(&bootp->saved.options); 262 service_remove_address(service_p); 263 (void)service_disable_autoaddr(service_p); 264 bootp->saved.our_ip.s_addr = 0; 265 bootp->try = 0; 266 bootp->enable_arp_collision_detection = FALSE; 267 service_publish_failure(service_p, status); 268 269 if (status != ipconfig_status_media_inactive_e) { 270 /* retry BOOTP again in a bit */ 271#define RETRY_INTERVAL_SECS (2 * 60) 272 tv.tv_sec = RETRY_INTERVAL_SECS; 273 tv.tv_usec = 0; 274 timer_set_relative(bootp->timer, tv, 275 (timer_func_t *)bootp_request, 276 service_p, (void *)IFEventID_start_e, NULL); 277 } 278 279 return; 280} 281 282static void 283bootp_arp_probe(ServiceRef service_p, IFEventID_t evid, void * event_data) 284{ 285 Service_bootp_t * bootp = (Service_bootp_t *)ServiceGetPrivate(service_p); 286 interface_t * if_p = service_interface(service_p); 287 288 switch (evid) { 289 case IFEventID_start_e: { 290 /* ALIGN: saved.pkt is uint32_t aligned, cast ok */ 291 struct bootp * reply = (struct bootp *)(void *)bootp->saved.pkt; 292 293 my_log(LOG_DEBUG, "BOOTP %s: ended at %d", if_name(if_p), 294 timer_current_secs() - bootp->start_secs); 295 (void)service_disable_autoaddr(service_p); 296 bootp_client_disable_receive(bootp->client); 297 timer_cancel(bootp->timer); 298 arp_client_cancel(bootp->arp); 299 arp_client_probe(bootp->arp, 300 (arp_result_func_t *)bootp_arp_probe, service_p, 301 (void *)IFEventID_arp_e, G_ip_zeroes, 302 reply->bp_yiaddr); 303 return; 304 } 305 case IFEventID_arp_e: { 306 arp_result_t * result = (arp_result_t *)event_data; 307 308 if (result->error) { 309 my_log(LOG_ERR, "BOOTP %s: arp probe failed, %s", 310 if_name(if_p), 311 arp_client_errmsg(bootp->arp)); 312 bootp_failed(service_p, ipconfig_status_internal_error_e, NULL); 313 return; 314 } 315 else { 316 /* ALIGN: saved.pkt is uint32_t aligned, cast ok */ 317 struct bootp * reply = 318 (struct bootp *)(void *)bootp->saved.pkt; 319 if (result->in_use) { 320 char msg[128]; 321 322 snprintf(msg, sizeof(msg), 323 IP_FORMAT " in use by " 324 EA_FORMAT ", BOOTP Server " IP_FORMAT, 325 IP_LIST(&reply->bp_yiaddr), 326 EA_LIST(result->addr.target_hardware), 327 IP_LIST(&reply->bp_siaddr)); 328 if (bootp->user_warned == FALSE) { 329 ServiceReportIPv4AddressConflict(service_p, 330 reply->bp_yiaddr); 331 bootp->user_warned = TRUE; 332 } 333 syslog(LOG_ERR, "BOOTP %s: %s", if_name(if_p), msg); 334 bootp_failed(service_p, ipconfig_status_address_in_use_e, 335 msg); 336 break; 337 } 338 } 339 bootp_success(service_p); 340 break; 341 } 342 default: 343 break; 344 } 345 return; 346} 347 348static void 349bootp_request(ServiceRef service_p, IFEventID_t evid, void * event_data) 350{ 351 Service_bootp_t * bootp = (Service_bootp_t *)ServiceGetPrivate(service_p); 352 interface_t * if_p = service_interface(service_p); 353 struct timeval tv; 354 355 switch (evid) { 356 case IFEventID_start_e: 357 my_log(LOG_DEBUG, "BOOTP %s: starting", if_name(if_p)); 358 (void)service_enable_autoaddr(service_p); 359 S_cancel_pending_events(service_p); 360 bootp->start_secs = timer_current_secs(); 361 bootp->wait_secs = G_initial_wait_secs; 362 bootp->gathering = FALSE; 363 bootp->saved.pkt_size = 0; 364 bootp->saved.rating = 0; 365 bootp->enable_arp_collision_detection = FALSE; 366 dhcpol_free(&bootp->saved.options); 367 make_bootp_request(&bootp->request, if_link_address(if_p), 368 if_link_arptype(if_p), 369 if_link_length(if_p)); 370 bootp->try = 0; 371 bootp->xid++; 372 bootp_client_enable_receive(bootp->client, 373 (bootp_receive_func_t *)bootp_request, 374 service_p, (void *)IFEventID_data_e); 375 /* FALL THROUGH */ 376 case IFEventID_timeout_e: 377 if (bootp->gathering == TRUE) { 378 bootp_arp_probe(service_p, IFEventID_start_e, NULL); 379 break; 380 } 381 bootp->try++; 382 if (bootp->try > 1) { 383 link_status_t link_status = service_link_status(service_p); 384 385 if (link_status.valid 386 && link_status.active == FALSE) { 387 bootp_failed(service_p, ipconfig_status_media_inactive_e, 388 NULL); 389 break; 390 } 391 } 392 if (bootp->try > (G_max_retries + 1)) { 393 bootp_failed(service_p, ipconfig_status_no_server_e, NULL); 394 break; 395 } 396 bootp->request.bp_secs 397 = htons((uint16_t)(timer_current_secs() - bootp->start_secs)); 398 bootp->request.bp_xid = htonl(bootp->xid); 399 /* send the packet */ 400 if (bootp_client_transmit(bootp->client, 401 G_ip_broadcast, G_ip_zeroes, 402 G_server_port, G_client_port, 403 &bootp->request, 404 sizeof(bootp->request)) < 0) { 405 my_log(LOG_ERR, 406 "BOOTP %s: transmit failed", if_name(if_p)); 407 } 408 /* wait for responses */ 409 tv.tv_sec = bootp->wait_secs; 410 tv.tv_usec = (suseconds_t)random_range(0, USECS_PER_SEC - 1); 411 my_log(LOG_DEBUG, "BOOTP %s: waiting at %d for %d.%06d", 412 if_name(if_p), 413 timer_current_secs() - bootp->start_secs, 414 tv.tv_sec, tv.tv_usec); 415 timer_set_relative(bootp->timer, tv, 416 (timer_func_t *)bootp_request, 417 service_p, (void *)IFEventID_timeout_e, NULL); 418 /* next time wait twice as long */ 419 bootp->wait_secs = (int)tv.tv_sec * 2; 420 if (bootp->wait_secs > G_max_wait_secs) 421 bootp->wait_secs = G_max_wait_secs; 422 break; 423 424 case IFEventID_data_e: { 425 bootp_receive_data_t *pkt = (bootp_receive_data_t *)event_data; 426 unsigned rating; 427 struct bootp * reply; 428 429 reply = (struct bootp *)pkt->data; 430 if ((ip_valid(reply->bp_yiaddr) == FALSE 431 && ip_valid(reply->bp_ciaddr) == FALSE) 432 || dhcp_packet_match(reply, bootp->xid, 433 (uint8_t) if_link_arptype(if_p), 434 if_link_address(if_p), 435 if_link_length(if_p)) == FALSE) { 436 /* not an interesting packet, drop the packet */ 437 break; /* out of case */ 438 } 439 rating = dhcpol_count_params(&pkt->options, 440 bootp_params, N_BOOTP_PARAMS); 441 if (bootp->saved.pkt_size == 0 442 || rating > bootp->saved.rating) { 443 dhcpol_free(&bootp->saved.options); 444 bcopy(pkt->data, bootp->saved.pkt, pkt->size); 445 bootp->saved.pkt_size = pkt->size; 446 bootp->saved.rating = rating; 447 dhcpol_parse_packet(&bootp->saved.options, 448 (void *)bootp->saved.pkt, 449 bootp->saved.pkt_size, 450 NULL); 451 if (rating == IDEAL_RATING) { 452 bootp_arp_probe(service_p, IFEventID_start_e, NULL); 453 break; 454 } 455 if (bootp->gathering == FALSE) { 456 struct timeval t = {0,0}; 457 t.tv_sec = G_gather_secs; 458 my_log(LOG_DEBUG, "BOOTP %s: gathering began at %d", 459 if_name(if_p), 460 timer_current_secs() - bootp->start_secs); 461 bootp->gathering = TRUE; 462 timer_set_relative(bootp->timer, t, 463 (timer_func_t *)bootp_request, 464 service_p, (void *)IFEventID_timeout_e, 465 NULL); 466 } 467 } 468 break; 469 } 470 default: 471 break; 472 } 473 return; 474} 475 476static void 477bootp_inactive(ServiceRef service_p) 478{ 479 Service_bootp_t * bootp = (Service_bootp_t *)ServiceGetPrivate(service_p); 480 481 S_cancel_pending_events(service_p); 482 service_remove_address(service_p); 483 (void)service_disable_autoaddr(service_p); 484 dhcpol_free(&bootp->saved.options); 485 service_publish_failure(service_p, ipconfig_status_media_inactive_e); 486 return; 487} 488 489ipconfig_status_t 490bootp_thread(ServiceRef service_p, IFEventID_t evid, void * event_data) 491{ 492 Service_bootp_t * bootp = (Service_bootp_t *)ServiceGetPrivate(service_p); 493 interface_t * if_p = service_interface(service_p); 494 ipconfig_status_t status = ipconfig_status_success_e; 495 496 switch (evid) { 497 case IFEventID_start_e: 498 if (bootp != NULL) { 499 my_log(LOG_ERR, "BOOTP %s: re-entering start state", 500 if_name(if_p)); 501 return (ipconfig_status_internal_error_e); 502 } 503 bootp = malloc(sizeof(*bootp)); 504 if (bootp == NULL) { 505 my_log(LOG_ERR, "BOOTP %s: malloc failed", 506 if_name(if_p)); 507 return (ipconfig_status_allocation_failed_e); 508 } 509 ServiceSetPrivate(service_p, bootp); 510 bzero(bootp, sizeof(*bootp)); 511 dhcpol_init(&bootp->saved.options); 512 bootp->xid = arc4random(); 513 bootp->timer = timer_callout_init(); 514 if (bootp->timer == NULL) { 515 my_log(LOG_ERR, "BOOTP %s: timer_callout_init failed", 516 if_name(if_p)); 517 status = ipconfig_status_allocation_failed_e; 518 goto stop; 519 } 520 (void)service_enable_autoaddr(service_p); 521 bootp->client = bootp_client_init(G_bootp_session, if_p); 522 if (bootp->client == NULL) { 523 my_log(LOG_ERR, "BOOTP %s: bootp_client_init failed", 524 if_name(if_p)); 525 status = ipconfig_status_allocation_failed_e; 526 goto stop; 527 } 528 bootp->arp = arp_client_init(G_arp_session, if_p); 529 if (bootp->arp == NULL) { 530 my_log(LOG_ERR, "BOOTP %s: arp_client_init failed", 531 if_name(if_p)); 532 status = ipconfig_status_allocation_failed_e; 533 goto stop; 534 } 535 bootp_request(service_p, IFEventID_start_e, NULL); 536 break; 537 case IFEventID_stop_e: { 538 stop: 539 my_log(LOG_DEBUG, "BOOTP %s: stop", if_name(if_p)); 540 541 if (bootp == NULL) { /* already stopped */ 542 my_log(LOG_DEBUG, "BOOTP %s: already stopped", 543 if_name(if_p)); 544 status = ipconfig_status_internal_error_e; /* shouldn't happen */ 545 break; 546 } 547 548 /* remove IP address */ 549 service_remove_address(service_p); 550 551 /* disable reception of packets */ 552 (void)service_disable_autoaddr(service_p); 553 554 /* clean-up resources */ 555 if (bootp->timer) { 556 timer_callout_free(&bootp->timer); 557 } 558 if (bootp->client) { 559 bootp_client_free(&bootp->client); 560 } 561 if (bootp->arp) { 562 arp_client_free(&bootp->arp); 563 } 564 dhcpol_free(&bootp->saved.options); 565 if (bootp) 566 free(bootp); 567 ServiceSetPrivate(service_p, NULL); 568 break; 569 } 570 case IFEventID_arp_collision_e: { 571 arp_collision_data_t * arpc; 572 char msg[128]; 573 struct bootp * reply; 574 575 arpc = (arp_collision_data_t *)event_data; 576 577 if (bootp == NULL) { 578 status = ipconfig_status_internal_error_e; 579 break; 580 } 581 if (bootp->saved.pkt_size == 0 582 || bootp->saved.our_ip.s_addr != arpc->ip_addr.s_addr 583 || bootp->enable_arp_collision_detection == FALSE) { 584 break; 585 } 586 587 /* defend our address, don't just give it up */ 588 if (ServiceDefendIPv4Address(service_p, arpc)) { 589 break; 590 } 591 592 /* ALIGN: saved.pkt is uint32_t aligned, cast ok */ 593 reply = (struct bootp *)(void *)bootp->saved.pkt; 594 snprintf(msg, sizeof(msg), 595 IP_FORMAT " in use by " 596 EA_FORMAT ", BOOTP Server " IP_FORMAT, 597 IP_LIST(&reply->bp_yiaddr), 598 EA_LIST(arpc->hwaddr), 599 IP_LIST(&reply->bp_siaddr)); 600 if (bootp->user_warned == FALSE) { 601 ServiceReportIPv4AddressConflict(service_p, 602 reply->bp_yiaddr); 603 bootp->user_warned = TRUE; 604 } 605 syslog(LOG_ERR, "BOOTP %s: %s", if_name(if_p), msg); 606 bootp_failed(service_p, ipconfig_status_address_in_use_e, 607 msg); 608 break; 609 } 610 case IFEventID_renew_e: 611 case IFEventID_link_status_changed_e: { 612 link_status_t link_status; 613 void * network_changed = event_data; 614 615 if (bootp == NULL) { 616 status = ipconfig_status_internal_error_e; 617 break; 618 } 619 if (network_changed != NULL) { 620 /* switched networks, remove IP address to avoid IP collisions */ 621 (void)service_remove_address(service_p); 622 } 623 link_status = service_link_status(service_p); 624 if (link_status.valid == TRUE) { 625 if (link_status.active == TRUE) { 626 /* confirm an address, get a new one, or timeout */ 627 bootp->user_warned = FALSE; 628 if (bootp->try != 1) { 629 bootp_request(service_p, IFEventID_start_e, NULL); 630 } 631 } 632 else { 633 /* ensure that we'll retry if the link goes back up */ 634 bootp->try = 0; 635 636 /* disallow collision detection while disconnected */ 637 bootp->enable_arp_collision_detection = FALSE; 638 639 S_cancel_pending_events(service_p); 640 } 641 } 642 break; 643 } 644 case IFEventID_link_timer_expired_e: 645 bootp_inactive(service_p); 646 break; 647 case IFEventID_get_dhcp_info_e: 648 if (ServiceGetActiveIPAddress(service_p).s_addr == 0 649 || bootp->saved.pkt_size == 0) { 650 break; 651 } 652 bootp_set_dhcp_info(bootp, (dhcp_info_t *)event_data); 653 break; 654 default: 655 break; 656 } /* switch */ 657 return (status); 658} 659