]> git.ipfire.org Git - thirdparty/dhcp.git/blobdiff - server/mdb.c
Update RELNOTES
[thirdparty/dhcp.git] / server / mdb.c
index 536407dd1ff56fce96e6342481ae6c6416606b33..b982cc16e51b6207b5277531be707c7630ab0be3 100644 (file)
@@ -3,12 +3,12 @@
    Server-specific in-memory database support. */
 
 /*
- * Copyright (c) 2004-2006 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2019 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.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
  *   Internet Systems Consortium, Inc.
- *   950 Charter Street
- *   Redwood City, CA 94063
+ *   PO Box 360
+ *   Newmarket, NH 03857 USA
  *   <info@isc.org>
- *   http://www.isc.org/
+ *   https://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.89 2007/05/08 23:05:22 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium.  All rights reserved.\n";
-#endif /* not lint */
-
 #include "dhcpd.h"
 #include "omapip/hash.h"
 
@@ -52,17 +41,25 @@ lease_id_hash_t *lease_hw_addr_hash;
 /*
  * We allow users to specify any option as a host identifier.
  *
- * Any host is uniquely identified by the combination of 
+ * Any host is uniquely identified by the combination of
  * option type & option data.
  *
- * We expect people will only use a few types of options as host 
+ * We expect people will only use a few types of options as host
  * identifier. Because of this, we store a list with an entry for
- * each option type. Each of these has a hash table, which contains 
+ * each option type. Each of these has a hash table, which contains
  * hash of the option data.
+ *
+ * For v6 we also include a relay count - this specifies which
+ * relay to check for the requested option.  As each different
+ * value of relays creates a new instance admins should use the
+ * same value across each option for all host-identifers.
+ * A value of 0 indicates that we aren't doing relay options
+ * and should simply look in the current option list.
  */
 typedef struct host_id_info {
        struct option *option;
        host_hash_t *values_hash;
+       int relays;
        struct host_id_info *next;
 } host_id_info_t;
 
@@ -70,7 +67,7 @@ static host_id_info_t *host_id_info = NULL;
 
 int numclasseswritten;
 
-omapi_object_type_t *dhcp_type_host;
+extern omapi_object_type_t *dhcp_type_host;
 
 isc_result_t enter_class(cd, dynamicp, commit)
        struct class *cd;
@@ -80,7 +77,7 @@ isc_result_t enter_class(cd, dynamicp, commit)
        if (!collections -> classes) {
                /* A subclass with no parent is invalid. */
                if (cd->name == NULL)
-                       return ISC_R_INVALIDARG;
+                       return DHCP_R_INVALIDARG;
 
                class_reference (&collections -> classes, cd, MDL);
        } else if (cd->name != NULL) {  /* regular class */
@@ -90,7 +87,7 @@ isc_result_t enter_class(cd, dynamicp, commit)
                        class_dereference(&c, MDL);
                        return ISC_R_EXISTS;
                }
-               
+
                /* Find the tail. */
                for (c = collections -> classes;
                     c -> nic; c = c -> nic)
