]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
[rt30281]
authorShawn Routhier <sar@isc.org>
Fri, 17 Aug 2012 19:05:49 +0000 (12:05 -0700)
committerShawn Routhier <sar@isc.org>
Fri, 17 Aug 2012 19:05:49 +0000 (12:05 -0700)
Modify the renew_lease6() code to properly handle a lease time
that is reduced rather than extended.
Fix the ATF tests for mdb6 and add a new test to check the
above condition.

RELNOTES
doc/devel/mainpage.dox
server/dhcpv6.c
server/mdb6.c
server/tests/mdb6_unittest.c

index 705244a5535c4995112d9edb37d4100626190351..2342e5e97fa4f9ff806708e703bce72f6bbe3380 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -1,6 +1,6 @@
              Internet Systems Consortium DHCP Distribution
-                             Version 4.2.0
-                            21 December 2012
+                             Version 4.3.0
+                            21 December 2013
 
                              Release Notes
 
@@ -110,6 +110,13 @@ work on other platforms. Please report any problems and suggested fixes to
   the doc directory. It is currently in early stages of development,
   but is expected to grow in the near future. [ISC-Bugs 25901]
 
+! An issue with the use of lease times was found and fixed.  Making
+  certain chagnes to the end time of an IPv6 lease could cause the
+  server to abort.  Thanks to Glen Eustace of Massey University,
+  New Zealand for finding this issue.
+  [ISC-Bugs #30281]
+  CVE: CVE-2012-3955
+
                        Changes since 4.2.3
 
 ! Add a check for a null pointer before calling the regexec function.
index ab5f66df91a77d14838f7b223b793373a5510ea0..b6d5c431dc256aefcf4204679a29989803d4857b 100644 (file)
@@ -8,6 +8,8 @@
  http://www.isc.org/software/dhcp website.
 
  @section toc Table Of Contents
+  - Server
+    - @subpage ipv6structures
   - @subpage tests
     - @subpage testsOverview
     - @subpage testsAtf
index 4912bb1923e2c24164e9ce5992078dfc910e9f21..12d31fca4488182996dc359d7dc5338df717db2e 100644 (file)
@@ -1837,9 +1837,6 @@ reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
                        ia_reference(&tmp->ia, reply->ia, MDL);
 
                        /* Commit 'hard' bindings. */
-                       tmp->hard_lifetime_end_time =
-                               tmp->soft_lifetime_end_time;
-                       tmp->soft_lifetime_end_time = 0;
                        renew_lease6(tmp->ipv6_pool, tmp);
                        schedule_lease_timeout(tmp->ipv6_pool);
 
@@ -2498,9 +2495,6 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
                        ia_reference(&tmp->ia, reply->ia, MDL);
 
                        /* Commit 'hard' bindings. */
-                       tmp->hard_lifetime_end_time =
-                               tmp->soft_lifetime_end_time;
-                       tmp->soft_lifetime_end_time = 0;
                        renew_lease6(tmp->ipv6_pool, tmp);
                        schedule_lease_timeout(tmp->ipv6_pool);
 
@@ -3370,9 +3364,6 @@ reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia) {
                        ia_reference(&tmp->ia, reply->ia, MDL);
 
                        /* Commit 'hard' bindings. */
-                       tmp->hard_lifetime_end_time =
-                               tmp->soft_lifetime_end_time;
-                       tmp->soft_lifetime_end_time = 0;
                        renew_lease6(tmp->ipv6_pool, tmp);
                        schedule_lease_timeout(tmp->ipv6_pool);
                }
index 7b888c23b4070512550d5b3b3815bae9c0423fb3..0e76264cda66ec4492fe8a381e1f2c18746f4be7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2007-2012 by Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * A brief description of the IPv6 structures as reverse engineered.
  *
  * There are three major data strucutes involved in the database:
- * ipv6_pool - this contains information about a pool of addresses or prefixes
+ *
+ * - ipv6_pool - this contains information about a pool of addresses or prefixes
  *             that the server is using.  This includes a hash table that
  *             tracks the active items and a pair of heap tables one for
  *             active items and one for non-active items.  The heap tables
  *             are used to determine the next items to be modified due to
  *             timing events (expire mostly).
- * ia_xx     - this contains information about a single IA from a request
+ * - ia_xx   - this contains information about a single IA from a request
  *             normally it will contain one pointer to a lease for the client
  *             but it may contain more in some circumstances.  There are 3
