xref: /openbsd-src/usr.sbin/dhcpd/memory.c (revision 799f675f6700f14e59124f9825c723e9f2ce19dc)
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, &lt, 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, &lt, 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