]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Expire old IPv6 leases. Also a number of fixes.
authorShane Kerr <shane@isc.org>
Fri, 18 May 2007 09:26:58 +0000 (09:26 +0000)
committerShane Kerr <shane@isc.org>
Fri, 18 May 2007 09:26:58 +0000 (09:26 +0000)
See RT ticket #16849 for details.

RELNOTES
includes/dhcpd.h
server/confpars.c
server/dhcpd.c
server/dhcpv6.c
server/mdb6.c

index 711c0ba196d399c9314b4c21d297d5011479ea56..21399a409d17253e4bd4f5ede08e85567f4329b9 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -39,8 +39,6 @@ release, which will be addressed shortly:
 
 - Dynamically allocated leases do not respond to Confirm messages.
 
-- Old (expired) leases are never cleaned.
-
 - The client and server can only operate DHCPv4 or DHCPv6 at a time,
   not both, so two instances of the daemons are required with the
   "-6" command line option.
@@ -52,6 +50,8 @@ the README file.
 
                        Changes since 4.0.0-20070413
 
+- Old (expired) leases are now cleaned.
+
 - IPv6 subnets now have support for arbitrary allocation ranges via
   a new 'range6' configuration directive.
 
index 022cd9c43187f9a91a12d2530611d4e9779dc470..a8f9ba8d41be4a7c4279fe0579351781432263f2 100644 (file)
@@ -1249,8 +1249,10 @@ typedef unsigned char option_mask [16];
 #define MAX_TIME 0x7fffffff
 #define MIN_TIME 0
 
+                                               /* these are referenced */
 typedef struct hash_table ia_na_hash_t;
 typedef struct hash_table iaaddr_hash_t;
