NetBSD/dist/dhcp/server/mdb.c

2324 lines
66 KiB
C

/* mdb.c
Server-specific in-memory database support. */
/*
* Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-2003 by Internet Software Consortium
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Internet Systems Consortium, Inc.
* 950 Charter Street
* Redwood City, CA 94063
* <info@isc.org>
* http://www.isc.org/
*
* This software has been written for Internet Systems Consortium
* by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
* To learn more about Internet Systems Consortium, see
* ``http://www.isc.org/''. To learn more about Vixie Enterprises,
* see ``http://www.vix.com''. To learn more about Nominum, Inc., see
* ``http://www.nominum.com''.
*/
#ifndef lint
static char copyright[] =
"$Id: mdb.c,v 1.4 2005/08/11 17:13:30 drochner Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
#include "omapip/hash.h"
struct subnet *subnets;
struct shared_network *shared_networks;
host_hash_t *host_hw_addr_hash;
host_hash_t *host_uid_hash;
host_hash_t *host_name_hash;
lease_hash_t *lease_uid_hash;
lease_hash_t *lease_ip_addr_hash;
lease_hash_t *lease_hw_addr_hash;
omapi_object_type_t *dhcp_type_host;
static int find_uid_statement (struct executable_statement *esp,
void *vp, int condp)
{
struct executable_statement **evp = vp;
if (esp -> op == supersede_option_statement &&
esp -> data.option &&
(esp -> data.option -> option -> universe ==
&dhcp_universe) &&
(esp -> data.option -> option -> code ==
DHO_DHCP_CLIENT_IDENTIFIER)) {
if (condp) {
log_error ("dhcp client identifier may not be %s",
"specified conditionally.");
} else if (!(*evp)) {
executable_statement_reference (evp, esp, MDL);
return 1;
} else {
log_error ("only one dhcp client identifier may be %s",
"specified");
}
}
return 0;
}
isc_result_t enter_host (hd, dynamicp, commit)
struct host_decl *hd;
int dynamicp;
int commit;
{
struct host_decl *hp = (struct host_decl *)0;
struct host_decl *np = (struct host_decl *)0;
struct executable_statement *esp;
if (!host_name_hash) {
if (!host_new_hash (&host_name_hash, 0, MDL))
log_fatal ("Can't allocate host name hash");
host_hash_add (host_name_hash,
(unsigned char *)hd -> name,
strlen (hd -> name), hd, MDL);
} else {
host_hash_lookup (&hp, host_name_hash,
(unsigned char *)hd -> name,
strlen (hd -> name), MDL);
/* If it's deleted, we can supersede it. */
if (hp && (hp -> flags & HOST_DECL_DELETED)) {
host_hash_delete (host_name_hash,
(unsigned char *)hd -> name,
strlen (hd -> name), MDL);
/* If the old entry wasn't dynamic, then we
always have to keep the deletion. */
if (hp -> flags & HOST_DECL_STATIC) {
hd -> flags |= HOST_DECL_STATIC;
}
host_dereference (&hp, MDL);
}
/* If we are updating an existing host declaration, we
can just delete it and add it again. */
if (hp && hp == hd) {
host_dereference (&hp, MDL);
delete_host (hd, 0);
if (!write_host (hd))
return ISC_R_IOERROR;
hd -> flags &= ~HOST_DECL_DELETED;
}
/* If there isn't already a host decl matching this
address, add it to the hash table. */
if (!hp) {
host_hash_add (host_name_hash,
(unsigned char *)hd -> name,
strlen (hd -> name), hd, MDL);
} else {
/* XXX actually, we have to delete the old one
XXX carefully and replace it. Not done yet. */
host_dereference (&hp, MDL);
return ISC_R_EXISTS;
}
}
if (hd -> n_ipaddr)
host_dereference (&hd -> n_ipaddr, MDL);
if (!hd -> type)
hd -> type = dhcp_type_host;
if (hd -> interface.hlen) {
if (!host_hw_addr_hash) {
if (!host_new_hash (&host_hw_addr_hash, 0, MDL))
log_fatal ("Can't allocate host/hw hash");
} else {
/* If there isn't already a host decl matching this
address, add it to the hash table. */
host_hash_lookup (&hp, host_hw_addr_hash,
hd -> interface.hbuf,
hd -> interface.hlen, MDL);
}
if (!hp)
host_hash_add (host_hw_addr_hash, hd -> interface.hbuf,
hd -> interface.hlen, hd, MDL);
else {
/* If there was already a host declaration for
this hardware address, add this one to the
end of the list. */
for (np = hp; np -> n_ipaddr; np = np -> n_ipaddr)
;
host_reference (&np -> n_ipaddr, hd, MDL);
host_dereference (&hp, MDL);
}
}
/* See if there's a statement that sets the client identifier.
This is a kludge - the client identifier really shouldn't be
set with an executable statement. */
esp = (struct executable_statement *)0;
if (executable_statement_foreach (hd -> group -> statements,
find_uid_statement, &esp, 0)) {
evaluate_option_cache (&hd -> client_identifier,
(struct packet *)0,
(struct lease *)0,
(struct client_state *)0,
(struct option_state *)0,
(struct option_state *)0, &global_scope,
esp -> data.option, MDL);
}
/* If we got a client identifier, hash this entry by
client identifier. */
if (hd -> client_identifier.len) {
/* If there's no uid hash, make one; otherwise, see if
there's already an entry in the hash for this host. */
if (!host_uid_hash) {
if (!host_new_hash (&host_uid_hash, 0, MDL))
log_fatal ("Can't allocate host/uid hash");
host_hash_add (host_uid_hash,
hd -> client_identifier.data,
hd -> client_identifier.len,
hd, MDL);
} else {
/* If there's already a host declaration for this
client identifier, add this one to the end of the
list. Otherwise, add it to the hash table. */
if (host_hash_lookup (&hp, host_uid_hash,
hd -> client_identifier.data,
hd -> client_identifier.len,
MDL)) {
/* Don't link it in twice... */
if (!np) {
for (np = hp; np -> n_ipaddr;
np = np -> n_ipaddr) {
if (hd == np)
break;
}
if (hd != np)
host_reference (&np -> n_ipaddr,
hd, MDL);
}
host_dereference (&hp, MDL);
} else {
host_hash_add (host_uid_hash,
hd -> client_identifier.data,
hd -> client_identifier.len,
hd, MDL);
}
}
}
if (dynamicp && commit) {
if (!write_host (hd))
return ISC_R_IOERROR;
if (!commit_leases ())
return ISC_R_IOERROR;
}
return ISC_R_SUCCESS;
}
isc_result_t delete_host (hd, commit)
struct host_decl *hd;
int commit;
{
struct host_decl *hp = (struct host_decl *)0;
struct host_decl *np = (struct host_decl *)0;
struct host_decl *foo;
int hw_head = 0, uid_head = 1;
/* Don't need to do it twice. */
if (hd -> flags & HOST_DECL_DELETED)
return ISC_R_SUCCESS;
/* But we do need to do it once! :') */
hd -> flags |= HOST_DECL_DELETED;
if (hd -> interface.hlen) {
if (host_hw_addr_hash) {
if (host_hash_lookup (&hp, host_hw_addr_hash,
hd -> interface.hbuf,
hd -> interface.hlen, MDL)) {
if (hp == hd) {
host_hash_delete (host_hw_addr_hash,
hd -> interface.hbuf,
hd -> interface.hlen, MDL);
hw_head = 1;
} else {
np = (struct host_decl *)0;
foo = (struct host_decl *)0;
host_reference (&foo, hp, MDL);
while (foo) {
if (foo == hd)
break;
if (np)
host_dereference (&np, MDL);
host_reference (&np, foo, MDL);
host_dereference (&foo, MDL);
if (np -> n_ipaddr)
host_reference (&foo, np -> n_ipaddr, MDL);
}
if (foo) {
host_dereference (&np -> n_ipaddr, MDL);
if (hd -> n_ipaddr)
host_reference (&np -> n_ipaddr,
hd -> n_ipaddr, MDL);
host_dereference (&foo, MDL);
}
if (np)
host_dereference (&np, MDL);
}
host_dereference (&hp, MDL);
}
}
}
/* If we got a client identifier, hash this entry by
client identifier. */
if (hd -> client_identifier.len) {
if (host_uid_hash) {
if (host_hash_lookup (&hp, host_uid_hash,
hd -> client_identifier.data,
hd -> client_identifier.len, MDL)) {
if (hp == hd) {
host_hash_delete (host_uid_hash,
hd -> client_identifier.data,
hd -> client_identifier.len, MDL);
uid_head = 1;
} else {
np = (struct host_decl *)0;
foo = (struct host_decl *)0;
host_reference (&foo, hp, MDL);
while (foo) {
if (foo == hd)
break;
if (np)
host_dereference (&np, MDL);
host_reference (&np, foo, MDL);
host_dereference (&foo, MDL);
if (np -> n_ipaddr)
host_reference (&foo, np -> n_ipaddr, MDL);
}
if (foo) {
host_dereference (&np -> n_ipaddr, MDL);
if (hd -> n_ipaddr)
host_reference (&np -> n_ipaddr,
hd -> n_ipaddr, MDL);
host_dereference (&foo, MDL);
}
if (np)
host_dereference (&np, MDL);
}
host_dereference (&hp, MDL);
}
}
}
if (hd -> n_ipaddr) {
if (uid_head && hd -> n_ipaddr -> client_identifier.len) {
host_hash_add
(host_uid_hash,
hd -> n_ipaddr -> client_identifier.data,
hd -> n_ipaddr -> client_identifier.len,
hd -> n_ipaddr, MDL);
}
if (hw_head && hd -> n_ipaddr -> interface.hlen) {
host_hash_add (host_hw_addr_hash,
hd -> n_ipaddr -> interface.hbuf,
hd -> n_ipaddr -> interface.hlen,
hd -> n_ipaddr, MDL);
}
host_dereference (&hd -> n_ipaddr, MDL);
}
if (host_name_hash) {
if (host_hash_lookup (&hp, host_name_hash,
(unsigned char *)hd -> name,
strlen (hd -> name), MDL)) {
if (hp == hd && !(hp -> flags & HOST_DECL_STATIC)) {
host_hash_delete (host_name_hash,
(unsigned char *)hd -> name,
strlen (hd -> name), MDL);
}
host_dereference (&hp, MDL);
}
}
if (commit) {
if (!write_host (hd))
return ISC_R_IOERROR;
if (!commit_leases ())
return ISC_R_IOERROR;
}
return ISC_R_SUCCESS;
}
int find_hosts_by_haddr (struct host_decl **hp, int htype,
const unsigned char *haddr, unsigned hlen,
const char *file, int line)
{
struct hardware h;
h.hlen = hlen + 1;
h.hbuf [0] = htype;
memcpy (&h.hbuf [1], haddr, hlen);
return host_hash_lookup (hp, host_hw_addr_hash,
h.hbuf, h.hlen, file, line);
}
int find_hosts_by_uid (struct host_decl **hp,
const unsigned char *data, unsigned len,
const char *file, int line)
{
return host_hash_lookup (hp, host_uid_hash, data, len, file, line);
}
/* More than one host_decl can be returned by find_hosts_by_haddr or
find_hosts_by_uid, and each host_decl can have multiple addresses.
Loop through the list of hosts, and then for each host, through the
list of addresses, looking for an address that's in the same shared
network as the one specified. Store the matching address through
the addr pointer, update the host pointer to point at the host_decl
that matched, and return the subnet that matched. */
int find_host_for_network (struct subnet **sp, struct host_decl **host,
struct iaddr *addr, struct shared_network *share)
{
int i;
struct iaddr ip_address;
struct host_decl *hp;
struct data_string fixed_addr;
memset (&fixed_addr, 0, sizeof fixed_addr);
for (hp = *host; hp; hp = hp -> n_ipaddr) {
if (!hp -> fixed_addr)
continue;
if (!evaluate_option_cache (&fixed_addr, (struct packet *)0,
(struct lease *)0,
(struct client_state *)0,
(struct option_state *)0,
(struct option_state *)0,
&global_scope,
hp -> fixed_addr, MDL))
continue;
for (i = 0; i < fixed_addr.len; i += 4) {
ip_address.len = 4;
memcpy (ip_address.iabuf,
fixed_addr.data + i, 4);
if (find_grouped_subnet (sp, share, ip_address, MDL)) {
struct host_decl *tmp = (struct host_decl *)0;
*addr = ip_address;
/* This is probably not necessary, but
just in case *host is the only reference
to that host declaration, make a temporary
reference so that dereferencing it doesn't
dereference hp out from under us. */
host_reference (&tmp, *host, MDL);
host_dereference (host, MDL);
host_reference (host, hp, MDL);
host_dereference (&tmp, MDL);
data_string_forget (&fixed_addr, MDL);
return 1;
}
}
data_string_forget (&fixed_addr, MDL);
}
return 0;
}
void new_address_range (cfile, low, high, subnet, pool, lpchain)
struct parse *cfile;
struct iaddr low, high;
struct subnet *subnet;
struct pool *pool;
struct lease **lpchain;
{
struct lease *address_range;
struct iaddr net;
unsigned min, max, i;
char lowbuf [16], highbuf [16], netbuf [16];
struct shared_network *share = subnet -> shared_network;
#if !defined (COMPACT_LEASES)
isc_result_t status;
#endif
struct lease *lt = (struct lease *)0;
/* All subnets should have attached shared network structures. */
if (!share) {
strcpy (netbuf, piaddr (subnet -> net));
log_fatal ("No shared network for network %s (%s)",
netbuf, piaddr (subnet -> netmask));
}
/* Initialize the hash table if it hasn't been done yet. */
if (!lease_uid_hash) {
if (!lease_new_hash (&lease_uid_hash, 0, MDL))
log_fatal ("Can't allocate lease/uid hash");
}
if (!lease_ip_addr_hash) {
if (!lease_new_hash (&lease_ip_addr_hash, 0, MDL))
log_fatal ("Can't allocate lease/ip hash");
}
if (!lease_hw_addr_hash) {
if (!lease_new_hash (&lease_hw_addr_hash, 0, MDL))
log_fatal ("Can't allocate lease/hw hash");
}
/* Make sure that high and low addresses are in same subnet. */
net = subnet_number (low, subnet -> netmask);
if (!addr_eq (net, subnet_number (high, subnet -> netmask))) {
strcpy (lowbuf, piaddr (low));
strcpy (highbuf, piaddr (high));
strcpy (netbuf, piaddr (subnet -> netmask));
log_fatal ("Address range %s to %s, netmask %s spans %s!",
lowbuf, highbuf, netbuf, "multiple subnets");
}
/* Make sure that the addresses are on the correct subnet. */
if (!addr_eq (net, subnet -> net)) {
strcpy (lowbuf, piaddr (low));
strcpy (highbuf, piaddr (high));
strcpy (netbuf, piaddr (subnet -> netmask));
log_fatal ("Address range %s to %s not on net %s/%s!",
lowbuf, highbuf, piaddr (subnet -> net), netbuf);
}
/* Get the high and low host addresses... */
max = host_addr (high, subnet -> netmask);
min = host_addr (low, subnet -> netmask);
/* Allow range to be specified high-to-low as well as low-to-high. */
if (min > max) {
max = min;
min = host_addr (high, subnet -> netmask);
}
/* Get a lease structure for each address in the range. */
#if defined (COMPACT_LEASES)
address_range = new_leases (max - min + 1, MDL);
if (!address_range) {
strcpy (lowbuf, piaddr (low));
strcpy (highbuf, piaddr (high));
log_fatal ("No memory for address range %s-%s.",
lowbuf, highbuf);
}
#endif
/* Fill out the lease structures with some minimal information. */
for (i = 0; i < max - min + 1; i++) {
struct lease *lp = (struct lease *)0;
#if defined (COMPACT_LEASES)
omapi_object_initialize ((omapi_object_t *)&address_range [i],
dhcp_type_lease,
0, sizeof (struct lease), MDL);
lease_reference (&lp, &address_range [i], MDL);
#else
status = lease_allocate (&lp, MDL);
if (status != ISC_R_SUCCESS)
log_fatal ("No memory for lease %s: %s",
piaddr (ip_addr (subnet -> net,
subnet -> netmask,
i + min)),
isc_result_totext (status));
#endif
lp -> ip_addr = ip_addr (subnet -> net,
subnet -> netmask, i + min);
lp -> starts = lp -> timestamp = MIN_TIME;
lp -> ends = MIN_TIME;
subnet_reference (&lp -> subnet, subnet, MDL);
pool_reference (&lp -> pool, pool, MDL);
lp -> binding_state = FTS_FREE;
lp -> next_binding_state = FTS_FREE;
lp -> flags = 0;
/* Remember the lease in the IP address hash. */
if (find_lease_by_ip_addr (&lt, lp -> ip_addr, MDL)) {
if (lt -> pool) {
parse_warn (cfile,
"lease %s is declared twice!",
piaddr (lp -> ip_addr));
} else
pool_reference (&lt -> pool, pool, MDL);
lease_dereference (&lt, MDL);
} else
lease_hash_add (lease_ip_addr_hash,
lp -> ip_addr.iabuf,
lp -> ip_addr.len, lp, MDL);
/* Put the lease on the chain for the caller. */
if (lpchain) {
if (*lpchain) {
lease_reference (&lp -> next, *lpchain, MDL);
lease_dereference (lpchain, MDL);
}
lease_reference (lpchain, lp, MDL);
}
lease_dereference (&lp, MDL);
}
}
int find_subnet (struct subnet **sp,
struct iaddr addr, const char *file, int line)
{
struct subnet *rv;
for (rv = subnets; rv; rv = rv -> next_subnet) {
if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) {
if (subnet_reference (sp, rv,
file, line) != ISC_R_SUCCESS)
return 0;
return 1;
}
}
return 0;
}
int find_grouped_subnet (struct subnet **sp,
struct shared_network *share, struct iaddr addr,
const char *file, int line)
{
struct subnet *rv;
for (rv = share -> subnets; rv; rv = rv -> next_sibling) {
if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) {
if (subnet_reference (sp, rv,
file, line) != ISC_R_SUCCESS)
return 0;
return 1;
}
}
return 0;
}
int subnet_inner_than (subnet, scan, warnp)
struct subnet *subnet, *scan;
int warnp;
{
if (addr_eq (subnet_number (subnet -> net, scan -> netmask),
scan -> net) ||
addr_eq (subnet_number (scan -> net, subnet -> netmask),
subnet -> net)) {
char n1buf [16];
int i, j;
for (i = 0; i < 32; i++)
if (subnet -> netmask.iabuf [3 - (i >> 3)]
& (1 << (i & 7)))
break;
for (j = 0; j < 32; j++)
if (scan -> netmask.iabuf [3 - (j >> 3)] &
(1 << (j & 7)))
break;
strcpy (n1buf, piaddr (subnet -> net));
if (warnp)
log_error ("%ssubnet %s/%d overlaps subnet %s/%d",
"Warning: ", n1buf, 32 - i,
piaddr (scan -> net), 32 - j);
if (i < j)
return 1;
}
return 0;
}
/* Enter a new subnet into the subnet list. */
void enter_subnet (subnet)
struct subnet *subnet;
{
struct subnet *scan = (struct subnet *)0;
struct subnet *next = (struct subnet *)0;
struct subnet *prev = (struct subnet *)0;
/* Check for duplicates... */
if (subnets)
subnet_reference (&next, subnets, MDL);
while (next) {
subnet_reference (&scan, next, MDL);
subnet_dereference (&next, MDL);
/* When we find a conflict, make sure that the
subnet with the narrowest subnet mask comes
first. */
if (subnet_inner_than (subnet, scan, 1)) {
if (prev) {
if (prev -> next_subnet)
subnet_dereference (&prev -> next_subnet, MDL);
subnet_reference (&prev -> next_subnet, subnet, MDL);
subnet_dereference (&prev, MDL);
} else {
subnet_dereference (&subnets, MDL);
subnet_reference (&subnets, subnet, MDL);
}
subnet_reference (&subnet -> next_subnet, scan, MDL);
subnet_dereference (&scan, MDL);
return;
}
subnet_reference (&prev, scan, MDL);
subnet_dereference (&scan, MDL);
}
if (prev)
subnet_dereference (&prev, MDL);
/* XXX use the BSD radix tree code instead of a linked list. */
if (subnets) {
subnet_reference (&subnet -> next_subnet, subnets, MDL);
subnet_dereference (&subnets, MDL);
}
subnet_reference (&subnets, subnet, MDL);
}
/* Enter a new shared network into the shared network list. */
void enter_shared_network (share)
struct shared_network *share;
{
if (shared_networks) {
shared_network_reference (&share -> next,
shared_networks, MDL);
shared_network_dereference (&shared_networks, MDL);
}
shared_network_reference (&shared_networks, share, MDL);
}
void new_shared_network_interface (cfile, share, name)
struct parse *cfile;
struct shared_network *share;
const char *name;
{
struct interface_info *ip;
isc_result_t status;
if (share -> interface) {
parse_warn (cfile,
"A subnet or shared network can't be connected %s",
"to two interfaces.");
return;
}
for (ip = interfaces; ip; ip = ip -> next)
if (!strcmp (ip -> name, name))
break;
if (!ip) {
status = interface_allocate (&ip, MDL);
if (status != ISC_R_SUCCESS)
log_fatal ("new_shared_network_interface %s: %s",
name, isc_result_totext (status));
if (strlen (name) > sizeof ip -> name) {
memcpy (ip -> name, name, (sizeof ip -> name) - 1);
ip -> name [(sizeof ip -> name) - 1] = 0;
} else
strcpy (ip -> name, name);
if (interfaces) {
interface_reference (&ip -> next, interfaces, MDL);
interface_dereference (&interfaces, MDL);
}
interface_reference (&interfaces, ip, MDL);
ip -> flags = INTERFACE_REQUESTED;
/* XXX this is a reference loop. */
shared_network_reference (&ip -> shared_network, share, MDL);
interface_reference (&share -> interface, ip, MDL);
}
}
/* Enter a lease into the system. This is called by the parser each
time it reads in a new lease. If the subnet for that lease has
already been read in (usually the case), just update that lease;
otherwise, allocate temporary storage for the lease and keep it around
until we're done reading in the config file. */
void enter_lease (lease)
struct lease *lease;
{
struct lease *comp = (struct lease *)0;
if (find_lease_by_ip_addr (&comp, lease -> ip_addr, MDL)) {
if (!comp -> pool) {
log_error ("undeclared lease found in database: %s",
piaddr (lease -> ip_addr));
} else
pool_reference (&lease -> pool, comp -> pool, MDL);
if (comp -> subnet)
subnet_reference (&lease -> subnet,
comp -> subnet, MDL);
lease_hash_delete (lease_ip_addr_hash,
lease -> ip_addr.iabuf,
lease -> ip_addr.len, MDL);
lease_dereference (&comp, MDL);
}
/* The only way a lease can get here without a subnet is if it's in
the lease file, but not in the dhcpd.conf file. In this case, we
*should* keep it around until it's expired, but never reallocate it
or renew it. Currently, to maintain consistency, we are not doing
this.
XXX fix this so that the lease is kept around until it expires.
XXX this will be important in IPv6 with addresses that become
XXX non-renewable as a result of a renumbering event. */
if (!lease -> subnet) {
log_error ("lease %s: no subnet.", piaddr (lease -> ip_addr));
return;
}
lease_hash_add (lease_ip_addr_hash,
lease -> ip_addr.iabuf,
lease -> ip_addr.len, lease, MDL);
}
/* Replace the data in an existing lease with the data in a new lease;
adjust hash tables to suit, and insertion sort the lease into the
list of leases by expiry time so that we can always find the oldest
lease. */
int supersede_lease (comp, lease, commit, propogate, pimmediate)
struct lease *comp, *lease;
int commit;
int propogate;
int pimmediate;
{
int enter_uid = 0;
int enter_hwaddr = 0;
struct lease *lp, **lq, *prev;
#if defined (FAILOVER_PROTOCOL)
/* We must commit leases before sending updates regarding them
to failover peers. It is, therefore, an error to set pimmediate
and not commit. */
if (pimmediate && !commit)
return 0;
#endif
/* If there is no sample lease, just do the move. */
if (!lease)
goto just_move_it;
/* Static leases are not currently kept in the database... */
if (lease -> flags & STATIC_LEASE)
return 1;
/* If the existing lease hasn't expired and has a different
unique identifier or, if it doesn't have a unique
identifier, a different hardware address, then the two
leases are in conflict. If the existing lease has a uid
and the new one doesn't, but they both have the same
hardware address, and dynamic bootp is allowed on this
lease, then we allow that, in case a dynamic BOOTP lease is
requested *after* a DHCP lease has been assigned. */
if (lease -> binding_state != FTS_ABANDONED &&
lease -> next_binding_state != FTS_ABANDONED &&
comp -> binding_state == FTS_ACTIVE &&
(((comp -> uid && lease -> uid) &&
(comp -> uid_len != lease -> uid_len ||
memcmp (comp -> uid, lease -> uid, comp -> uid_len))) ||
(!comp -> uid &&
((comp -> hardware_addr.hlen !=
lease -> hardware_addr.hlen) ||
memcmp (comp -> hardware_addr.hbuf,
lease -> hardware_addr.hbuf,
comp -> hardware_addr.hlen))))) {
log_error ("Lease conflict at %s",
piaddr (comp -> ip_addr));
}
/* If there's a Unique ID, dissociate it from the hash
table and free it if necessary. */
if (comp -> uid) {
uid_hash_delete (comp);
enter_uid = 1;
if (comp -> uid != &comp -> uid_buf [0]) {
dfree (comp -> uid, MDL);
comp -> uid_max = 0;
comp -> uid_len = 0;
}
comp -> uid = (unsigned char *)0;
} else
enter_uid = 1;
if (comp -> hardware_addr.hlen &&
((comp -> hardware_addr.hlen !=
lease -> hardware_addr.hlen) ||
memcmp (comp -> hardware_addr.hbuf,
lease -> hardware_addr.hbuf,
comp -> hardware_addr.hlen))) {
hw_hash_delete (comp);
enter_hwaddr = 1;
} else if (!comp -> hardware_addr.hlen)
enter_hwaddr = 1;
/* If the lease has been billed to a class, remove the billing. */
if (comp -> billing_class != lease -> billing_class) {
if (comp -> billing_class)
unbill_class (comp, comp -> billing_class);
if (lease -> billing_class)
bill_class (comp, lease -> billing_class);
}
/* Copy the data files, but not the linkages. */
comp -> starts = lease -> starts;
if (lease -> uid) {
if (lease -> uid_len <= sizeof (lease -> uid_buf)) {
memcpy (comp -> uid_buf,
lease -> uid, lease -> uid_len);
comp -> uid = &comp -> uid_buf [0];
comp -> uid_max = sizeof comp -> uid_buf;
comp -> uid_len = lease -> uid_len;
} else if (lease -> uid != &lease -> uid_buf [0]) {
comp -> uid = lease -> uid;
comp -> uid_max = lease -> uid_max;
lease -> uid = (unsigned char *)0;
lease -> uid_max = 0;
comp -> uid_len = lease -> uid_len;
lease -> uid_len = 0;
} else {
log_fatal ("corrupt lease uid."); /* XXX */
}
} else {
comp -> uid = (unsigned char *)0;
comp -> uid_len = comp -> uid_max = 0;
}
if (comp -> host)
host_dereference (&comp -> host, MDL);
host_reference (&comp -> host, lease -> host, MDL);
comp -> hardware_addr = lease -> hardware_addr;
comp -> flags = ((lease -> flags & ~PERSISTENT_FLAGS) |
(comp -> flags & ~EPHEMERAL_FLAGS));
if (comp -> scope)
binding_scope_dereference (&comp -> scope, MDL);
if (lease -> scope) {
binding_scope_reference (&comp -> scope, lease -> scope, MDL);
binding_scope_dereference (&lease -> scope, MDL);
}
if (comp -> agent_options)
option_chain_head_dereference (&comp -> agent_options, MDL);
if (lease -> agent_options) {
/* Only retain the agent options if the lease is still
affirmatively associated with a client. */
if (lease -> next_binding_state == FTS_ACTIVE ||
lease -> next_binding_state == FTS_EXPIRED)
option_chain_head_reference (&comp -> agent_options,
lease -> agent_options,
MDL);
option_chain_head_dereference (&lease -> agent_options, MDL);
}
/* Record the hostname information in the lease. */
if (comp -> client_hostname)
dfree (comp -> client_hostname, MDL);
comp -> client_hostname = lease -> client_hostname;
lease -> client_hostname = (char *)0;
if (lease -> on_expiry) {
if (comp -> on_expiry)
executable_statement_dereference (&comp -> on_expiry,
MDL);
executable_statement_reference (&comp -> on_expiry,
lease -> on_expiry,
MDL);
}
if (lease -> on_commit) {
if (comp -> on_commit)
executable_statement_dereference (&comp -> on_commit,
MDL);
executable_statement_reference (&comp -> on_commit,
lease -> on_commit,
MDL);
}
if (lease -> on_release) {
if (comp -> on_release)
executable_statement_dereference (&comp -> on_release,
MDL);
executable_statement_reference (&comp -> on_release,
lease -> on_release, MDL);
}
/* Record the lease in the uid hash if necessary. */
if (enter_uid && comp -> uid) {
uid_hash_add (comp);
}
/* Record it in the hardware address hash if necessary. */
if (enter_hwaddr && lease -> hardware_addr.hlen) {
hw_hash_add (comp);
}
#if defined (FAILOVER_PROTOCOL)
comp -> cltt = lease -> cltt;
comp -> tstp = lease -> tstp;
comp -> tsfp = lease -> tsfp;
#endif /* FAILOVER_PROTOCOL */
comp -> ends = lease -> ends;
comp -> next_binding_state = lease -> next_binding_state;
just_move_it:
if (!comp -> pool) {
log_error ("Supersede_lease: lease %s with no pool.",
piaddr (comp -> ip_addr));
return 0;
}
/* Figure out which queue it's on. */
switch (comp -> binding_state) {
case FTS_FREE:
lq = &comp -> pool -> free;
comp -> pool -> free_leases--;
break;
case FTS_ACTIVE:
lq = &comp -> pool -> active;
break;
case FTS_EXPIRED:
case FTS_RELEASED:
case FTS_RESET:
lq = &comp -> pool -> expired;
break;
case FTS_ABANDONED:
lq = &comp -> pool -> abandoned;
break;
case FTS_BACKUP:
lq = &comp -> pool -> backup;
comp -> pool -> backup_leases--;
break;
default:
log_error ("Lease with bogus binding state: %d",
comp -> binding_state);
#if defined (BINDING_STATE_DEBUG)
abort ();
#endif
return 0;
}
/* Remove the lease from its current place in its current
timer sequence. */
prev = (struct lease *)0;
for (lp = *lq; lp; lp = lp -> next) {
if (lp == comp)
break;
prev = lp;
}
if (!lp) {
log_error ("Lease with binding state %s not on its queue.",
(comp -> binding_state < 1 ||
comp -> binding_state > FTS_LAST)
? "unknown"
: binding_state_names [comp -> binding_state - 1]);
return 0;
}
if (prev) {
lease_dereference (&prev -> next, MDL);
if (comp -> next) {
lease_reference (&prev -> next, comp -> next, MDL);
lease_dereference (&comp -> next, MDL);
}
} else {
lease_dereference (lq, MDL);
if (comp -> next) {
lease_reference (lq, comp -> next, MDL);
lease_dereference (&comp -> next, MDL);
}
}
/* Make the state transition. */
if (commit || !pimmediate)
make_binding_state_transition (comp);
/* Put the lease back on the appropriate queue. If the lease
is corrupt (as detected by lease_enqueue), don't go any farther. */
if (!lease_enqueue (comp))
return 0;
/* If this is the next lease that will timeout on the pool,
zap the old timeout and set the timeout on this pool to the
time that the lease's next event will happen.
We do not actually set the timeout unless commit is true -
we don't want to thrash the timer queue when reading the
lease database. Instead, the database code calls the
expiry event on each pool after reading in the lease file,
and the expiry code sets the timer if there's anything left
to expire after it's run any outstanding expiry events on
the pool. */
if ((commit || !pimmediate) &&
comp -> sort_time != MIN_TIME &&
comp -> sort_time > cur_time &&
(comp -> sort_time < comp -> pool -> next_event_time ||
comp -> pool -> next_event_time == MIN_TIME)) {
comp -> pool -> next_event_time = comp -> sort_time;
add_timeout (comp -> pool -> next_event_time,
pool_timer, comp -> pool,
(tvref_t)pool_reference,
(tvunref_t)pool_dereference);
}
if (commit) {
if (!write_lease (comp))
return 0;
if (!commit_leases ())
return 0;
}
#if defined (FAILOVER_PROTOCOL)
if (propogate) {
comp -> desired_binding_state = comp -> binding_state;
if (!dhcp_failover_queue_update (comp, pimmediate))
return 0;
}
#endif
/* If the current binding state has already expired, do an
expiry event right now. */
/* XXX At some point we should optimize this so that we don't
XXX write the lease twice, but this is a safe way to fix the
XXX problem for 3.0 (I hope!). */
if ((commit || !pimmediate) &&
comp -> sort_time < cur_time &&
comp -> next_binding_state != comp -> binding_state)
pool_timer (comp -> pool);
return 1;
}
void make_binding_state_transition (struct lease *lease)
{
#if defined (FAILOVER_PROTOCOL)
dhcp_failover_state_t *peer;
if (lease && lease -> pool && lease -> pool -> failover_peer)
peer = lease -> pool -> failover_peer;
else
peer = (dhcp_failover_state_t *)0;
#endif
/* If the lease was active and is now no longer active, but isn't
released, then it just expired, so do the expiry event. */
if (lease -> next_binding_state != lease -> binding_state &&
((
#if defined (FAILOVER_PROTOCOL)
peer &&
(lease -> binding_state == FTS_EXPIRED ||
(peer -> i_am == secondary &&
lease -> binding_state == FTS_ACTIVE)) &&
(lease -> next_binding_state == FTS_FREE ||
lease -> next_binding_state == FTS_BACKUP)) ||
(!peer &&
#endif
lease -> binding_state == FTS_ACTIVE &&
lease -> next_binding_state != FTS_RELEASED))) {
#if defined (NSUPDATE)
ddns_removals (lease);
#endif
if (lease -> on_expiry) {
execute_statements ((struct binding_value **)0,
(struct packet *)0, lease,
(struct client_state *)0,
(struct option_state *)0,
(struct option_state *)0, /* XXX */
&lease -> scope,
lease -> on_expiry);
if (lease -> on_expiry)
executable_statement_dereference
(&lease -> on_expiry, MDL);
}
/* No sense releasing a lease after it's expired. */
if (lease -> on_release)
executable_statement_dereference (&lease -> on_release,
MDL);
/* Get rid of client-specific bindings that are only
correct when the lease is active. */
if (lease -> billing_class)
unbill_class (lease, lease -> billing_class);
if (lease -> agent_options)
option_chain_head_dereference (&lease -> agent_options,
MDL);
if (lease -> client_hostname) {
dfree (lease -> client_hostname, MDL);
lease -> client_hostname = (char *)0;
}
if (lease -> host)
host_dereference (&lease -> host, MDL);
/* Send the expiry time to the peer. */
lease -> tstp = lease -> ends;
}
/* If the lease was active and is now released, do the release
event. */
if (lease -> next_binding_state != lease -> binding_state &&
((
#if defined (FAILOVER_PROTOCOL)
peer &&
lease -> binding_state == FTS_RELEASED &&
(lease -> next_binding_state == FTS_FREE ||
lease -> next_binding_state == FTS_BACKUP)) ||
(!peer &&
#endif
lease -> binding_state == FTS_ACTIVE &&
lease -> next_binding_state == FTS_RELEASED))) {
#if defined (NSUPDATE)
ddns_removals (lease);
#endif
if (lease -> on_release) {
execute_statements ((struct binding_value **)0,
(struct packet *)0, lease,
(struct client_state *)0,
(struct option_state *)0,
(struct option_state *)0, /* XXX */
&lease -> scope,
lease -> on_release);
executable_statement_dereference (&lease -> on_release,
MDL);
}
/* A released lease can't expire. */
if (lease -> on_expiry)
executable_statement_dereference (&lease -> on_expiry,
MDL);
/* Get rid of client-specific bindings that are only
correct when the lease is active. */
if (lease -> billing_class)
unbill_class (lease, lease -> billing_class);
if (lease -> agent_options)
option_chain_head_dereference (&lease -> agent_options,
MDL);
if (lease -> client_hostname) {
dfree (lease -> client_hostname, MDL);
lease -> client_hostname = (char *)0;
}
if (lease -> host)
host_dereference (&lease -> host, MDL);
/* Send the release time (should be == cur_time) to the
peer. */
lease -> tstp = lease -> ends;
}
#if defined (DEBUG_LEASE_STATE_TRANSITIONS)
log_debug ("lease %s moves from %s to %s",
piaddr (lease -> ip_addr),
binding_state_print (lease -> binding_state),
binding_state_print (lease -> next_binding_state));
#endif
lease -> binding_state = lease -> next_binding_state;
switch (lease -> binding_state) {
case FTS_ACTIVE:
#if defined (FAILOVER_PROTOCOL)
if (lease -> pool && lease -> pool -> failover_peer)
lease -> next_binding_state = FTS_EXPIRED;
else
#endif
lease -> next_binding_state = FTS_FREE;
break;
case FTS_EXPIRED:
case FTS_RELEASED:
case FTS_ABANDONED:
case FTS_RESET:
lease -> next_binding_state = FTS_FREE;
/* If we are not in partner_down, leases don't go from
EXPIRED to FREE on a timeout - only on an update.
If we're in partner_down, they expire at mclt past
the time we entered partner_down. */
if (lease -> pool -> failover_peer &&
lease -> pool -> failover_peer -> me.state == partner_down)
lease -> tsfp =
(lease -> pool -> failover_peer -> me.stos +
lease -> pool -> failover_peer -> mclt);
break;
case FTS_FREE:
case FTS_BACKUP:
lease -> next_binding_state = lease -> binding_state;
break;
}
#if defined (DEBUG_LEASE_STATE_TRANSITIONS)
log_debug ("lease %s: next binding state %s",
piaddr (lease -> ip_addr),
binding_state_print (lease -> next_binding_state));
#endif
}
/* Copy the contents of one lease into another, correctly maintaining
reference counts. */
int lease_copy (struct lease **lp,
struct lease *lease, const char *file, int line)
{
struct lease *lt = (struct lease *)0;
isc_result_t status;
status = lease_allocate (&lt, MDL);
if (status != ISC_R_SUCCESS)
return 0;
lt -> ip_addr = lease -> ip_addr;
lt -> starts = lease -> starts;
lt -> ends = lease -> ends;
lt -> timestamp = lease -> timestamp;
lt -> uid_len = lease -> uid_len;
lt -> uid_max = lease -> uid_max;
if (lease -> uid == lease -> uid_buf) {
lt -> uid = lt -> uid_buf;
memcpy (lt -> uid_buf, lease -> uid_buf, sizeof lt -> uid_buf);
} else if (!lease -> uid_max) {
lt -> uid = (unsigned char *)0;
} else {
lt -> uid = dmalloc (lt -> uid_max, MDL);
if (!lt -> uid) {
lease_dereference (&lt, MDL);
return 0;
}
memcpy (lt -> uid, lease -> uid, lease -> uid_max);
}
if (lease -> client_hostname) {
lt -> client_hostname =
dmalloc (strlen (lease -> client_hostname) + 1, MDL);
if (!lt -> client_hostname) {
lease_dereference (&lt, MDL);
return 0;
}
strcpy (lt -> client_hostname, lease -> client_hostname);
}
if (lease -> scope)
binding_scope_reference (&lt -> scope, lease -> scope, MDL);
if (lease -> agent_options)
option_chain_head_reference (&lt -> agent_options,
lease -> agent_options, MDL);
host_reference (&lt -> host, lease -> host, file, line);
subnet_reference (&lt -> subnet, lease -> subnet, file, line);
pool_reference (&lt -> pool, lease -> pool, file, line);
class_reference (&lt -> billing_class,
lease -> billing_class, file, line);
lt -> hardware_addr = lease -> hardware_addr;
if (lease -> on_expiry)
executable_statement_reference (&lt -> on_expiry,
lease -> on_expiry,
file, line);
if (lease -> on_commit)
executable_statement_reference (&lt -> on_commit,
lease -> on_commit,
file, line);
if (lease -> on_release)
executable_statement_reference (&lt -> on_release,
lease -> on_release,
file, line);
lt -> flags = lease -> flags;
lt -> tstp = lease -> tstp;
lt -> tsfp = lease -> tsfp;
lt -> cltt = lease -> cltt;
lt -> binding_state = lease -> binding_state;
lt -> next_binding_state = lease -> next_binding_state;
status = lease_reference (lp, lt, file, line);
lease_dereference (&lt, MDL);
return status == ISC_R_SUCCESS;
}
/* Release the specified lease and re-hash it as appropriate. */
void release_lease (lease, packet)
struct lease *lease;
struct packet *packet;
{
/* If there are statements to execute when the lease is
released, execute them. */
#if defined (NSUPDATE)
ddns_removals (lease);
#endif
if (lease -> on_release) {
execute_statements ((struct binding_value **)0,
packet, lease, (struct client_state *)0,
packet -> options,
(struct option_state *)0, /* XXX */
&lease -> scope, lease -> on_release);
if (lease -> on_release)
executable_statement_dereference (&lease -> on_release,
MDL);
}
/* We do either the on_release or the on_expiry events, but
not both (it's possible that they could be the same,
in any case). */
if (lease -> on_expiry)
executable_statement_dereference (&lease -> on_expiry, MDL);
if (lease -> binding_state != FTS_FREE &&
lease -> binding_state != FTS_BACKUP &&
lease -> binding_state != FTS_RELEASED &&
lease -> binding_state != FTS_EXPIRED &&
lease -> binding_state != FTS_RESET) {
if (lease -> on_commit)
executable_statement_dereference (&lease -> on_commit,
MDL);
/* Blow away any bindings. */
if (lease -> scope)
binding_scope_dereference (&lease -> scope, MDL);
lease -> ends = cur_time;
#if defined (FAILOVER_PROTOCOL)
if (lease -> pool && lease -> pool -> failover_peer) {
lease -> next_binding_state = FTS_RELEASED;
} else {
lease -> next_binding_state = FTS_FREE;
}
#else
lease -> next_binding_state = FTS_FREE;
#endif
supersede_lease (lease, (struct lease *)0, 1, 1, 1);
}
}
/* Abandon the specified lease (set its timeout to infinity and its
particulars to zero, and re-hash it as appropriate. */
void abandon_lease (lease, message)
struct lease *lease;
const char *message;
{
struct lease *lt = (struct lease *)0;
if (!lease_copy (&lt, lease, MDL))
return;
if (lt->scope)
binding_scope_dereference(&lt->scope, MDL);
lt -> ends = cur_time; /* XXX */
lt -> next_binding_state = FTS_ABANDONED;
log_error ("Abandoning IP address %s: %s",
piaddr (lease -> ip_addr), message);
lt -> hardware_addr.hlen = 0;
if (lt -> uid && lt -> uid != lt -> uid_buf)
dfree (lt -> uid, MDL);
lt -> uid = (unsigned char *)0;
lt -> uid_len = 0;
lt -> uid_max = 0;
supersede_lease (lease, lt, 1, 1, 1);
lease_dereference (&lt, MDL);
}
/* Abandon the specified lease (set its timeout to infinity and its
particulars to zero, and re-hash it as appropriate. */
void dissociate_lease (lease)
struct lease *lease;
{
struct lease *lt = (struct lease *)0;
if (!lease_copy (&lt, lease, MDL))
return;
#if defined (FAILOVER_PROTOCOL)
if (lease -> pool && lease -> pool -> failover_peer) {
lt -> next_binding_state = FTS_RESET;
} else {
lt -> next_binding_state = FTS_FREE;
}
#else
lt -> next_binding_state = FTS_FREE;
#endif
lt -> ends = cur_time; /* XXX */
lt -> hardware_addr.hlen = 0;
if (lt -> uid && lt -> uid != lt -> uid_buf)
dfree (lt -> uid, MDL);
lt -> uid = (unsigned char *)0;
lt -> uid_len = 0;
lt -> uid_max = 0;
supersede_lease (lease, lt, 1, 1, 1);
lease_dereference (&lt, MDL);
}
/* Timer called when a lease in a particular pool expires. */
void pool_timer (vpool)
void *vpool;
{
struct pool *pool;
struct lease *next = (struct lease *)0;
struct lease *lease = (struct lease *)0;
struct lease **lptr [5];
TIME next_expiry = MAX_TIME;
int i;
pool = (struct pool *)vpool;
#define FREE_LEASES 0
lptr [FREE_LEASES] = &pool -> free;
#define ACTIVE_LEASES 1
lptr [ACTIVE_LEASES] = &pool -> active;
#define EXPIRED_LEASES 2
lptr [EXPIRED_LEASES] = &pool -> expired;
#define ABANDONED_LEASES 3
lptr [ABANDONED_LEASES] = &pool -> abandoned;
#define BACKUP_LEASES 4
lptr [BACKUP_LEASES] = &pool -> backup;
for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
/* If there's nothing on the queue, skip it. */
if (!*(lptr [i]))
continue;
#if defined (FAILOVER_PROTOCOL)
if (pool -> failover_peer &&
pool -> failover_peer -> me.state != partner_down) {
/* The secondary can't remove a lease from the
active state except in partner_down. */
if (i == ACTIVE_LEASES &&
pool -> failover_peer -> i_am == secondary)
continue;
/* Leases in an expired state don't move to
free because of a timeout unless we're in
partner_down. */
if (i == EXPIRED_LEASES)
continue;
}
#endif
lease_reference (&lease, *(lptr [i]), MDL);
while (lease) {
/* Remember the next lease in the list. */
if (next)
lease_dereference (&next, MDL);
if (lease -> next)
lease_reference (&next, lease -> next, MDL);
/* If we've run out of things to expire on this list,
stop. */
if (lease -> sort_time > cur_time) {
if (lease -> sort_time < next_expiry)
next_expiry = lease -> sort_time;
break;
}
/* If there is a pending state change, and
this lease has gotten to the time when the
state change should happen, just call
supersede_lease on it to make the change
happen. */
if (lease -> next_binding_state !=
lease -> binding_state)
supersede_lease (lease,
(struct lease *)0, 1, 1, 1);
lease_dereference (&lease, MDL);
if (next)
lease_reference (&lease, next, MDL);
}
if (next)
lease_dereference (&next, MDL);
if (lease)
lease_dereference (&lease, MDL);
}
if (next_expiry != MAX_TIME) {
pool -> next_event_time = next_expiry;
add_timeout (pool -> next_event_time, pool_timer, pool,
(tvref_t)pool_reference,
(tvunref_t)pool_dereference);
} else
pool -> next_event_time = MIN_TIME;
}
/* Locate the lease associated with a given IP address... */
int find_lease_by_ip_addr (struct lease **lp, struct iaddr addr,
const char *file, int line)
{
return lease_hash_lookup (lp, lease_ip_addr_hash,
addr.iabuf, addr.len, file, line);
}
int find_lease_by_uid (struct lease **lp, const unsigned char *uid,
unsigned len, const char *file, int line)
{
if (len == 0)
return 0;
return lease_hash_lookup (lp, lease_uid_hash, uid, len, file, line);
}
int find_lease_by_hw_addr (struct lease **lp,
const unsigned char *hwaddr, unsigned hwlen,
const char *file, int line)
{
if (hwlen == 0)
return 0;
return lease_hash_lookup (lp, lease_hw_addr_hash,
hwaddr, hwlen, file, line);
}
/* Add the specified lease to the uid hash. */
void uid_hash_add (lease)
struct lease *lease;
{
struct lease *head = (struct lease *)0;
struct lease *next = (struct lease *)0;
/* If it's not in the hash, just add it. */
if (!find_lease_by_uid (&head, lease -> uid, lease -> uid_len, MDL))
lease_hash_add (lease_uid_hash, lease -> uid,
lease -> uid_len, lease, MDL);
else {
/* Otherwise, attach it to the end of the list. */
while (head -> n_uid) {
lease_reference (&next, head -> n_uid, MDL);
lease_dereference (&head, MDL);
lease_reference (&head, next, MDL);
lease_dereference (&next, MDL);
}
lease_reference (&head -> n_uid, lease, MDL);
lease_dereference (&head, MDL);
}
}
/* Delete the specified lease from the uid hash. */
void uid_hash_delete (lease)
struct lease *lease;
{
struct lease *head = (struct lease *)0;
struct lease *scan;
/* If it's not in the hash, we have no work to do. */
if (!find_lease_by_uid (&head, lease -> uid, lease -> uid_len, MDL)) {
if (lease -> n_uid)
lease_dereference (&lease -> n_uid, MDL);
return;
}
/* If the lease we're freeing is at the head of the list,
remove the hash table entry and add a new one with the
next lease on the list (if there is one). */
if (head == lease) {
lease_hash_delete (lease_uid_hash,
lease -> uid, lease -> uid_len, MDL);
if (lease -> n_uid) {
lease_hash_add (lease_uid_hash,
lease -> n_uid -> uid,
lease -> n_uid -> uid_len,
lease -> n_uid, MDL);
lease_dereference (&lease -> n_uid, MDL);
}
} else {
/* Otherwise, look for the lease in the list of leases
attached to the hash table entry, and remove it if
we find it. */
for (scan = head; scan -> n_uid; scan = scan -> n_uid) {
if (scan -> n_uid == lease) {
lease_dereference (&scan -> n_uid, MDL);
if (lease -> n_uid) {
lease_reference (&scan -> n_uid,
lease -> n_uid, MDL);
lease_dereference (&lease -> n_uid,
MDL);
}
break;
}
}
}
lease_dereference (&head, MDL);
}
/* Add the specified lease to the hardware address hash. */
void hw_hash_add (lease)
struct lease *lease;
{
struct lease *head = (struct lease *)0;
struct lease *next = (struct lease *)0;
/* If it's not in the hash, just add it. */
if (!find_lease_by_hw_addr (&head, lease -> hardware_addr.hbuf,
lease -> hardware_addr.hlen, MDL))
lease_hash_add (lease_hw_addr_hash,
lease -> hardware_addr.hbuf,
lease -> hardware_addr.hlen,
lease, MDL);
else {
/* Otherwise, attach it to the end of the list. */
while (head -> n_hw) {
lease_reference (&next, head -> n_hw, MDL);
lease_dereference (&head, MDL);
lease_reference (&head, next, MDL);
lease_dereference (&next, MDL);
}
lease_reference (&head -> n_hw, lease, MDL);
lease_dereference (&head, MDL);
}
}
/* Delete the specified lease from the hardware address hash. */
void hw_hash_delete (lease)
struct lease *lease;
{
struct lease *head = (struct lease *)0;
struct lease *next = (struct lease *)0;
/* If it's not in the hash, we have no work to do. */
if (!find_lease_by_hw_addr (&head, lease -> hardware_addr.hbuf,
lease -> hardware_addr.hlen, MDL)) {
if (lease -> n_hw)
lease_dereference (&lease -> n_hw, MDL);
return;
}
/* If the lease we're freeing is at the head of the list,
remove the hash table entry and add a new one with the
next lease on the list (if there is one). */
if (head == lease) {
lease_hash_delete (lease_hw_addr_hash,
lease -> hardware_addr.hbuf,
lease -> hardware_addr.hlen, MDL);
if (lease -> n_hw) {
lease_hash_add (lease_hw_addr_hash,
lease -> n_hw -> hardware_addr.hbuf,
lease -> n_hw -> hardware_addr.hlen,
lease -> n_hw, MDL);
lease_dereference (&lease -> n_hw, MDL);
}
} else {
/* Otherwise, look for the lease in the list of leases
attached to the hash table entry, and remove it if
we find it. */
while (head -> n_hw) {
if (head -> n_hw == lease) {
lease_dereference (&head -> n_hw, MDL);
if (lease -> n_hw) {
lease_reference (&head -> n_hw,
lease -> n_hw, MDL);
lease_dereference (&lease -> n_hw,
MDL);
}
break;
}
lease_reference (&next, head -> n_hw, MDL);
lease_dereference (&head, MDL);
lease_reference (&head, next, MDL);
lease_dereference (&next, MDL);
}
}
if (head)
lease_dereference (&head, MDL);
}
/* Write all interesting leases to permanent storage. */
int write_leases ()
{
struct lease *l;
struct shared_network *s;
struct pool *p;
struct host_decl *hp;
struct group_object *gp;
struct hash_bucket *hb;
int i;
int num_written;
struct lease **lptr [5];
/* Write all the dynamically-created group declarations. */
if (group_name_hash) {
num_written = 0;
for (i = 0; i < group_name_hash -> hash_count; i++) {
for (hb = group_name_hash -> buckets [i];
hb; hb = hb -> next) {
gp = (struct group_object *)hb -> value;
if ((gp -> flags & GROUP_OBJECT_DYNAMIC) ||
((gp -> flags & GROUP_OBJECT_STATIC) &&
(gp -> flags & GROUP_OBJECT_DELETED))) {
if (!write_group (gp))
return 0;
++num_written;
}
}
}
log_info ("Wrote %d group decls to leases file.", num_written);
}
/* Write all the deleted host declarations. */
if (host_name_hash) {
num_written = 0;
for (i = 0; i < host_name_hash -> hash_count; i++) {
for (hb = host_name_hash -> buckets [i];
hb; hb = hb -> next) {
hp = (struct host_decl *)hb -> value;
if (((hp -> flags & HOST_DECL_STATIC) &&
(hp -> flags & HOST_DECL_DELETED))) {
if (!write_host (hp))
return 0;
++num_written;
}
}
}
log_info ("Wrote %d deleted host decls to leases file.",
num_written);
}
/* Write all the new, dynamic host declarations. */
if (host_name_hash) {
num_written = 0;
for (i = 0; i < host_name_hash -> hash_count; i++) {
for (hb = host_name_hash -> buckets [i];
hb; hb = hb -> next) {
hp = (struct host_decl *)hb -> value;
if ((hp -> flags & HOST_DECL_DYNAMIC)) {
if (!write_host (hp))
++num_written;
}
}
}
log_info ("Wrote %d new dynamic host decls to leases file.",
num_written);
}
#if defined (FAILOVER_PROTOCOL)
/* Write all the failover states. */
if (!dhcp_failover_write_all_states ())
return 0;
#endif
/* Write all the leases. */
num_written = 0;
for (s = shared_networks; s; s = s -> next) {
for (p = s -> pools; p; p = p -> next) {
lptr [FREE_LEASES] = &p -> free;
lptr [ACTIVE_LEASES] = &p -> active;
lptr [EXPIRED_LEASES] = &p -> expired;
lptr [ABANDONED_LEASES] = &p -> abandoned;
lptr [BACKUP_LEASES] = &p -> backup;
for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
for (l = *(lptr [i]); l; l = l -> next) {
#if !defined (DEBUG_DUMP_ALL_LEASES)
if (l -> hardware_addr.hlen ||
l -> uid_len ||
(l -> binding_state != FTS_FREE))
#endif
{
if (!write_lease (l))
return 0;
num_written++;
}
}
}
}
}
log_info ("Wrote %d leases to leases file.", num_written);
if (!commit_leases ())
return 0;
return 1;
}
int lease_enqueue (struct lease *comp)
{
struct lease **lq, *prev, *lp;
/* No queue to put it on? */
if (!comp -> pool)
return 0;
/* Figure out which queue it's going to. */
switch (comp -> binding_state) {
case FTS_FREE:
lq = &comp -> pool -> free;
comp -> pool -> free_leases++;
comp -> sort_time = comp -> ends;
break;
case FTS_ACTIVE:
lq = &comp -> pool -> active;
comp -> sort_time = comp -> ends;
break;
case FTS_EXPIRED:
case FTS_RELEASED:
case FTS_RESET:
lq = &comp -> pool -> expired;
comp -> sort_time = comp -> ends;
break;
case FTS_ABANDONED:
lq = &comp -> pool -> abandoned;
comp -> sort_time = comp -> ends;
break;
case FTS_BACKUP:
lq = &comp -> pool -> backup;
comp -> pool -> backup_leases++;
comp -> sort_time = comp -> ends;
break;
default:
log_error ("Lease with bogus binding state: %d",
comp -> binding_state);
#if defined (BINDING_STATE_DEBUG)
abort ();
#endif
return 0;
}
/* Insertion sort the lease onto the appropriate queue. */
prev = (struct lease *)0;
for (lp = *lq; lp; lp = lp -> next) {
if (lp -> sort_time >= comp -> sort_time)
break;
prev = lp;
}
if (prev) {
if (prev -> next) {
lease_reference (&comp -> next, prev -> next, MDL);
lease_dereference (&prev -> next, MDL);
}
lease_reference (&prev -> next, comp, MDL);
} else {
if (*lq) {
lease_reference (&comp -> next, *lq, MDL);
lease_dereference (lq, MDL);
}
lease_reference (lq, comp, MDL);
}
return 1;
}
/* For a given lease, sort it onto the right list in its pool and put it
in each appropriate hash, understanding that it's already by definition
in lease_ip_addr_hash. */
void lease_instantiate (const unsigned char *val, unsigned len,
struct lease *lease)
{
struct class *class;
/* XXX If the lease doesn't have a pool at this point, it's an
XXX orphan, which we *should* keep around until it expires,
XXX but which right now we just forget. */
if (!lease -> pool) {
lease_hash_delete (lease_ip_addr_hash,
lease -> ip_addr.iabuf,
lease -> ip_addr.len, MDL);
return;
}
/* Put the lease on the right queue. */
lease_enqueue (lease);
/* Record the lease in the uid hash if possible. */
if (lease -> uid) {
uid_hash_add (lease);
}
/* Record it in the hardware address hash if possible. */
if (lease -> hardware_addr.hlen) {
hw_hash_add (lease);
}
/* If the lease has a billing class, set up the billing. */
if (lease -> billing_class) {
class = (struct class *)0;
class_reference (&class, lease -> billing_class, MDL);
class_dereference (&lease -> billing_class, MDL);
/* If the lease is available for allocation, the billing
is invalid, so we don't keep it. */
if (lease -> binding_state == FTS_ACTIVE ||
lease -> binding_state == FTS_EXPIRED ||
lease -> binding_state == FTS_RELEASED ||
lease -> binding_state == FTS_RESET)
bill_class (lease, class);
class_dereference (&class, MDL);
}
return;
}
/* Run expiry events on every pool. This is called on startup so that
any expiry events that occurred after the server stopped and before it
was restarted can be run. At the same time, if failover support is
compiled in, we compute the balance of leases for the pool. */
void expire_all_pools ()
{
struct shared_network *s;
struct pool *p;
int i;
struct lease *l;
struct lease **lptr [5];
/* First, go over the hash list and actually put all the leases
on the appropriate lists. */
lease_hash_foreach (lease_ip_addr_hash, lease_instantiate);
/* Loop through each pool in each shared network and call the
expiry routine on the pool. */
for (s = shared_networks; s; s = s -> next) {
for (p = s -> pools; p; p = p -> next) {
pool_timer (p);
p -> lease_count = 0;
p -> free_leases = 0;
p -> backup_leases = 0;
lptr [FREE_LEASES] = &p -> free;
lptr [ACTIVE_LEASES] = &p -> active;
lptr [EXPIRED_LEASES] = &p -> expired;
lptr [ABANDONED_LEASES] = &p -> abandoned;
lptr [BACKUP_LEASES] = &p -> backup;
for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
for (l = *(lptr [i]); l; l = l -> next) {
p -> lease_count++;
if (l -> ends <= cur_time) {
if (l -> binding_state == FTS_FREE)
p -> free_leases++;
else if (l -> binding_state == FTS_BACKUP)
p -> backup_leases++;
}
#if defined (FAILOVER_PROTOCOL)
if (p -> failover_peer &&
l -> tstp > l -> tsfp &&
!(l -> flags & ON_UPDATE_QUEUE)) {
l -> desired_binding_state = l -> binding_state;
dhcp_failover_queue_update (l, 1);
}
#endif
}
}
}
}
}
void dump_subnets ()
{
struct lease *l;
struct shared_network *s;
struct subnet *n;
struct pool *p;
struct lease **lptr [5];
int i;
log_info ("Subnets:");
for (n = subnets; n; n = n -> next_subnet) {
log_debug (" Subnet %s", piaddr (n -> net));
log_debug (" netmask %s",
piaddr (n -> netmask));
}
log_info ("Shared networks:");
for (s = shared_networks; s; s = s -> next) {
log_info (" %s", s -> name);
for (p = s -> pools; p; p = p -> next) {
lptr [FREE_LEASES] = &p -> free;
lptr [ACTIVE_LEASES] = &p -> active;
lptr [EXPIRED_LEASES] = &p -> expired;
lptr [ABANDONED_LEASES] = &p -> abandoned;
lptr [BACKUP_LEASES] = &p -> backup;
for (i = FREE_LEASES; i <= BACKUP_LEASES; i++) {
for (l = *(lptr [i]); l; l = l -> next) {
print_lease (l);
}
}
}
}
}
HASH_FUNCTIONS (lease, const unsigned char *, struct lease, lease_hash_t,
lease_reference, lease_dereference)
HASH_FUNCTIONS (host, const unsigned char *, struct host_decl, host_hash_t,
host_reference, host_dereference)
HASH_FUNCTIONS (class, const char *, struct class, class_hash_t,
class_reference, class_dereference)
#if defined (DEBUG_MEMORY_LEAKAGE) && \
defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
extern struct hash_table *dns_zone_hash;
extern struct interface_info **interface_vector;
extern int interface_count;
dhcp_control_object_t *dhcp_control_object;
extern struct hash_table *auth_key_hash;
struct hash_table *universe_hash;
struct universe **universes;
int universe_count, universe_max;
#if 0
extern int end;
#endif
#if defined (COMPACT_LEASES)
extern struct lease *lease_hunks;
#endif
void free_everything ()
{
struct subnet *sc = (struct subnet *)0, *sn = (struct subnet *)0;
struct shared_network *nc = (struct shared_network *)0,
*nn = (struct shared_network *)0;
struct pool *pc = (struct pool *)0, *pn = (struct pool *)0;
struct lease *lc = (struct lease *)0, *ln = (struct lease *)0;
struct interface_info *ic = (struct interface_info *)0,
*in = (struct interface_info *)0;
struct class *cc = (struct class *)0, *cn = (struct class *)0;
struct collection *lp;
void *st = (shared_networks
? (shared_networks -> next
? shared_networks -> next -> next : 0) : 0);
int i;
/* Get rid of all the hash tables. */
if (host_hw_addr_hash)
host_free_hash_table (&host_hw_addr_hash, MDL);
host_hw_addr_hash = 0;
if (host_uid_hash)
host_free_hash_table (&host_uid_hash, MDL);
host_uid_hash = 0;
if (lease_uid_hash)
lease_free_hash_table (&lease_uid_hash, MDL);
lease_uid_hash = 0;
if (lease_ip_addr_hash)
lease_free_hash_table (&lease_ip_addr_hash, MDL);
lease_ip_addr_hash = 0;
if (lease_hw_addr_hash)
lease_free_hash_table (&lease_hw_addr_hash, MDL);
lease_hw_addr_hash = 0;
if (host_name_hash)
host_free_hash_table (&host_name_hash, MDL);
host_name_hash = 0;
if (dns_zone_hash)
dns_zone_free_hash_table (&dns_zone_hash, MDL);
dns_zone_hash = 0;
#if 0
if (auth_key_hash)
auth_key_free_hash_table (&auth_key_hash, MDL);
#endif
auth_key_hash = 0;
omapi_object_dereference ((omapi_object_t **)&dhcp_control_object,
MDL);
for (lp = collections; lp; lp = lp -> next) {
if (lp -> classes) {
class_reference (&cn, lp -> classes, MDL);
do {
if (cn) {
class_reference (&cc, cn, MDL);
class_dereference (&cn, MDL);
}
if (cc -> nic) {
class_reference (&cn, cc -> nic, MDL);
class_dereference (&cc -> nic, MDL);
}
group_dereference (&cc -> group, MDL);
if (cc -> hash) {
class_free_hash_table (&cc -> hash, MDL);
cc -> hash = (struct hash_table *)0;
}
class_dereference (&cc, MDL);
} while (cn);
class_dereference (&lp -> classes, MDL);
}
}
if (interface_vector) {
for (i = 0; i < interface_count; i++) {
if (interface_vector [i])
interface_dereference (&interface_vector [i], MDL);
}
dfree (interface_vector, MDL);
interface_vector = 0;
}
if (interfaces) {
interface_reference (&in, interfaces, MDL);
do {
if (in) {
interface_reference (&ic, in, MDL);
interface_dereference (&in, MDL);
}
if (ic -> next) {
interface_reference (&in, ic -> next, MDL);
interface_dereference (&ic -> next, MDL);
}
omapi_unregister_io_object ((omapi_object_t *)ic);
if (ic -> shared_network) {
if (ic -> shared_network -> interface)
interface_dereference
(&ic -> shared_network -> interface, MDL);
shared_network_dereference (&ic -> shared_network, MDL);
}
interface_dereference (&ic, MDL);
} while (in);
interface_dereference (&interfaces, MDL);
}
/* Subnets are complicated because of the extra links. */
if (subnets) {
subnet_reference (&sn, subnets, MDL);
do {
if (sn) {
subnet_reference (&sc, sn, MDL);
subnet_dereference (&sn, MDL);
}
if (sc -> next_subnet) {
subnet_reference (&sn, sc -> next_subnet, MDL);
subnet_dereference (&sc -> next_subnet, MDL);
}
if (sc -> next_sibling)
subnet_dereference (&sc -> next_sibling, MDL);
if (sc -> shared_network)
shared_network_dereference (&sc -> shared_network, MDL);
group_dereference (&sc -> group, MDL);
if (sc -> interface)
interface_dereference (&sc -> interface, MDL);
subnet_dereference (&sc, MDL);
} while (sn);
subnet_dereference (&subnets, MDL);
}
/* So are shared networks. */
if (shared_networks) {
shared_network_reference (&nn, shared_networks, MDL);
do {
if (nn) {
shared_network_reference (&nc, nn, MDL);
shared_network_dereference (&nn, MDL);
}
if (nc -> next) {
shared_network_reference (&nn, nc -> next, MDL);
shared_network_dereference (&nc -> next, MDL);
}
/* As are pools. */
if (nc -> pools) {
pool_reference (&pn, nc -> pools, MDL);
do {
struct lease **lptr [5];
if (pn) {
pool_reference (&pc, pn, MDL);
pool_dereference (&pn, MDL);
}
if (pc -> next) {
pool_reference (&pn, pc -> next, MDL);
pool_dereference (&pc -> next, MDL);
}
lptr [FREE_LEASES] = &pc -> free;
lptr [ACTIVE_LEASES] = &pc -> active;
lptr [EXPIRED_LEASES] = &pc -> expired;
lptr [ABANDONED_LEASES] = &pc -> abandoned;
lptr [BACKUP_LEASES] = &pc -> backup;
/* As (sigh) are leases. */
for (i = 0; i < 5; i++) {
if (*lptr [i]) {
lease_reference (&ln, *lptr [i], MDL);
do {
if (ln) {
lease_reference (&lc, ln, MDL);
lease_dereference (&ln, MDL);
}
if (lc -> next) {
lease_reference (&ln, lc -> next, MDL);
lease_dereference (&lc -> next, MDL);
}
if (lc -> billing_class)
class_dereference (&lc -> billing_class,
MDL);
if (lc -> state)
free_lease_state (lc -> state, MDL);
lc -> state = (struct lease_state *)0;
if (lc -> n_hw)
lease_dereference (&lc -> n_hw, MDL);
if (lc -> n_uid)
lease_dereference (&lc -> n_uid, MDL);
lease_dereference (&lc, MDL);
} while (ln);
lease_dereference (lptr [i], MDL);
}
}
if (pc -> group)
group_dereference (&pc -> group, MDL);
if (pc -> shared_network)
shared_network_dereference (&pc -> shared_network,
MDL);
pool_dereference (&pc, MDL);
} while (pn);
pool_dereference (&nc -> pools, MDL);
}
/* Because of a circular reference, we need to nuke this
manually. */
group_dereference (&nc -> group, MDL);
shared_network_dereference (&nc, MDL);
} while (nn);
shared_network_dereference (&shared_networks, MDL);
}
cancel_all_timeouts ();
relinquish_timeouts ();
trace_free_all ();
group_dereference (&root_group, MDL);
executable_statement_dereference (&default_classification_rules, MDL);
shutdown_state = shutdown_drop_omapi_connections;
omapi_io_state_foreach (dhcp_io_shutdown, 0);
shutdown_state = shutdown_listeners;
omapi_io_state_foreach (dhcp_io_shutdown, 0);
shutdown_state = shutdown_dhcp;
omapi_io_state_foreach (dhcp_io_shutdown, 0);
omapi_object_dereference ((omapi_object_t **)&icmp_state, MDL);
universe_free_hash_table (&universe_hash, MDL);
for (i = 0; i < universe_count; i++) {
union {
const char *c;
char *s;
} foo;
if (universes [i]) {
if (universes [i] -> hash)
option_free_hash_table (&universes [i] -> hash,
MDL);
#if 0
if (universes [i] -> name > (char *)&end) {
foo.c = universes [i] -> name;
dfree (foo.s, MDL);
}
if (universes [i] > (struct universe *)&end)
dfree (universes [i], MDL);
#endif
}
}
dfree (universes, MDL);
relinquish_free_lease_states ();
relinquish_free_pairs ();
relinquish_free_expressions ();
relinquish_free_binding_values ();
relinquish_free_option_caches ();
relinquish_free_packets ();
relinquish_lease_hunks ();
relinquish_hash_bucket_hunks ();
omapi_type_relinquish ();
}
#endif /* DEBUG_MEMORY_LEAKAGE_ON_EXIT */