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, <, 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, <, 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 |
|
|
} |