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