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