+       int num_inactive;                       /* count of inactive IAADDR */
 
 struct iaaddr {
        int refcnt;                             /* reference count */
@@ -1260,6 +1262,12 @@ struct iaaddr {
        time_t valid_lifetime_end_time;         /* time address expires */
        struct ia_na *ia_na;                    /* IA for this address */
        struct ipv6_pool *ipv6_pool;            /* pool for this address */
+/*
+ * For now, just pick an arbitrary time to keep old leases
+ * around (value in seconds).
+ */
+#define EXPIRED_IPV6_CLEANUP_TIME (60*60)
+
        int heap_index;                         /* index into heap, or -1 
                                                   (internal use only) */
 };
@@ -3110,6 +3118,7 @@ isc_result_t dhcp_failover_process_update_request_all (dhcp_failover_state_t *,
                                                       failover_message_t *);
 isc_result_t dhcp_failover_process_update_done (dhcp_failover_state_t *,
                                                failover_message_t *);
+void ia_na_remove_all_iaaddr(struct ia_na *ia_na, const char *file, int line);
 void dhcp_failover_recover_done (void *);
 void failover_print PROTO ((char *, unsigned *, unsigned, const char *));
 void update_partner PROTO ((struct lease *));
@@ -3194,10 +3203,11 @@ isc_result_t find_ipv6_pool(struct ipv6_pool **pool,
 isc_boolean_t ipv6_addr_in_pool(const struct in6_addr *addr, 
                                const struct ipv6_pool *pool);
 
-void expire_leases(time_t now);
 isc_result_t renew_leases(struct ia_na *ia_na);
 isc_result_t release_leases(struct ia_na *ia_na);
 isc_result_t decline_leases(struct ia_na *ia_na);
+void schedule_lease_timeout(struct ipv6_pool *pool);
+void schedule_all_ipv6_lease_timeouts();
 
 void mark_hosts_unavailable(void);
 void mark_interfaces_unavailable(void);
index 61b696b6d112297a65d7ae555f7269ddb64897eb..218a0789616ea8ad6b33ba592a3e1b6ad37bf494 100644 (file)
@@ -34,7 +34,7 @@
 
 #ifndef lint
 static char copyright[] =
-"$Id: confpars.c,v 1.163 2007/05/08 23:05:21 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium.  All rights reserved.\n";
+"$Id: confpars.c,v 1.164 2007/05/18 09:26:58 shane Exp $ Copyright (c) 2004-2006 Internet Systems Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
 #include "dhcpd.h"
@@ -3805,6 +3805,7 @@ parse_ia_na_declaration(struct parse *cfile) {
        enum dhcp_token token;
        struct ia_na *ia_na;
        const char *val;
+       struct ia_na *old_ia_na;
        int len;
        u_int32_t iaid;
        struct iaddr iaddr;
@@ -3959,23 +3960,42 @@ parse_ia_na_declaration(struct parse *cfile) {
                add_lease6(pool, iaaddr, end_time);
                switch (state) {
                        case FTS_ABANDONED:
-                               decline_lease6(pool, iaaddr);
+                               release_lease6(pool, iaaddr);
                                break;
                        case FTS_EXPIRED:
                                decline_lease6(pool, iaaddr);
                                iaaddr->state = FTS_EXPIRED;
                                break;
                        case FTS_RELEASED:
-                               decline_lease6(pool, iaaddr);
-                               iaaddr->state = FTS_RELEASED;
+                               release_lease6(pool, iaaddr);
                                break;
                }
                ipv6_pool_dereference(&pool, MDL);
                iaaddr_dereference(&iaaddr, MDL);
        }
 
-       ia_na_hash_add(ia_active, (char *)ia_na->iaid_duid.data,
-                      ia_na->iaid_duid.len, ia_na, MDL);
+       /*
+        * If we have an existing record for this IA_NA, remove it.
+        */
+       old_ia_na = NULL;
+       if (ia_na_hash_lookup(&old_ia_na, ia_active,
+                             (char *)ia_na->iaid_duid.data,
+                             ia_na->iaid_duid.len, MDL)) {
+               ia_na_hash_delete(ia_active, 
+                                 (char *)ia_na->iaid_duid.data,
+                                 ia_na->iaid_duid.len, MDL);
+               ia_na_remove_all_iaaddr(old_ia_na, MDL);
+               ia_na_dereference(&old_ia_na, MDL);
+       }
+
+       /*
+        * If we have addresses, add this, otherwise don't bother.
+        */
+       if (ia_na->num_iaaddr > 0) {
+               ia_na_hash_add(ia_active, (char *)ia_na->iaid_duid.data,
+                              ia_na->iaid_duid.len, ia_na, MDL);
+       }
+       ia_na_dereference(&ia_na, MDL);
 }
 
 /*
index 6de4d9f2b9302fa835ea3199d16ed3d85b7fd1f9..2c0a8d16af3979f5f50d0cdcd04b95d73694ecfa 100644 (file)
@@ -34,7 +34,7 @@
 
 #ifndef lint
 static char ocopyright[] =
-"$Id: dhcpd.c,v 1.122 2007/05/08 23:05:22 dhankins Exp $ Copyright 2004-2006 Internet Systems Consortium.";
+"$Id: dhcpd.c,v 1.123 2007/05/18 09:26:58 shane Exp $ Copyright 2004-2006 Internet Systems Consortium.";
 #endif
 
   static char copyright[] =
@@ -952,6 +952,11 @@ void postdb_startup (void)
        /* Initialize the failover listener state. */
        dhcp_failover_startup ();
 #endif
+
+       /*
+        * Begin our lease timeout background task.
+        */
+       schedule_all_ipv6_lease_timeouts();
 }
 
 /* Print usage message. */
index fca44c5608fa71fc09408c58225aaf1e808b6236..96a463cdae1d4a3da9c92916aeeb4b29f480fcdf 100644 (file)
@@ -989,6 +989,7 @@ lease_to_client(struct data_string *reply_ret,
        u_int32_t iaid;
        struct ia_na *ia_na;
        struct ia_na *existing_ia_na;
+       struct ia_na *old_ia_na;
        int i;
 
        /*
@@ -1548,9 +1549,21 @@ lease_to_client(struct data_string *reply_ret,
                         * Otherwise save the IA_NA, for the same reason.
                         */
                        else if (packet->dhcpv6_msg_type != DHCPV6_SOLICIT) {
-                               ia_na_hash_delete(ia_active, 
-                                                 (char *)ia_na->iaid_duid.data,
-                                                 ia_na->iaid_duid.len, MDL);
+                               /*
+                                * Remove previous version of this IA_NA,
+                                * if one exists.
+                                */
+                               struct data_string *d = &ia_na->iaid_duid;
+                               old_ia_na = NULL;
+                               if (ia_na_hash_lookup(&old_ia_na, ia_active,
+                                                     (char *)d->data,
+                                                     d->len, MDL)) {
+                                       ia_na_hash_delete(ia_active, 
+                                                         (char *)d->data,
+                                                         d->len, MDL);
+                                       ia_na_dereference(&old_ia_na, MDL);
+                               }
+
                                /*
                                 * ia_na_add_iaaddr() will reference the
                                 * lease, so we need to dereference the
@@ -1570,6 +1583,7 @@ lease_to_client(struct data_string *reply_ret,
                                               ia_na->iaid_duid.len, 
                                               ia_na, MDL);
                                write_ia_na(ia_na);
+                               schedule_lease_timeout(lease->ipv6_pool);
 
                                /* If this constitutes a binding, and we
                                 * are performing ddns updates, then give
index 2f94bd9ad64c28dad62f99e24b591adf28e7aff7..fcfc2c13a042c0f16aa1ea8e2b054a3acec5e2b2 100644 (file)
@@ -321,6 +321,8 @@ ia_na_remove_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr,
                                ia_na->iaaddr[j-1] = ia_na->iaaddr[j];
                        }
                        /* decrease our total count */
+                       /* remove the back-reference in the IAADDR itself */
+                       ia_na_dereference(&iaaddr->ia_na, file, line);
                        ia_na->num_iaaddr--;
                        return;
                }
@@ -328,6 +330,19 @@ ia_na_remove_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr,
        log_error("%s(%d): IAADDR not in IA_NA", file, line);
 }
 
+ * Remove all addresses from an IA_NA.
+ */
+void
+ia_na_remove_all_iaaddr(struct ia_na *ia_na, const char *file, int line) {
+       int i, j;
+
+       for (i=0; i<ia_na->num_iaaddr; i++) {
+               iaaddr_dereference(&(ia_na->iaaddr[i]), file, line);
+       }
+       ia_na->num_iaaddr = 0;
+}
+
+/*
 /*
  * Helper function for lease heaps.
  * Makes the top of the heap the oldest lease.
@@ -572,8 +587,7 @@ activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
        struct iaaddr *test_iaaddr;
        struct data_string new_ds;
        struct iaaddr *iaaddr;
-       isc_result_t iaaddr_allocate_result;
-       isc_result_t insert_result;
+       isc_result_t result;
 
        /* 
         * Use the UID as our initial seed for the hash
@@ -616,8 +630,8 @@ activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
                        return ISC_R_NOMEMORY;
                }
                new_ds.data = new_ds.buffer->data;
-               memcpy((char *)new_ds.data, ds.data, ds.len);
-               memcpy((char *)new_ds.data + ds.len, &tmp, sizeof(tmp));
+               memcpy(new_ds.buffer->data, ds.data, ds.len);
+               memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp));
                data_string_forget(&ds, MDL);
                data_string_copy(&ds, &new_ds, MDL);
                data_string_forget(&new_ds, MDL);
@@ -630,14 +644,21 @@ activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
         * to hold it.
         */
        iaaddr = NULL;
-       iaaddr_allocate_result = iaaddr_allocate(&iaaddr, MDL);
-       if (iaaddr_allocate_result != ISC_R_SUCCESS) {
-               return iaaddr_allocate_result;
+       result = iaaddr_allocate(&iaaddr, MDL);
+       if (result != ISC_R_SUCCESS) {
+               return result;
        }
        memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
 
-       iaaddr_reference(addr, iaaddr, MDL);
-       return add_lease6(pool, iaaddr, valid_lifetime_end_time);
+       /*
+        * Add the lease to the pool.
+        */
+       result = add_lease6(pool, iaaddr, valid_lifetime_end_time);
+       if (result == ISC_R_SUCCESS) {
+               iaaddr_reference(addr, iaaddr, MDL);
+       }
+       iaaddr_dereference(&iaaddr, MDL);
+       return result;
 }
 
 /*
@@ -648,24 +669,58 @@ isc_result_t
 add_lease6(struct ipv6_pool *pool, struct iaaddr *iaaddr,
           time_t valid_lifetime_end_time) {
        isc_result_t insert_result;
+       struct iaaddr *test_iaaddr;
+       struct iaaddr *tmp_iaaddr;
 
        iaaddr->state = FTS_ACTIVE;
        iaaddr->valid_lifetime_end_time = valid_lifetime_end_time;
        ipv6_pool_reference(&iaaddr->ipv6_pool, pool, MDL);
 
+       /*
+        * If this IAADDR is already in our structures, remove the 
+        * old one.
+        */
+       test_iaaddr = NULL;
+       if (iaaddr_hash_lookup(&test_iaaddr, pool->addrs,
+                              &iaaddr->addr, sizeof(iaaddr->addr), MDL)) {
+               isc_heap_delete(pool->active_timeouts, test_iaaddr->heap_index);
+               iaaddr_hash_delete(pool->addrs, &test_iaaddr->addr, 
+                                  sizeof(test_iaaddr->addr), MDL);
+               pool->num_active--;
+               
+               /*
+                * We're going to do a bit of evil trickery here.
+                *
+                * We need to dereference the entry once to remove our
+                * current reference (in test_iaaddr), and then one
+                * more time to remove the reference left when the
+                * address was added to the pool before.
+                */
+               tmp_iaaddr = test_iaaddr;
+               iaaddr_dereference(&test_iaaddr, MDL);
+               iaaddr_dereference(&tmp_iaaddr, MDL);
+       }
+
        /* 
         * Add IAADDR to our structures.
         */
-        iaaddr_hash_add(pool->addrs, &iaaddr->addr, 
-                       sizeof(iaaddr->addr), iaaddr, MDL);
-       insert_result = isc_heap_insert(pool->active_timeouts, iaaddr);
+       tmp_iaaddr = NULL;
+       iaaddr_reference(&tmp_iaaddr, iaaddr, MDL);
+        iaaddr_hash_add(pool->addrs, &tmp_iaaddr->addr, 
+                       sizeof(tmp_iaaddr->addr), iaaddr, MDL);
+       insert_result = isc_heap_insert(pool->active_timeouts, tmp_iaaddr);
        if (insert_result != ISC_R_SUCCESS) {
                iaaddr_hash_delete(pool->addrs, &iaaddr->addr, 
                                   sizeof(iaaddr->addr), MDL);
-               iaaddr_dereference(&iaaddr, MDL);
+               iaaddr_dereference(&tmp_iaaddr, MDL);
                return insert_result;
        }
 
+       /* 
+        * Note: we intentionally leave tmp_iaaddr referenced; there
+        * is a reference in the heap/hash, after all.
+        */
+
        /*
         * And we're done.
         */
@@ -673,6 +728,9 @@ add_lease6(struct ipv6_pool *pool, struct iaaddr *iaaddr,
        return ISC_R_SUCCESS;
 }
 
+/*
+ * Determine if an address is present in a pool or not.
+ */
 isc_boolean_t
 lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
        struct iaaddr *test_iaaddr;
@@ -687,6 +745,27 @@ lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
        }
 }
 
+/*
+ * Put the lease on our active pool.
+ */
+static isc_result_t
+move_lease_to_active(struct ipv6_pool *pool, struct iaaddr *addr) {
+       isc_result_t insert_result;
+       int old_heap_index;
+
+       old_heap_index = addr->heap_index;
+       insert_result = isc_heap_insert(pool->active_timeouts, addr);
+       if (insert_result == ISC_R_SUCCESS) {
+                       iaaddr_hash_add(pool->addrs, &addr->addr, 
+                               sizeof(addr->addr), addr, MDL);
+               isc_heap_delete(pool->inactive_timeouts, old_heap_index);
+               pool->num_active++;
+               pool->num_inactive--;
+               addr->state = FTS_ACTIVE;
+       }
+       return insert_result;
+}
+
 /*
  * Renew an lease in the pool.
  *
@@ -694,14 +773,44 @@ lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
  * and then invoke renew_lease() on the address.
  *
  * WARNING: lease times must only be extended, never reduced!!!
- *
- * We return a isc_result_t so this function can be called the same
- * as release or decline.
  */
 isc_result_t
 renew_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
-       isc_heap_decreased(pool->active_timeouts, addr->heap_index);
-       return ISC_R_SUCCESS;
+       /*
+        * If we're already active, then we can just move our expiration
+        * time down the heap. 
+        *
+        * Otherwise, we have to move from the inactive heap to the 
+        * active heap.
+        */
+       if (addr->state == FTS_ACTIVE) {
+               isc_heap_decreased(pool->active_timeouts, addr->heap_index);
+               return ISC_R_SUCCESS;
+       } else {
+               return move_lease_to_active(pool, addr);
+       }
+}
+
+/*
+ * Put the lease on our inactive pool, with the specified state.
+ */
+static isc_result_t
+move_lease_to_inactive(struct ipv6_pool *pool, struct iaaddr *addr, 
+                      binding_state_t state) {
+       isc_result_t insert_result;
+       int old_heap_index;
+
+       old_heap_index = addr->heap_index;
+       insert_result = isc_heap_insert(pool->inactive_timeouts, addr);
+       if (insert_result == ISC_R_SUCCESS) {
+               iaaddr_hash_delete(pool->addrs, 
+                                  &addr->addr, sizeof(addr->addr), MDL);
+               isc_heap_delete(pool->active_timeouts, old_heap_index);
+               addr->state = state;
+               pool->num_active--;
+               pool->num_inactive++;
+       }
+       return insert_result;
 }
 
 /*
@@ -717,7 +826,7 @@ renew_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
 isc_result_t
 expire_lease6(struct iaaddr **addr, struct ipv6_pool *pool, time_t now) {
        struct iaaddr *tmp;
-       isc_result_t insert_result;
+       isc_result_t result;
 
        if (addr == NULL) {
                log_error("%s(%d): NULL pointer reference", MDL);
@@ -732,42 +841,16 @@ expire_lease6(struct iaaddr **addr, struct ipv6_pool *pool, time_t now) {
                tmp = (struct iaaddr *)isc_heap_element(pool->active_timeouts, 
                                                        1);
                if (now > tmp->valid_lifetime_end_time) {
-                       insert_result = isc_heap_insert(pool->inactive_timeouts,
-                                                       tmp);
-                       if (insert_result != ISC_R_SUCCESS) {
-                               return insert_result;
+                       result = move_lease_to_inactive(pool, tmp, FTS_EXPIRED);
+                       if (result == ISC_R_SUCCESS) {
+                               iaaddr_reference(addr, tmp, MDL);
                        }
-                       iaaddr_hash_delete(pool->addrs, 
-                                          &tmp->addr, sizeof(tmp->addr), MDL);
-                       isc_heap_delete(pool->active_timeouts, 1);
-                       tmp->state = FTS_EXPIRED;
-                       iaaddr_reference(addr, tmp, MDL);
-                       pool->num_active--;
+                       return result;
                }
        }
        return ISC_R_SUCCESS;
 }
 
-/*
- * Put the lease on our inactive pool, with the specified state.
- */
-static isc_result_t
-move_lease_to_inactive(struct ipv6_pool *pool, struct iaaddr *addr, 
-                      binding_state_t state) {
-       isc_result_t insert_result;
-       int old_heap_index;
-
-       old_heap_index = addr->heap_index;
-       insert_result = isc_heap_insert(pool->inactive_timeouts, addr);
-       if (insert_result == ISC_R_SUCCESS) {
-               iaaddr_hash_delete(pool->addrs, 
-                                  &addr->addr, sizeof(addr->addr), MDL);
-               isc_heap_delete(pool->active_timeouts, old_heap_index);
-               addr->state = state;
-               pool->num_active--;
-       }
-       return insert_result;
-}
 
 /*
  * For a declined lease, leave it on the "active" pool, but mark
@@ -775,6 +858,14 @@ move_lease_to_inactive(struct ipv6_pool *pool, struct iaaddr *addr,
  */
 isc_result_t
 decline_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