@@ -150,11 +147,12 @@ static int find_uid_statement (struct executable_statement *esp,
 
 
 static host_id_info_t *
-find_host_id_info(unsigned int option_code) {
+find_host_id_info(unsigned int option_code, int relays) {
        host_id_info_t *p;
 
-       for (p=host_id_info; p != NULL; p = p->next) {
-               if (p->option->code == option_code) {
+       for (p = host_id_info; p != NULL; p = p->next) {
+               if ((p->option->code == option_code) &&
+                   (p->relays == relays)) {
                        break;
                }
        }
@@ -183,8 +181,6 @@ hash_print_hosts(struct hash_table *h) {
 
 void
 change_host_uid(struct host_decl *host, const char *uid, int len) {
-       struct host_decl *old_entry;
-
        /* XXX: should consolidate this type of code throughout */
        if (host_uid_hash == NULL) {
                if (!host_new_hash(&host_uid_hash, HOST_HASH_SIZE, MDL)) {
@@ -192,7 +188,7 @@ change_host_uid(struct host_decl *host, const char *uid, int len) {
                }
        }
 
-       /* 
+       /*
         * Remove the old entry, if one exists.
         */
        if (host->client_identifier.data != NULL) {
@@ -203,7 +199,7 @@ change_host_uid(struct host_decl *host, const char *uid, int len) {
                data_string_forget(&host->client_identifier, MDL);
        }
 
-       /* 
+       /*
         * Set our new value.
         */
        memset(&host->client_identifier, 0, sizeof(host->client_identifier));
@@ -217,7 +213,7 @@ change_host_uid(struct host_decl *host, const char *uid, int len) {
        /*
         * And add to hash.
         */
-       host_hash_add(host_uid_hash, host->client_identifier.data, 
+       host_hash_add(host_uid_hash, host->client_identifier.data,
                      host->client_identifier.len, host, MDL);
 }
 
@@ -314,16 +310,35 @@ isc_result_t enter_host (hd, dynamicp, commit)
        /* 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,
+       esp = NULL;
+       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);
+               struct data_string cid;
+               memset(&cid, 0, sizeof(cid));
+               (void) evaluate_option_cache (&cid,
+                                             NULL, NULL, NULL, NULL, NULL,
+                                             &global_scope,
+                                             esp->data.option, MDL);
+
+               if (hd->client_identifier.len > 0 && cid.len > 0) {
+                       char uid_buf[256];
+                       char cid_buf[256];
+                       print_hex_or_string(hd->client_identifier.len,
+                                           hd->client_identifier.data,
+                                           sizeof(uid_buf) - 1, uid_buf);
+
+                       print_hex_or_string(cid.len, cid.data,
+                                           sizeof(cid_buf) - 1, cid_buf);
+
+                       log_error ("Warning, host declaration '%s'"
+                                  " already has uid '%s',"
+                                  " ignoring dhcp-client-identifier '%s'",
+                                  hd->name, uid_buf, cid_buf);
+
+                       data_string_forget(&cid, MDL);
+               } else {
+                       memcpy(&hd->client_identifier, &cid, sizeof(cid));
+               }
        }
 
        /* If we got a client identifier, hash this entry by
@@ -378,34 +393,36 @@ isc_result_t enter_host (hd, dynamicp, commit)
                 * Look for the host identifier information for this option,
                 * and create a new entry if there is none.
                 */
-               h_id_info = find_host_id_info(hd->host_id_option->code);
+               h_id_info = find_host_id_info(hd->host_id_option->code,
+                                             hd->relays);
                if (h_id_info == NULL) {
                        h_id_info = dmalloc(sizeof(*h_id_info), MDL);
                        if (h_id_info == NULL) {
                                log_fatal("No memory for host-identifier "
                                          "option information.");
                        }
-                       option_reference(&h_id_info->option, 
+                       option_reference(&h_id_info->option,
                                         hd->host_id_option, MDL);
-                       if (!host_new_hash(&h_id_info->values_hash, 
+                       if (!host_new_hash(&h_id_info->values_hash,
                                           HOST_HASH_SIZE, MDL)) {
-                               log_fatal("No memory for host-identifer "
+                               log_fatal("No memory for host-identifier "
                                          "option hash.");
                        }
+                       h_id_info->relays = hd->relays;
                        h_id_info->next = host_id_info;
                        host_id_info = h_id_info;
                }
 
-               if (host_hash_lookup(&hp, h_id_info->values_hash, 
+               if (host_hash_lookup(&hp, h_id_info->values_hash,
                                     hd->host_id.data, hd->host_id.len, MDL)) {
-                       /* 
-                        * If this option is already present, then add 
+                       /*
+                        * If this option is already present, then add
                         * this host to the list in n_ipaddr, unless
                         * we have already done so previously.
                         *
                         * XXXSK: This seems scary to me, but I don't
-                        *        fully understand how these are used. 
-                        *        Shouldn't there be multiple lists, or 
+                        *        fully understand how these are used.
+                        *        Shouldn't there be multiple lists, or
                         *        maybe we should just forbid duplicates?
                         */
                        if (np == NULL) {
@@ -419,7 +436,7 @@ isc_result_t enter_host (hd, dynamicp, commit)
                        }
                        host_dereference(&hp, MDL);
                } else {
-                       host_hash_add(h_id_info->values_hash, 
+                       host_hash_add(h_id_info->values_hash,
                                      hd->host_id.data,
                                      hd->host_id.len,
                                      hd, MDL);
@@ -445,16 +462,25 @@ isc_result_t delete_class (cp, commit)
 
        /* do the write first as we won't be leaving it in any data
           structures, unlike the host objects */
-       
+
        if (commit) {
                write_named_billing_class ((unsigned char *)cp->name, 0, cp);
                if (!commit_leases ())
                        return ISC_R_IOERROR;
        }
-       
-       unlink_class(&cp);              /* remove from collections */
 
-       class_dereference(&cp, MDL);
+       /*
+        * If this is a subclass remove it from the class's hash table
+        */
+       if (cp->superclass) {
+               class_hash_delete(cp->superclass->hash,
+                                 (const char *)cp->hash_string.data,
+                                 cp->hash_string.len,
+                                 MDL);
+       }
+
+       /* remove from collections */
+       unlink_class(&cp);
 
        return ISC_R_SUCCESS;
 }
@@ -467,7 +493,6 @@ isc_result_t delete_host (hd, commit)
        struct host_decl *hp = (struct host_decl *)0;
        struct host_decl *np = (struct host_decl *)0;
        struct host_decl *foo;
-       struct executable_statement *esp;
        int hw_head = 0, uid_head = 1;
 
        /* Don't need to do it twice. */
@@ -607,8 +632,13 @@ int find_hosts_by_haddr (struct host_decl **hp, int htype,
                         const unsigned char *haddr, unsigned hlen,
                         const char *file, int line)
 {
-       struct host_decl *foo;
        struct hardware h;
+#if defined(LDAP_CONFIGURATION)
+       int ret;
+
+       if ((ret = find_haddr_in_ldap (hp, htype, hlen, haddr, file, line)))
+               return ret;
+#endif
 
        h.hlen = hlen + 1;
        h.hbuf [0] = htype;
@@ -626,7 +656,7 @@ int find_hosts_by_uid (struct host_decl **hp,
 }
 
 int
-find_hosts_by_option(struct host_decl **hp, 
+find_hosts_by_option(struct host_decl **hp,
                     struct packet *packet,
                     struct option_state *opt_state,
                     const char *file, int line) {
@@ -634,21 +664,53 @@ find_hosts_by_option(struct host_decl **hp,
        struct option_cache *oc;
        struct data_string data;
        int found;
-       
+       struct packet *relay_packet;
+       struct option_state *relay_state;
+
+#if defined(LDAP_CONFIGURATION)
+       if ((found = find_client_in_ldap (hp, packet, opt_state, file, line)))
+               return found;
+#endif
+
        for (p = host_id_info; p != NULL; p = p->next) {
-               oc = lookup_option(p->option->universe, 
-                                  opt_state, p->option->code);
+               relay_packet = packet;
+               relay_state = opt_state;
+
+               /* If this option block is for a relay (relays != 0)
+                * and we are processing the main options and not
+                * options from the IA (packet->options == opt_state)
+                * try to find the proper relay
+                */
+               if ((p->relays != 0) && (packet->options == opt_state)) {
+                       int i = p->relays;
+                       while ((i != 0) &&
+                              (relay_packet->dhcpv6_container_packet != NULL)) {
+                               relay_packet =
+                                       relay_packet->dhcpv6_container_packet;
+                               i--;
+                       }
+                       /* We wanted a specific relay but were
+                        * unable to find it */
+                       if ((p->relays <= MAX_V6RELAY_HOPS) && (i != 0))
+                               continue;
+
+                       relay_state = relay_packet->options;
+               }
+
+               oc = lookup_option(p->option->universe,
+                                  relay_state, p->option->code);
                if (oc != NULL) {
                        memset(&data, 0, sizeof(data));
-                       if (!evaluate_option_cache(&data, packet, NULL, NULL,
-                                                  opt_state, NULL,
-                                                  &global_scope, oc, 
+
+                       if (!evaluate_option_cache(&data, relay_packet, NULL,
+                                                  NULL, relay_state, NULL,
+                                                  &global_scope, oc,
                                                   MDL)) {
                                log_error("Error evaluating option cache");
                                return 0;
                        }
-                       
-                       found = host_hash_lookup(hp, p->values_hash, 
+
+                       found = host_hash_lookup(hp, p->values_hash,
                                                 data.data, data.len,
                                                 file, line);
 
@@ -674,7 +736,6 @@ int find_host_for_network (struct subnet **sp, struct host_decl **host,
                           struct iaddr *addr, struct shared_network *share)
 {
        int i;
-       struct subnet *subnet;
        struct iaddr ip_address;
        struct host_decl *hp;
        struct data_string fixed_addr;
@@ -724,13 +785,17 @@ void new_address_range (cfile, low, high, subnet, pool, lpchain)
        struct pool *pool;
        struct lease **lpchain;
 {
-       struct lease *address_range, *lp, *plp;
-       struct iaddr net;
-       unsigned min, max, i;
+#if defined(COMPACT_LEASES)
+       struct lease *address_range;
+       unsigned s;
+#endif
+       unsigned min, max, i, num_addrs;
        char lowbuf [16], highbuf [16], netbuf [16];
        struct shared_network *share = subnet -> shared_network;
-       isc_result_t status;
        struct lease *lt = (struct lease *)0;
+#if !defined(COMPACT_LEASES)
+       isc_result_t status;
+#endif
 
        /* All subnets should have attached shared network structures. */
        if (!share) {
@@ -755,23 +820,19 @@ void new_address_range (cfile, low, high, subnet, pool, lpchain)
                        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 high and low addresses are in this subnet. */
+       if (!addr_eq(subnet->net, subnet_number(low, subnet->netmask))) {
+               strcpy(lowbuf, piaddr(low));
+               strcpy(netbuf, piaddr(subnet->net));
+               log_fatal("bad range, address %s not in subnet %s netmask %s",
+                         lowbuf, netbuf, piaddr(subnet->netmask));
        }
 
-       /* 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);
+       if (!addr_eq(subnet->net, subnet_number(high, subnet->netmask))) {
+               strcpy(highbuf, piaddr(high));
+               strcpy(netbuf, piaddr(subnet->net));
+               log_fatal("bad range, address %s not in subnet %s netmask %s",
+                         highbuf, netbuf, piaddr(subnet->netmask));
        }
 
        /* Get the high and low host addresses... */
@@ -784,9 +845,30 @@ void new_address_range (cfile, low, high, subnet, pool, lpchain)
                min = host_addr (high, subnet -> netmask);
        }
 
+       /* get the number of addresses we want, and add it to the pool info
+        * this value is only for use when setting up lease chains and will
+        * be overwritten when expire_all_pools is run
+        */
+       num_addrs = max - min + 1;
+#if defined (BINARY_LEASES)
+       pool->lease_count += num_addrs;
+#endif
+
        /* Get a lease structure for each address in the range. */
 #if defined (COMPACT_LEASES)
-       address_range = new_leases (max - min + 1, MDL);
+       s = (num_addrs + 1) * sizeof (struct lease);
+       /* Check unsigned overflow in new_leases().
+          With 304 byte lease structure (x64_86), this happens at
+          range 10.0.0.0 10.215.148.52; */
+       if (((s % sizeof (struct lease)) != 0) ||
+           ((s / sizeof (struct lease)) != (num_addrs + 1))) {
+               strcpy (lowbuf, piaddr (low));
+               strcpy (highbuf, piaddr (high));
+               parse_warn (cfile, "%s-%s is an overly large address range.",
+                          lowbuf, highbuf);
+               log_fatal ("Memory overflow.");
+       }
+       address_range = new_leases (num_addrs, MDL);
        if (!address_range) {
                strcpy (lowbuf, piaddr (low));
                strcpy (highbuf, piaddr (high));
@@ -796,7 +878,7 @@ void new_address_range (cfile, low, high, subnet, pool, lpchain)
 #endif
 
        /* Fill out the lease structures with some minimal information. */
-       for (i = 0; i < max - min + 1; i++) {
+       for (i = 0; i < num_addrs; i++) {
                struct lease *lp = (struct lease *)0;
 #if defined (COMPACT_LEASES)
                omapi_object_initialize ((omapi_object_t *)&address_range [i],
@@ -812,15 +894,15 @@ void new_address_range (cfile, low, high, subnet, pool, lpchain)
                                                    i + min)),
                                   isc_result_totext (status));
 #endif
-               lp -> ip_addr = ip_addr (subnet -> net,
-                                        subnet -> netmask, i + min);
-               lp -> starts = 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;
+               lp->ip_addr = ip_addr(subnet->net, subnet->netmask, i + min);
+               lp->starts = 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->rewind_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)) {
@@ -853,6 +935,10 @@ int find_subnet (struct subnet **sp,
        struct subnet *rv;
 
        for (rv = subnets; rv; rv = rv -> next_subnet) {
+#if defined(DHCP4o6)
+               if (addr.len != rv->netmask.len)
+                       continue;
+#endif
                if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) {
                        if (subnet_reference (sp, rv,
                                              file, line) != ISC_R_SUCCESS)
@@ -870,6 +956,10 @@ int find_grouped_subnet (struct subnet **sp,
        struct subnet *rv;
 
        for (rv = share -> subnets; rv; rv = rv -> next_sibling) {
+#if defined(DHCP4o6)
+               if (addr.len != rv->netmask.len)
+                       continue;
+#endif
                if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) {
                        if (subnet_reference (sp, rv,
                                              file, line) != ISC_R_SUCCESS)
@@ -881,10 +971,14 @@ int find_grouped_subnet (struct subnet **sp,
 }
 
 /* XXX: could speed up if everyone had a prefix length */
-int 
-subnet_inner_than(const struct subnet *subnet, 
+int
+subnet_inner_than(const struct subnet *subnet,
                  const struct subnet *scan,
                  int warnp) {
+#if defined(DHCP4o6)
+       if (subnet->net.len != scan->net.len)
+               return 0;
+#endif
        if (addr_eq(subnet_number(subnet->net, scan->netmask), scan->net) ||
            addr_eq(subnet_number(scan->net, subnet->netmask), subnet->net)) {
                char n1buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255")];
@@ -954,7 +1048,7 @@ void enter_subnet (subnet)
        }
        subnet_reference (&subnets, subnet, MDL);
 }
-       
+
 /* Enter a new shared network into the shared network list. */
 
 void enter_shared_network (share)
@@ -967,7 +1061,7 @@ void enter_shared_network (share)
        }
        shared_network_reference (&shared_networks, share, MDL);
 }
-       
+
 void new_shared_network_interface (cfile, share, name)
        struct parse *cfile;
        struct shared_network *share;
@@ -977,12 +1071,12 @@ void new_shared_network_interface (cfile, share, name)
        isc_result_t status;
 
        if (share -> interface) {
-               parse_warn (cfile, 
+               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;
@@ -1018,7 +1112,6 @@ void enter_lease (lease)
        struct lease *lease;
 {
        struct lease *comp = (struct lease *)0;
-       isc_result_t status;
 
        if (find_lease_by_ip_addr (&comp, lease -> ip_addr, MDL)) {
                if (!comp -> pool) {
@@ -1058,16 +1151,15 @@ void enter_lease (lease)
    list of leases by expiry time so that we can always find the oldest
    lease. */
 
-int supersede_lease (comp, lease, commit, propogate, pimmediate)
+int supersede_lease (comp, lease, commit, propogate, pimmediate, from_pool)
        struct lease *comp, *lease;
        int commit;
        int propogate;
        int pimmediate;
+       int from_pool;
 {
-       int enter_uid = 0;
-       int enter_hwaddr = 0;
-       struct lease *lp, **lq, *prev;
-       TIME lp_next_state;
+       LEASE_STRUCT_PTR lq;
+       struct timeval tv;
 #if defined (FAILOVER_PROTOCOL)
        int do_pool_check = 0;
 
@@ -1077,7 +1169,6 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
        if (pimmediate && !commit)
                return 0;
 #endif
-
        /* If there is no sample lease, just do the move. */
        if (!lease)
                goto just_move_it;
@@ -1113,33 +1204,26 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
 
        /* 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;
+       if (comp->uid) {
+               uid_hash_delete(comp);
+               if (comp->uid != comp->uid_buf) {
+                       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 there's a hardware address, remove the lease from its
+        * old position in the hash bucket's ordered list.
+        */
+       if (comp->hardware_addr.hlen)
+               hw_hash_delete(comp);
 
        /* 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 (comp->billing_class)
+                       unbill_class(comp);
                if (lease -> billing_class)
                        bill_class (comp, lease -> billing_class);
        }
@@ -1171,8 +1255,6 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
                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) {
@@ -1199,39 +1281,38 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
        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,
+       if (lease->on_star.on_expiry) {
+               if (comp->on_star.on_expiry)
+                       executable_statement_dereference
+                               (&comp->on_star.on_expiry, MDL);
+               executable_statement_reference (&comp->on_star.on_expiry,
+                                               lease->on_star.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,
+       if (lease->on_star.on_commit) {
+               if (comp->on_star.on_commit)
+                       executable_statement_dereference
+                               (&comp->on_star.on_commit, MDL);
+               executable_statement_reference (&comp->on_star.on_commit,
+                                               lease->on_star.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);
+       if (lease->on_star.on_release) {
+               if (comp->on_star.on_release)
+                       executable_statement_dereference
+                               (&comp->on_star.on_release, MDL);
+               executable_statement_reference (&comp->on_star.on_release,
+                                               lease->on_star.on_release,
+                                               MDL);
        }
 
        /* Record the lease in the uid hash if necessary. */
-       if (enter_uid && comp -> uid) {
-               uid_hash_add (comp);
-       }
+       if (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 (comp->hardware_addr.hlen)
+               hw_hash_add(comp);
 
        comp->cltt = lease->cltt;
 #if defined (FAILOVER_PROTOCOL)
@@ -1242,10 +1323,21 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
        comp->ends = lease->ends;
        comp->next_binding_state = lease->next_binding_state;
 
+       /*
+        * If we have a control block pointer copy it in.
+        * We don't zero out an older ponter as it is still
+        * in use.  We shouldn't need to overwrite an
+        * old pointer with a new one as the old transaction
+        * should have been cancelled before getting here.
+        */
+       if (lease->ddns_cb != NULL)
+               comp->ddns_cb = lease->ddns_cb;
+
       just_move_it:
 #if defined (FAILOVER_PROTOCOL)
-       /* Atsfp should be cleared upon any state change that implies
-        * propogation whether supersede_lease was given a copy lease
+       /*
+        * Atsfp should be cleared upon any state change that implies
+        * propagation whether supersede_lease was given a copy lease
         * structure or not (often from the pool_timer()).
         */
        if (propogate)
@@ -1261,9 +1353,12 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
        /* Figure out which queue it's on. */
        switch (comp -> binding_state) {
              case FTS_FREE:
-               lq = &comp -> pool -> free;
-               if (!(comp->flags & RESERVED_LEASE))
+               if (comp->flags & RESERVED_LEASE)
+                       lq = &comp->pool->reserved;
+               else {
+                       lq = &comp->pool->free;
                        comp->pool->free_leases--;
+               }
 
 #if defined(FAILOVER_PROTOCOL)
                do_pool_check = 1;
@@ -1285,9 +1380,12 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
                break;
 
              case FTS_BACKUP:
-               lq = &comp -> pool -> backup;
-               if (!(comp->flags & RESERVED_LEASE))
+               if (comp->flags & RESERVED_LEASE)
+                       lq = &comp->pool->reserved;
+               else {
+                       lq = &comp->pool->backup;
                        comp->pool->backup_leases--;
+               }
 
 #if defined(FAILOVER_PROTOCOL)
                do_pool_check = 1;
@@ -1305,34 +1403,14 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
 
        /* Remove the lease from its current place in its current
           timer sequence. */
-       /* XXX this is horrid. */
-       prev = (struct lease *)0;
-       for (lp = *lq; lp; lp = lp -> next) {
-               if (lp == comp)
-                       break;
-               prev = lp;
-       }
+       LEASE_REMOVEP(lq, comp);
 
-       if (!lp) {
-               log_fatal("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]);
-       }
-
-       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);
-               }
+       /* Now that we've done the flag-affected queue removal
+        * we can update the new lease's flags, if there's an
+        * existing lease */
+       if (lease) {
+               comp->flags = ((lease->flags & ~PERSISTENT_FLAGS) |
+                               (comp->flags & ~EPHEMERAL_FLAGS));
        }
 
        /* Make the state transition. */
@@ -1347,7 +1425,7 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
        /* 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
@@ -1361,13 +1439,33 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
            (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,
+               tv . tv_sec = comp -> pool -> next_event_time;
+               tv . tv_usec = 0;
+               add_timeout (&tv,
                             pool_timer, comp -> pool,
                             (tvref_t)pool_reference,
                             (tvunref_t)pool_dereference);
        }
 
        if (commit) {
+#if defined(FAILOVER_PROTOCOL)
+               /*
+                * If commit and propogate are set, then we can save a
+                * possible fsync later in BNDUPD socket transmission by
+                * stepping the rewind state forward to the new state, in
+                * case it has changed.  This is only worth doing if the
+                * failover connection is currently connected, as in this
+                * case it is likely we will be transmitting to the peer very
+                * shortly.
+                */
+               if (propogate && (comp->pool->failover_peer != NULL) &&
+                   ((comp->pool->failover_peer->service_state ==
+                                                           cooperating) ||
+                    (comp->pool->failover_peer->service_state ==
+                                                           not_responding)))
+                       comp->rewind_binding_state = comp->binding_state;
+#endif
+
                if (!write_lease (comp))
                        return 0;
                if ((server_starting & SS_NOSYNC) == 0) {
@@ -1386,25 +1484,28 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate)
                dhcp_failover_pool_check(comp->pool);
 #endif
 
-       /* If the current binding state has already expired, do an
-          expiry event right now. */
+       /* If the current binding state has already expired and we haven't
+        * been called from pool_timer, 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);
+       if ((from_pool == 0) &&
+           (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)
+       if (lease -> pool && lease -> pool -> failover_peer)
                peer = lease -> pool -> failover_peer;
        else
                peer = (dhcp_failover_state_t *)0;
@@ -1416,39 +1517,36 @@ void make_binding_state_transition (struct lease *lease)
            ((
 #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)) ||
+                   (lease->binding_state == FTS_EXPIRED ||
+                    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, NULL);
+               (void) ddns_removals(lease, NULL, NULL, ISC_TRUE);
 #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)
+               if (lease->on_star.on_expiry) {
+                       execute_statements(NULL, NULL, lease,
+                                          NULL, NULL, NULL,
+                                          &lease->scope,
+                                          lease->on_star.on_expiry,
+                                          NULL);
+                       if (lease->on_star.on_expiry)
                                executable_statement_dereference
-                                       (&lease -> on_expiry, MDL);
+                                       (&lease->on_star.on_expiry, MDL);
                }
-               
+
                /* No sense releasing a lease after it's expired. */
-               if (lease -> on_release)
-                       executable_statement_dereference (&lease -> on_release,
-                                                         MDL);
+               if (lease->on_star.on_release)
+                       executable_statement_dereference
+                               (&lease->on_star.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->billing_class)
+                       unbill_class(lease);
                if (lease -> agent_options)
                        option_chain_head_dereference (&lease -> agent_options,
                                                       MDL);
@@ -1477,29 +1575,42 @@ void make_binding_state_transition (struct lease *lease)
              lease -> binding_state == FTS_ACTIVE &&
              lease -> next_binding_state == FTS_RELEASED))) {
 #if defined (NSUPDATE)
-               ddns_removals(lease, NULL);
+               /*
+                * Note: ddns_removals() is also iterated when the lease
+                * enters state 'released' in 'release_lease()'.  The below
+                * is caught when a peer receives a BNDUPD from a failover
+                * peer; it may not have received the client's release (it
+                * may have been offline).
+                *
+                * We could remove the call from release_lease() because
+                * it will also catch here on the originating server after the
+                * peer acknowledges the state change.  However, there could
+                * be many hours inbetween, and in this case we /know/ the
+                * client is no longer using the lease when we receive the
+                * release message.  This is not true of expiry, where the
+                * peer may have extended the lease.
+                */
+               (void) ddns_removals(lease, NULL, NULL, ISC_TRUE);
 #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);
+               if (lease->on_star.on_release) {
+                       execute_statements(NULL, NULL, lease,
+                                          NULL, NULL, NULL,
+                                          &lease->scope,
+                                          lease->on_star.on_release,
+                                          NULL);
+                       executable_statement_dereference
+                               (&lease->on_star.on_release, MDL);
                }
-               
+
                /* A released lease can't expire. */
-               if (lease -> on_expiry)
-                       executable_statement_dereference (&lease -> on_expiry,
-                                                         MDL);
+               if (lease->on_star.on_expiry)
+                       executable_statement_dereference
+                               (&lease->on_star.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->billing_class)
+                       unbill_class(lease);
                if (lease -> agent_options)
                        option_chain_head_dereference (&lease -> agent_options,
                                                       MDL);
@@ -1537,16 +1648,19 @@ void make_binding_state_transition (struct lease *lease)
              case FTS_RELEASED:
              case FTS_ABANDONED:
              case FTS_RESET:
-               lease -> next_binding_state = FTS_FREE;
+               lease->next_binding_state = FTS_FREE;
+#if defined(FAILOVER_PROTOCOL)
                /* 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);
+               if ((lease->pool != NULL) &&
+                   (lease->pool->failover_peer != NULL) &&
+                   (lease->pool->failover_peer->me.state == partner_down))
+                       lease->tsfp =
+                           (lease->pool->failover_peer->me.stos +
+                            lease->pool->failover_peer->mclt);
+#endif /* FAILOVER_PROTOCOL */
                break;
 
              case FTS_FREE:
@@ -1559,7 +1673,6 @@ void make_binding_state_transition (struct lease *lease)
                   piaddr (lease -> ip_addr),
                   binding_state_print (lease -> next_binding_state));
 #endif
-
 }
 
 /* Copy the contents of one lease into another, correctly maintaining
@@ -1612,17 +1725,17 @@ int lease_copy (struct lease **lp,
        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,
+       if (lease->on_star.on_expiry)
+               executable_statement_reference (&lt->on_star.on_expiry,
+                                               lease->on_star.on_expiry,
                                                file, line);
-       if (lease -> on_commit)
-               executable_statement_reference (&lt -> on_commit,
-                                               lease -> on_commit,
+       if (lease->on_star.on_commit)
+               executable_statement_reference (&lt->on_star.on_commit,
+                                               lease->on_star.on_commit,
                                                file, line);
-       if (lease -> on_release)
-               executable_statement_reference (&lt -> on_release,
-                                               lease -> on_release,
+       if (lease->on_star.on_release)
+               executable_statement_reference (&lt->on_star.on_release,
+                                               lease->on_star.on_release,
                                                file, line);
        lt->flags = lease->flags;
        lt->tstp = lease->tstp;
@@ -1631,6 +1744,7 @@ int lease_copy (struct lease **lp,
        lt->cltt = lease -> cltt;
        lt->binding_state = lease->binding_state;
        lt->next_binding_state = lease->next_binding_state;
+       lt->rewind_binding_state = lease->rewind_binding_state;
        status = lease_reference(lp, lt, file, line);
        lease_dereference(&lt, MDL);
        return status == ISC_R_SUCCESS;
@@ -1644,33 +1758,33 @@ void release_lease (lease, packet)
        /* If there are statements to execute when the lease is
           released, execute them. */
 #if defined (NSUPDATE)
-       ddns_removals(lease, NULL);
+       (void) ddns_removals(lease, NULL, NULL, ISC_FALSE);
 #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);
+       if (lease->on_star.on_release) {
+               execute_statements (NULL, packet, lease,
+                                   NULL, packet->options,
+                                   NULL, &lease->scope,
+                                   lease->on_star.on_release, NULL);
+               if (lease->on_star.on_release)
+                       executable_statement_dereference
+                               (&lease->on_star.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->on_star.on_expiry)
+               executable_statement_dereference
+                       (&lease->on_star.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);
+               if (lease->on_star.on_commit)
+                       executable_statement_dereference
+                               (&lease->on_star.on_commit, MDL);
 
                /* Blow away any bindings. */
                if (lease -> scope)
@@ -1685,14 +1799,27 @@ void release_lease (lease, packet)
                lease->tstp = cur_time;
 #if defined (FAILOVER_PROTOCOL)
                if (lease -> pool && lease -> pool -> failover_peer) {
-                       lease -> next_binding_state = FTS_RELEASED;
+                       dhcp_failover_state_t *peer = NULL;
+
+                       if (lease->pool != NULL)
+                               peer = lease->pool->failover_peer;
+
+                       if ((peer->service_state == not_cooperating) &&
+                           (((peer->i_am == primary) &&
+                             (lease->rewind_binding_state == FTS_FREE)) ||
+                            ((peer->i_am == secondary) &&
+                             (lease->rewind_binding_state == FTS_BACKUP)))) {
+                               lease->next_binding_state =
+                                                 lease->rewind_binding_state;
+                       } else
+                               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);
+               supersede_lease(lease, NULL, 1, 1, 1, 0);
        }
 }
 
@@ -1703,32 +1830,50 @@ void abandon_lease (lease, message)
        struct lease *lease;
        const char *message;
 {
-       struct lease *lt = (struct lease *)0;
+       struct lease *lt = NULL;
 #if defined (NSUPDATE)
-       ddns_removals(lease, NULL);
+       (void) ddns_removals(lease, NULL, NULL, ISC_FALSE);
 #endif
 
-       if (!lease_copy (&lt, lease, MDL))
+       if (!lease_copy(&lt, lease, MDL)) {
                return;
+       }
 
-       if (lt->scope)
+       if (lt->scope) {
                binding_scope_dereference(&lt->scope, MDL);
+       }
 
-       lt -> ends = cur_time; /* XXX */
-       lt -> next_binding_state = FTS_ABANDONED;
+       /* Calculate the abandone expiry time.  If it wraps,
+        * use the maximum expiry time. */
+       lt->ends = cur_time + abandon_lease_time;
+       if (lt->ends < cur_time || lt->ends > MAX_TIME) {
+               lt->ends = MAX_TIME;
+       }
 
-       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);
+       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 = NULL;
+       lt->uid_len = 0;
+       lt->uid_max = 0;
+       supersede_lease(lease, lt, 1, 1, 1, 0);
+       lease_dereference(&lt, MDL);
 }
 
+#if 0
+/*
+ * This doesn't appear to be in use for anything anymore.
+ * I'm ifdeffing it now and if there are no complaints in
+ * the future it will be removed.
+ * SAR
+ */
+
 /* Abandon the specified lease (set its timeout to infinity and its
    particulars to zero, and re-hash it as appropriate. */
 
@@ -1737,7 +1882,7 @@ void dissociate_lease (lease)
 {
        struct lease *lt = (struct lease *)0;
 #if defined (NSUPDATE)
-       ddns_removals(lease, NULL);
+       (void) ddns_removals(lease, NULL, NULL, ISC_FALSE);
 #endif
 
        if (!lease_copy (&lt, lease, MDL))
@@ -1759,71 +1904,87 @@ void dissociate_lease (lease)
        lt -> uid = (unsigned char *)0;
        lt -> uid_len = 0;
        lt -> uid_max = 0;
-       supersede_lease (lease, lt, 1, 1, 1);
+       supersede_lease (lease, lt, 1, 1, 1, 0);
        lease_dereference (&lt, MDL);
 }
+#endif
 
 /* Timer called when a lease in a particular pool expires. */
 void pool_timer (vpool)
        void *vpool;
 {
        struct pool *pool;
-       struct lease *lt = (struct lease *)0;
-       struct lease *next = (struct lease *)0;
-       struct lease *lease = (struct lease *)0;
+       struct lease *next = NULL;
+       struct lease *lease = NULL;
+       struct lease *ltemp = NULL;
 #define FREE_LEASES 0
 #define ACTIVE_LEASES 1
 #define EXPIRED_LEASES 2
 #define ABANDONED_LEASES 3
 #define BACKUP_LEASES 4
 #define RESERVED_LEASES 5
-       struct lease **lptr[RESERVED_LEASES+1];
+       LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1];
        TIME next_expiry = MAX_TIME;
        int i;
+       struct timeval tv;
 
        pool = (struct pool *)vpool;
 
-       lptr [FREE_LEASES] = &pool -> free;
-       lptr [ACTIVE_LEASES] = &pool -> active;
-       lptr [EXPIRED_LEASES] = &pool -> expired;
-       lptr [ABANDONED_LEASES] = &pool -> abandoned;
-       lptr [BACKUP_LEASES] = &pool -> backup;
+       lptr[FREE_LEASES] = &pool->free;
+       lptr[ACTIVE_LEASES] = &pool->active;
+       lptr[EXPIRED_LEASES] = &pool->expired;
+       lptr[ABANDONED_LEASES] = &pool->abandoned;
+       lptr[BACKUP_LEASES] = &pool->backup;
        lptr[RESERVED_LEASES] = &pool->reserved;
 
        for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
                /* If there's nothing on the queue, skip it. */
-               if (!*(lptr [i]))
+               if (!(LEASE_NOT_EMPTYP(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)
+               if (pool->failover_peer &&
+                   pool->failover_peer->me.state != partner_down) {
+                       /*
+                        * Normally the secondary doesn't initiate expiration
+                        * events (unless in partner-down), but rather relies
+                        * on the primary to expire the lease.  However, when
+                        * disconnected from its peer, the server is allowed to
+                        * rewind a lease to the previous state that the peer
+                        * would have recorded it.  This means there may be
+                        * opportunities for active->free or active->backup
+                        * expirations while out of contact.
+                        *
+                        * Q: Should we limit this expiration to
+                        *    comms-interrupt rather than not-normal?
+                        */
+                       if ((i == ACTIVE_LEASES) &&
+                           (pool->failover_peer->i_am == secondary) &&
+                           (pool->failover_peer->me.state == normal))
                                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);
+#endif
+               lease_reference(&lease, LEASE_GET_FIRSTP(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);
+                               lease_dereference(&next, MDL);
+                       ltemp = LEASE_GET_NEXTP(lptr[i], lease);
+                       if (ltemp)
+                               lease_reference(&next, ltemp, 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;
+                       if (lease->sort_time > cur_time) {
+                               if (lease->sort_time < next_expiry)
+                                       next_expiry = lease->sort_time;
                                break;
                        }
 
@@ -1832,27 +1993,56 @@ void pool_timer (vpool)
                           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);
+                       if (lease->next_binding_state != lease->binding_state)
+                       {
+#if defined(FAILOVER_PROTOCOL)
+                               dhcp_failover_state_t *peer = NULL;
+
+                               if (lease->pool != NULL)
+                                       peer = lease->pool->failover_peer;
+
+                               /* Can we rewind the lease to a free state? */
+                               if (peer != NULL &&
+                                   peer->service_state == not_cooperating &&
+                                   lease->next_binding_state == FTS_EXPIRED &&
+                                   ((peer->i_am == primary &&
+                                     lease->rewind_binding_state == FTS_FREE)
+                                       ||
+                                    (peer->i_am == secondary &&
+                                     lease->rewind_binding_state ==
+                                                               FTS_BACKUP)))
+                                       lease->next_binding_state =
+                                                  lease->rewind_binding_state;
+#endif
+                               supersede_lease(lease, NULL, 1, 1, 1, 1);
+                       }
 
-                       lease_dereference (&lease, MDL);
+                       lease_dereference(&lease, MDL);
                        if (next)
-                               lease_reference (&lease, next, MDL);
+                               lease_reference(&lease, next, MDL);
                }
                if (next)
-                       lease_dereference (&next, MDL);
+                       lease_dereference(&next, MDL);
                if (lease)
-                       lease_dereference (&lease, MDL);
+                       lease_dereference(&lease, MDL);
        }
-       if (next_expiry != MAX_TIME) {
-               pool -> next_event_time = next_expiry;
-               add_timeout (pool -> next_event_time, pool_timer, pool,
+
+       /* If we found something to expire and its expiration time
+        * is either less than the current expiration time or the
+        * current expiration time is already expired update the
+        * timer.
+        */
+       if ((next_expiry != MAX_TIME) &&
+           ((pool->next_event_time > next_expiry) ||
+            (pool->next_event_time <= cur_time))) {
+               pool->next_event_time = next_expiry;
+               tv.tv_sec = pool->next_event_time;
+               tv.tv_usec = 0;
+               add_timeout (&tv, pool_timer, pool,
                             (tvref_t)pool_reference,
                             (tvunref_t)pool_dereference);
        } else
-               pool -> next_event_time = MIN_TIME;
+               pool->next_event_time = MIN_TIME;
 
 }
 
@@ -1878,9 +2068,17 @@ int find_lease_by_hw_addr (struct lease **lp,
                           const char *file, int line)
 {
        if (hwlen == 0)
-               return 0;
-       return lease_id_hash_lookup(lp, lease_hw_addr_hash, hwaddr, hwlen,
-                                   file, line);
+               return (0);
+
+       /*
+        * If it's an infiniband address don't bother
+        * as we don't have a useful address to hash.
+        */
+       if ((hwlen == 1) && (hwaddr[0] == HTYPE_INFINIBAND))
+               return (0);
+
+       return (lease_id_hash_lookup(lp, lease_hw_addr_hash, hwaddr, hwlen,
+                                    file, line));
 }
 
 /* If the lease is preferred over the candidate, return truth.  The
@@ -2045,6 +2243,8 @@ void uid_hash_delete (lease)
 }
 
 /* Add the specified lease to the hardware address hash. */
+/* We don't add leases with infiniband addresses to the
+ * hash as there isn't any address to hash on. */
 
 void
 hw_hash_add(struct lease *lease)
@@ -2054,6 +2254,14 @@ hw_hash_add(struct lease *lease)
        struct lease *prev = NULL;
        struct lease *next = NULL;
 
+       /*
+        * If it's an infiniband address don't bother
+        * as we don't have a useful address to hash.
+        */
+       if ((lease->hardware_addr.hlen == 1) &&
+           (lease->hardware_addr.hbuf[0] == HTYPE_INFINIBAND))
+               return;
+
        /* 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))
@@ -2125,6 +2333,14 @@ void hw_hash_delete (lease)
        struct lease *head = (struct lease *)0;
        struct lease *next = (struct lease *)0;
 
+       /*
+        * If it's an infiniband address don't bother
+        * as we don't have a useful address to hash.
+        */
+       if ((lease->hardware_addr.hlen == 1) &&
+           (lease->hardware_addr.hbuf[0] == HTYPE_INFINIBAND))
+               return;
+
        /* 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)) {
@@ -2172,13 +2388,50 @@ void hw_hash_delete (lease)
                lease_dereference (&head, MDL);
 }
 
+/* Write v4 leases to permanent storage. */
+int write_leases4(void) {
+       struct lease *l;
+       struct shared_network *s;
+       struct pool *p;
+       LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1];
+       int num_written = 0, i;
+
+       /* Write all the leases. */
+       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;
+               lptr[RESERVED_LEASES] = &p->reserved;
+
+               for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
+                   for (l = LEASE_GET_FIRSTP(lptr[i]);
+                        l != NULL;
+                        l = LEASE_GET_NEXTP(lptr[i], l)) {
+#if !defined (DEBUG_DUMP_ALL_LEASES)
+                       if (l->hardware_addr.hlen != 0 || l->uid_len != 0 ||
+                           l->tsfp != 0 || l->binding_state != FTS_FREE)
+#endif
+                       {
+                           if (write_lease(l) == 0)
+                                   return (0);
+                           num_written++;
+                       }
+                   }
+               }
+           }
+       }
+
+       log_info ("Wrote %d leases to leases file.", num_written);
+       return (1);
+}
+
 /* 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;
@@ -2186,7 +2439,6 @@ int write_leases ()
        struct collection *colp;
        int i;
        int num_written;
-       struct lease **lptr[RESERVED_LEASES+1];
 
        /* write all the dynamically-created class declarations. */
        if (collections->classes) {
@@ -2199,12 +2451,12 @@ int write_leases ()
                        }
                }
 
-               /* XXXJAB this number doesn't include subclasses... */ 
+               /* XXXJAB this number doesn't include subclasses... */
                log_info ("Wrote %d class decls to leases file.",
                          numclasseswritten);
        }
-       
-                       
+
+
        /* Write all the dynamically-created group declarations. */
        if (group_name_hash) {
            num_written = 0;
@@ -2266,41 +2518,150 @@ int write_leases ()
                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;
-               lptr [RESERVED_LEASES] = &p->reserved;
+       switch (local_family) {
+             case AF_INET:
+               if (write_leases4() == 0)
+                       return (0);
+               break;
+#ifdef DHCPv6
+             case AF_INET6:
+               if (write_leases6() == 0)
+                       return (0);
+               break;
+#endif /* DHCPv6 */
+       }
 
-               for (i = FREE_LEASES; i <= RESERVED_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++;
-                       }
-                   }
+       if (commit_leases() == 0)
+               return (0);
+       return (1);
+}
+
+#if !defined (BINARY_LEASES)
+/* Unlink all the leases in the queue. */
+void lease_remove_all(struct lease **lq) {
+       struct lease *lp, *ln = NULL;
+
+       /* nothing to do */
+       if (*lq == NULL)
+               return;
+
+       /* We simply derefernce the first item in the list.  When
+        * it's reference counter goes to zero it will be cleaned
+        * and the reference counter
+        *
+        * Get a pointer to the first item in the list and then
+        * drop the reference from the queue pointer
+        */
+       lease_reference(&lp, *lq, MDL);
+       lease_dereference(lq, MDL);
+
+       do {
+               /* if we have a next save a pointer to it and unlink it */
+               if (lp->next) {
+                       lease_reference(&ln, lp->next, MDL);
+                       lease_dereference(&lp->next, MDL);
                }
-           }
+
+               /* get rid of what we currently have */
+               lease_dereference(&lp, MDL);
+
+               /* move the next to the current and loop */
+               lp = ln;
+               ln = NULL;
+       } while (lp != NULL);
+}
+
+/*
+ * This routine walks through a given lease queue (lq) looking
+ * for comp.  If it doesn't find the lease it is a fatal error
+ * as it should be on the given queue.  Once we find the lease
+ * we can remove it from this list.
+ */
+void lease_remove(struct lease **lq, struct lease *comp)
+{
+       struct lease *prev, *lp;
+
+       prev = NULL;
+       for (lp = *lq; lp != NULL; lp = lp->next) {
+               if (lp == comp)
+                       break;
+               prev = lp;
        }
-       log_info ("Wrote %d leases to leases file.", num_written);
-       if (!write_leases6()) {
-               return 0;
+
+       if (!lp) {
+               log_fatal("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]);
        }
-       if (!commit_leases ())
-               return 0;
-       return 1;
+
+       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);
+               }
+       }
+}
+
+/* This routine inserts comp into lq in a sorted fashion.
+ * The sort key is comp->sort_time, smaller values are
+ * placed earlier in the list.
+ */
+void lease_insert(struct lease **lq, struct lease *comp)
+{
+       struct lease *prev, *lp;
+       static struct lease **last_lq = NULL;
+       static struct lease *last_insert_point = NULL;
+
+       /* This only works during server startup: during runtime, the last
+        * lease may be dequeued in between calls.  If the queue is the same
+        * as was used previously, and the lease structure isn't (this is not
+        * a re-queue), use that as a starting point for the insertion-sort.
+        */
+       if ((server_starting & SS_QFOLLOW) && (lq == last_lq) &&
+           (comp != last_insert_point) &&
+           (last_insert_point->sort_time <= comp->sort_time)) {
+               prev = last_insert_point;
+               lp = prev->next;
+       } else {
+               prev = NULL;
+               lp = *lq;
+       }
+
+       /* Insertion sort the lease onto the appropriate queue. */
+       for (; lp != NULL ; 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);
+       }
+       last_insert_point = comp;
+       last_lq = lq;
+
+       return;
 }
+#endif
 
 /* In addition to placing this lease upon a lease queue depending on its
  * state, it also keeps track of the number of FREE and BACKUP leases in
@@ -2316,9 +2677,7 @@ int write_leases ()
  */
 int lease_enqueue (struct lease *comp)
 {
-       struct lease **lq, *prev, *lp;
-       static struct lease **last_lq = NULL;
-       static struct lease *last_insert_point = NULL;
+       LEASE_STRUCT_PTR lq;
 
        /* No queue to put it on? */
        if (!comp -> pool)
@@ -2392,43 +2751,8 @@ int lease_enqueue (struct lease *comp)
                return 0;
        }
 
-       /* This only works during server startup: during runtime, the last
-        * lease may be dequeued inbetween calls.  If the queue is the same
-        * as was used previously, and the lease structure isn't (this is not
-        * a re-queue), use that as a starting point for the insertion-sort.
-        */
-       if ((server_starting & SS_QFOLLOW) && (lq == last_lq) &&
-           (comp != last_insert_point) && 
-           (last_insert_point->sort_time <= comp->sort_time)) {
-               prev = last_insert_point;
-               lp = prev->next;
-       } else {
-               prev = NULL;
-               lp = *lq;
-       }
+       LEASE_INSERTP(lq, comp);
 
-       /* Insertion sort the lease onto the appropriate queue. */
-       for (; 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);
-       }
-       last_insert_point = comp;
-       last_lq = lq;
        return 1;
 }
 
@@ -2449,7 +2773,23 @@ lease_instantiate(const void *key, unsigned len, void *object)
                                     lease->ip_addr.len, MDL);
                return ISC_R_SUCCESS;
        }
-               
+
+#if defined (FAILOVER_PROTOCOL)
+       /* If the lease is in FTS_BACKUP but there is no peer, then the
+        * pool must have been formerly configured for failover and
+        * is now configured as standalone. This means we need to
+        * move the lease to FTS_FREE to make it available. */
+       if ((lease->binding_state == FTS_BACKUP) &&
+           (lease->pool->failover_peer == NULL)) {
+#else
+       /* We aren't compiled for failover, so just move to FTS_FREE */
+       if (lease->binding_state == FTS_BACKUP) {
+#endif
+               lease->binding_state = FTS_FREE;
+               lease->next_binding_state = FTS_FREE;
+               lease->rewind_binding_state = FTS_FREE;
+       }
+
        /* Put the lease on the right queue.  Failure to queue is probably
         * due to a bogus binding state.  In such a case, we claim success,
         * so that later leases in a hash_foreach are processed, but we
@@ -2495,14 +2835,37 @@ void expire_all_pools ()
 {
        struct shared_network *s;
        struct pool *p;
-       struct hash_bucket *hb;
        int i;
        struct lease *l;
-       struct lease **lptr[RESERVED_LEASES+1];
+       LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1];
 
        /* Indicate that we are in the startup phase */
        server_starting = SS_NOSYNC | SS_QFOLLOW;
 
+#if defined (BINARY_LEASES)
+       /* set up the growth factors for the binary leases.
+        * We use 100% for free, 50% for active and backup
+        * 20% for expired, abandoned and reserved
+        * but no less than 100, 50, and 20.
+        */
+       for (s = shared_networks; s; s = s -> next) {
+           for (p = s -> pools; p != NULL; p = p -> next) {
+               size_t num_f = 100, num_a = 50, num_e = 20;
+               if (p->lease_count > 100) {
+                   num_f = p->lease_count;
+                   num_a = num_f / 2;
+                   num_e = num_f / 5;
+               }
+               lc_init_growth(&p->free, num_f);
+               lc_init_growth(&p->active, num_a);
+               lc_init_growth(&p->expired, num_a);
+               lc_init_growth(&p->abandoned, num_e);
+               lc_init_growth(&p->backup, num_e);
+               lc_init_growth(&p->reserved, num_e);
+           }
+       }
+#endif
+
        /* First, go over the hash list and actually put all the leases
           on the appropriate lists. */
        lease_ip_hash_foreach(lease_ip_addr_hash, lease_instantiate);
@@ -2530,19 +2893,21 @@ void expire_all_pools ()
                lptr [RESERVED_LEASES] = &p->reserved;
 
                for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
-                   for (l = *(lptr [i]); l; l = l -> next) {
+                   for (l = LEASE_GET_FIRSTP(lptr[i]);
+                        l != NULL;
+                        l = LEASE_GET_NEXTP(lptr[i], l)) {
                        p -> lease_count++;
                        if (l -> ends <= cur_time) {
                                if (l->binding_state == FTS_FREE) {
                                        if (i == FREE_LEASES)
                                                p->free_leases++;
-                                       else
+                                       else if (i != RESERVED_LEASES)
                                                log_fatal("Impossible case "
                                                          "at %s:%d.", MDL);
                                } else if (l->binding_state == FTS_BACKUP) {
                                        if (i == BACKUP_LEASES)
                                                p->backup_leases++;
-                                       else
+                                       else if (i != RESERVED_LEASES)
                                                log_fatal("Impossible case "
                                                          "at %s:%d.", MDL);
                                }
@@ -2570,7 +2935,7 @@ void dump_subnets ()
        struct shared_network *s;
        struct subnet *n;
        struct pool *p;
-       struct lease **lptr[RESERVED_LEASES+1];
+       LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1];
        int i;
 
        log_info ("Subnets:");
@@ -2591,7 +2956,9 @@ void dump_subnets ()
                lptr [RESERVED_LEASES] = &p->reserved;
 
                for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
-                   for (l = *(lptr [i]); l; l = l -> next) {
+                   for (l = LEASE_GET_FIRSTP(lptr[i]);
+                        l != NULL;
+                        l = LEASE_GET_NEXTP(lptr[i], l)) {
                            print_lease (l);
                    }
                }
@@ -2626,23 +2993,19 @@ extern int end;
 extern struct lease *lease_hunks;
 #endif
 
-void free_everything ()
+void free_everything(void)
 {
        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 lease *lc = NULL, *ln = NULL,  *ltemp = NULL;
        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);
@@ -2651,13 +3014,13 @@ void free_everything ()
                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_id_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_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_id_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);
@@ -2763,7 +3126,15 @@ void free_everything ()
        }
 
        /* So are shared networks. */
+       /* XXX: this doesn't work presently, but i'm ok just filtering
+        * it out of the noise (you get a bigger spike on the real leaks).
+        * It would be good to fix this, but it is not a "real bug," so not
+        * today.  This hack is incomplete, it doesn't trim out sub-values.
+        */
        if (shared_networks) {
+               shared_network_dereference (&shared_networks, MDL);
+       /* This is the old method (tries to free memory twice, broken) */
+       } else if (0) {
            shared_network_reference (&nn, shared_networks, MDL);
            do {
                if (nn) {
@@ -2779,8 +3150,8 @@ void free_everything ()
                if (nc -> pools) {
                    pool_reference (&pn, nc -> pools, MDL);
                    do {
-                       struct lease **lptr[RESERVED_LEASES+1];
-                       
+                       LEASE_STRUCT_PTR lptr[RESERVED_LEASES+1];
+
                        if (pn) {
                            pool_reference (&pc, pn, MDL);
                            pool_dereference (&pn, MDL);
@@ -2789,7 +3160,7 @@ void free_everything ()
                            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;
@@ -2799,17 +3170,22 @@ void free_everything ()
 
                        /* As (sigh) are leases. */
                        for (i = FREE_LEASES ; i <= RESERVED_LEASES ; i++) {
-                           if (*lptr [i]) {
-                               lease_reference (&ln, *lptr [i], MDL);
+                           if (LEASE_NOT_EMPTYP(lptr[i])) {
+                               lease_reference(&ln, LEASE_GET_FIRSTP(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);
+                                   /* save a pointer to the current lease */
+                                   lease_reference (&lc, ln, MDL);
+                                   lease_dereference (&ln, MDL);
+
+                                   /* get the next lease if there is one */
+                                   ltemp = LEASE_GET_NEXTP(lptr[i], lc);
+                                   if (ltemp != NULL) {
+                                       lease_reference(&ln, ltemp, MDL);
                                    }
+
+                                   /* remove the current lease from the queue */
+                                   LEASE_REMOVEP(lptr[i], lc);
+
                                    if (lc -> billing_class)
                                       class_dereference (&lc -> billing_class,
                                                          MDL);
@@ -2822,7 +3198,6 @@ void free_everything ()
                                        lease_dereference (&lc -> n_uid, MDL);
                                    lease_dereference (&lc, MDL);
                                } while (ln);
-                               lease_dereference (lptr [i], MDL);
                            }
                        }
                        if (pc -> group)
@@ -2844,6 +3219,9 @@ void free_everything ()
 
        cancel_all_timeouts ();
        relinquish_timeouts ();
+#if defined(DELAYED_ACK)
+       relinquish_ackqueue();
+#endif
        trace_free_all ();
        group_dereference (&root_group, MDL);
        executable_statement_dereference (&default_classification_rules, MDL);
@@ -2859,14 +3237,21 @@ void free_everything ()
 
        universe_free_hash_table (&universe_hash, MDL);
        for (i = 0; i < universe_count; i++) {
+#if 0
                union {
                        const char *c;
                        char *s;
                } foo;
+#endif
                if (universes [i]) {
-                       if (universes [i] -> hash)
-                           option_free_hash_table (&universes [i] -> hash,
-                                                   MDL);
+                       if (universes[i]->name_hash)
+                           option_name_free_hash_table(
+                                               &universes[i]->name_hash,
+                                               MDL);
+                       if (universes[i]->code_hash)
+                           option_code_free_hash_table(
+                                               &universes[i]->code_hash,
+                                               MDL);
 #if 0
                        if (universes [i] -> name > (char *)&end) {
                                foo.c = universes [i] -> name;
@@ -2885,7 +3270,9 @@ void free_everything ()
        relinquish_free_binding_values ();
        relinquish_free_option_caches ();
        relinquish_free_packets ();
+#if defined(COMPACT_LEASES)
        relinquish_lease_hunks ();
+#endif
        relinquish_hash_bucket_hunks ();
        omapi_type_relinquish ();
 }