GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/dhcpd/memory.c Lines: 0 363 0.0 %
Date: 2017-11-07 Branches: 0 238 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: memory.c,v 1.28 2017/02/13 23:04:05 krw 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
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 *
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 *
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 *
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
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 *
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 *
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
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
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
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
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
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
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
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
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
				free(comp->uid);
506
				comp->uid_max = 0;
507
				comp->uid_len = 0;
508
			}
509
			comp->uid = NULL;
510
		} else
511
			enter_uid = 1;
512
513
		if (comp->hardware_addr.htype &&
514
		    hwaddrcmp(&comp->hardware_addr, &lease->hardware_addr)) {
515
			hw_hash_delete(comp);
516
			enter_hwaddr = 1;
517
			do_pftable = 1;
518
		} else if (!comp->hardware_addr.htype) {
519
			enter_hwaddr = 1;
520
			do_pftable = 1;
521
		}
522
523
		/* Copy the data files, but not the linkages. */
524
		comp->starts = lease->starts;
525
		if (lease->uid) {
526
			if (lease->uid_len <= sizeof (lease->uid_buf)) {
527
				memcpy(comp->uid_buf, lease->uid,
528
				    lease->uid_len);
529
				comp->uid = &comp->uid_buf[0];
530
				comp->uid_max = sizeof comp->uid_buf;
531
			} else if (lease->uid != &lease->uid_buf[0]) {
532
				comp->uid = lease->uid;
533
				comp->uid_max = lease->uid_max;
534
				lease->uid = NULL;
535
				lease->uid_max = 0;
536
			} else {
537
				fatalx("corrupt lease uid."); /* XXX */
538
			}
539
		} else {
540
			comp->uid = NULL;
541
			comp->uid_max = 0;
542
		}
543
		comp->uid_len = lease->uid_len;
544
		comp->host = lease->host;
545
		comp->hardware_addr = lease->hardware_addr;
546
		comp->flags = ((lease->flags & ~PERSISTENT_FLAGS) |
547
		    (comp->flags & ~EPHEMERAL_FLAGS));
548
549
		/* Record the lease in the uid hash if necessary. */
550
		if (enter_uid && lease->uid)
551
			uid_hash_add(comp);
552
553
		/* Record it in the hardware address hash if necessary. */
554
		if (enter_hwaddr && lease->hardware_addr.htype)
555
			hw_hash_add(comp);
556
557
		/* Remove the lease from its current place in the
558
		   timeout sequence. */
559
		if (comp->prev)
560
			comp->prev->next = comp->next;
561
		else
562
			comp->shared_network->leases = comp->next;
563
		if (comp->next)
564
			comp->next->prev = comp->prev;
565
		if (comp->shared_network->last_lease == comp)
566
			comp->shared_network->last_lease = comp->prev;
567
568
		/* Find the last insertion point... */
569
		if (comp == comp->shared_network->insertion_point ||
570
		    !comp->shared_network->insertion_point)
571
			lp = comp->shared_network->leases;
572
		else
573
			lp = comp->shared_network->insertion_point;
574
575
		if (!lp) {
576
			/* Nothing on the list yet?    Just make comp the
577
			   head of the list. */
578
			comp->shared_network->leases = comp;
579
			comp->shared_network->last_lease = comp;
580
		} else if (lp->ends > lease->ends) {
581
			/* Skip down the list until we run out of list
582
			   or find a place for comp. */
583
			while (lp->next && lp->ends > lease->ends) {
584
				lp = lp->next;
585
			}
586
			if (lp->ends > lease->ends) {
587
				/* If we ran out of list, put comp
588
				   at the end. */
589
				lp->next = comp;
590
				comp->prev = lp;
591
				comp->next = NULL;
592
				comp->shared_network->last_lease = comp;
593
			} else {
594
				/* If we didn't, put it between lp and
595
				   the previous item on the list. */
596
				if ((comp->prev = lp->prev))
597
					comp->prev->next = comp;
598
				comp->next = lp;
599
				lp->prev = comp;
600
			}
601
		} else {
602
			/* Skip up the list until we run out of list
603
			   or find a place for comp. */
604
			while (lp->prev && lp->ends < lease->ends) {
605
				lp = lp->prev;
606
			}
607
			if (lp->ends < lease->ends) {
608
				/* If we ran out of list, put comp
609
				   at the beginning. */
610
				lp->prev = comp;
611
				comp->next = lp;
612
				comp->prev = NULL;
613
				comp->shared_network->leases = comp;
614
			} else {
615
				/* If we didn't, put it between lp and
616
				   the next item on the list. */
617
				if ((comp->next = lp->next))
618
					comp->next->prev = comp;
619
				comp->prev = lp;
620
				lp->next = comp;
621
			}
622
		}