- *             hash tables to aid in accessing these one each for NA, TA and PD
- * iasubopt  - the v6 lease structure.  These are creaeted dynamically when
+ *             hash tables to aid in accessing these one each for NA, TA and PD.
+ * - iasubopt- the v6 lease structure.  These are created dynamically when
  *             a client asks for something and will eventually be destroyed
  *             if the client doesn't re-ask for that item.  A lease has space
  *             for backpointers to the IA and to the pool to which it belongs.
- *             The pool backpointer is always filled, the IA pointer may not be
+ *             The pool backpointer is always filled, the IA pointer may not be.
  *
  * In normal use we then have something like this:
  *
+ * \verbatim
  * ia hash tables
  *  ia_na_active                           +----------------+
  *  ia_ta_active          +------------+   | pool           |
@@ -53,6 +55,7 @@
  * |  iasubopt array |<---|  iaptr     |<--|  inactive heap |
  * |   lease ptr     |--->|            |   |                |
  * +-----------------+    +------------+   +----------------+
+ * \endverbatim
  *
  * For the pool either the inactive heap will have a pointer
  * or both the active heap and the active hash will have pointers.
@@ -936,7 +939,7 @@ create_lease6(struct ipv6_pool *pool, struct iasubopt **addr,
 }
 
 
-/*! \file server/mdb6.c
+/*!
  *
  * \brief Cleans up leases when reading from a lease file
  *
@@ -1237,29 +1240,49 @@ move_lease_to_active(struct ipv6_pool *pool, struct iasubopt *lease) {
        return insert_result;
 }
 
-/*
- * Renew an lease in the pool.
+/*!
+ * \brief Renew a lease in the pool.
+ *
+ * The hard_lifetime_end_time of the lease should be set to
+ * the current expiration time.
+ * The soft_lifetime_end_time of the lease should be set to
+ * the desired expiration time.
+ *
+ * This routine will compare the two and call the correct
+ * heap routine to move the lease.  If the lease is active
+ * and the new expiration time is greater (the normal case)
+ * then we call isc_heap_decreased() as a larger time is a
+ * lower priority.  If the new expiration time is less then
+ * we call isc_heap_increased().
+ *
+ * If the lease is abandoned then it will be on the active list
+ * and we will always call isc_heap_increased() as the previous
+ * expiration would have been all 1s (as close as we can get
+ * to infinite).
  *
- * To do this, first set the new hard_lifetime_end_time for the resource,
- * and then invoke renew_lease6() on it.
+ * If the lease is moving to active we call that routine
+ * which will move it from the inactive list to the active list.
  *
- * WARNING: lease times must only be extended, never reduced!!!
+ * \param pool a pool the lease belongs to
+ * \param lease the lease to be renewed
+ *
+ * \return result of the renew operation (ISC_R_SUCCESS if successful,
+           ISC_R_NOMEMORY when run out of memory)
  */
 isc_result_t
 renew_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
-       /*
-        * If we're already active, then we can just move our expiration
-        * time down the heap. 
-        *
-        * If we're abandoned then we are already on the active list
-        * but we need to retag the lease and move our expiration
-        * from infinite to the current value
-        *
-        * Otherwise, we have to move from the inactive heap to the 
-        * active heap.
-        */
+       time_t old_end_time = lease->hard_lifetime_end_time;
+       lease->hard_lifetime_end_time = lease->soft_lifetime_end_time;
+       lease->soft_lifetime_end_time = 0;
+
        if (lease->state == FTS_ACTIVE) {
-               isc_heap_decreased(pool->active_timeouts, lease->heap_index);
+               if (old_end_time <= lease->hard_lifetime_end_time) {
+                       isc_heap_decreased(pool->active_timeouts,
+                                          lease->heap_index);
+               } else {
+                       isc_heap_increased(pool->active_timeouts,
+                                          lease->heap_index);
+               }
                return ISC_R_SUCCESS;
        } else if (lease->state == FTS_ABANDONED) {
                char tmp_addr[INET6_ADDRSTRLEN];
@@ -1922,9 +1945,8 @@ change_leases(struct ia_xx *ia,
 /*
  * Renew all leases in an IA from all pools.
  *
- * The new hard_lifetime_end_time should be updated for the addresses/prefixes.
- *
- * WARNING: lease times must only be extended, never reduced!!!
+ * The new lifetime should be in the soft_lifetime_end_time
+ * and will be moved to hard_lifetime_end_time by renew_lease6.
  */
 isc_result_t 
 renew_leases(struct ia_xx *ia) {
index ab5f87cfcbf3c4dd3ae2b022515f04414bc36dfd..56b47184d08a1274ddb9857b0a0efef5f315f00d 100644 (file)
@@ -34,6 +34,11 @@ void build_prefix6(struct in6_addr *pref, const struct in6_addr *net_start_pref,
                    int pool_bits, int pref_bits,
                    const struct data_string *input);
 
+/*
+ * Basic iaaddr manipulation.
+ * Verify construction and referencing of an iaaddr.
+ */
+
 ATF_TC(iaaddr_basic);
 ATF_TC_HEAD(iaaddr_basic, tc)
 {
@@ -44,10 +49,15 @@ ATF_TC_BODY(iaaddr_basic, tc)
 {
     struct iasubopt *iaaddr;
     struct iasubopt *iaaddr_copy;
-    /*
-     * Test 0: Basic iaaddr manipulation.
-     */
+
+    /* set up dhcp globals */
+    dhcp_context_create();
+
+    /* and other common arguments */
     iaaddr = NULL;
+    iaaddr_copy = NULL;
+
+    /* tests */
     if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
         atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL);
     }
@@ -57,7 +67,6 @@ ATF_TC_BODY(iaaddr_basic, tc)
     if (iaaddr->heap_index != -1) {
         atf_tc_fail("ERROR: bad heap_index %s:%d", MDL);
     }
-    iaaddr_copy = NULL;
     if (iasubopt_reference(&iaaddr_copy, iaaddr, MDL) != ISC_R_SUCCESS) {
         atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL);
     }
@@ -69,6 +78,10 @@ ATF_TC_BODY(iaaddr_basic, tc)
     }
 }
 
+/*
+ * Basic iaaddr sanity checks.
+ * Verify that the iaaddr code does some sanity checking.
+ */
 
 ATF_TC(iaaddr_negative);
 ATF_TC_HEAD(iaaddr_negative, tc)
@@ -81,6 +94,10 @@ ATF_TC_BODY(iaaddr_negative, tc)
     struct iasubopt *iaaddr;
     struct iasubopt *iaaddr_copy;
 
+    /* set up dhcp globals */
+    dhcp_context_create();
+
+    /* tests */
     /* bogus allocate arguments */
     if (iasubopt_allocate(NULL, MDL) != DHCP_R_INVALIDARG) {
         atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL);
@@ -121,6 +138,9 @@ ATF_TC_BODY(iaaddr_negative, tc)
     }
 }
 
+/*
+ * Basic ia_na manipulation.
+ */
 
 ATF_TC(ia_na_basic);
 ATF_TC_HEAD(ia_na_basic, tc)
@@ -135,46 +155,55 @@ ATF_TC_BODY(ia_na_basic, tc)
     struct ia_xx *ia_na_copy;
     struct iasubopt *iaaddr;
 
-    /*
-     * Test 2: Basic ia_na manipulation.
-     */
+    /* set up dhcp globals */
+    dhcp_context_create();
+
+    /* and other common arguments */
     iaid = 666;
     ia_na = NULL;
+    ia_na_copy = NULL;
+    iaaddr = NULL;
+
+    /* tests */
     if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_allocate() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL);
     }
     if (memcmp(ia_na->iaid_duid.data, &iaid, sizeof(iaid)) != 0) {
-        atf_tc_fail("ERROR: bad IAID_DUID %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad IAID_DUID %s:%d", MDL);
     }
     if (memcmp(ia_na->iaid_duid.data+sizeof(iaid), "TestDUID", 8) != 0) {
-        atf_tc_fail("ERROR: bad IAID_DUID %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad IAID_DUID %s:%d", MDL);
     }
     if (ia_na->num_iasubopt != 0) {
-        atf_tc_fail("ERROR: bad num_iasubopt %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_iasubopt %s:%d", MDL);
     }
-    ia_na_copy = NULL;
     if (ia_reference(&ia_na_copy, ia_na, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_reference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_reference() %s:%d", MDL);
     }
-    iaaddr = NULL;
     if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: iasubopt_allocate() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL);
     }
     if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_add_iasubopt() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_add_iasubopt() %s:%d", MDL);
     }
     ia_remove_iasubopt(ia_na, iaaddr, MDL);
     if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: iasubopt_reference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL);
     }
     if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL);
     }
     if (ia_dereference(&ia_na_copy, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL);
     }
 }
 
+/*
+ * Lots of iaaddr in our ia_na.
+ * Create many iaaddrs and attach them to an ia_na
+ * then clean up by removing them one at a time and
+ * all at once by dereferencing the ia_na.
+ */
 
 ATF_TC(ia_na_manyaddrs);
 ATF_TC_HEAD(ia_na_manyaddrs, tc)
@@ -188,26 +217,27 @@ ATF_TC_BODY(ia_na_manyaddrs, tc)
     struct ia_xx *ia_na;
     struct iasubopt *iaaddr;
     int i;
-    /*
-     * Test 3: lots of iaaddr in our ia_na
-     */
 
+    /* set up dhcp globals */
+    dhcp_context_create();
+
+    /* tests */
     /* lots of iaaddr that we delete */
     iaid = 666;
     ia_na = NULL;
     if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_allocate() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL);
     }
     for (i=0; i<100; i++) {
         iaaddr = NULL;
         if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
-            atf_tc_fail("ERROR: iasubopt_allocate() %s:%d\n", MDL);
+            atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL);
         }
         if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
-            atf_tc_fail("ERROR: ia_add_iasubopt() %s:%d\n", MDL);
+            atf_tc_fail("ERROR: ia_add_iasubopt() %s:%d", MDL);
         }
         if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
-            atf_tc_fail("ERROR: iasubopt_reference() %s:%d\n", MDL);
+            atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL);
         }
     }
 
@@ -220,32 +250,37 @@ ATF_TC_BODY(ia_na_manyaddrs, tc)
     }
 #endif
     if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL);
     }
 
     /* lots of iaaddr, let dereference cleanup */
     iaid = 666;
     ia_na = NULL;
     if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_allocate() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL);
     }
     for (i=0; i<100; i++) {
         iaaddr = NULL;
         if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
-            atf_tc_fail("ERROR: iasubopt_allocate() %s:%d\n", MDL);
+            atf_tc_fail("ERROR: iasubopt_allocate() %s:%d", MDL);
         }
         if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
-            atf_tc_fail("ERROR: ia_add_iasubopt() %s:%d\n", MDL);
+            atf_tc_fail("ERROR: ia_add_iasubopt() %s:%d", MDL);
         }
         if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
-            atf_tc_fail("ERROR: iasubopt_reference() %s:%d\n", MDL);
+            atf_tc_fail("ERROR: iasubopt_reference() %s:%d", MDL);
         }
     }
     if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL);
     }
 }
 
+/*
+ * Basic ia_na sanity checks.
+ * Verify that the ia_na code does some sanity checking.
+ */
+
 ATF_TC(ia_na_negative);
 ATF_TC_HEAD(ia_na_negative, tc)
 {
@@ -257,56 +292,65 @@ ATF_TC_BODY(ia_na_negative, tc)
     uint32_t iaid;
     struct ia_xx *ia_na;
     struct ia_xx *ia_na_copy;
-    /*
-     * Test 4: Errors in ia_na.
-     */
+
+    /* set up dhcp globals */
+    dhcp_context_create();
+
+    /* tests */
     /* bogus allocate arguments */
     if (ia_allocate(NULL, 123, "", 0, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ia_allocate() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL);
     }
     ia_na = (struct ia_xx *)1;
     if (ia_allocate(&ia_na, 456, "", 0, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ia_allocate() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL);
     }
 
     /* bogus reference arguments */
     iaid = 666;
     ia_na = NULL;
     if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_allocate() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL);
     }
     if (ia_reference(NULL, ia_na, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ia_reference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_reference() %s:%d", MDL);
     }
     ia_na_copy = (struct ia_xx *)1;
     if (ia_reference(&ia_na_copy, ia_na, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ia_reference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_reference() %s:%d", MDL);
     }
     ia_na_copy = NULL;
     if (ia_reference(&ia_na_copy, NULL, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ia_reference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_reference() %s:%d", MDL);
     }
     if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL);
     }
 
     /* bogus dereference arguments */
     if (ia_dereference(NULL, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ia_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL);
     }
 
     /* bogus remove */
     iaid = 666;
     ia_na = NULL;
     if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_allocate() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL);
     }
     ia_remove_iasubopt(ia_na, NULL, MDL);
     if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_dereference() %s:%d", MDL);
     }
 }
 
+/*
+ * Basic ipv6_pool manipulation.
+ * Verify that basic pool operations work properly.
+ * The operations include creating a pool and creating,
+ * renewing, expiring, releasing and declining addresses.
+ */
+
 ATF_TC(ipv6_pool_basic);
 ATF_TC_HEAD(ipv6_pool_basic, tc)
 {
@@ -325,129 +369,139 @@ ATF_TC_BODY(ipv6_pool_basic, tc)
     struct iasubopt *expired_iaaddr;
     unsigned int attempts;
 
-    /*
-     * Test 5: Basic ipv6_pool manipulation.
-     */
+    /* set up dhcp globals */
+    dhcp_context_create();
 
-    /* allocate, reference */
+    /* and other common arguments */
     inet_pton(AF_INET6, "1:2:3:4::", &addr);
+
+    uid = "client0";
+    memset(&ds, 0, sizeof(ds));
+    ds.len = strlen(uid);
+    if (!buffer_allocate(&ds.buffer, ds.len, MDL)) {
+        atf_tc_fail("Out of memory");
+    }
+    ds.data = ds.buffer->data;
+    memcpy((char *)ds.data, uid, ds.len);
+
+    /* tests */
+    /* allocate, reference */
     pool = NULL;
-    if (ipv6_pool_allocate(&pool, 0, &addr, 64, 128, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+    if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr,
+                           64, 128, MDL) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL);
     }
     if (pool->num_active != 0) {
-        atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
     }
     if (pool->bits != 64) {
-        atf_tc_fail("ERROR: bad bits %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad bits %s:%d", MDL);
     }
     inet_ntop(AF_INET6, &pool->start_addr, addr_buf, sizeof(addr_buf));
     if (strcmp(inet_ntop(AF_INET6, &pool->start_addr, addr_buf,
                          sizeof(addr_buf)), "1:2:3:4::") != 0) {
-        atf_tc_fail("ERROR: bad start_addr %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad start_addr %s:%d", MDL);
     }
     pool_copy = NULL;
     if (ipv6_pool_reference(&pool_copy, pool, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL);
     }
 
     /* create_lease6, renew_lease6, expire_lease6 */
-    uid = "client0";
-    memset(&ds, 0, sizeof(ds));
-    ds.len = strlen(uid);
-    if (!buffer_allocate(&ds.buffer, ds.len, MDL)) {
-        atf_tc_fail("Out of memory\n");
-    }
-    ds.data = ds.buffer->data;
-    memcpy((char *)ds.data, uid, ds.len);
+    iaaddr = NULL;
     if (create_lease6(pool, &iaaddr,
                       &attempts, &ds, 1) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: create_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: create_lease6() %s:%d", MDL);
     }
     if (pool->num_inactive != 1) {
-        atf_tc_fail("ERROR: bad num_inactive %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_inactive %s:%d", MDL);
     }
     if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: renew_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL);
     }
     if (pool->num_active != 1) {
-        atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
     }
     expired_iaaddr = NULL;
     if (expire_lease6(&expired_iaaddr, pool, 0) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: expire_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL);
     }
     if (expired_iaaddr != NULL) {
-        atf_tc_fail("ERROR: should not have expired a lease %s:%d\n", MDL);
+        atf_tc_fail("ERROR: should not have expired a lease %s:%d", MDL);
     }
     if (pool->num_active != 1) {
-        atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
     }
     if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: expire_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL);
     }
     if (expired_iaaddr == NULL) {
-        atf_tc_fail("ERROR: should have expired a lease %s:%d\n", MDL);
+        atf_tc_fail("ERROR: should have expired a lease %s:%d", MDL);
     }
     if (iasubopt_dereference(&expired_iaaddr, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
     }
     if (pool->num_active != 0) {
-        atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
     }
     if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
     }
 
     /* release_lease6, decline_lease6 */
     if (create_lease6(pool, &iaaddr, &attempts,
               &ds, 1) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: create_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: create_lease6() %s:%d", MDL);
     }
     if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: renew_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL);
     }
     if (pool->num_active != 1) {
-        atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
     }
     if (release_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: decline_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: decline_lease6() %s:%d", MDL);
     }
     if (pool->num_active != 0) {
-        atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
     }
     if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
     }
     if (create_lease6(pool, &iaaddr, &attempts,
               &ds, 1) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: create_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: create_lease6() %s:%d", MDL);
     }
     if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: renew_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL);
     }
     if (pool->num_active != 1) {
-        atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
     }
     if (decline_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: decline_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: decline_lease6() %s:%d", MDL);
     }
     if (pool->num_active != 1) {
-        atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
     }
     if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
     }
 
     /* dereference */
     if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL);
     }
     if (ipv6_pool_dereference(&pool_copy, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL);
     }
 }
 
+/*
+ * Basic ipv6_pool sanity checks.
+ * Verify that the ipv6_pool code does some sanity checking.
+ */
+
 ATF_TC(ipv6_pool_negative);
 ATF_TC_HEAD(ipv6_pool_negative, tc)
 {
@@ -460,37 +514,48 @@ ATF_TC_BODY(ipv6_pool_negative, tc)
     struct ipv6_pool *pool;
     struct ipv6_pool *pool_copy;
 
-    /*
-     * Test 6: Error ipv6_pool manipulation
-     */
-    if (ipv6_pool_allocate(NULL, 0, &addr,
-                   64, 128, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+    /* set up dhcp globals */
+    dhcp_context_create();
+
+    /* and other common arguments */
+    inet_pton(AF_INET6, "1:2:3:4::", &addr);
+
+    /* tests */
+    if (ipv6_pool_allocate(NULL, D6O_IA_NA, &addr,
+                           64, 128, MDL) != DHCP_R_INVALIDARG) {
+        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL);
     }
     pool = (struct ipv6_pool *)1;
-    if (ipv6_pool_allocate(&pool, 0, &addr,
-                   64, 128, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+    if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr,
+                           64, 128, MDL) != DHCP_R_INVALIDARG) {
+        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL);
     }
     if (ipv6_pool_reference(NULL, pool, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL);
     }
     pool_copy = (struct ipv6_pool *)1;
     if (ipv6_pool_reference(&pool_copy, pool, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL);
     }
     pool_copy = NULL;
     if (ipv6_pool_reference(&pool_copy, NULL, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_reference() %s:%d", MDL);
     }
     if (ipv6_pool_dereference(NULL, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL);
     }
     if (ipv6_pool_dereference(&pool_copy, MDL) != DHCP_R_INVALIDARG) {
-        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL);
     }
 }
 
+
+/*
+ * Order of expiration.
+ * Add several addresses to a pool and check that
+ * they expire in the proper order.
+ */
+
 ATF_TC(expire_order);
 ATF_TC_HEAD(expire_order, tc)
 {
@@ -503,68 +568,215 @@ ATF_TC_BODY(expire_order, tc)
     struct ipv6_pool *pool;
     struct in6_addr addr;
         int i;
+    char *uid;
     struct data_string ds;
     struct iasubopt *expired_iaaddr;
     unsigned int attempts;
 
-    /*
-     * Test 7: order of expiration
-     */
+    /* set up dhcp globals */
+    dhcp_context_create();
+
+    /* and other common arguments */
+    inet_pton(AF_INET6, "1:2:3:4::", &addr);
+
+    uid = "client0";
+    memset(&ds, 0, sizeof(ds));
+    ds.len = strlen(uid);
+    if (!buffer_allocate(&ds.buffer, ds.len, MDL)) {
+        atf_tc_fail("Out of memory");
+    }
+    ds.data = ds.buffer->data;
+    memcpy((char *)ds.data, uid, ds.len);
+
+    iaaddr = NULL;
+    expired_iaaddr = NULL;
+
+    /* tests */
     pool = NULL;
-    if (ipv6_pool_allocate(&pool, 0, &addr, 64, 128, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+    if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr,
+                           64, 128, MDL) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL);
     }
+
     for (i=10; i<100; i+=10) {
         if (create_lease6(pool, &iaaddr, &attempts,
                   &ds, i) != ISC_R_SUCCESS) {
-            atf_tc_fail("ERROR: create_lease6() %s:%d\n", MDL);
+            atf_tc_fail("ERROR: create_lease6() %s:%d", MDL);
                 }
         if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
-            atf_tc_fail("ERROR: renew_lease6() %s:%d\n", MDL);
+            atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL);
                 }
         if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
-            atf_tc_fail("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+            atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
                 }
         if (pool->num_active != (i / 10)) {
-            atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+            atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
                 }
     }
     if (pool->num_active != 9) {
-        atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
     }
+
     for (i=10; i<100; i+=10) {
         if (expire_lease6(&expired_iaaddr,
                   pool, 1000) != ISC_R_SUCCESS) {
-            atf_tc_fail("ERROR: expire_lease6() %s:%d\n", MDL);
+            atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL);
                 }
         if (expired_iaaddr == NULL) {
-            atf_tc_fail("ERROR: should have expired a lease %s:%d\n",
+            atf_tc_fail("ERROR: should have expired a lease %s:%d",
                    MDL);
                 }
         if (pool->num_active != (9 - (i / 10))) {
-            atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+            atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
                 }
         if (expired_iaaddr->hard_lifetime_end_time != i) {
-            atf_tc_fail("ERROR: bad hard_lifetime_end_time %s:%d\n",
+            atf_tc_fail("ERROR: bad hard_lifetime_end_time %s:%d",
                    MDL);
                 }
         if (iasubopt_dereference(&expired_iaaddr, MDL) !=
                 ISC_R_SUCCESS) {
-            atf_tc_fail("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+            atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
                 }
     }
     if (pool->num_active != 0) {
-        atf_tc_fail("ERROR: bad num_active %s:%d\n", MDL);
+        atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
     }
     expired_iaaddr = NULL;
     if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: expire_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL);
     }
     if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL);
     }
 }
 
+/*
+ * Reduce the expiration period of a lease.
+ * This test reduces the expiration period of
+ * a lease to verify we process reductions
+ * properly.
+ */
+ATF_TC(expire_order_reduce);
+ATF_TC_HEAD(expire_order_reduce, tc)
+{
+    atf_tc_set_md_var(tc, "descr", "This test case checks that reducing "
+                      "the expiration time of a lease works properly.");
+}
+ATF_TC_BODY(expire_order_reduce, tc)
+{
+    struct iasubopt *iaaddr1, *iaaddr2;
+    struct ipv6_pool *pool;
+    struct in6_addr addr;
+    char *uid;
+    struct data_string ds;
+    struct iasubopt *expired_iaaddr;
+    unsigned int attempts;
+
+    /* set up dhcp globals */
+    dhcp_context_create();
+
+    /* and other common arguments */
+    inet_pton(AF_INET6, "1:2:3:4::", &addr);
+
+    uid = "client0";
+    memset(&ds, 0, sizeof(ds));
+    ds.len = strlen(uid);
+    if (!buffer_allocate(&ds.buffer, ds.len, MDL)) {
+        atf_tc_fail("Out of memory");
+    }
+    ds.data = ds.buffer->data;
+    memcpy((char *)ds.data, uid, ds.len);
+
+    pool = NULL;
+    iaaddr1 = NULL;
+    iaaddr2 = NULL;
+    expired_iaaddr = NULL;
+
+    /*
+     * Add two leases iaaddr1 with expire time of 200
+     * and iaaddr2 with expire time of 300.  Then update
+     * iaaddr2 to expire in 100 instead.  This should cause
+     * iaaddr2 to move with the hash list.
+     */
+    /* create pool and add iaaddr1 and iaaddr2 */
+    if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr,
+                           64, 128, MDL) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL);
+    }
+    if (create_lease6(pool, &iaaddr1, &attempts, &ds, 200) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: create_lease6() %s:%d", MDL);
+    }
+    if (renew_lease6(pool, iaaddr1) != ISC_R_SUCCESS) {
+            atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL);
+    }
+    if (create_lease6(pool, &iaaddr2, &attempts, &ds, 300) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: create_lease6() %s:%d", MDL);
+    }
+    if (renew_lease6(pool, iaaddr2) != ISC_R_SUCCESS) {
+            atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL);
+    }
+
+    /* verify pool */
+    if (pool->num_active != 2) {
+        atf_tc_fail("ERROR: bad num_active %s:%d", MDL);
+    }
+
+    /* reduce lease for iaaddr2 */
+    iaaddr2->soft_lifetime_end_time = 100;
+    if (renew_lease6(pool, iaaddr2) != ISC_R_SUCCESS) {
+            atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL);
+    }
+
+    /* expire a lease, it should be iaaddr2 with an expire time of 100 */
+    if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL);
+    }
+    if (expired_iaaddr == NULL) {
+        atf_tc_fail("ERROR: should have expired a lease %s:%d", MDL);
+    }
+    if (expired_iaaddr != iaaddr2) {
+        atf_tc_fail("Error: incorrect lease expired %s:%d", MDL);
+    }
+    if (expired_iaaddr->hard_lifetime_end_time != 100) {
+        atf_tc_fail("ERROR: bad hard_lifetime_end_time %s:%d", MDL);
+    }
+    if (iasubopt_dereference(&expired_iaaddr, MDL) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
+    }
+
+    /* expire a lease, it should be iaaddr1 with an expire time of 200 */
+    if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: expire_lease6() %s:%d", MDL);
+    }
+    if (expired_iaaddr == NULL) {
+        atf_tc_fail("ERROR: should have expired a lease %s:%d", MDL);
+    }
+    if (expired_iaaddr != iaaddr1) {
+        atf_tc_fail("Error: incorrect lease expired %s:%d", MDL);
+    }
+    if (expired_iaaddr->hard_lifetime_end_time != 200) {
+        atf_tc_fail("ERROR: bad hard_lifetime_end_time %s:%d", MDL);
+    }
+    if (iasubopt_dereference(&expired_iaaddr, MDL) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
+    }
+
+    /* cleanup */
+    if (iasubopt_dereference(&iaaddr1, MDL) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
+    }
+    if (iasubopt_dereference(&iaaddr2, MDL) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
+    }
+    if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL);
+    }
+}
+
+/*
+ * Small pool.
+ * check that a small pool behaves properly.
+ */
 
 ATF_TC(small_pool);
 ATF_TC_HEAD(small_pool, tc)
@@ -577,47 +789,70 @@ ATF_TC_BODY(small_pool, tc)
     struct in6_addr addr;
     struct ipv6_pool *pool;
     struct iasubopt *iaaddr;
+    char *uid;
     struct data_string ds;
     unsigned int attempts;
 
-    /*
-     * Test 8: small pool
-     */
-    pool = NULL;
+    /* set up dhcp globals */
+    dhcp_context_create();
+
+    /* and other common arguments */
+    inet_pton(AF_INET6, "1:2:3:4::", &addr);
     addr.s6_addr[14] = 0x81;
-    if (ipv6_pool_allocate(&pool, 0, &addr, 127, 128, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+
+    uid = "client0";
+    memset(&ds, 0, sizeof(ds));
+    ds.len = strlen(uid);
+    if (!buffer_allocate(&ds.buffer, ds.len, MDL)) {
+        atf_tc_fail("Out of memory");
+    }
+    ds.data = ds.buffer->data;
+    memcpy((char *)ds.data, uid, ds.len);
+
+    pool = NULL;
+    iaaddr = NULL;
+
+    /* tests */
+    if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr,
+                           127, 128, MDL) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL);
     }
+
     if (create_lease6(pool, &iaaddr, &attempts,
               &ds, 42) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: create_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: create_lease6() %s:%d", MDL);
     }
     if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: renew_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL);
     }
     if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
     }
     if (create_lease6(pool, &iaaddr, &attempts,
               &ds, 11) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: create_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: create_lease6() %s:%d", MDL);
     }
     if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: renew_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: renew_lease6() %s:%d", MDL);
     }
     if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: iasubopt_dereference() %s:%d", MDL);
     }
     if (create_lease6(pool, &iaaddr, &attempts,
               &ds, 11) != ISC_R_NORESOURCES) {
-        atf_tc_fail("ERROR: create_lease6() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: create_lease6() %s:%d", MDL);
     }
     if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL);
     }
-    addr.s6_addr[14] = 0;
 }
 
+/*
+ * Address to pool mapping.
+ * Verify that we find the proper pool for an address
+ * or don't find a pool if we don't have one for the given
+ * address.
+ */
 ATF_TC(many_pools);
 ATF_TC_HEAD(many_pools, tc)
 {
@@ -629,49 +864,55 @@ ATF_TC_BODY(many_pools, tc)
     struct in6_addr addr;
     struct ipv6_pool *pool;
 
-    /*
-     * Test 9: functions across all pools
-     */
+    /* set up dhcp globals */
+    dhcp_context_create();
+
+    /* and other common arguments */
+    inet_pton(AF_INET6, "1:2:3:4::", &addr);
+
+    /* tests */
+
     pool = NULL;
-    if (ipv6_pool_allocate(&pool, 0, &addr, 64, 128, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+    if (ipv6_pool_allocate(&pool, D6O_IA_NA, &addr,
+                           64, 128, MDL) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: ipv6_pool_allocate() %s:%d", MDL);
     }
     if (add_ipv6_pool(pool) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: add_ipv6_pool() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: add_ipv6_pool() %s:%d", MDL);
     }
     if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL);
     }
     pool = NULL;
-    if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+    if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: find_ipv6_pool() %s:%d", MDL);
     }
     if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL);
     }
     inet_pton(AF_INET6, "1:2:3:4:ffff:ffff:ffff:ffff", &addr);
     pool = NULL;
-    if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+    if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) {
+        atf_tc_fail("ERROR: find_ipv6_pool() %s:%d", MDL);
     }
     if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ipv6_pool_dereference() %s:%d", MDL);
     }
     inet_pton(AF_INET6, "1:2:3:5::", &addr);
     pool = NULL;
-    if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_NOTFOUND) {
-        atf_tc_fail("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+    if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_NOTFOUND) {
+        atf_tc_fail("ERROR: find_ipv6_pool() %s:%d", MDL);
     }
     inet_pton(AF_INET6, "1:2:3:3:ffff:ffff:ffff:ffff", &addr);
     pool = NULL;
-    if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_NOTFOUND) {
-        atf_tc_fail("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+    if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_NOTFOUND) {
+        atf_tc_fail("ERROR: find_ipv6_pool() %s:%d", MDL);
     }
 
 /*  iaid = 666;
     ia_na = NULL;
     if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
-        atf_tc_fail("ERROR: ia_allocate() %s:%d\n", MDL);
+        atf_tc_fail("ERROR: ia_allocate() %s:%d", MDL);
     }*/
 
     {
@@ -708,6 +949,7 @@ ATF_TP_ADD_TCS(tp)
     ATF_TP_ADD_TC(tp, ipv6_pool_basic);
     ATF_TP_ADD_TC(tp, ipv6_pool_negative);
     ATF_TP_ADD_TC(tp, expire_order);
+    ATF_TP_ADD_TC(tp, expire_order_reduce);
     ATF_TP_ADD_TC(tp, small_pool);
     ATF_TP_ADD_TC(tp, many_pools);