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