623
		comp->shared_network->insertion_point = comp;
624
		comp->ends = lease->ends;
625
	}
626
627
	pfmsg('L', lease); /* address is leased. remove from purgatory */
628
	if (do_pftable) /* address changed hwaddr. remove from overload */
629
		pfmsg('C', lease);
630
631
	/* Return zero if we didn't commit the lease to permanent storage;
632
	   nonzero if we did. */
633
	return commit && write_lease(comp) && commit_leases();
634
}
635
636
/* Release the specified lease and re-hash it as appropriate. */
637
638
void
639
release_lease(struct lease *lease)
640
{
641
	struct lease lt;
642
643
	lt = *lease;
644
	if (lt.ends > cur_time) {
645
		lt.ends = cur_time;
646
		supersede_lease(lease, &lt, 1);
647
		log_info("Released lease for IP address %s",
648
		    piaddr(lease->ip_addr));
649
		pfmsg('R', lease);
650
	}
651
}
652
653
654
/*
655
 * Abandon the specified lease for the specified time. sets it's
656
 * particulars to zero, the end time appropriately and re-hash it as
657
 * appropriate. abandons permanently if abtime is 0
658
 */
659
void
660
abandon_lease(struct lease *lease, char *message)
661
{
662
	struct lease lt;
663
	time_t abtime;
664
665
	abtime = lease->subnet->group->default_lease_time;
666
	lease->flags |= ABANDONED_LEASE;
667
	lt = *lease;
668
	lt.ends = cur_time + abtime;
669
	log_warnx("Abandoning IP address %s for %lld seconds: %s",
670
	    piaddr(lease->ip_addr), (long long)abtime, message);
671
	lt.hardware_addr.htype = 0;
672
	lt.hardware_addr.hlen = 0;
673
	lt.uid = NULL;
674
	lt.uid_len = 0;
675
	supersede_lease(lease, &lt, 1);
676
677
	pfmsg('A', lease); /* address is abandoned. send to purgatory */
678
	return;
679
}
680
681
/* Locate the lease associated with a given IP address... */
682
struct lease *
683
find_lease_by_ip_addr(struct iaddr addr)
684
{
685
	return (struct lease *)hash_lookup(lease_ip_addr_hash,
686
	    addr.iabuf, addr.len);
687
}
688
689
struct lease *find_lease_by_uid(unsigned char *uid, int len)
690
{
691
	return (struct lease *)hash_lookup(lease_uid_hash, uid, len);
692
}
693
694
struct lease *
695
find_lease_by_hw_addr(unsigned char *hwaddr, int hwlen)
696
{
697
	return (struct lease *)hash_lookup(lease_hw_addr_hash, hwaddr, hwlen);
698
}
699
700
/* Add the specified lease to the uid hash. */
701
void
702
uid_hash_add(struct lease *lease)
703
{
704
	struct lease *head = find_lease_by_uid(lease->uid, lease->uid_len);
705
	struct lease *scan;
706
707
	/* If it's not in the hash, just add it. */
708
	if (!head)
709
		add_hash(lease_uid_hash, lease->uid,
710
		    lease->uid_len, (unsigned char *)lease);
711
	else {
712
		/* Otherwise, attach it to the end of the list. */
713
		for (scan = head; scan->n_uid; scan = scan->n_uid)
714
			;
715
		scan->n_uid = lease;
716
	}
717
}
718
719
/* Delete the specified lease from the uid hash. */
720
void
721
uid_hash_delete(struct lease *lease)
722
{
723
	struct lease *head = find_lease_by_uid(lease->uid, lease->uid_len);
724
	struct lease *scan;
725
726
	/* If it's not in the hash, we have no work to do. */
727
	if (!head) {
728
		lease->n_uid = NULL;
729
		return;
730
	}
731
732
	/* If the lease we're freeing is at the head of the list,
733
	   remove the hash table entry and add a new one with the
734
	   next lease on the list (if there is one). */
735
	if (head == lease) {
736
		delete_hash_entry(lease_uid_hash, lease->uid, lease->uid_len);
737
		if (lease->n_uid)
738
			add_hash(lease_uid_hash, lease->n_uid->uid,
739
			    lease->n_uid->uid_len,
740
			    (unsigned char *)(lease->n_uid));
741
	} else {
742
		/* Otherwise, look for the lease in the list of leases
743
		   attached to the hash table entry, and remove it if
744
		   we find it. */
745
		for (scan = head; scan->n_uid; scan = scan->n_uid) {
746
			if (scan->n_uid == lease) {
747
				scan->n_uid = scan->n_uid->n_uid;
748
				break;
749
			}
750
		}
751
	}
752
	lease->n_uid = NULL;
753
}
754
755
/* Add the specified lease to the hardware address hash. */
756
void
757
hw_hash_add(struct lease *lease)
758
{
759
	struct lease *head = find_lease_by_hw_addr(lease->hardware_addr.haddr,
760
	    lease->hardware_addr.hlen);
761
	struct lease *scan;
762
763
	/* If it's not in the hash, just add it. */
764
	if (!head)
765
		add_hash(lease_hw_addr_hash, lease->hardware_addr.haddr,
766
		    lease->hardware_addr.hlen, (unsigned char *)lease);
767
	else {
768
		/* Otherwise, attach it to the end of the list. */
769
		for (scan = head; scan->n_hw; scan = scan->n_hw)
770
			;
771
		scan->n_hw = lease;
772
	}
773
}
774
775
/* Delete the specified lease from the hardware address hash. */
776
void
777
hw_hash_delete(struct lease *lease)
778
{
779
	struct lease *head = find_lease_by_hw_addr(lease->hardware_addr.haddr,
780
	    lease->hardware_addr.hlen);
781
	struct lease *scan;
782
783
	/* If it's not in the hash, we have no work to do. */
784
	if (!head) {
785
		lease->n_hw = NULL;
786
		return;
787
	}
788
789
	/* If the lease we're freeing is at the head of the list,
790
	   remove the hash table entry and add a new one with the
791
	   next lease on the list (if there is one). */
792
	if (head == lease) {
793
		delete_hash_entry(lease_hw_addr_hash,
794
		    lease->hardware_addr.haddr, lease->hardware_addr.hlen);
795
		if (lease->n_hw)
796
			add_hash(lease_hw_addr_hash,
797
			    lease->n_hw->hardware_addr.haddr,
798
			    lease->n_hw->hardware_addr.hlen,
799
			    (unsigned char *)(lease->n_hw));
800
	} else {
801
		/*
802
		 * Otherwise, look for the lease in the list of leases
803
		 * attached to the hash table entry, and remove it if
804
		 * we find it.
805
		 */
806
		for (scan = head; scan->n_hw; scan = scan->n_hw) {
807
			if (scan->n_hw == lease) {
808
				scan->n_hw = scan->n_hw->n_hw;
809
				break;
810
			}
811
		}
812
	}
813
	lease->n_hw = NULL;
814
}
815
816
817
struct class *
818
add_class(int type, char *name)
819
{
820
	struct class *class;
821
	char *tname;
822
823
	class = calloc(1, sizeof(*class));
824
	tname = strdup(name);
825
826
	if (!vendor_class_hash)
827
		vendor_class_hash = new_hash();
828
	if (!user_class_hash)
829
		user_class_hash = new_hash();
830
831
	if (!tname || !class || !vendor_class_hash || !user_class_hash) {
832
		log_warnx("No memory for %s.", name);
833
		free(class);
834
		free(tname);
835
		return NULL;
836
	}
837
838
	class->name = tname;
839
840
	if (type)
841
		add_hash(user_class_hash, (unsigned char *)tname,
842
		    strlen(tname), (unsigned char *)class);
843
	else
844
		add_hash(vendor_class_hash, (unsigned char *)tname,
845
		    strlen(tname), (unsigned char *)class);
846
847
	return class;
848
}
849
850
struct class *
851
find_class(int type, unsigned char *name, int len)
852
{
853
	return (struct class *)hash_lookup(type ? user_class_hash :
854
	    vendor_class_hash, name, len);
855
}
856
857
struct group *
858
clone_group(struct group *group, char *caller)
859
{
860
	struct group *g;
861
862
	g = calloc(1, sizeof(struct group));
863
	if (!g)
864
		fatalx("%s: can't allocate new group", caller);
865
	*g = *group;
866
	return g;
867
}
868
869
/* Write all interesting leases to permanent storage. */
870
871
void
872
write_leases(void)
873
{
874
	struct lease *l;
875
	struct shared_network *s;
876
877
	for (s = shared_networks; s; s = s->next) {
878
		for (l = s->leases; l; l = l->next) {
879
			if (l->hardware_addr.hlen || l->uid_len ||
880
			    (l->flags & ABANDONED_LEASE)) {
881
				if (!write_lease(l))
882
					fatalx("Can't rewrite lease database");
883
				if (syncsend)
884
					sync_lease(l);
885
			}
886
		}
887
	}
888
	if (!commit_leases())
889
		fatal("Can't commit leases to new database");
890
}