1 /* $OpenBSD: memory.c,v 1.14 2006/08/09 22:23:53 cloder Exp $ */ 2 3 /* 4 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of The Internet Software Consortium nor the names 17 * of its contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 21 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 22 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * This software has been written for the Internet Software Consortium 35 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 36 * Enterprises. To learn more about the Internet Software Consortium, 37 * see ``http://www.vix.com/isc''. To learn more about Vixie 38 * Enterprises, see ``http://www.vix.com''. 39 */ 40 41 #include "dhcpd.h" 42 43 struct subnet *subnets; 44 static struct shared_network *shared_networks; 45 static struct hash_table *host_hw_addr_hash; 46 static struct hash_table *host_uid_hash; 47 static struct hash_table *lease_uid_hash; 48 static struct hash_table *lease_ip_addr_hash; 49 static struct hash_table *lease_hw_addr_hash; 50 static struct lease *dangling_leases; 51 52 static struct hash_table *vendor_class_hash; 53 static struct hash_table *user_class_hash; 54 55 void 56 enter_host(struct host_decl *hd) 57 { 58 struct host_decl *hp = NULL, *np = NULL; 59 60 hd->n_ipaddr = NULL; 61 if (hd->interface.hlen) { 62 if (!host_hw_addr_hash) 63 host_hw_addr_hash = new_hash(); 64 else 65 hp = (struct host_decl *)hash_lookup(host_hw_addr_hash, 66 hd->interface.haddr, hd->interface.hlen); 67 68 /* 69 * If there isn't already a host decl matching this 70 * address, add it to the hash table. 71 */ 72 if (!hp) 73 add_hash(host_hw_addr_hash, hd->interface.haddr, 74 hd->interface.hlen, (unsigned char *)hd); 75 } 76 77 /* 78 * If there was already a host declaration for this hardware 79 * address, add this one to the end of the list. 80 */ 81 if (hp) { 82 for (np = hp; np->n_ipaddr; np = np->n_ipaddr) 83 ; 84 np->n_ipaddr = hd; 85 } 86 87 if (hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER]) { 88 if (!tree_evaluate(hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER])) 89 return; 90 91 /* If there's no uid hash, make one; otherwise, see if 92 there's already an entry in the hash for this host. */ 93 if (!host_uid_hash) { 94 host_uid_hash = new_hash(); 95 hp = NULL; 96 } else 97 hp = (struct host_decl *)hash_lookup(host_uid_hash, 98 hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER]->value, 99 hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER]->len); 100 101 /* 102 * If there's already a host declaration for this 103 * client identifier, add this one to the end of the 104 * list. Otherwise, add it to the hash table. 105 */ 106 if (hp) { 107 /* Don't link it in twice... */ 108 if (!np) { 109 for (np = hp; np->n_ipaddr; 110 np = np->n_ipaddr) 111 ; 112 np->n_ipaddr = hd; 113 } 114 } else { 115 add_hash(host_uid_hash, 116 hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER]->value, 117 hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER]->len, 118 (unsigned char *)hd); 119 } 120 } 121 } 122 123 struct host_decl * 124 find_hosts_by_haddr(int htype, unsigned char *haddr, int hlen) 125 { 126 return (struct host_decl *)hash_lookup(host_hw_addr_hash, 127 haddr, hlen); 128 } 129 130 struct host_decl * 131 find_hosts_by_uid(unsigned char *data, int len) 132 { 133 return (struct host_decl *)hash_lookup(host_uid_hash, data, len); 134 } 135 136 /* 137 * More than one host_decl can be returned by find_hosts_by_haddr or 138 * find_hosts_by_uid, and each host_decl can have multiple addresses. 139 * Loop through the list of hosts, and then for each host, through the 140 * list of addresses, looking for an address that's in the same shared 141 * network as the one specified. Store the matching address through 142 * the addr pointer, update the host pointer to point at the host_decl 143 * that matched, and return the subnet that matched. 144 */ 145 struct subnet * 146 find_host_for_network(struct host_decl **host, struct iaddr *addr, 147 struct shared_network *share) 148 { 149 struct subnet *subnet; 150 struct iaddr ip_address; 151 struct host_decl *hp; 152 int i; 153 154 for (hp = *host; hp; hp = hp->n_ipaddr) { 155 if (!hp->fixed_addr || !tree_evaluate(hp->fixed_addr)) 156 continue; 157 for (i = 0; i < hp->fixed_addr->len; i += 4) { 158 ip_address.len = 4; 159 memcpy(ip_address.iabuf, hp->fixed_addr->value + i, 4); 160 subnet = find_grouped_subnet(share, ip_address); 161 if (subnet) { 162 *addr = ip_address; 163 *host = hp; 164 return subnet; 165 } 166 } 167 } 168 return NULL; 169 } 170 171 void 172 new_address_range(struct iaddr low, struct iaddr high, struct subnet *subnet, 173 int dynamic) 174 { 175 struct lease *address_range, *lp, *plp; 176 struct iaddr net; 177 int min, max, i; 178 char lowbuf[16], highbuf[16], netbuf[16]; 179 struct shared_network *share = subnet->shared_network; 180 struct hostent *h; 181 struct in_addr ia; 182 183 /* All subnets should have attached shared network structures. */ 184 if (!share) { 185 strlcpy(netbuf, piaddr(subnet->net), sizeof(netbuf)); 186 error("No shared network for network %s (%s)", 187 netbuf, piaddr(subnet->netmask)); 188 } 189 190 /* Initialize the hash table if it hasn't been done yet. */ 191 if (!lease_uid_hash) 192 lease_uid_hash = new_hash(); 193 if (!lease_ip_addr_hash) 194 lease_ip_addr_hash = new_hash(); 195 if (!lease_hw_addr_hash) 196 lease_hw_addr_hash = new_hash(); 197 198 /* Make sure that high and low addresses are in same subnet. */ 199 net = subnet_number(low, subnet->netmask); 200 if (!addr_eq(net, subnet_number(high, subnet->netmask))) { 201 strlcpy(lowbuf, piaddr(low), sizeof(lowbuf)); 202 strlcpy(highbuf, piaddr(high), sizeof(highbuf)); 203 strlcpy(netbuf, piaddr(subnet->netmask), sizeof(netbuf)); 204 error("Address range %s to %s, netmask %s spans %s!", 205 lowbuf, highbuf, netbuf, "multiple subnets"); 206 } 207 208 /* Make sure that the addresses are on the correct subnet. */ 209 if (!addr_eq(net, subnet->net)) { 210 strlcpy(lowbuf, piaddr(low), sizeof(lowbuf)); 211 strlcpy(highbuf, piaddr(high), sizeof(highbuf)); 212 strlcpy(netbuf, piaddr(subnet->netmask), sizeof(netbuf)); 213 error("Address range %s to %s not on net %s/%s!", 214 lowbuf, highbuf, piaddr(subnet->net), netbuf); 215 } 216 217 /* Get the high and low host addresses... */ 218 max = host_addr(high, subnet->netmask); 219 min = host_addr(low, subnet->netmask); 220 221 /* Allow range to be specified high-to-low as well as low-to-high. */ 222 if (min > max) { 223 max = min; 224 min = host_addr(high, subnet->netmask); 225 } 226 227 /* Get a lease structure for each address in the range. */ 228 address_range = new_leases(max - min + 1, "new_address_range"); 229 if (!address_range) { 230 strlcpy(lowbuf, piaddr(low), sizeof(lowbuf)); 231 strlcpy(highbuf, piaddr(high), sizeof(highbuf)); 232 error("No memory for address range %s-%s.", lowbuf, highbuf); 233 } 234 memset(address_range, 0, (sizeof *address_range) * (max - min + 1)); 235 236 /* Fill in the last lease if it hasn't been already... */ 237 if (!share->last_lease) 238 share->last_lease = &address_range[0]; 239 240 /* Fill out the lease structures with some minimal information. */ 241 for (i = 0; i < max - min + 1; i++) { 242 address_range[i].ip_addr = ip_addr(subnet->net, 243 subnet->netmask, i + min); 244 address_range[i].starts = address_range[i].timestamp = MIN_TIME; 245 address_range[i].ends = MIN_TIME; 246 address_range[i].subnet = subnet; 247 address_range[i].shared_network = share; 248 address_range[i].flags = dynamic ? DYNAMIC_BOOTP_OK : 0; 249 250 memcpy(&ia, address_range[i].ip_addr.iabuf, 4); 251 252 if (subnet->group->get_lease_hostnames) { 253 h = gethostbyaddr((char *)&ia, sizeof ia, AF_INET); 254 if (!h) 255 warning("No hostname for %s", inet_ntoa(ia)); 256 else { 257 int len = strlen(h->h_name) + 1; 258 259 address_range[i].hostname = malloc(len); 260 if (!address_range[i].hostname) 261 error("no memory for hostname %s.", 262 h->h_name); 263 strlcpy(address_range[i].hostname, 264 h->h_name, len); 265 } 266 } 267 268 /* Link this entry into the list. */ 269 address_range[i].next = share->leases; 270 address_range[i].prev = NULL; 271 share->leases = &address_range[i]; 272 if (address_range[i].next) 273 address_range[i].next->prev = share->leases; 274 add_hash(lease_ip_addr_hash, address_range[i].ip_addr.iabuf, 275 address_range[i].ip_addr.len, 276 (unsigned char *)&address_range[i]); 277 } 278 279 /* Find out if any dangling leases are in range... */ 280 plp = NULL; 281 for (lp = dangling_leases; lp; lp = lp->next) { 282 struct iaddr lnet; 283 int lhost; 284 285 lnet = subnet_number(lp->ip_addr, subnet->netmask); 286 lhost = host_addr(lp->ip_addr, subnet->netmask); 287 288 /* If it's in range, fill in the real lease structure with 289 the dangling lease's values, and remove the lease from 290 the list of dangling leases. */ 291 if (addr_eq(lnet, subnet->net) && lhost >= i && lhost <= max) { 292 if (plp) { 293 plp->next = lp->next; 294 } else { 295 dangling_leases = lp->next; 296 } 297 lp->next = NULL; 298 address_range[lhost - i].hostname = lp->hostname; 299 address_range[lhost - i].client_hostname = 300 lp->client_hostname; 301 supersede_lease(&address_range[lhost - i], lp, 0); 302 free_lease(lp, "new_address_range"); 303 } else 304 plp = lp; 305 } 306 } 307 308 struct subnet * 309 find_subnet(struct iaddr addr) 310 { 311 struct subnet *rv; 312 313 for (rv = subnets; rv; rv = rv->next_subnet) { 314 if (addr_eq(subnet_number(addr, rv->netmask), rv->net)) 315 return rv; 316 } 317 return NULL; 318 } 319 320 struct subnet * 321 find_grouped_subnet(struct shared_network *share, struct iaddr addr) 322 { 323 struct subnet *rv; 324 325 for (rv = share->subnets; rv; rv = rv->next_sibling) { 326 if (addr_eq(subnet_number(addr, rv->netmask), rv->net)) 327 return rv; 328 } 329 return NULL; 330 } 331 332 int 333 subnet_inner_than(struct subnet *subnet, struct subnet *scan, int warnp) 334 { 335 if (addr_eq(subnet_number(subnet->net, scan->netmask), scan->net) || 336 addr_eq(subnet_number(scan->net, subnet->netmask), subnet->net)) { 337 char n1buf[16]; 338 int i, j; 339 340 for (i = 0; i < 32; i++) 341 if (subnet->netmask.iabuf[3 - (i >> 3)] & 342 (1 << (i & 7))) 343 break; 344 for (j = 0; j < 32; j++) 345 if (scan->netmask.iabuf[3 - (j >> 3)] & 346 (1 << (j & 7))) 347 break; 348 strlcpy(n1buf, piaddr(subnet->net), sizeof(n1buf)); 349 if (warnp) 350 warning("%ssubnet %s/%d conflicts with subnet %s/%d", 351 "Warning: ", n1buf, 32 - i, 352 piaddr(scan->net), 32 - j); 353 if (i < j) 354 return 1; 355 } 356 return 0; 357 } 358 359 /* Enter a new subnet into the subnet list. */ 360 void 361 enter_subnet(struct subnet *subnet) 362 { 363 struct subnet *scan, *prev = NULL; 364 365 /* Check for duplicates... */ 366 for (scan = subnets; scan; scan = scan->next_subnet) { 367 /* 368 * When we find a conflict, make sure that the 369 * subnet with the narrowest subnet mask comes 370 * first. 371 */ 372 if (subnet_inner_than(subnet, scan, 1)) { 373 if (prev) { 374 prev->next_subnet = subnet; 375 } else 376 subnets = subnet; 377 subnet->next_subnet = scan; 378 return; 379 } 380 prev = scan; 381 } 382 383 /* XXX use the BSD radix tree code instead of a linked list. */ 384 subnet->next_subnet = subnets; 385 subnets = subnet; 386 } 387 388 /* Enter a new shared network into the shared network list. */ 389 void 390 enter_shared_network(struct shared_network *share) 391 { 392 /* XXX Sort the nets into a balanced tree to make searching quicker. */ 393 share->next = shared_networks; 394 shared_networks = share; 395 } 396 397 /* 398 * Enter a lease into the system. This is called by the parser each 399 * time it reads in a new lease. If the subnet for that lease has 400 * already been read in (usually the case), just update that lease; 401 * otherwise, allocate temporary storage for the lease and keep it around 402 * until we're done reading in the config file. 403 */ 404 void 405 enter_lease(struct lease *lease) 406 { 407 struct lease *comp = find_lease_by_ip_addr(lease->ip_addr); 408 409 /* If we don't have a place for this lease yet, save it for later. */ 410 if (!comp) { 411 comp = new_lease("enter_lease"); 412 if (!comp) 413 error("No memory for lease %s\n", 414 piaddr(lease->ip_addr)); 415 *comp = *lease; 416 comp->next = dangling_leases; 417 comp->prev = NULL; 418 dangling_leases = comp; 419 } else { 420 /* Record the hostname information in the lease. */ 421 comp->hostname = lease->hostname; 422 comp->client_hostname = lease->client_hostname; 423 supersede_lease(comp, lease, 0); 424 } 425 } 426 427 /* 428 * Replace the data in an existing lease with the data in a new lease; 429 * adjust hash tables to suit, and insertion sort the lease into the 430 * list of leases by expiry time so that we can always find the oldest 431 * lease. 432 */ 433 int 434 supersede_lease(struct lease *comp, struct lease *lease, int commit) 435 { 436 int enter_uid = 0; 437 int enter_hwaddr = 0; 438 int do_pftable = 0; 439 struct lease *lp; 440 441 /* Static leases are not currently kept in the database... */ 442 if (lease->flags & STATIC_LEASE) 443 return 1; 444 445 /* 446 * If the existing lease hasn't expired and has a different 447 * unique identifier or, if it doesn't have a unique 448 * identifier, a different hardware address, then the two 449 * leases are in conflict. If the existing lease has a uid 450 * and the new one doesn't, but they both have the same 451 * hardware address, and dynamic bootp is allowed on this 452 * lease, then we allow that, in case a dynamic BOOTP lease is 453 * requested *after* a DHCP lease has been assigned. 454 */ 455 if (!(lease->flags & ABANDONED_LEASE) && 456 comp->ends > cur_time && 457 (((comp->uid && lease->uid) && 458 (comp->uid_len != lease->uid_len || 459 memcmp (comp->uid, lease->uid, comp->uid_len))) || 460 (!comp->uid && 461 ((comp->hardware_addr.htype != 462 lease->hardware_addr.htype) || 463 (comp->hardware_addr.hlen != 464 lease->hardware_addr.hlen) || 465 memcmp(comp->hardware_addr.haddr, lease->hardware_addr.haddr, 466 comp->hardware_addr.hlen))))) { 467 warning("Lease conflict at %s", piaddr(comp->ip_addr)); 468 return 0; 469 } else { 470 /* If there's a Unique ID, dissociate it from the hash 471 table and free it if necessary. */ 472 if (comp->uid) { 473 uid_hash_delete(comp); 474 enter_uid = 1; 475 if (comp->uid != &comp->uid_buf[0]) { 476 free(comp->uid); 477 comp->uid_max = 0; 478 comp->uid_len = 0; 479 } 480 comp->uid = NULL; 481 } else 482 enter_uid = 1; 483 484 if (comp->hardware_addr.htype && 485 ((comp->hardware_addr.hlen != 486 lease->hardware_addr.hlen) || 487 (comp->hardware_addr.htype != 488 lease->hardware_addr.htype) || 489 memcmp(comp->hardware_addr.haddr, lease->hardware_addr.haddr, 490 comp->hardware_addr.hlen))) { 491 hw_hash_delete(comp); 492 enter_hwaddr = 1; 493 do_pftable = 1; 494 } else if (!comp->hardware_addr.htype) { 495 enter_hwaddr = 1; 496 do_pftable = 1; 497 } 498 499 /* Copy the data files, but not the linkages. */ 500 comp->starts = lease->starts; 501 if (lease->uid) { 502 if (lease->uid_len <= sizeof (lease->uid_buf)) { 503 memcpy(comp->uid_buf, lease->uid, lease->uid_len); 504 comp->uid = &comp->uid_buf[0]; 505 comp->uid_max = sizeof comp->uid_buf; 506 } else if (lease->uid != &lease->uid_buf[0]) { 507 comp->uid = lease->uid; 508 comp->uid_max = lease->uid_max; 509 lease->uid = NULL; 510 lease->uid_max = 0; 511 } else { 512 error("corrupt lease uid."); /* XXX */ 513 } 514 } else { 515 comp->uid = NULL; 516 comp->uid_max = 0; 517 } 518 comp->uid_len = lease->uid_len; 519 comp->host = lease->host; 520 comp->hardware_addr = lease->hardware_addr; 521 comp->flags = ((lease->flags & ~PERSISTENT_FLAGS) | 522 (comp->flags & ~EPHEMERAL_FLAGS)); 523 524 /* Record the lease in the uid hash if necessary. */ 525 if (enter_uid && lease->uid) 526 uid_hash_add(comp); 527 528 /* Record it in the hardware address hash if necessary. */ 529 if (enter_hwaddr && lease->hardware_addr.htype) 530 hw_hash_add(comp); 531 532 /* Remove the lease from its current place in the 533 timeout sequence. */ 534 if (comp->prev) 535 comp->prev->next = comp->next; 536 else 537 comp->shared_network->leases = comp->next; 538 if (comp->next) 539 comp->next->prev = comp->prev; 540 if (comp->shared_network->last_lease == comp) 541 comp->shared_network->last_lease = comp->prev; 542 543 /* Find the last insertion point... */ 544 if (comp == comp->shared_network->insertion_point || 545 !comp->shared_network->insertion_point) 546 lp = comp->shared_network->leases; 547 else 548 lp = comp->shared_network->insertion_point; 549 550 if (!lp) { 551 /* Nothing on the list yet? Just make comp the 552 head of the list. */ 553 comp->shared_network->leases = comp; 554 comp->shared_network->last_lease = comp; 555 } else if (lp->ends > lease->ends) { 556 /* Skip down the list until we run out of list 557 or find a place for comp. */ 558 while (lp->next && lp->ends > lease->ends) { 559 lp = lp->next; 560 } 561 if (lp->ends > lease->ends) { 562 /* If we ran out of list, put comp 563 at the end. */ 564 lp->next = comp; 565 comp->prev = lp; 566 comp->next = NULL; 567 comp->shared_network->last_lease = comp; 568 } else { 569 /* If we didn't, put it between lp and 570 the previous item on the list. */ 571 if ((comp->prev = lp->prev)) 572 comp->prev->next = comp; 573 comp->next = lp; 574 lp->prev = comp; 575 } 576 } else { 577 /* Skip up the list until we run out of list 578 or find a place for comp. */ 579 while (lp->prev && lp->ends < lease->ends) { 580 lp = lp->prev; 581 } 582 if (lp->ends < lease->ends) { 583 /* If we ran out of list, put comp 584 at the beginning. */ 585 lp->prev = comp; 586 comp->next = lp; 587 comp->prev = NULL; 588 comp->shared_network->leases = comp; 589 } else { 590 /* If we didn't, put it between lp and 591 the next item on the list. */ 592 if ((comp->next = lp->next)) 593 comp->next->prev = comp; 594 comp->prev = lp; 595 lp->next = comp; 596 } 597 } 598 comp->shared_network->insertion_point = comp; 599 comp->ends = lease->ends; 600 } 601 602 pfmsg('L', lease); /* address is leased. remove from purgatory */ 603 if (do_pftable) /* address changed hwaddr. remove from overload */ 604 pfmsg('C', lease); 605 606 /* Return zero if we didn't commit the lease to permanent storage; 607 nonzero if we did. */ 608 return commit && write_lease(comp) && commit_leases(); 609 } 610 611 /* Release the specified lease and re-hash it as appropriate. */ 612 613 void 614 release_lease(struct lease *lease) 615 { 616 struct lease lt; 617 618 lt = *lease; 619 if (lt.ends > cur_time) { 620 lt.ends = cur_time; 621 supersede_lease(lease, <, 1); 622 note("Released lease for IP address %s", 623 piaddr(lease->ip_addr)); 624 } 625 } 626 627 628 /* 629 * Abandon the specified lease for the specified time. sets it's 630 * particulars to zero, the end time appropriately and re-hash it as 631 * appropriate. abandons permanently if abtime is 0 632 */ 633 void 634 abandon_lease(struct lease *lease, char *message) 635 { 636 struct lease lt; 637 time_t abtime; 638 639 abtime = lease->subnet->group->default_lease_time; 640 lease->flags |= ABANDONED_LEASE; 641 lt = *lease; 642 lt.ends = cur_time + abtime; 643 warning("Abandoning IP address %s for %d seconds: %s", 644 piaddr(lease->ip_addr), abtime, message); 645 lt.hardware_addr.htype = 0; 646 lt.hardware_addr.hlen = 0; 647 lt.uid = NULL; 648 lt.uid_len = 0; 649 supersede_lease(lease, <, 1); 650 651 pfmsg('A', lease); /* address is abandoned. send to purgatory */ 652 return; 653 } 654 655 /* Locate the lease associated with a given IP address... */ 656 struct lease * 657 find_lease_by_ip_addr(struct iaddr addr) 658 { 659 return (struct lease *)hash_lookup(lease_ip_addr_hash, 660 addr.iabuf, addr.len); 661 } 662 663 struct lease *find_lease_by_uid(unsigned char *uid, int len) 664 { 665 return (struct lease *)hash_lookup(lease_uid_hash, uid, len); 666 } 667 668 struct lease * 669 find_lease_by_hw_addr(unsigned char *hwaddr, int hwlen) 670 { 671 return (struct lease *)hash_lookup(lease_hw_addr_hash, hwaddr, hwlen); 672 } 673 674 /* Add the specified lease to the uid hash. */ 675 void 676 uid_hash_add(struct lease *lease) 677 { 678 struct lease *head = find_lease_by_uid(lease->uid, lease->uid_len); 679 struct lease *scan; 680 681 /* If it's not in the hash, just add it. */ 682 if (!head) 683 add_hash(lease_uid_hash, lease->uid, 684 lease->uid_len, (unsigned char *)lease); 685 else { 686 /* Otherwise, attach it to the end of the list. */ 687 for (scan = head; scan->n_uid; scan = scan->n_uid) 688 ; 689 scan->n_uid = lease; 690 } 691 } 692 693 /* Delete the specified lease from the uid hash. */ 694 void 695 uid_hash_delete(struct lease *lease) 696 { 697 struct lease *head = find_lease_by_uid(lease->uid, lease->uid_len); 698 struct lease *scan; 699 700 /* If it's not in the hash, we have no work to do. */ 701 if (!head) { 702 lease->n_uid = NULL; 703 return; 704 } 705 706 /* If the lease we're freeing is at the head of the list, 707 remove the hash table entry and add a new one with the 708 next lease on the list (if there is one). */ 709 if (head == lease) { 710 delete_hash_entry(lease_uid_hash, lease->uid, lease->uid_len); 711 if (lease->n_uid) 712 add_hash(lease_uid_hash, lease->n_uid->uid, 713 lease->n_uid->uid_len, 714 (unsigned char *)(lease->n_uid)); 715 } else { 716 /* Otherwise, look for the lease in the list of leases 717 attached to the hash table entry, and remove it if 718 we find it. */ 719 for (scan = head; scan->n_uid; scan = scan->n_uid) { 720 if (scan->n_uid == lease) { 721 scan->n_uid = scan->n_uid->n_uid; 722 break; 723 } 724 } 725 } 726 lease->n_uid = NULL; 727 } 728 729 /* Add the specified lease to the hardware address hash. */ 730 void 731 hw_hash_add(struct lease *lease) 732 { 733 struct lease *head = find_lease_by_hw_addr(lease->hardware_addr.haddr, 734 lease->hardware_addr.hlen); 735 struct lease *scan; 736 737 /* If it's not in the hash, just add it. */ 738 if (!head) 739 add_hash(lease_hw_addr_hash, lease->hardware_addr.haddr, 740 lease->hardware_addr.hlen, (unsigned char *)lease); 741 else { 742 /* Otherwise, attach it to the end of the list. */ 743 for (scan = head; scan->n_hw; scan = scan->n_hw) 744 ; 745 scan->n_hw = lease; 746 } 747 } 748 749 /* Delete the specified lease from the hardware address hash. */ 750 void 751 hw_hash_delete(struct lease *lease) 752 { 753 struct lease *head = find_lease_by_hw_addr(lease->hardware_addr.haddr, 754 lease->hardware_addr.hlen); 755 struct lease *scan; 756 757 /* If it's not in the hash, we have no work to do. */ 758 if (!head) { 759 lease->n_hw = NULL; 760 return; 761 } 762 763 /* If the lease we're freeing is at the head of the list, 764 remove the hash table entry and add a new one with the 765 next lease on the list (if there is one). */ 766 if (head == lease) { 767 delete_hash_entry(lease_hw_addr_hash, 768 lease->hardware_addr.haddr, lease->hardware_addr.hlen); 769 if (lease->n_hw) 770 add_hash(lease_hw_addr_hash, 771 lease->n_hw->hardware_addr.haddr, 772 lease->n_hw->hardware_addr.hlen, 773 (unsigned char *)(lease->n_hw)); 774 } else { 775 /* 776 * Otherwise, look for the lease in the list of leases 777 * attached to the hash table entry, and remove it if 778 * we find it. 779 */ 780 for (scan = head; scan->n_hw; scan = scan->n_hw) { 781 if (scan->n_hw == lease) { 782 scan->n_hw = scan->n_hw->n_hw; 783 break; 784 } 785 } 786 } 787 lease->n_hw = NULL; 788 } 789 790 791 struct class * 792 add_class(int type, char *name) 793 { 794 struct class *class = new_class("add_class"); 795 char *tname = malloc(strlen(name) + 1); 796 797 if (!vendor_class_hash) 798 vendor_class_hash = new_hash(); 799 if (!user_class_hash) 800 user_class_hash = new_hash(); 801 802 if (!tname || !class || !vendor_class_hash || !user_class_hash) 803 return NULL; 804 805 memset(class, 0, sizeof *class); 806 strlcpy(tname, name, strlen(name) + 1); 807 class->name = tname; 808 809 if (type) 810 add_hash(user_class_hash, (unsigned char *)tname, 811 strlen(tname), (unsigned char *)class); 812 else 813 add_hash(vendor_class_hash, (unsigned char *)tname, 814 strlen(tname), (unsigned char *)class); 815 return class; 816 } 817 818 struct class * 819 find_class(int type, unsigned char *name, int len) 820 { 821 return (struct class *)hash_lookup(type ? user_class_hash : 822 vendor_class_hash, name, len); 823 } 824 825 struct group * 826 clone_group(struct group *group, char *caller) 827 { 828 struct group *g = new_group(caller); 829 if (!g) 830 error("%s: can't allocate new group", caller); 831 *g = *group; 832 return g; 833 } 834 835 /* Write all interesting leases to permanent storage. */ 836 837 void 838 write_leases(void) 839 { 840 struct lease *l; 841 struct shared_network *s; 842 843 for (s = shared_networks; s; s = s->next) { 844 for (l = s->leases; l; l = l->next) { 845 if (l->hardware_addr.hlen || l->uid_len || 846 (l->flags & ABANDONED_LEASE)) 847 if (!write_lease(l)) 848 error("Can't rewrite lease database"); 849 } 850 } 851 if (!commit_leases()) 852 error("Can't commit leases to new database: %m"); 853 } 854