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 * dhcpd.c 25 * - DHCP server 26 */ 27 28/* 29 * Modification History 30 * June 17, 1998 Dieter Siegmund (dieter@apple.com) 31 * - initial revision 32 */ 33#include <unistd.h> 34#include <stdlib.h> 35#include <sys/stat.h> 36#include <sys/socket.h> 37#include <sys/ioctl.h> 38#include <sys/file.h> 39#include <sys/time.h> 40#include <errno.h> 41#include <net/if.h> 42#include <netinet/in.h> 43#include <netinet/in_systm.h> 44#include <netinet/ip.h> 45#include <netinet/udp.h> 46#include <netinet/bootp.h> 47#include <netinet/if_ether.h> 48#include <syslog.h> 49#include <arpa/inet.h> 50#include <net/if_arp.h> 51#include <mach/boolean.h> 52#include <notify.h> 53#include "util.h" 54#include "netinfo.h" 55#include "dhcp.h" 56#include "rfc_options.h" 57#include "dhcp_options.h" 58#include "host_identifier.h" 59#include "hostlist.h" 60#include "interfaces.h" 61#include "dhcpd.h" 62#include "NICache.h" 63#include "NICachePrivate.h" 64#include "dhcplib.h" 65#include "bootpd.h" 66#include "subnets.h" 67#include "bootpdfile.h" 68#include "bootplookup.h" 69#include "nbo.h" 70 71 72typedef long dhcp_time_secs_t; 73#define DHCP_INFINITE_TIME ((dhcp_time_secs_t)-1) 74 75#define MAX_RETRY 5 76 77static boolean_t S_extend_leases = TRUE; 78 79typedef struct { 80 PLCache_t list; 81} DHCPLeases_t; 82 83static DHCPLeases_t S_leases; 84 85void 86DHCPLeases_free(DHCPLeases_t * leases) 87{ 88 PLCache_free(&leases->list); 89 bzero(leases, sizeof(*leases)); 90} 91 92#define DHCP_LEASES_FILE "/var/db/dhcpd_leases" 93boolean_t 94DHCPLeases_init(DHCPLeases_t * leases) 95{ 96 bzero(leases, sizeof(*leases)); 97 PLCache_init(&leases->list); 98#define ARBITRARILY_LARGE_NUMBER (100 * 1024 * 1024) 99 PLCache_set_max(&leases->list, ARBITRARILY_LARGE_NUMBER); 100 if (PLCache_read(&leases->list, DHCP_LEASES_FILE) == FALSE) { 101 goto failed; 102 } 103 return (TRUE); 104 failed: 105 DHCPLeases_free(leases); 106 return (FALSE); 107} 108 109static boolean_t S_remove_host(PLCacheEntry_t * * entry); 110 111boolean_t 112DHCPLeases_reclaim(DHCPLeases_t * leases, interface_t * if_p, 113 struct in_addr giaddr, struct timeval * time_in_p, 114 struct in_addr * client_ip) 115{ 116 PLCacheEntry_t * scan; 117 118 for (scan = leases->list.tail; scan; scan = scan->prev) { 119 dhcp_time_secs_t expiry = 0; 120 struct in_addr iaddr; 121 int ip_index; 122 ni_namelist * ip_nl_p; 123 int lease_index; 124 125 /* check the IP address */ 126 ip_index = (int)ni_proplist_match(scan->pl, NIPROP_IPADDR, NULL); 127 if (ip_index == NI_INDEX_NULL) { 128 continue; 129 } 130 ip_nl_p = &scan->pl.nipl_val[ip_index].nip_val; 131 if (ip_nl_p->ninl_len == 0) { 132 continue; 133 } 134 if (inet_aton(ip_nl_p->ninl_val[0], &iaddr) == 0) { 135 continue; 136 } 137 if (!ip_address_reachable(iaddr, giaddr, if_p)) { 138 /* not applicable to this network */ 139 continue; 140 } 141 /* check the lease expiration */ 142 lease_index = (int)ni_proplist_match(scan->pl, NIPROP_DHCP_LEASE, NULL); 143 if (lease_index != NI_INDEX_NULL) { 144 ni_namelist * lease_nl_p; 145 long val; 146 147 lease_nl_p = &scan->pl.nipl_val[lease_index].nip_val; 148 if (lease_nl_p->ninl_len == 0) { 149 continue; 150 } 151 val = strtol(lease_nl_p->ninl_val[0], NULL, 0); 152 if (val == LONG_MAX && errno == ERANGE) { 153 continue; 154 } 155 expiry = (dhcp_time_secs_t)val; 156 } 157 if (lease_index == NI_INDEX_NULL || time_in_p->tv_sec > expiry) { 158 if (S_remove_host(&scan)) { 159 my_log(LOG_DEBUG, "dhcp: reclaimed address %s", 160 inet_ntoa(iaddr)); 161 *client_ip = iaddr; 162 return (TRUE); 163 } 164 } 165 } 166 return (FALSE); 167} 168 169 170int 171dhcp_max_message_size(dhcpol_t * options) 172{ 173 u_char * opt; 174 int opt_len; 175 int val = DHCP_PACKET_MIN; 176 177 opt = dhcpol_find(options, dhcptag_max_dhcp_message_size_e, 178 &opt_len, NULL); 179 if (opt != NULL && opt_len == 2) { 180 u_int16_t sval = net_uint16_get(opt); 181 if (sval > DHCP_PACKET_MIN) { 182 val = sval; 183 } 184 } 185 return (val); 186} 187 188void 189dhcp_init() 190{ 191 static boolean_t first = TRUE; 192 193 if (first == TRUE) { 194 if (DHCPLeases_init(&S_leases) == FALSE) { 195 return; 196 } 197 first = FALSE; 198 } 199 else { 200 DHCPLeases_t new_leases; 201 202 my_log(LOG_INFO, "dhcp: re-reading lease list"); 203 if (DHCPLeases_init(&new_leases) == TRUE) { 204 DHCPLeases_free(&S_leases); 205 S_leases = new_leases; 206 } 207 } 208 return; 209} 210 211boolean_t 212DHCPLeases_ip_in_use(DHCPLeases_t * leases, struct in_addr ip) 213{ 214 PLCacheEntry_t * entry = PLCache_lookup_ip(&leases->list, ip); 215 return (entry != NULL); 216} 217 218static void 219S_generate_lease_change_notification(void) 220{ 221 notify_post(DHCPD_LEASES_NOTIFICATION_KEY); 222 return; 223} 224 225static boolean_t 226S_remove_host(PLCacheEntry_t * * entry) 227{ 228 PLCacheEntry_t * ent = *entry; 229 230 PLCache_remove(&S_leases.list, ent); 231 PLCacheEntry_free(ent); 232 *entry = NULL; 233 PLCache_write(&S_leases.list, DHCP_LEASES_FILE); 234 S_generate_lease_change_notification(); 235 return (TRUE); 236} 237 238static __inline__ boolean_t 239S_commit_mods() 240{ 241 return (PLCache_write(&S_leases.list, DHCP_LEASES_FILE)); 242} 243 244static __inline__ char * 245S_lease_propval(ni_proplist * pl_p) 246{ 247 return (ni_valforprop(pl_p, NIPROP_DHCP_LEASE)); 248} 249 250#define LEASE_FORMAT "0x%lx" 251 252static void 253S_set_lease(ni_proplist * pl_p, dhcp_time_secs_t lease_time_expiry, 254 boolean_t * mod) 255{ 256 char buf[32]; 257 258 snprintf(buf, sizeof(buf), LEASE_FORMAT, lease_time_expiry); 259 ni_set_prop(pl_p, NIPROP_DHCP_LEASE, buf, mod); 260 return; 261} 262 263static dhcp_time_secs_t 264S_lease_time_expiry(ni_proplist * pl_p) 265{ 266 dhcp_time_secs_t expiry = DHCP_INFINITE_TIME; 267 ni_name str = S_lease_propval(pl_p); 268 long val; 269 270 if (str) { 271 val = strtol(str, NULL, 0); 272 if (val == LONG_MAX && errno == ERANGE) { 273 my_log(LOG_INFO, "S_lease_time_expiry: lease '%s' bad", str); 274 return (0); 275 } 276 expiry = (dhcp_time_secs_t)val; 277 } 278 return (expiry); 279 280} 281 282struct dhcp * 283make_dhcp_reply(struct dhcp * reply, int pkt_size, 284 struct in_addr server_id, dhcp_msgtype_t msg, 285 struct dhcp * request, dhcpoa_t * options) 286{ 287 *reply = *request; 288 reply->dp_hops = 0; 289 reply->dp_secs = 0; 290 reply->dp_op = BOOTREPLY; 291 bcopy(rfc_magic, reply->dp_options, sizeof(rfc_magic)); 292 dhcpoa_init(options, reply->dp_options + sizeof(rfc_magic), 293 pkt_size - sizeof(struct dhcp) - sizeof(rfc_magic)); 294 /* make the reply a dhcp message */ 295 if (dhcpoa_add_dhcpmsg(options, msg) != dhcpoa_success_e) { 296 my_log(LOG_INFO, 297 "make_dhcp_reply: couldn't add dhcp message tag %d: %s", msg, 298 dhcpoa_err(options)); 299 goto err; 300 } 301 /* add our server identifier */ 302 if (dhcpoa_add(options, dhcptag_server_identifier_e, 303 sizeof(server_id), &server_id) != dhcpoa_success_e) { 304 my_log(LOG_INFO, 305 "make_dhcp_reply: couldn't add server identifier tag: %s", 306 dhcpoa_err(options)); 307 goto err; 308 } 309 return (reply); 310 err: 311 return (NULL); 312} 313 314static struct dhcp * 315make_dhcp_nak(struct dhcp * reply, int pkt_size, 316 struct in_addr server_id, dhcp_msgtype_t * msg_p, 317 const char * nak_msg, struct dhcp * request, 318 dhcpoa_t * options) 319{ 320 struct dhcp * r; 321 322 if (debug) 323 printf("sending a NAK: '%s'\n", nak_msg); 324 325 r = make_dhcp_reply(reply, pkt_size, server_id, dhcp_msgtype_nak_e, 326 request, options); 327 if (r == NULL) 328 return (NULL); 329 330 r->dp_ciaddr.s_addr = 0; 331 r->dp_yiaddr.s_addr = 0; 332 333 if (nak_msg) { 334 if (dhcpoa_add(options, dhcptag_message_e, (int)strlen(nak_msg), 335 nak_msg) != dhcpoa_success_e) { 336 my_log(LOG_INFO, "dhcpd: couldn't add NAK message type: %s", 337 dhcpoa_err(options)); 338 goto err; 339 } 340 } 341 if (dhcpoa_add(options, dhcptag_end_e, 0, 0) != dhcpoa_success_e) { 342 my_log(LOG_INFO, "dhcpd: couldn't add end tag: %s", 343 dhcpoa_err(options)); 344 goto err; 345 } 346 *msg_p = dhcp_msgtype_nak_e; 347 return (r); 348 err: 349 return (NULL); 350} 351 352static struct hosts * S_pending_hosts = NULL; 353 354#define DEFAULT_PENDING_SECS 60 355 356static bool 357S_ipinuse(void * arg, struct in_addr ip) 358{ 359 struct hosts * hp; 360 struct timeval * time_in_p = (struct timeval *)arg; 361 362 if (bootp_getbyip_file(ip, NULL, NULL) 363#if !TARGET_OS_EMBEDDED 364 || ((use_open_directory == TRUE) 365 && bootp_getbyip_ds(ip, NULL, NULL)) 366#endif /* !TARGET_OS_EMBEDDED */ 367 ) { 368 return (TRUE); 369 } 370 371 if (DHCPLeases_ip_in_use(&S_leases, ip) == TRUE) { 372 return (TRUE); 373 } 374 hp = hostbyip(S_pending_hosts, ip); 375 if (hp) { 376 u_long pending_secs = time_in_p->tv_sec - hp->tv.tv_sec; 377 378 if (pending_secs < DEFAULT_PENDING_SECS) { 379 my_log(LOG_DEBUG, "dhcpd: %s will remain pending %d secs", 380 inet_ntoa(ip), DEFAULT_PENDING_SECS - pending_secs); 381 return (TRUE); 382 } 383 hostfree(&S_pending_hosts, hp); /* remove it from the list */ 384 return (FALSE); 385 } 386 387 return (FALSE); 388} 389 390#define DHCPD_CREATOR "dhcpd" 391 392static char * 393S_get_hostname(void * hostname_opt, int hostname_opt_len) 394{ 395 396 if (hostname_opt && hostname_opt_len > 0) { 397 int i; 398 char * h = (char *)hostname_opt; 399 char * hostname = malloc(hostname_opt_len + 1); 400 401 for (i = 0; i < hostname_opt_len; i++) { 402 char ch = h[i]; 403 if (ch == 0 || ch == '\n') { 404 ch = '.'; 405 } 406 hostname[i] = ch; 407 } 408 hostname[hostname_opt_len] = '\0'; 409 return (hostname); 410 } 411 return (NULL); 412} 413 414static boolean_t 415S_create_host(char * idstr, char * hwstr, 416 struct in_addr iaddr, void * hostname_opt, int hostname_opt_len, 417 dhcp_time_secs_t lease_time_expiry) 418{ 419 char lease_str[128]; 420 ni_proplist pl; 421 422 423 /* add DHCP-specific properties */ 424 NI_INIT(&pl); 425 if (hostname_opt) { 426 char * h; 427 h = S_get_hostname(hostname_opt, hostname_opt_len); 428 429 ni_proplist_addprop(&pl, NIPROP_NAME, (ni_name)h); 430 free(h); 431 } 432 ni_proplist_addprop(&pl, NIPROP_IPADDR, 433 (ni_name) inet_ntoa(iaddr)); 434 ni_proplist_addprop(&pl, NIPROP_HWADDR, 435 (ni_name) hwstr); 436 ni_proplist_addprop(&pl, NIPROP_IDENTIFIER, 437 (ni_name) idstr); 438 snprintf(lease_str, sizeof(lease_str), LEASE_FORMAT, lease_time_expiry); 439 ni_proplist_addprop(&pl, NIPROP_DHCP_LEASE, (ni_name)lease_str); 440 441 PLCache_add(&S_leases.list, PLCacheEntry_create(pl)); 442 PLCache_write(&S_leases.list, DHCP_LEASES_FILE); 443 ni_proplist_free(&pl); 444 S_generate_lease_change_notification(); 445 return (TRUE); 446} 447 448typedef enum { 449 dhcp_binding_none_e = 0, 450 dhcp_binding_permanent_e, 451 dhcp_binding_temporary_e, 452} dhcp_binding_t; 453 454static SubnetRef 455acquire_ip(struct in_addr giaddr, interface_t * if_p, 456 struct timeval * time_in_p, struct in_addr * iaddr_p) 457{ 458 SubnetRef subnet = NULL; 459 460 if (subnets == NULL) { 461 return (NULL); 462 } 463 if (giaddr.s_addr) { 464 *iaddr_p = giaddr; 465 subnet = SubnetListAcquireAddress(subnets, iaddr_p, S_ipinuse, 466 time_in_p); 467 } 468 else { 469 int i; 470 inet_addrinfo_t * info; 471 472 for (i = 0; i < if_inet_count(if_p); i++) { 473 info = if_inet_addr_at(if_p, i); 474 *iaddr_p = info->netaddr; 475 subnet = SubnetListAcquireAddress(subnets, iaddr_p, S_ipinuse, 476 time_in_p); 477 if (subnet != NULL) { 478 break; 479 } 480 } 481 } 482 return (subnet); 483} 484 485boolean_t 486dhcp_bootp_allocate(char * idstr, char * hwstr, struct dhcp * rq, 487 interface_t * if_p, struct timeval * time_in_p, 488 struct in_addr * iaddr_p, SubnetRef * subnet_p) 489{ 490 PLCacheEntry_t * entry = NULL; 491 struct in_addr iaddr; 492 dhcp_time_secs_t lease_time_expiry = 0; 493 subnet_match_args_t match; 494 dhcp_lease_time_t max_lease; 495 boolean_t modified = FALSE; 496 SubnetRef subnet = NULL; 497 498 bzero(&match, sizeof(match)); 499 match.if_p = if_p; 500 match.giaddr = rq->dp_giaddr; 501 match.has_binding = FALSE; 502 503 if (bootp_getbyhw_file(rq->dp_htype, rq->dp_chaddr, rq->dp_hlen, 504 subnet_match, &match, &iaddr, NULL, NULL) 505#if !TARGET_OS_EMBEDDED 506 || ((use_open_directory == TRUE) 507 && bootp_getbyhw_ds(rq->dp_htype, rq->dp_chaddr, rq->dp_hlen, 508 subnet_match, &match, &iaddr, NULL, NULL)) 509#endif /* !TARGET_OS_EMBEDDED */ 510 ) { 511 512 /* infinite lease */ 513 *iaddr_p = iaddr; 514 if (subnets != NULL) { 515 /* try exact */ 516 subnet = SubnetListGetSubnetForAddress(subnets, iaddr, TRUE); 517 if (subnet == NULL) { 518 /* try any */ 519 subnet = SubnetListGetSubnetForAddress(subnets, iaddr, FALSE); 520 } 521 } 522 *subnet_p = subnet; 523 return (TRUE); 524 } 525 526 match.has_binding = FALSE; 527 entry = PLCache_lookup_identifier(&S_leases.list, idstr, 528 subnet_match, &match, &iaddr, 529 NULL); 530 if (entry) { 531 if (subnets != NULL) { 532 subnet = SubnetListGetSubnetForAddress(subnets, iaddr, TRUE); 533 } 534 if (subnet != NULL) { 535 max_lease = SubnetGetMaxLease(subnet); 536 lease_time_expiry = max_lease + time_in_p->tv_sec; 537 S_set_lease(&entry->pl, lease_time_expiry, &modified); 538 539 PLCache_make_head(&S_leases.list, entry); 540 *iaddr_p = iaddr; 541 *subnet_p = subnet; 542 if (modified && S_commit_mods() == FALSE) { 543 return (FALSE); 544 } 545 return (TRUE); 546 } 547 /* remove the old binding, it's not valid */ 548 PLCache_remove(&S_leases.list, entry); 549 modified = TRUE; 550 entry = NULL; 551 } 552 553 subnet = acquire_ip(rq->dp_giaddr, if_p, time_in_p, &iaddr); 554 if (subnet == NULL) { 555 if (DHCPLeases_reclaim(&S_leases, if_p, rq->dp_giaddr, 556 time_in_p, &iaddr)) { 557 subnet = SubnetListGetSubnetForAddress(subnets, iaddr, TRUE); 558 } 559 if (subnet == NULL) { 560 if (debug) { 561 printf("no ip addresses\n"); 562 } 563 if (modified) { 564 S_commit_mods(); 565 } 566 return (FALSE); 567 } 568 } 569 *subnet_p = subnet; 570 *iaddr_p = iaddr; 571 max_lease = SubnetGetMaxLease(subnet); 572 lease_time_expiry = max_lease + time_in_p->tv_sec; 573 if (S_create_host(idstr, hwstr, 574 iaddr, NULL, 0, lease_time_expiry) == FALSE) { 575 return (FALSE); 576 } 577 return (TRUE); 578} 579 580void 581dhcp_request(request_t * request, dhcp_msgtype_t msgtype, 582 boolean_t dhcp_allocate) 583{ 584 dhcp_binding_t binding = dhcp_binding_none_e; 585 char cid_type; 586 int cid_len; 587 void * cid; 588 PLCacheEntry_t * entry = NULL; 589 boolean_t has_binding = FALSE; 590 void * hostname_opt = NULL; 591 int hostname_opt_len = 0; 592 char * hostname = NULL; 593 char * hwstr = NULL; 594 char * idstr = NULL; 595 struct in_addr iaddr; 596 dhcp_lease_time_t lease = 0; 597 dhcp_time_secs_t lease_time_expiry = 0; 598 int len; 599 int max_packet; 600 dhcp_lease_time_t min_lease; 601 dhcp_lease_time_t max_lease; 602 boolean_t modified = FALSE; 603 dhcpoa_t options; 604 boolean_t orphan = FALSE; 605 struct dhcp * reply = NULL; 606 dhcp_msgtype_t reply_msgtype = dhcp_msgtype_none_e; 607 struct dhcp * rq = request->pkt; 608 char scratch_idstr[128]; 609 char scratch_hwstr[sizeof(rq->dp_chaddr) * 3]; 610 SubnetRef subnet = NULL; 611 dhcp_lease_time_t * suggested_lease = NULL; 612 dhcp_cstate_t state = dhcp_cstate_none_e; 613 uint32_t txbuf[ETHERMTU / sizeof(uint32_t)]; 614 boolean_t use_broadcast = FALSE; 615 616 iaddr.s_addr = 0; 617 max_packet = dhcp_max_message_size(request->options_p); 618 if (max_packet > sizeof(txbuf)) { 619 max_packet = sizeof(txbuf); 620 } 621 /* need to exclude the IP/UDP header from what we send back */ 622 max_packet -= DHCP_PACKET_OVERHEAD; 623 624 /* check for a client identifier */ 625 cid = dhcpol_find(request->options_p, dhcptag_client_identifier_e, 626 &cid_len, NULL); 627 if (cid != NULL) { 628 if (cid_len > 1) { 629 /* use the client identifier as provided */ 630 cid_type = *((char *)cid); 631 cid_len--; 632 cid++; 633 } 634 else { 635 cid = NULL; 636 } 637 } 638 if (cid == NULL 639 || (dhcp_ignore_client_identifier && rq->dp_hlen != 0)) { 640 /* use the hardware address as the identifier */ 641 cid = rq->dp_chaddr; 642 cid_type = rq->dp_htype; 643 cid_len = rq->dp_hlen; 644 } 645 if (cid_len == 0) { 646 goto no_reply; 647 } 648 idstr = identifierToStringWithBuffer(cid_type, cid, cid_len, 649 scratch_idstr, sizeof(scratch_idstr)); 650 if (idstr == NULL) { 651 goto no_reply; 652 } 653 if (cid_type == 0) { 654 hwstr = identifierToStringWithBuffer(rq->dp_htype, rq->dp_chaddr, 655 rq->dp_hlen, scratch_hwstr, 656 sizeof(scratch_hwstr)); 657 if (hwstr == NULL) { 658 goto no_reply; 659 } 660 } 661 else { 662 hwstr = idstr; 663 } 664 665 hostname_opt = dhcpol_find(request->options_p, dhcptag_host_name_e, 666 &hostname_opt_len, NULL); 667 if (hostname_opt && hostname_opt_len) { 668 my_log(LOG_INFO, "DHCP %s [%s]: %s <%.*s>", 669 dhcp_msgtype_names(msgtype), if_name(request->if_p), idstr, 670 hostname_opt_len, hostname_opt); 671 } 672 else { 673 my_log(LOG_INFO, "DHCP %s [%s]: %s", 674 dhcp_msgtype_names(msgtype), if_name(request->if_p), idstr); 675 } 676 677 suggested_lease = 678 (dhcp_lease_time_t *)dhcpol_find(request->options_p, 679 dhcptag_lease_time_e, 680 &len, NULL); 681 if (cid_type != 0) { 682 subnet_match_args_t match; 683 684 bzero(&match, sizeof(match)); 685 match.if_p = request->if_p; 686 match.giaddr = rq->dp_giaddr; 687 match.ciaddr = rq->dp_ciaddr; 688 match.has_binding = FALSE; 689 690 if (bootp_getbyhw_file(cid_type, cid, cid_len, 691 subnet_match, &match, &iaddr, 692 &hostname, NULL) 693#if !TARGET_OS_EMBEDDED 694 || ((use_open_directory == TRUE) 695 && bootp_getbyhw_ds(cid_type, cid, cid_len, 696 subnet_match, &match, &iaddr, 697 &hostname, NULL)) 698#endif /* !TARGET_OS_EMBEDDED */ 699 ) { 700 binding = dhcp_binding_permanent_e; 701 lease_time_expiry = DHCP_INFINITE_TIME; 702 } 703 if (match.has_binding == TRUE) { 704 has_binding = TRUE; 705 } 706 } 707 708 if (binding == dhcp_binding_none_e) { 709 boolean_t some_binding = FALSE; 710 subnet_match_args_t match; 711 712 bzero(&match, sizeof(match)); 713 match.if_p = request->if_p; 714 match.giaddr = rq->dp_giaddr; 715 match.ciaddr = rq->dp_ciaddr; 716 717 /* no permanent netinfo binding: check for a lease */ 718 entry = PLCache_lookup_identifier(&S_leases.list, idstr, 719 subnet_match, &match, &iaddr, 720 &some_binding); 721 if (some_binding == TRUE) { 722 has_binding = TRUE; 723 } 724 if (entry != NULL) { 725 if (subnets != NULL) { 726 subnet = SubnetListGetSubnetForAddress(subnets, iaddr, TRUE); 727 } 728 if (subnet == NULL || SubnetDoesAllocate(subnet) == FALSE) { 729 S_remove_host(&entry); 730 my_log(LOG_INFO, "dhcpd: removing %s binding for %s", 731 idstr, inet_ntoa(iaddr)); 732 orphan = TRUE; 733 entry = NULL; 734 } 735 else { 736 binding = dhcp_binding_temporary_e; 737 lease_time_expiry = S_lease_time_expiry(&entry->pl); 738 PLCache_make_head(&S_leases.list, entry); 739 } 740 } 741 } 742 if (binding != dhcp_binding_none_e) { 743 /* client is already bound on this subnet */ 744 if (lease_time_expiry == DHCP_INFINITE_TIME) { 745 /* permanent entry */ 746 lease = DHCP_INFINITE_LEASE; 747 } 748 else { 749 max_lease = SubnetGetMaxLease(subnet); 750 min_lease = SubnetGetMinLease(subnet); 751 if (suggested_lease) { 752 lease = dhcp_lease_ntoh(*suggested_lease); 753 if (lease > max_lease) 754 lease = max_lease; 755 else if (lease < min_lease) 756 lease = min_lease; 757 } 758 else if ((request->time_in_p->tv_sec + min_lease) 759 >= lease_time_expiry) { 760 /* expired lease: give it the default lease */ 761 lease = min_lease; 762 } 763 else { /* give the host the remaining time on the lease */ 764 lease = (dhcp_lease_time_t) 765 (lease_time_expiry - request->time_in_p->tv_sec); 766 } 767 } 768 } 769 770 switch (msgtype) { 771 case dhcp_msgtype_discover_e: { 772 state = dhcp_cstate_init_e; 773 774 { /* delete the pending host entry */ 775 struct hosts * hp; 776 hp = hostbyaddr(S_pending_hosts, cid_type, cid, cid_len, 777 NULL, NULL); 778 if (hp) 779 hostfree(&S_pending_hosts, hp); 780 } 781 782 if (binding != dhcp_binding_none_e) { 783 /* client is already bound on this subnet */ 784 } 785 else if (dhcp_allocate == FALSE) { 786 /* NetBoot 1.0 enabled, but DHCP is not */ 787 goto no_reply; 788 } 789 else { /* find an ip address */ 790 /* allocate a new ip address */ 791 subnet = acquire_ip(rq->dp_giaddr, 792 request->if_p, request->time_in_p, &iaddr); 793 if (subnet == NULL) { 794 if (DHCPLeases_reclaim(&S_leases, request->if_p, 795 rq->dp_giaddr, 796 request->time_in_p, &iaddr)) { 797 if (subnets != NULL) { 798 subnet = SubnetListGetSubnetForAddress(subnets, iaddr, 799 TRUE); 800 } 801 } 802 if (subnet == NULL) { 803 if (debug) { 804 printf("no ip addresses\n"); 805 } 806 goto no_reply; /* out of ip addresses */ 807 } 808 } 809 max_lease = SubnetGetMaxLease(subnet); 810 min_lease = SubnetGetMinLease(subnet); 811 if (suggested_lease) { 812 lease = dhcp_lease_ntoh(*suggested_lease); 813 if (lease > max_lease) 814 lease = max_lease; 815 else if (lease < min_lease) 816 lease = min_lease; 817 } 818 else { 819 lease = min_lease; 820 } 821 } 822 { /* keep track of this offer in the pending hosts list */ 823 struct hosts * hp; 824 825 hp = hostadd(&S_pending_hosts, request->time_in_p, 826 cid_type, cid, cid_len, 827 &iaddr, NULL, NULL); 828 if (hp == NULL) 829 goto no_reply; 830 hp->lease = lease; 831 } 832 /* 833 * allow for drift between server/client clocks by offering 834 * a lease shorter than the recorded value 835 */ 836 if (lease == DHCP_INFINITE_LEASE) 837 lease = dhcp_lease_hton(lease); 838 else 839 lease = dhcp_lease_hton(lease_prorate(lease)); 840 841 /* form a reply */ 842 reply = make_dhcp_reply((struct dhcp *)txbuf, max_packet, 843 if_inet_addr(request->if_p), 844 reply_msgtype = dhcp_msgtype_offer_e, 845 rq, &options); 846 if (reply == NULL) 847 goto no_reply; 848 reply->dp_ciaddr.s_addr = 0; 849 reply->dp_yiaddr = iaddr; 850 if (dhcpoa_add(&options, dhcptag_lease_time_e, sizeof(lease), 851 &lease) != dhcpoa_success_e) { 852 my_log(LOG_INFO, "dhcpd: couldn't add lease time tag: %s", 853 dhcpoa_err(&options)); 854 goto no_reply; 855 } 856 break; 857 } 858 case dhcp_msgtype_request_e: { 859 const char * nak = NULL; 860 int optlen; 861 struct in_addr * req_ip; 862 struct in_addr * server_id; 863 864 server_id = (struct in_addr *) 865 dhcpol_find(request->options_p, dhcptag_server_identifier_e, 866 &optlen, NULL); 867 req_ip = (struct in_addr *) 868 dhcpol_find(request->options_p, dhcptag_requested_ip_address_e, 869 &optlen, NULL); 870 if (server_id) { /* SELECT */ 871 struct hosts * hp = hostbyaddr(S_pending_hosts, cid_type, 872 cid, cid_len, 873 NULL, FALSE); 874 if (debug) 875 printf("SELECT\n"); 876 state = dhcp_cstate_select_e; 877 878 if (server_id->s_addr != if_inet_addr(request->if_p).s_addr) { 879 if (debug) 880 printf("client selected %s\n", inet_ntoa(*server_id)); 881 /* clean up */ 882 if (hp) { 883 hostfree(&S_pending_hosts, hp); 884 } 885 886 if (binding == dhcp_binding_temporary_e) { 887 S_remove_host(&entry); 888 } 889 if (detect_other_dhcp_server(request->if_p)) { 890 my_log(LOG_INFO, 891 "dhcpd: detected another DHCP server %s," 892 " disabling DHCP on %s", 893 inet_ntoa(*server_id), 894 if_name(request->if_p)); 895 disable_dhcp_on_interface(request->if_p); 896 } 897 goto no_reply; 898 } 899 if (binding == dhcp_binding_none_e && hp == NULL) { 900 goto no_reply; 901 } 902 903 if (hp) { 904 iaddr = hp->iaddr; 905 if (hp->lease == DHCP_INFINITE_LEASE) 906 lease_time_expiry = DHCP_INFINITE_LEASE; 907 else { 908 lease_time_expiry 909 = hp->lease + request->time_in_p->tv_sec; 910 } 911 lease = (dhcp_lease_time_t)hp->lease; 912 } 913 else { 914 /* this case only happens if the client sends 915 * a REQUEST without sending a DISCOVER first 916 * but we have a binding 917 */ 918 919 /* iaddr, lease_time_expiry, lease 920 * are all set above 921 */ 922 } 923 if (req_ip == NULL 924 || req_ip->s_addr != iaddr.s_addr) { 925 if (req_ip == NULL) { 926 my_log(LOG_INFO, 927 "dhcpd: host %s sends SELECT without" 928 " Requested IP option", idstr); 929 } 930 else { 931 my_log(LOG_INFO, 932 "dhcpd: host %s sends SELECT with wrong" 933 " IP address %s, should be " IP_FORMAT, 934 idstr, inet_ntoa(*req_ip), IP_LIST(&iaddr)); 935 } 936 use_broadcast = TRUE; 937 reply = make_dhcp_nak((struct dhcp *)txbuf, max_packet, 938 if_inet_addr(request->if_p), 939 &reply_msgtype, 940 "protocol error in SELECT state", 941 rq, &options); 942 if (reply) 943 goto reply; 944 goto no_reply; 945 } 946 if (binding != dhcp_binding_none_e) { 947 if (binding == dhcp_binding_temporary_e) { 948 if (hostname_opt && hostname_opt_len > 0) { 949 char * h; 950 951 h = S_get_hostname(hostname_opt, 952 hostname_opt_len); 953 ni_set_prop(&entry->pl, NIPROP_NAME, h, &modified); 954 free(h); 955 } 956 S_set_lease(&entry->pl, lease_time_expiry, &modified); 957 } 958 } 959 else { /* create a new host entry */ 960 if (subnets != NULL) { 961 subnet = SubnetListGetSubnetForAddress(subnets, iaddr, 962 TRUE); 963 } 964 if (subnet == NULL 965 || S_create_host(idstr, hwstr, iaddr, 966 hostname_opt, hostname_opt_len, 967 lease_time_expiry) == FALSE) { 968 reply = make_dhcp_nak((struct dhcp *)txbuf, 969 max_packet, 970 if_inet_addr(request->if_p), 971 &reply_msgtype, 972 "unexpected server failure", 973 rq, &options); 974 if (reply) 975 goto reply; 976 goto no_reply; 977 } 978 } 979 } /* select */ 980 else /* init-reboot/renew/rebind */ { 981 if (req_ip) { /* init-reboot */ 982 if (debug) 983 printf("init-reboot\n"); 984 state = dhcp_cstate_init_reboot_e; 985 if (binding == dhcp_binding_none_e) { 986 if (orphan == FALSE) { 987 my_log(LOG_DEBUG, "dhcpd: INIT-REBOOT host " 988 "%s binding for %s with another server", 989 idstr, inet_ntoa(*req_ip)); 990 goto no_reply; 991 } 992 nak = "requested address no longer available"; 993 use_broadcast = TRUE; 994 goto send_nak; 995 } 996 if (req_ip->s_addr != iaddr.s_addr) { 997 nak = "requested address incorrect"; 998 use_broadcast = TRUE; 999 goto send_nak; 1000 } 1001 } /* init-reboot */ 1002 else if (rq->dp_ciaddr.s_addr) { /* renew/rebind */ 1003 if (debug) { 1004 if (request->dstaddr_p == NULL 1005 || ntohl(request->dstaddr_p->s_addr) 1006 == INADDR_BROADCAST) 1007 printf("rebind\n"); 1008 else 1009 printf("renew\n"); 1010 } 1011 if (binding == dhcp_binding_none_e) { 1012 if (orphan == FALSE) { 1013 if (debug) { 1014 if (has_binding) 1015 printf("Client binding is not applicable\n"); 1016 else 1017 printf("No binding for client\n"); 1018 } 1019 goto no_reply; 1020 } 1021 nak = "requested address no longer available"; 1022 use_broadcast = TRUE; 1023 goto send_nak; 1024 } 1025 if (request->dstaddr_p == NULL 1026 || ntohl(request->dstaddr_p->s_addr) == INADDR_BROADCAST 1027 || rq->dp_giaddr.s_addr) { /* REBIND */ 1028 state = dhcp_cstate_rebind_e; 1029 if (rq->dp_ciaddr.s_addr != iaddr.s_addr) { 1030 if (debug) 1031 printf("Incorrect ciaddr " IP_FORMAT 1032 " should be " IP_FORMAT "\n", 1033 IP_LIST(&rq->dp_ciaddr), 1034 IP_LIST(&iaddr)); 1035 goto no_reply; 1036 } 1037 } 1038 else { /* RENEW */ 1039 state = dhcp_cstate_renew_e; 1040 if (rq->dp_ciaddr.s_addr != iaddr.s_addr) { 1041 my_log(LOG_INFO, 1042 "dhcpd: client ciaddr=%s should use " 1043 IP_FORMAT, inet_ntoa(rq->dp_ciaddr), 1044 IP_LIST(&iaddr)); 1045 iaddr = rq->dp_ciaddr; /* trust it anyways */ 1046 } 1047 } 1048 } /* renew/rebind */ 1049 else { 1050 my_log(LOG_DEBUG, 1051 "dhcpd: host %s in unknown state", idstr); 1052 goto no_reply; 1053 } 1054 1055 if (binding == dhcp_binding_permanent_e) { 1056 lease = DHCP_INFINITE_LEASE; 1057 } 1058 else { 1059 if (hostname_opt && hostname_opt_len > 0) { 1060 char * h; 1061 h = S_get_hostname(hostname_opt, hostname_opt_len); 1062 ni_set_prop(&entry->pl, NIPROP_NAME, h, &modified); 1063 free(h); 1064 } 1065 max_lease = SubnetGetMaxLease(subnet); 1066 min_lease = SubnetGetMaxLease(subnet); 1067 if (suggested_lease) { 1068 lease = dhcp_lease_ntoh(*suggested_lease); 1069 if (lease > max_lease) 1070 lease = max_lease; 1071 else if (lease < min_lease) 1072 lease = min_lease; 1073 } 1074 else if (S_extend_leases) { 1075 /* automatically extend the lease */ 1076 lease = min_lease; 1077 my_log(LOG_DEBUG, 1078 "dhcpd: %s lease extended to %s client", 1079 inet_ntoa(iaddr), dhcp_cstate_str(state)); 1080 } 1081 else { 1082 if (request->time_in_p->tv_sec >= lease_time_expiry) { 1083 /* send a nak */ 1084 nak = "lease expired"; 1085 goto send_nak; 1086 } 1087 /* give the host the remaining time on the lease */ 1088 lease = (dhcp_lease_time_t) 1089 (lease_time_expiry - request->time_in_p->tv_sec); 1090 } 1091 if (lease == DHCP_INFINITE_LEASE) { 1092 lease_time_expiry = DHCP_INFINITE_TIME; 1093 } 1094 else { 1095 lease_time_expiry = lease + request->time_in_p->tv_sec; 1096 } 1097 S_set_lease(&entry->pl, lease_time_expiry, &modified); 1098 } 1099 } /* init-reboot/renew/rebind */ 1100 send_nak: 1101 if (nak) { 1102 reply = make_dhcp_nak((struct dhcp *)txbuf, max_packet, 1103 if_inet_addr(request->if_p), 1104 &reply_msgtype, nak, 1105 rq, &options); 1106 if (reply) 1107 goto reply; 1108 goto no_reply; 1109 } 1110 /* 1111 * allow for drift between server/client clocks by offering 1112 * a lease shorter than the recorded value 1113 */ 1114 if (lease == DHCP_INFINITE_LEASE) 1115 lease = dhcp_lease_hton(lease); 1116 else 1117 lease = dhcp_lease_hton(lease_prorate(lease)); 1118 1119 reply = make_dhcp_reply((struct dhcp *)txbuf, max_packet, 1120 if_inet_addr(request->if_p), 1121 reply_msgtype = dhcp_msgtype_ack_e, 1122 rq, &options); 1123 reply->dp_yiaddr = iaddr; 1124 if (dhcpoa_add(&options, dhcptag_lease_time_e, 1125 sizeof(lease), &lease) != dhcpoa_success_e) { 1126 my_log(LOG_INFO, "dhcpd: couldn't add lease time tag: %s", 1127 dhcpoa_err(&options)); 1128 goto no_reply; 1129 } 1130 break; 1131 } 1132 case dhcp_msgtype_decline_e: { 1133 int optlen; 1134 struct in_addr * req_ip; 1135 struct in_addr * server_id; 1136 1137 server_id = (struct in_addr *) 1138 dhcpol_find(request->options_p, dhcptag_server_identifier_e, 1139 &optlen, NULL); 1140 req_ip = (struct in_addr *) 1141 dhcpol_find(request->options_p, dhcptag_requested_ip_address_e, 1142 &optlen, NULL); 1143 if (server_id == NULL || req_ip == NULL) { 1144 goto no_reply; 1145 } 1146 if (server_id->s_addr != if_inet_addr(request->if_p).s_addr) { 1147 my_log(LOG_DEBUG, "dhcpd: host %s " 1148 "declines IP %s from server " IP_FORMAT, 1149 idstr, inet_ntoa(*req_ip), IP_LIST(server_id)); 1150 goto no_reply; 1151 } 1152 1153 if (binding == dhcp_binding_temporary_e 1154 && iaddr.s_addr == req_ip->s_addr) { 1155 ni_delete_prop(&entry->pl, NIPROP_IDENTIFIER, &modified); 1156 S_set_lease(&entry->pl, 1157 request->time_in_p->tv_sec + DHCP_DECLINE_WAIT_SECS, 1158 &modified); 1159 ni_set_prop(&entry->pl, NIPROP_DHCP_DECLINED, 1160 idstr, &modified); 1161 my_log(LOG_INFO, "dhcpd: IP %s declined by %s", 1162 inet_ntoa(iaddr), idstr); 1163 if (debug) { 1164 printf("marking host %s as declined\n", inet_ntoa(iaddr)); 1165 } 1166 } 1167 break; 1168 } 1169 case dhcp_msgtype_release_e: { 1170 if (binding == dhcp_binding_temporary_e) { 1171 if (debug) { 1172 printf("%s released by client, setting expiration to now\n", 1173 inet_ntoa(iaddr)); 1174 } 1175 /* set the lease expiration time to now */ 1176 S_set_lease(&entry->pl, request->time_in_p->tv_sec, &modified); 1177 } 1178 break; 1179 } 1180 case dhcp_msgtype_inform_e: { 1181 iaddr = rq->dp_ciaddr; 1182 reply = make_dhcp_reply((struct dhcp *)txbuf, max_packet, 1183 if_inet_addr(request->if_p), 1184 reply_msgtype = dhcp_msgtype_ack_e, 1185 rq, &options); 1186 if (reply) 1187 goto reply; 1188 goto no_reply; 1189 } 1190 default: { 1191 if (debug) { 1192 printf("unknown message ignored\n"); 1193 } 1194 break; 1195 } 1196 } 1197 1198 reply: 1199 if (debug) 1200 printf("state=%s\n", dhcp_cstate_str(state)); 1201 if (binding == dhcp_binding_temporary_e && modified) { 1202 if (S_commit_mods() == FALSE) 1203 goto no_reply; 1204 } 1205 { /* check the seconds field */ 1206 u_int16_t secs; 1207 1208 secs = (u_int16_t)ntohs(rq->dp_secs); 1209 if (secs < reply_threshold_seconds) { 1210 if (debug) { 1211 printf("rp->dp_secs %d < threshold %d\n", 1212 secs, reply_threshold_seconds); 1213 } 1214 goto no_reply; 1215 } 1216 1217 } 1218 if (reply) { 1219 if (reply_msgtype == dhcp_msgtype_ack_e || 1220 reply_msgtype == dhcp_msgtype_offer_e) { 1221 int num_params; 1222 const uint8_t * params; 1223 1224 params = (const uint8_t *) 1225 dhcpol_find(request->options_p, 1226 dhcptag_parameter_request_list_e, 1227 &num_params, NULL); 1228 1229 bzero(reply->dp_file, sizeof(reply->dp_file)); 1230 1231 reply->dp_siaddr = if_inet_addr(request->if_p); 1232 strlcpy((char *)reply->dp_sname, server_name, 1233 sizeof(reply->dp_sname)); 1234 1235 /* add the client-specified parameters */ 1236 if (params != NULL) 1237 (void)add_subnet_options(hostname, iaddr, 1238 request->if_p, 1239 &options, params, num_params); 1240 /* terminate the options */ 1241 if (dhcpoa_add(&options, dhcptag_end_e, 0, NULL) 1242 != dhcpoa_success_e) { 1243 my_log(LOG_INFO, "couldn't add end tag: %s", 1244 dhcpoa_err(&options)); 1245 goto no_reply; 1246 } 1247 } 1248 { 1249 int size = sizeof(struct dhcp) + sizeof(rfc_magic) 1250 + dhcpoa_used(&options); 1251 1252 if (size < sizeof(struct bootp)) { 1253 /* pad out to BOOTP-sized packet */ 1254 size = sizeof(struct bootp); 1255 } 1256 if (debug) { 1257 printf("\nSending: DHCP %s (size %d)\n", 1258 dhcp_msgtype_names(reply_msgtype), size); 1259 } 1260 if (sendreply(request->if_p, (struct bootp *)reply, size, 1261 use_broadcast, &iaddr)) { 1262 if (hostname == NULL && entry != NULL) { 1263 hostname = ni_valforprop(&entry->pl, NIPROP_NAME); 1264 if (hostname != NULL) 1265 hostname = strdup(hostname); 1266 } 1267 my_log(LOG_INFO, "%s sent %s %s pktsize %d", 1268 dhcp_msgtype_names(reply_msgtype), 1269 (hostname != NULL) 1270 ? hostname : (char *)"<no hostname>", 1271 inet_ntoa(iaddr), size); 1272 } 1273 } 1274 } 1275 no_reply: 1276 if (hostname != NULL) 1277 free(hostname); 1278 if (idstr != scratch_idstr) 1279 free(idstr); 1280 if (hwstr != NULL && hwstr != idstr && hwstr != scratch_hwstr) 1281 free(hwstr); 1282 return; 1283} 1284