]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Soft binding side-effects
authorFrancis Dupont <fdupont@isc.org>
Thu, 21 Feb 2008 10:31:25 +0000 (10:31 +0000)
committerFrancis Dupont <fdupont@isc.org>
Thu, 21 Feb 2008 10:31:25 +0000 (10:31 +0000)
RELNOTES
includes/dhcpd.h
server/confpars.c
server/db.c
server/dhcpv6.c
server/mdb6.c

index 35f6d5e5bad05e0c78839987109ab641bb560b00..8dfa5d9e31a8b08028a1b36d850deb4b37ceb36e 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -165,6 +165,8 @@ work on other platforms. Please report any problems and suggested fixes to
 
 - DHCPv6 released resources are now marked as released by the client.
 
+- 'Soft' bindings have no more side-effects.
+
                        Changes since 4.0.0b3
 
 - The reverse dns name for PTR updates on IPv6 addresses has been fixed to
index 9bfdf73617d9c6556812a6e263739126e24b44da..d11ea8acd0bda9ea50074d756db84c67c3bdb993 100644 (file)
@@ -1333,11 +1333,12 @@ struct iaaddr {
        u_int8_t plen;                          /* unused/placeholder */
        binding_state_t state;                  /* state */
        struct binding_scope *scope;            /* "set var = value;" */
-       time_t valid_lifetime_end_time;         /* time address expires */
+       time_t hard_lifetime_end_time;          /* time address expires */
+       time_t soft_lifetime_end_time;          /* time ephemeral 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
+ * For now, just pick an arbitrary time to keep old hard leases
  * around (value in seconds).
  */
 #define EXPIRED_IPV6_CLEANUP_TIME (60*60)
@@ -1388,7 +1389,8 @@ struct iaprefix {
        u_int8_t plen;                          /* prefix length */
        binding_state_t state;                  /* state */
        struct binding_scope *scope;            /* "set var = value;" */
-       time_t valid_lifetime_end_time;         /* time prefix expires */
+       time_t hard_lifetime_end_time;          /* time prefix expires */
+       time_t soft_lifetime_end_time;          /* time ephemeral expires */
        struct ia_pd *ia_pd;                    /* IA for this prefix */
        struct ipv6_ppool *ipv6_ppool;          /* pool for this prefix */
        int heap_index;                         /* index into heap, or -1 
@@ -3315,11 +3317,11 @@ isc_result_t ipv6_ppool_reference(struct ipv6_ppool **ppool,
                                  const char *file, int line);
 isc_result_t ipv6_ppool_dereference(struct ipv6_ppool **ppool,
                                    const char *file, int line);
-isc_result_t activate_lease6(struct ipv6_pool *pool,
-                            struct iaaddr **addr,
-                            unsigned int *attempts,
-                            const struct data_string *uid,
-                            time_t valid_lifetime_end_time);
+isc_result_t create_lease6(struct ipv6_pool *pool,
+                          struct iaaddr **addr,
+                          unsigned int *attempts,
+                          const struct data_string *uid,
+                          time_t soft_lifetime_end_time);
 isc_result_t add_lease6(struct ipv6_pool *pool,
                        struct iaaddr *addr,
                        time_t valid_lifetime_end_time);
@@ -3333,11 +3335,11 @@ isc_boolean_t lease6_exists(const struct ipv6_pool *pool,
 isc_result_t mark_address_unavailble(struct ipv6_pool *pool,
                                     const struct in6_addr *addr);
 
-isc_result_t activate_prefix6(struct ipv6_ppool *ppool,
-                             struct iaprefix **pref,
-                             unsigned int *attempts,
-                             const struct data_string *uid,
-                             time_t valid_lifetime_end_time);
+isc_result_t create_prefix6(struct ipv6_ppool *ppool,
+                           struct iaprefix **pref,
+                           unsigned int *attempts,
+                           const struct data_string *uid,
+                           time_t soft_lifetime_end_time);
 isc_result_t add_prefix6(struct ipv6_ppool *ppool,
                         struct iaprefix *pref,
                         time_t valid_lifetime_end_time);
index a367d61c6605ec6bf97baf856ad71488cd755f1c..2121225fe2a89e46151216dbe44dde50fe788cec 100644 (file)
@@ -4317,7 +4317,8 @@ parse_ia_na_declaration(struct parse *cfile) {
                }
                memcpy(&iaaddr->addr, iaddr.iabuf, sizeof(iaaddr->addr));
                iaaddr->state = state;
-               iaaddr->valid_lifetime_end_time = end_time;
+               if (iaaddr->state == FTS_RELEASED)
+                       iaaddr->hard_lifetime_end_time = end_time;
 
                if (scope != NULL) {
                        binding_scope_reference(&iaaddr->scope, scope, MDL);
@@ -4611,7 +4612,8 @@ parse_ia_ta_declaration(struct parse *cfile) {
                }
                memcpy(&iaaddr->addr, iaddr.iabuf, sizeof(iaaddr->addr));
                iaaddr->state = state;
-               iaaddr->valid_lifetime_end_time = end_time;
+               if (iaaddr->state == FTS_RELEASED)
+                       iaaddr->hard_lifetime_end_time = end_time;
 
                if (scope != NULL) {
                        binding_scope_reference(&iaaddr->scope, scope, MDL);
@@ -4906,7 +4908,8 @@ parse_ia_pd_declaration(struct parse *cfile) {
                memcpy(&iapref->pref, iaddr.iabuf, sizeof(iapref->pref));
                iapref->plen = plen;
                iapref->state = state;
-               iapref->valid_lifetime_end_time = end_time;
+               if (iapref->state == FTS_RELEASED)
+                       iapref->hard_lifetime_end_time = end_time;
 
                if (scope != NULL) {
                        binding_scope_reference(&iapref->scope, scope, MDL);
index 0c5364652eb5cca001677ba19e1323d0e4aa6965..e2af0a28255889fc1f310ec0d6308027d275ffc2 100644 (file)
@@ -562,7 +562,13 @@ write_ia(const struct ia_na *ia) {
                /* Note that from here on out, the \n is prepended to the
                 * next write, rather than appended to the current write.
                 */
-               tval = print_time(iaaddr->valid_lifetime_end_time);
+               if ((iaaddr->state == FTS_ACTIVE) ||
+                   (iaaddr->state == FTS_ABANDONED) ||
+                   (iaaddr->hard_lifetime_end_time != 0)) {
+                       tval = print_time(iaaddr->hard_lifetime_end_time);
+               } else {
+                       tval = print_time(iaaddr->soft_lifetime_end_time);
+               }
                if (tval == NULL) {
                        goto error_exit;
                }
@@ -665,7 +671,12 @@ write_ia_pd(const struct ia_pd *ia_pd) {
                /* Note that from here on out, the \n is prepended to the
                 * next write, rather than appended to the current write.
                 */
-               tval = print_time(iapref->valid_lifetime_end_time);
+               if ((iapref->state == FTS_ACTIVE) ||
+                   (iapref->state == FTS_ABANDONED)) {
+                       tval = print_time(iapref->hard_lifetime_end_time);
+               } else {
+                       tval = print_time(iapref->soft_lifetime_end_time);
+               }
                if (tval == NULL) {
                        goto error_exit;
                }
index 52a1b97dfdf5c58d565d87aa71376d1194e503ea..b4f1adbedd0aba04a80bf1fee301dd343e259148 100644 (file)
@@ -979,7 +979,8 @@ try_client_v6_address(struct iaaddr **addr,
        }
        (*addr)->addr = tmp_addr;
 
-       result = add_lease6(pool, *addr, 0);
+       /* Default is soft binding for 2 minutes. */
+       result = add_lease6(pool, *addr, cur_time + 120);
        if (result != ISC_R_SUCCESS) {
                iaaddr_dereference(addr, MDL);
        }
@@ -1038,8 +1039,8 @@ pick_v6_address(struct iaaddr **addr, struct shared_network *shared_network,
 
                p = shared_network->ipv6_pools[i];
                if (((p->bits & POOL_IS_FOR_TEMP) == 0) &&
-                   (activate_lease6(p, addr, &attempts, 
-                                    client_id, 0) == ISC_R_SUCCESS)) {
+                   (create_lease6(p, addr, &attempts, client_id,
+                                  cur_time + 120) == ISC_R_SUCCESS)) {
                        /*
                         * Record the pool used (or next one if there 
                         * was a collision).
@@ -1123,7 +1124,8 @@ try_client_v6_prefix(struct iaprefix **pref,
        (*pref)->pref = tmp_pref;
        (*pref)->plen = tmp_plen;
 
-       result = add_prefix6(ppool, *pref, 0);
+       /* Default is soft binding for 2 minutes. */
+       result = add_prefix6(ppool, *pref, cur_time + 120);
        if (result != ISC_R_SUCCESS) {
                iaprefix_dereference(pref, MDL);
        }
@@ -1173,8 +1175,8 @@ pick_v6_prefix(struct iaprefix **pref, int plen,
                        continue;
                }
 
-               if (activate_prefix6(p, pref, &attempts, 
-                                    client_id, 0) == ISC_R_SUCCESS) {
+               if (create_prefix6(p, pref, &attempts, client_id,
+                                  cur_time + 120) == ISC_R_SUCCESS) {
                        log_debug("Picking pool prefix %s/%u",
                                  inet_ntop(AF_INET6, &((*pref)->pref),
                                            tmp_buf, sizeof(tmp_buf)),
@@ -1507,13 +1509,11 @@ reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
        struct option_state *packet_ia;
        struct option_cache *oc;
        struct data_string ia_data, data;
-       isc_boolean_t lease_in_database;
 
        /* Initialize values that will get cleaned up on return. */
        packet_ia = NULL;
        memset(&ia_data, 0, sizeof(ia_data));
        memset(&data, 0, sizeof(data));
-       lease_in_database = ISC_FALSE;
        /* 
         * Note that find_client_address() may set reply->lease. 
         */
@@ -1541,7 +1541,7 @@ reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
        /* Create an IA_NA structure. */
        if (ia_na_allocate(&reply->ia_na, iaid, (char *)reply->client_id.data, 
                           reply->client_id.len, MDL) != ISC_R_SUCCESS) {
-               log_error("lease_to_client: no memory for ia_na.");
+               log_error("reply_process_ia_na: no memory for ia_na.");
                status = ISC_R_NOMEMORY;
                goto cleanup;
        }
@@ -1796,11 +1796,15 @@ reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
                                ia_na_dereference(&tmp->ia_na, MDL);
                        ia_na_reference(&tmp->ia_na, reply->ia_na, 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);
 
                        /*
-                        * If this constitutes a 'hard' binding, perform ddns
-                        * updates.
+                        * Perform ddns updates.
                         */
                        oc = lookup_option(&server_universe, reply->opt_state,
                                           SV_DDNS_UPDATES);
@@ -1831,23 +1835,6 @@ reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
                               ia_id->len, reply->ia_na, MDL);
 
                write_ia(reply->ia_na);
-
-               /* 
-                * Note that we wrote the lease into the database,
-                * so that we know not to release it when we're done
-                * with this function.
-                */
-               lease_in_database = ISC_TRUE;
-
-       /*
-        * If this is a soft binding, we will check to see if we are 
-        * suggesting the existing database entry to the client.
-        */
-       } else if ((status != ISC_R_CANCELED) && !reply->static_lease &&
-           (reply->old_ia != NULL)) {
-               if (ia_na_equal(reply->old_ia, reply->ia_na)) {
-                       lease_in_database = ISC_TRUE;
-               }
        }
 
       cleanup:
@@ -1863,12 +1850,8 @@ reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
                ia_na_dereference(&reply->ia_na, MDL);
        if (reply->old_ia != NULL)
                ia_na_dereference(&reply->old_ia, MDL);
-       if (reply->lease != NULL) {
-               if (!lease_in_database) {
-                       release_lease6(reply->lease->ipv6_pool, reply->lease);
-               }
+       if (reply->lease != NULL)
                iaaddr_dereference(&reply->lease, MDL);
-       }
        if (reply->fixed.data != NULL)
                data_string_forget(&reply->fixed, MDL);
 
@@ -2245,7 +2228,7 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
        /* Create an IA_TA structure. */
        if (ia_na_allocate(&reply->ia_ta, iaid, (char *)reply->client_id.data, 
                           reply->client_id.len, MDL) != ISC_R_SUCCESS) {
-               log_error("lease_to_client: no memory for ia_ta.");
+               log_error("reply_process_ia_ta: no memory for ia_ta.");
                status = ISC_R_NOMEMORY;
                goto cleanup;
        }
@@ -2421,6 +2404,11 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
                                ia_na_dereference(&tmp->ia_na, MDL);
                        ia_na_reference(&tmp->ia_na, reply->ia_ta, 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);
 
                        /*
@@ -2472,6 +2460,8 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
                ia_na_dereference(&reply->ia_ta, MDL);
        if (reply->old_ia != NULL)
                ia_na_dereference(&reply->old_ia, MDL);
+       if (reply->lease != NULL)
+               iaaddr_dereference(&reply->lease, MDL);
 
        /*
         * ISC_R_CANCELED is a status code used by the addr processing to
@@ -2516,8 +2506,8 @@ find_client_temporaries(struct reply_state *reply) {
                /*
                 * Get an address in this temporary pool.
                 */
-               status = activate_lease6(p, &reply->lease, &attempts,
-                                        &reply->client_id, 0);
+               status = create_lease6(p, &reply->lease, &attempts,
+                                      &reply->client_id, cur_time + 120);
                if (status != ISC_R_SUCCESS) {
                        log_debug("Unable to get a temporary address.");
                        goto cleanup;
@@ -2757,9 +2747,11 @@ reply_process_is_addressed(struct reply_state *reply, struct ia_na *ia,
        /* Perform dynamic lease related update work. */
        if (reply->lease != NULL) {
                /* Advance (or rewind) the valid lifetime. */
-               reply->lease->valid_lifetime_end_time = cur_time +
-                                                       reply->send_valid;
-               renew_lease6(reply->lease->ipv6_pool, reply->lease);
+               if (reply->buf.reply.msg_type == DHCPV6_REPLY) {
+                       reply->lease->soft_lifetime_end_time =
+                               cur_time + reply->send_valid;
+                       /* Wait before renew! */
+               }
 
                status = ia_na_add_iaaddr(ia, reply->lease, MDL);
                if (status != ISC_R_SUCCESS) {
@@ -2846,8 +2838,8 @@ lease_compare(struct iaaddr *alpha, struct iaaddr *beta) {
                        /* Choose the lease with the longest lifetime (most
                         * likely the most recently allocated).
                         */
-                       if (alpha->valid_lifetime_end_time < 
-                           beta->valid_lifetime_end_time)
+                       if (alpha->hard_lifetime_end_time < 
+                           beta->hard_lifetime_end_time)
                                return beta;
                        else
                                return alpha;
@@ -2868,8 +2860,13 @@ lease_compare(struct iaaddr *alpha, struct iaaddr *beta) {
 
                      case FTS_EXPIRED:
                        /* Choose the most recently expired lease. */
-                       if (alpha->valid_lifetime_end_time <
-                           beta->valid_lifetime_end_time)
+                       if (alpha->hard_lifetime_end_time <
+                           beta->hard_lifetime_end_time)
+                               return beta;
+                       else if ((alpha->hard_lifetime_end_time ==
+                                 beta->hard_lifetime_end_time) &&
+                                (alpha->soft_lifetime_end_time <
+                                 beta->soft_lifetime_end_time))
                                return beta;
                        else
                                return alpha;
@@ -2890,8 +2887,8 @@ lease_compare(struct iaaddr *alpha, struct iaaddr *beta) {
 
                      case FTS_ABANDONED:
                        /* Choose the lease that was abandoned longest ago. */
-                       if (alpha->valid_lifetime_end_time <
-                           beta->valid_lifetime_end_time)
+                       if (alpha->hard_lifetime_end_time <
+                           beta->hard_lifetime_end_time)
                                return alpha;
 
                      default:
@@ -2918,13 +2915,11 @@ reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia_pd) {
        struct option_state *packet_ia;
        struct option_cache *oc;
        struct data_string ia_pd_data, data;
-       isc_boolean_t prefix_in_database;
 
        /* Initialize values that will get cleaned up on return. */
        packet_ia = NULL;
        memset(&ia_pd_data, 0, sizeof(ia_pd_data));
        memset(&data, 0, sizeof(data));
-       prefix_in_database = ISC_FALSE;
        /* 
         * Note that find_client_prefix() may set reply->prefix. 
         */
@@ -3168,6 +3163,11 @@ reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia_pd) {
                                ia_pd_dereference(&tmp->ia_pd, MDL);
                        ia_pd_reference(&tmp->ia_pd, reply->ia_pd, MDL);
 
+                       /* Commit 'hard' bindings. */
+                       tmp->hard_lifetime_end_time =
+                               tmp->soft_lifetime_end_time;
+                       tmp->soft_lifetime_end_time = 0;
+                       renew_prefix6(tmp->ipv6_ppool, tmp);
                        schedule_prefix_timeout(tmp->ipv6_ppool);
                }
 
@@ -3186,24 +3186,6 @@ reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia_pd) {
                               ia_id->len, reply->ia_pd, MDL);
 
                write_ia_pd(reply->ia_pd);
-
-               /* 
-                * Note that we wrote the prefix into the database,
-                * so that we know not to release it when we're done
-                * with this function.
-                */
-               prefix_in_database = ISC_TRUE;
-
-       /*
-        * If this is a soft binding, we will check to see if we are 
-        * suggesting the existing database entry to the client.
-        */
-       } else if ((status != ISC_R_CANCELED) &&
-                  (reply->static_prefixes == 0) &&
-                  (reply->old_ia_pd != NULL)) {
-               if (ia_pd_equal(reply->old_ia_pd, reply->ia_pd)) {
-                       prefix_in_database = ISC_TRUE;
-               }
        }
 
       cleanup:
@@ -3215,27 +3197,12 @@ reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia_pd) {
                data_string_forget(&ia_pd_data, MDL);
        if (data.data != NULL)
                data_string_forget(&data, MDL);
+       if (reply->ia_pd != NULL)
+               ia_pd_dereference(&reply->ia_pd, MDL);
        if (reply->old_ia_pd != NULL)
                ia_pd_dereference(&reply->old_ia_pd, MDL);
        if (reply->prefix != NULL)
                iaprefix_dereference(&reply->prefix, MDL);
-       if (!prefix_in_database) {
-               /*
-                * Cleanup soft bindings, assume:
-                *  reply->static_prefixes == 0
-                *  reply->ia_pd != NULL
-                *  reply->ia_pd->num_iaprefix != 0
-                */
-               struct iaprefix *tmp;
-               int i;
-
-               for (i = 0 ; i < reply->ia_pd->num_iaprefix ; i++) {
-                       tmp = reply->ia_pd->iaprefix[i];
-                       release_prefix6(tmp->ipv6_ppool, tmp);
-               }
-       }
-       if (reply->ia_pd != NULL)
-               ia_pd_dereference(&reply->ia_pd, MDL);
 
        /*
         * ISC_R_CANCELED is a status code used by the prefix processing to
@@ -3259,11 +3226,9 @@ reply_process_prefix(struct reply_state *reply, struct option_cache *pref) {
        struct iaddrcidrnet tmp_pref;
        struct option_cache *oc;
        struct data_string iapref, data;
-       isc_boolean_t held_prefix;
        isc_result_t status = ISC_R_SUCCESS;
 
        /* Initializes values that will be cleaned up. */
-       held_prefix = ISC_FALSE;
        memset(&iapref, 0, sizeof(iapref));
        memset(&data, 0, sizeof(data));
        /* Note that reply->prefix may be set by prefix_is_owned() */
@@ -3353,9 +3318,6 @@ reply_process_prefix(struct reply_state *reply, struct option_cache *pref) {
                                /* status remains success - ignore */
                                goto cleanup;
                        }
-
-                       held_prefix = ISC_TRUE;
-
                /*
                 * RFC3633 section 18.2.3:
                 *
@@ -3467,8 +3429,6 @@ reply_process_prefix(struct reply_state *reply, struct option_cache *pref) {
                data_string_forget(&iapref, MDL);
        if (data.data != NULL)
                data_string_forget(&data, MDL);
-       if (held_prefix && (status != ISC_R_SUCCESS))
-               release_prefix6(reply->prefix->ipv6_ppool, reply->prefix);
        if (reply->prefix != NULL)
                iaprefix_dereference(&reply->prefix, MDL);
 
@@ -3739,9 +3699,11 @@ reply_process_is_prefixed(struct reply_state *reply, struct ia_pd *ia_pd,
        /* Perform dynamic prefix related update work. */
        if (reply->prefix != NULL) {
                /* Advance (or rewind) the valid lifetime. */
-               reply->prefix->valid_lifetime_end_time = cur_time +
-                                                       reply->send_valid;
-               renew_prefix6(reply->prefix->ipv6_ppool, reply->prefix);
+               if (reply->buf.reply.msg_type == DHCPV6_REPLY) {
+                       reply->prefix->soft_lifetime_end_time =
+                               cur_time + reply->send_valid;
+                       /* Wait before renew! */
+               }
 
                status = ia_pd_add_iaprefix(ia_pd, reply->prefix, MDL);
                if (status != ISC_R_SUCCESS) {
@@ -3840,8 +3802,8 @@ prefix_compare(struct reply_state *reply,
                        /* Choose the prefix with the longest lifetime (most
                         * likely the most recently allocated).
                         */
-                       if (alpha->valid_lifetime_end_time < 
-                           beta->valid_lifetime_end_time)
+                       if (alpha->hard_lifetime_end_time < 
+                           beta->hard_lifetime_end_time)
                                return beta;
                        else
                                return alpha;
@@ -3862,8 +3824,13 @@ prefix_compare(struct reply_state *reply,
 
                      case FTS_EXPIRED:
                        /* Choose the most recently expired prefix. */
-                       if (alpha->valid_lifetime_end_time <
-                           beta->valid_lifetime_end_time)
+                       if (alpha->hard_lifetime_end_time <
+                           beta->hard_lifetime_end_time)
+                               return beta;
+                       else if ((alpha->hard_lifetime_end_time ==
+                                 beta->hard_lifetime_end_time) &&
+                                (alpha->soft_lifetime_end_time <
+                                 beta->soft_lifetime_end_time))
                                return beta;
                        else
                                return alpha;
@@ -3884,8 +3851,8 @@ prefix_compare(struct reply_state *reply,
 
                      case FTS_ABANDONED:
                        /* Choose the prefix that was abandoned longest ago. */
-                       if (alpha->valid_lifetime_end_time <
-                           beta->valid_lifetime_end_time)
+                       if (alpha->hard_lifetime_end_time <
+                           beta->hard_lifetime_end_time)
                                return alpha;
 
                      default:
index 3cd3b2b7cc3229dcc50ee94225613490566ace62..0c0a18990ec50c5b12d4c99c9abf77b769f27e44 100644 (file)
@@ -782,8 +782,13 @@ lease_older(void *a, void *b) {
        struct iaaddr *ia = (struct iaaddr *)a;
        struct iaaddr *ib = (struct iaaddr *)b;
 
-       return difftime(ia->valid_lifetime_end_time, 
-                       ib->valid_lifetime_end_time) < 0;
+       if (ia->hard_lifetime_end_time == ib->hard_lifetime_end_time) {
+               return difftime(ia->soft_lifetime_end_time,
+                               ib->soft_lifetime_end_time) < 0;
+       } else {
+               return difftime(ia->hard_lifetime_end_time, 
+                               ib->hard_lifetime_end_time) < 0;
+       }
 }
 
 /*
@@ -1108,7 +1113,7 @@ ipv6_ppool_dereference(struct ipv6_ppool **ppool, const char *file, int line) {
  * the non-network part.
  */
 static void
-create_address(struct in6_addr *addr, 
+build_address6(struct in6_addr *addr, 
               const struct in6_addr *net_start_addr, int net_bits, 
               const struct data_string *input) {
        MD5_CTX ctx;
@@ -1153,7 +1158,7 @@ create_address(struct in6_addr *addr,
  * Create a temporary address by a variant of RFC 4941 algo.
  */
 static void
-create_temporary(struct in6_addr *addr, 
+build_temporary6(struct in6_addr *addr, 
                 const struct in6_addr *net_start_addr, 
                 const struct data_string *input) {
        static u_int8_t history[8];
@@ -1220,9 +1225,9 @@ static struct in6_addr resany;
  * the long term.
  */
 isc_result_t
-activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr, 
-               unsigned int *attempts,
-               const struct data_string *uid, time_t valid_lifetime_end_time) {
+create_lease6(struct ipv6_pool *pool, struct iaaddr **addr, 
+             unsigned int *attempts,
+             const struct data_string *uid, time_t soft_lifetime_end_time) {
        struct data_string ds;
        struct in6_addr tmp;
        struct iaaddr *test_iaaddr;
@@ -1260,13 +1265,13 @@ activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
                }
 
                /* 
-                * Create an address or a temporary address.
+                * Build an address or a temporary address.
                 */
                if ((pool->bits & POOL_IS_FOR_TEMP) == 0) {
-                       create_address(&tmp, &pool->start_addr,
+                       build_address6(&tmp, &pool->start_addr,
                                       pool->bits, &ds);
                } else {
-                       create_temporary(&tmp, &pool->start_addr, &ds);
+                       build_temporary6(&tmp, &pool->start_addr, &ds);
                }
 
                /*
@@ -1328,7 +1333,7 @@ activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
        /*
         * Add the lease to the pool (note state is free, not active?!).
         */
-       result = add_lease6(pool, iaaddr, valid_lifetime_end_time);
+       result = add_lease6(pool, iaaddr, soft_lifetime_end_time);
        if (result == ISC_R_SUCCESS) {
                iaaddr_reference(addr, iaaddr, MDL);
        }
@@ -1351,7 +1356,6 @@ add_lease6(struct ipv6_pool *pool, struct iaaddr *iaaddr,
        if (iaaddr->state == 0)
                iaaddr->state = FTS_ACTIVE;
 
-       iaaddr->valid_lifetime_end_time = valid_lifetime_end_time;
        ipv6_pool_reference(&iaaddr->ipv6_pool, pool, MDL);
 
        /*
@@ -1401,6 +1405,7 @@ add_lease6(struct ipv6_pool *pool, struct iaaddr *iaaddr,
        iaaddr_reference(&tmp_iaaddr, iaaddr, MDL);
        if ((tmp_iaaddr->state == FTS_ACTIVE) ||
            (tmp_iaaddr->state == FTS_ABANDONED)) {
+               tmp_iaaddr->hard_lifetime_end_time = valid_lifetime_end_time;
                iaaddr_hash_add(pool->addrs, &tmp_iaaddr->addr, 
                                sizeof(tmp_iaaddr->addr), iaaddr, MDL);
                insert_result = isc_heap_insert(pool->active_timeouts,
@@ -1408,6 +1413,7 @@ add_lease6(struct ipv6_pool *pool, struct iaaddr *iaaddr,
                if (insert_result == ISC_R_SUCCESS)
                        pool->num_active++;
        } else {
+               tmp_iaaddr->soft_lifetime_end_time = valid_lifetime_end_time;
                insert_result = isc_heap_insert(pool->inactive_timeouts,
                                                tmp_iaaddr);
                if (insert_result == ISC_R_SUCCESS)
@@ -1469,7 +1475,7 @@ move_lease_to_active(struct ipv6_pool *pool, struct iaaddr *addr) {
 /*
  * Renew an lease in the pool.
  *
- * To do this, first set the new valid_lifetime_end_time for the address, 
+ * To do this, first set the new hard_lifetime_end_time for the address, 
  * and then invoke renew_lease() on the address.
  *
  * WARNING: lease times must only be extended, never reduced!!!
@@ -1550,7 +1556,7 @@ expire_lease6(struct iaaddr **addr, struct ipv6_pool *pool, time_t now) {
        if (pool->num_active > 0) {
                tmp = (struct iaaddr *)isc_heap_element(pool->active_timeouts, 
                                                        1);
-               if (now > tmp->valid_lifetime_end_time) {
+               if (now > tmp->hard_lifetime_end_time) {
                        result = move_lease_to_inactive(pool, tmp, FTS_EXPIRED);
                        if (result == ISC_R_SUCCESS) {
                                iaaddr_reference(addr, tmp, MDL);
@@ -1577,7 +1583,7 @@ decline_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
                }
        }
        addr->state = FTS_ABANDONED;
-       addr->valid_lifetime_end_time = MAX_TIME;
+       addr->hard_lifetime_end_time = MAX_TIME;
        isc_heap_decreased(pool->active_timeouts, addr->heap_index);
        return ISC_R_SUCCESS;
 }
@@ -1599,7 +1605,7 @@ release_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
  * the part subject to allocation.
  */
 static void
-create_prefix(struct in6_addr *pref, 
+build_prefix6(struct in6_addr *pref, 
              const struct in6_addr *net_start_pref,
              int pool_bits, int pref_bits,
              const struct data_string *input) {
@@ -1679,10 +1685,10 @@ create_prefix(struct in6_addr *pref,
  * the long term.
  */
 isc_result_t
-activate_prefix6(struct ipv6_ppool *ppool, struct iaprefix **pref, 
-                unsigned int *attempts,
-                const struct data_string *uid,
-                time_t valid_lifetime_end_time) {
+create_prefix6(struct ipv6_ppool *ppool, struct iaprefix **pref, 
+              unsigned int *attempts,
+              const struct data_string *uid,
+              time_t soft_lifetime_end_time) {
        struct data_string ds;
        struct in6_addr tmp;
        struct iaprefix *test_iapref;
@@ -1707,9 +1713,9 @@ activate_prefix6(struct ipv6_ppool *ppool, struct iaprefix **pref,
                }
 
                /* 
-                * Create a prefix
+                * Build a prefix
                 */
-               create_prefix(&tmp, &ppool->start_pref,
+               build_prefix6(&tmp, &ppool->start_pref,
                              (int)ppool->pool_plen, (int)ppool->alloc_plen,
                              &ds);
 
@@ -1757,7 +1763,7 @@ activate_prefix6(struct ipv6_ppool *ppool, struct iaprefix **pref,
        /*
         * Add the prefix to the pool (note state is free, not active?!).
         */
-       result = add_prefix6(ppool, iapref, valid_lifetime_end_time);
+       result = add_prefix6(ppool, iapref, soft_lifetime_end_time);
        if (result == ISC_R_SUCCESS) {
                iaprefix_reference(pref, iapref, MDL);
        }
@@ -1780,7 +1786,6 @@ add_prefix6(struct ipv6_ppool *ppool, struct iaprefix *iapref,
        if (iapref->state == 0)
                iapref->state = FTS_ACTIVE;
 
-       iapref->valid_lifetime_end_time = valid_lifetime_end_time;
        ipv6_ppool_reference(&iapref->ipv6_ppool, ppool, MDL);
 
        /*
@@ -1830,6 +1835,7 @@ add_prefix6(struct ipv6_ppool *ppool, struct iaprefix *iapref,
        iaprefix_reference(&tmp_iapref, iapref, MDL);
        if ((tmp_iapref->state == FTS_ACTIVE) ||
            (tmp_iapref->state == FTS_ABANDONED)) {
+               tmp_iapref->hard_lifetime_end_time = valid_lifetime_end_time;
                iaprefix_hash_add(ppool->prefs, &tmp_iapref->pref, 
                                  sizeof(tmp_iapref->pref), iapref, MDL);
                insert_result = isc_heap_insert(ppool->active_timeouts,
@@ -1837,6 +1843,7 @@ add_prefix6(struct ipv6_ppool *ppool, struct iaprefix *iapref,
                if (insert_result == ISC_R_SUCCESS)
                        ppool->num_active++;
        } else {
+               tmp_iapref->soft_lifetime_end_time = valid_lifetime_end_time;
                insert_result = isc_heap_insert(ppool->inactive_timeouts,
                                                tmp_iapref);
                if (insert_result == ISC_R_SUCCESS)
@@ -1902,7 +1909,7 @@ move_prefix_to_active(struct ipv6_ppool *ppool, struct iaprefix *pref) {
 /*
  * Renew a prefix in the pool.
  *
- * To do this, first set the new valid_lifetime_end_time for the prefix, 
+ * To do this, first set the new hard_lifetime_end_time for the prefix, 
  * and then invoke renew_prefix() on the prefix.
  *
  * WARNING: lease times must only be extended, never reduced!!!
@@ -1983,7 +1990,7 @@ expire_prefix6(struct iaprefix **pref, struct ipv6_ppool *ppool, time_t now) {
        if (ppool->num_active > 0) {
                tmp = (struct iaprefix *)
                        isc_heap_element(ppool->active_timeouts, 1);
-               if (now > tmp->valid_lifetime_end_time) {
+               if (now > tmp->hard_lifetime_end_time) {
                        result = move_prefix_to_inactive(ppool, tmp,
                                                         FTS_EXPIRED);
                        if (result == ISC_R_SUCCESS) {
@@ -2107,12 +2114,18 @@ cleanup_old_expired(struct ipv6_pool *pool) {
        struct ia_na *ia;
        struct ia_na *ia_active;
        unsigned char *tmpd;
+       time_t timeout;
        
        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) {
+               if (tmp->hard_lifetime_end_time != 0) {
+                       timeout = tmp->hard_lifetime_end_time;
+                       timeout += EXPIRED_IPV6_CLEANUP_TIME;
+               } else {
+                       timeout = tmp->soft_lifetime_end_time;
+               }
+               if (cur_time < timeout) {
                        break;
                }
 
@@ -2216,16 +2229,20 @@ schedule_lease_timeout(struct ipv6_pool *pool) {
        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 + 1;
+               if (tmp->hard_lifetime_end_time < next_timeout) {
+                       next_timeout = tmp->hard_lifetime_end_time + 1;
                }
        }
 
        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 (tmp->hard_lifetime_end_time != 0) {
+                       timeout = tmp->hard_lifetime_end_time;
+                       timeout += EXPIRED_IPV6_CLEANUP_TIME;
+               } else {
+                       timeout = tmp->soft_lifetime_end_time + 1;
+               }
                if (timeout < next_timeout) {
                        next_timeout = timeout;
                }
@@ -2258,12 +2275,18 @@ cleanup_old_pexpired(struct ipv6_ppool *ppool) {
        struct ia_pd *ia_pd;
        struct ia_pd *ia_active;
        unsigned char *tmpd;
+       time_t timeout;
        
        while (ppool->num_inactive > 0) {
                tmp = (struct iaprefix *)
                        isc_heap_element(ppool->inactive_timeouts, 1);
-               if (cur_time < 
-                   tmp->valid_lifetime_end_time + EXPIRED_IPV6_CLEANUP_TIME) {
+               if (tmp->hard_lifetime_end_time != 0) {
+                       timeout = tmp->hard_lifetime_end_time;
+                       timeout += EXPIRED_IPV6_CLEANUP_TIME;
+               } else {
+                       timeout = tmp->soft_lifetime_end_time;
+               }
+               if (cur_time < timeout) {
                        break;
                }
 
@@ -2351,16 +2374,20 @@ schedule_prefix_timeout(struct ipv6_ppool *ppool) {
        if (ppool->num_active > 0) {
                tmp = (struct iaprefix *)
                        isc_heap_element(ppool->active_timeouts, 1);
-               if (tmp->valid_lifetime_end_time < next_timeout) {
-                       next_timeout = tmp->valid_lifetime_end_time + 1;
+               if (tmp->hard_lifetime_end_time < next_timeout) {
+                       next_timeout = tmp->hard_lifetime_end_time + 1;
                }
        }
 
        if (ppool->num_inactive > 0) {
                tmp = (struct iaprefix *)
                        isc_heap_element(ppool->inactive_timeouts, 1);
-               timeout = tmp->valid_lifetime_end_time + 
-                         EXPIRED_IPV6_CLEANUP_TIME;
+               if (tmp->hard_lifetime_end_time != 0) {
+                       timeout = tmp->hard_lifetime_end_time;
+                       timeout += EXPIRED_IPV6_CLEANUP_TIME;
+               } else {
+                       timeout = tmp->soft_lifetime_end_time + 1;
+               }
                if (timeout < next_timeout) {
                        next_timeout = timeout;
                }
@@ -2529,7 +2556,7 @@ change_leases(struct ia_na *ia,
 /*
  * Renew all leases in an IA from all pools.
  *
- * The new valid_lifetime_end_time should be updated for the addresses.
+ * The new hard_lifetime_end_time should be updated for the addresses.
  *
  * WARNING: lease times must only be extended, never reduced!!!
  */
@@ -2630,7 +2657,7 @@ change_prefixes(struct ia_pd *ia_pd,
 /*
  * Renew all prefixes in an IA_PD from all pools.
  *
- * The new valid_lifetime_end_time should be updated for the addresses.
+ * The new hard_lifetime_end_time should be updated for the addresses.
  *
  * WARNING: lease times must only be extended, never reduced!!!
  */
@@ -3139,7 +3166,7 @@ main(int argc, char *argv[]) {
                return 1;
        }
 
-       /* activate_lease6, renew_lease6, expire_lease6 */
+       /* create_lease6, renew_lease6, expire_lease6 */
        uid = "client0";
        memset(&ds, 0, sizeof(ds));
        ds.len = strlen(uid);
@@ -3149,9 +3176,9 @@ main(int argc, char *argv[]) {
        }
        ds.data = ds.buffer->data;
        memcpy((char *)ds.data, uid, ds.len);
-       if (activate_lease6(pool, &iaaddr, 
-                           &attempts, &ds, 1) != ISC_R_SUCCESS) {
-               printf("ERROR: activate_lease6() %s:%d\n", MDL);
+       if (create_lease6(pool, &iaaddr, 
+                         &attempts, &ds, 1) != ISC_R_SUCCESS) {
+               printf("ERROR: create_lease6() %s:%d\n", MDL);
                return 1;
        }
        if (pool->num_inactive != 1) {
@@ -3201,9 +3228,9 @@ main(int argc, char *argv[]) {
        }
 
        /* release_lease6, decline_lease6 */
-       if (activate_lease6(pool, &iaaddr, &attempts, 
-                           &ds, 1) != ISC_R_SUCCESS) {
-               printf("ERROR: activate_lease6() %s:%d\n", MDL);
+       if (create_lease6(pool, &iaaddr, &attempts, 
+                         &ds, 1) != ISC_R_SUCCESS) {
+               printf("ERROR: create_lease6() %s:%d\n", MDL);
                return 1;
        }
        if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
@@ -3226,9 +3253,9 @@ main(int argc, char *argv[]) {
                printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
                return 1;
        }
-       if (activate_lease6(pool, &iaaddr, &attempts, 
-                           &ds, 1) != ISC_R_SUCCESS) {
-               printf("ERROR: activate_lease6() %s:%d\n", MDL);
+       if (create_lease6(pool, &iaaddr, &attempts, 
+                         &ds, 1) != ISC_R_SUCCESS) {
+               printf("ERROR: create_lease6() %s:%d\n", MDL);
                return 1;
        }
        if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
@@ -3306,9 +3333,9 @@ main(int argc, char *argv[]) {
                return 1;
        }
        for (i=10; i<100; i+=10) {
-               if (activate_lease6(pool, &iaaddr, &attempts,
-                                   &ds, i) != ISC_R_SUCCESS) {
-                       printf("ERROR: activate_lease6() %s:%d\n", MDL);
+               if (create_lease6(pool, &iaaddr, &attempts,
+                                 &ds, i) != ISC_R_SUCCESS) {
+                       printf("ERROR: create_lease6() %s:%d\n", MDL);
                        return 1;
                }
                if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
@@ -3343,8 +3370,8 @@ main(int argc, char *argv[]) {
                        printf("ERROR: bad num_active %s:%d\n", MDL);
                        return 1;
                }
-               if (expired_iaaddr->valid_lifetime_end_time != i) {
-                       printf("ERROR: bad valid_lifetime_end_time %s:%d\n", 
+               if (expired_iaaddr->hard_lifetime_end_time != i) {
+                       printf("ERROR: bad hard_lifetime_end_time %s:%d\n", 
                               MDL);
                        return 1;
                }
@@ -3376,9 +3403,9 @@ main(int argc, char *argv[]) {
                printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
                return 1;
        }
-       if (activate_lease6(pool, &iaaddr, &attempts, 
-                           &ds, 42) != ISC_R_SUCCESS) {
-               printf("ERROR: activate_lease6() %s:%d\n", MDL);
+       if (create_lease6(pool, &iaaddr, &attempts, 
+                         &ds, 42) != ISC_R_SUCCESS) {
+               printf("ERROR: create_lease6() %s:%d\n", MDL);
                return 1;
        }
        if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
@@ -3389,9 +3416,9 @@ main(int argc, char *argv[]) {
                printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
                return 1;
        }
-       if (activate_lease6(pool, &iaaddr, &attempts, 
-                           &ds, 11) != ISC_R_SUCCESS) {
-               printf("ERROR: activate_lease6() %s:%d\n", MDL);
+       if (create_lease6(pool, &iaaddr, &attempts, 
+                         &ds, 11) != ISC_R_SUCCESS) {
+               printf("ERROR: create_lease6() %s:%d\n", MDL);
                return 1;
        }
        if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
@@ -3402,9 +3429,9 @@ main(int argc, char *argv[]) {
                printf("ERROR: iaaddr_dereference() %s:%d\n", MDL);
                return 1;
        }
-       if (activate_lease6(pool, &iaaddr, &attempts, 
-                           &ds, 11) != ISC_R_NORESOURCES) {
-               printf("ERROR: activate_lease6() %s:%d\n", MDL);
+       if (create_lease6(pool, &iaaddr, &attempts, 
+                         &ds, 11) != ISC_R_NORESOURCES) {
+               printf("ERROR: create_lease6() %s:%d\n", MDL);
                return 1;
        }
        if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {