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