+       isc_result_t result;
+
+       if (addr->state != FTS_ACTIVE) {
+               result = move_lease_to_active(pool, addr);
+               if (result != ISC_R_SUCCESS) {
+                       return result;
+               }
+       }
        addr->state = FTS_ABANDONED;
        addr->valid_lifetime_end_time = MAX_TIME;
        isc_heap_decreased(pool->active_timeouts, addr->heap_index);
@@ -786,7 +877,11 @@ decline_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
  */
 isc_result_t
 release_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
-       return move_lease_to_inactive(pool, addr, FTS_RELEASED);
+       if (addr->state == FTS_ACTIVE) {
+               return move_lease_to_inactive(pool, addr, FTS_RELEASED);
+       } else {
+               return ISC_R_SUCCESS;
+       }
 }
 
 /*
@@ -835,34 +930,127 @@ add_ipv6_pool(struct ipv6_pool *pool) {
 }
 
 
+static void
+cleanup_old_expired(struct ipv6_pool *pool) {
+       struct iaaddr *tmp;
+       struct ia_na *ia_na;
+       
+       while (pool->num_inactive > 0) {
+               tmp = (struct iaaddr *)isc_heap_element(pool->inactive_timeouts,
+                                                       1);
+               if (cur_time < 
+                   tmp->valid_lifetime_end_time + EXPIRED_IPV6_CLEANUP_TIME) {
+                       break;
+               }
+
+               isc_heap_delete(pool->inactive_timeouts, tmp->heap_index);
+               pool->num_inactive--;
+
+               ia_na = NULL;
+               ia_na_reference(&ia_na, tmp->ia_na, MDL);
+               ia_na_remove_iaaddr(ia_na, tmp, MDL);
+               iaaddr_dereference(&tmp, MDL);
+               if (ia_na->num_iaaddr <= 0) {
+                       ia_na_hash_delete(ia_active, 
+                                         (char *)ia_na->iaid_duid.data,
+                                         ia_na->iaid_duid.len, MDL);
+               }
+               ia_na_dereference(&ia_na, MDL);
+       }
+}
+
+static void
+lease_timeout_support(void *vpool) {
+       struct ipv6_pool *pool;
+       struct iaaddr *addr;
+       
+       pool = (struct ipv6_pool *)vpool;
+       for (;;) {
+               /*
+                * Get the next lease scheduled to expire.
+                *
+                * Note that if there are no leases in the pool, 
+                * expire_lease6() will return ISC_R_SUCCESS with 
+                * a NULL lease.
+                */
+               addr = NULL;
+               if (expire_lease6(&addr, pool, cur_time) != ISC_R_SUCCESS) {
+                       break;
+               }
+               if (addr == NULL) {
+                       break;
+               }
+
+               /* Look to see if there were ddns updates, and if
+                * so, drop them.
+                *
+                * DH: Do we want to do this on a special 'depref'
+                * timer rather than expiration timer?
+                */
+               ddns_removals(NULL, addr);
+
+               write_ia_na(addr->ia_na);
+
+               iaaddr_dereference(&addr, MDL);
+       }
+
+       /*
+        * Do some cleanup of our expired leases.
+        */
+       cleanup_old_expired(pool);
+
+       /*
+        * Schedule next round of expirations.
+        */
+       schedule_lease_timeout(pool);
+}
+
 /*
- * Remove all leases that have expired from the active pool.
+ * For a given pool, add a timer that will remove the next
+ * lease to expire.
  */
 void 
