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