-expire_leases(time_t now) {
-       struct ipv6_pool *pool;
+schedule_lease_timeout(struct ipv6_pool *pool) {
+       struct iaaddr *tmp;
+       time_t timeout;
+       time_t next_timeout;
+
+       next_timeout = MAX_TIME;
+
+       if (pool->num_active > 0) {
+               tmp = (struct iaaddr *)isc_heap_element(pool->active_timeouts, 
+                                                       1);
+               if (tmp->valid_lifetime_end_time < next_timeout) {
+                       next_timeout = tmp->valid_lifetime_end_time;
+               }
+       }
+
+       if (pool->num_inactive > 0) {
+               tmp = (struct iaaddr *)isc_heap_element(pool->inactive_timeouts,
+                                                       1);
+               timeout = tmp->valid_lifetime_end_time + 
+                         EXPIRED_IPV6_CLEANUP_TIME;
+               if (timeout < next_timeout) {
+                       next_timeout = timeout;
+               }
+       }
+
+       if (next_timeout < MAX_TIME) {
+               add_timeout(next_timeout, lease_timeout_support, pool,
+                           (tvref_t)ipv6_pool_reference, 
+                           (tvunref_t)ipv6_pool_dereference);
+       }
+}
+
+/*
+ * Schedule timeouts across all pools.
+ */
+void
+schedule_all_ipv6_lease_timeouts(void) {
        int i;
-       struct iaaddr *addr;
 
        for (i=0; i<num_pools; i++) {
-               pool = pools[i];
-               for (;;) {
-                       addr = NULL;
-                       if (expire_lease6(&addr, pool, now) != ISC_R_SUCCESS) {
-                               break;
-                       }
-                       if (addr == NULL) {
-                               break;
-                       }
-                       /* Look to see if there were ddns updates, and if
-                        * so, drop them.
-                        *
-                        * DH: Do we want to do this on a special 'depref'
-                        * timer rather than expiration timer?
-                        */
-                       ddns_removals(NULL, addr);
-                       iaaddr_dereference(&addr, MDL);
-               }
+               schedule_lease_timeout(pools[i]);
        }
 }