]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Cleanup IA_NA dependencies
authorFrancis Dupont <fdupont@isc.org>
Wed, 2 Jan 2008 23:47:22 +0000 (23:47 +0000)
committerFrancis Dupont <fdupont@isc.org>
Wed, 2 Jan 2008 23:47:22 +0000 (23:47 +0000)
12 files changed:
client/clparse.c
client/dhc6.c
client/dhclient.c
common/conflex.c
common/parse.c
includes/dhcpd.h
includes/dhctoken.h
server/confpars.c
server/db.c
server/dhcpd.c
server/dhcpv6.c
server/mdb6.c

index a4921f5703772e89de62dac3daa22ba78b308500..6adad35f41da9f2f63fd44517edecabb0ceefcb6 100644 (file)
@@ -43,8 +43,11 @@ struct option *default_requested_options[NUM_DEFAULT_REQUESTED_OPTS + 1];
 static void parse_client_default_duid(struct parse *cfile);
 static void parse_client6_lease_statement(struct parse *cfile);
 #ifdef DHCPv6
-static struct dhc6_ia *parse_client6_ia_statement(struct parse *cfile);
+static struct dhc6_ia *parse_client6_ia_na_statement(struct parse *cfile);
+static struct dhc6_ia *parse_client6_ia_ta_statement(struct parse *cfile);
+static struct dhc6_ia *parse_client6_ia_pd_statement(struct parse *cfile);
 static struct dhc6_addr *parse_client6_iaaddr_statement(struct parse *cfile);
+static struct dhc6_addr *parse_client6_iaprefix_statement(struct parse *cfile);
 #endif /* DHCPv6 */
 
 /* client-conf-file :== client-declarations END_OF_FILE
@@ -1292,7 +1295,29 @@ parse_client6_lease_statement(struct parse *cfile)
 
                switch(token) {
                      case IA_NA:
-                       *ia = parse_client6_ia_statement(cfile);
+                       *ia = parse_client6_ia_na_statement(cfile);
+                       if (*ia != NULL) {
+                               ia = &(*ia)->next;
+                               has_ia = 1;
+                       }
+
+                       no_semi = 1;
+
+                       break;
+
+                     case IA_TA:
+                       *ia = parse_client6_ia_ta_statement(cfile);
+                       if (*ia != NULL) {
+                               ia = &(*ia)->next;
+                               has_ia = 1;
+                       }
+
+                       no_semi = 1;
+
+                       break;
+
+                     case IA_PD:
+                       *ia = parse_client6_ia_pd_statement(cfile);
                        if (*ia != NULL) {
                                ia = &(*ia)->next;
                                has_ia = 1;
@@ -1461,7 +1486,7 @@ parse_client6_lease_statement(struct parse *cfile)
  */
 #ifdef DHCPv6
 static struct dhc6_ia *
-parse_client6_ia_statement(struct parse *cfile)
+parse_client6_ia_na_statement(struct parse *cfile)
 {
        struct data_string id;
        struct option_cache *oc = NULL;
@@ -1476,6 +1501,7 @@ parse_client6_ia_statement(struct parse *cfile)
                skip_to_semi(cfile);
                return NULL;
        }
+       ia->ia_type = D6O_IA_NA;
 
        /* Get IAID. */
        memset(&id, 0, sizeof(id));
@@ -1593,6 +1619,260 @@ parse_client6_ia_statement(struct parse *cfile)
 }
 #endif /* DHCPv6 */
 
+/* Parse an ia_ta object from the client lease.
+ */
+#ifdef DHCPv6
+static struct dhc6_ia *
+parse_client6_ia_ta_statement(struct parse *cfile)
+{
+       struct data_string id;
+       struct option_cache *oc = NULL;
+       struct dhc6_ia *ia;
+       struct dhc6_addr **addr;
+       const char *val;
+       int token, no_semi;
+
+       ia = dmalloc(sizeof(*ia), MDL);
+       if (ia == NULL) {
+               parse_warn(cfile, "Out of memory allocating IA_TA state.");
+               skip_to_semi(cfile);
+               return NULL;
+       }
+       ia->ia_type = D6O_IA_TA;
+
+       /* Get IAID. */
+       memset(&id, 0, sizeof(id));
+       if (parse_cshl(&id, cfile)) {
+               if (id.len == 4)
+                       memcpy(ia->iaid, id.data, 4);
+               else {
+                       parse_warn(cfile, "Expecting IAID of length 4, got %d.",
+                                  id.len);
+                       skip_to_semi(cfile);
+                       dfree(ia, MDL);
+                       return NULL;
+               }
+               data_string_forget(&id, MDL);
+       } else {
+               parse_warn(cfile, "Expecting IAID.");
+               skip_to_semi(cfile);
+               dfree(ia, MDL);
+               return NULL;
+       }
+
+       token = next_token(NULL, NULL, cfile);
+       if (token != LBRACE) {
+               parse_warn(cfile, "Expecting open curly brace.");
+               skip_to_semi(cfile);
+               dfree(ia, MDL);
+               return NULL;
+       }
+
+       option_state_allocate(&ia->options, MDL);
+       if (ia->options == NULL) {
+               parse_warn(cfile, "Unable to allocate option state.");
+               skip_to_rbrace(cfile, 1);
+               dfree(ia, MDL);
+               return NULL;
+       }
+
+       addr = &ia->addrs;
+       token = next_token(&val, NULL, cfile);
+       while (token != RBRACE) {
+               no_semi = 0;
+
+               switch (token) {
+                     case STARTS:
+                       token = next_token(&val, NULL, cfile);
+                       if (token == NUMBER) {
+                               ia->starts = atoi(val);
+                       } else {
+                               parse_warn(cfile, "Expecting a number.");
+                               skip_to_semi(cfile);
+                               no_semi = 1;
+                       }
+                       break;
+
+                       /* No RENEW or REBIND */
+
+                     case IAADDR:
+                       *addr = parse_client6_iaaddr_statement(cfile);
+
+                       if (*addr != NULL)
+                               addr = &(*addr)->next;
+
+                       no_semi = 1;
+
+                       break;
+
+                     case OPTION:
+                       if (parse_option_decl(&oc, cfile)) {
+                               save_option(oc->option->universe,
+                                           ia->options, oc);
+                               option_cache_dereference(&oc, MDL);
+                       }
+                       no_semi = 1;
+                       break;
+
+                     default:
+                       parse_warn(cfile, "Unexpected token.");
+                       no_semi = 1;
+                       skip_to_semi(cfile);
+                       break;
+               }
+
+               if (!no_semi)
+                       parse_semi(cfile);
+
+               token = next_token(&val, NULL, cfile);
+
+               if (token == END_OF_FILE) {
+                       parse_warn(cfile, "Unexpected end of file.");
+                       break;
+               }
+       }
+
+       return ia;
+}
+#endif /* DHCPv6 */
+
+/* Parse an ia_pd object from the client lease.
+ */
+#ifdef DHCPv6
+static struct dhc6_ia *
+parse_client6_ia_pd_statement(struct parse *cfile)
+{
+       struct data_string id;
+       struct option_cache *oc = NULL;
+       struct dhc6_ia *ia;
+       struct dhc6_addr **pref;
+       const char *val;
+       int token, no_semi;
+
+       ia = dmalloc(sizeof(*ia), MDL);
+       if (ia == NULL) {
+               parse_warn(cfile, "Out of memory allocating IA_PD state.");
+               skip_to_semi(cfile);
+               return NULL;
+       }
+       ia->ia_type = D6O_IA_PD;
+
+       /* Get IAID. */
+       memset(&id, 0, sizeof(id));
+       if (parse_cshl(&id, cfile)) {
+               if (id.len == 4)
+                       memcpy(ia->iaid, id.data, 4);
+               else {
+                       parse_warn(cfile, "Expecting IAID of length 4, got %d.",
+                                  id.len);
+                       skip_to_semi(cfile);
+                       dfree(ia, MDL);
+                       return NULL;
+               }
+               data_string_forget(&id, MDL);
+       } else {
+               parse_warn(cfile, "Expecting IAID.");
+               skip_to_semi(cfile);
+               dfree(ia, MDL);
+               return NULL;
+       }
+
+       token = next_token(NULL, NULL, cfile);
+       if (token != LBRACE) {
+               parse_warn(cfile, "Expecting open curly brace.");
+               skip_to_semi(cfile);
+               dfree(ia, MDL);
+               return NULL;
+       }
+
+       option_state_allocate(&ia->options, MDL);
+       if (ia->options == NULL) {
+               parse_warn(cfile, "Unable to allocate option state.");
+               skip_to_rbrace(cfile, 1);
+               dfree(ia, MDL);
+               return NULL;
+       }
+
+       pref = &ia->addrs;
+       token = next_token(&val, NULL, cfile);
+       while (token != RBRACE) {
+               no_semi = 0;
+
+               switch (token) {
+                     case STARTS:
+                       token = next_token(&val, NULL, cfile);
+                       if (token == NUMBER) {
+                               ia->starts = atoi(val);
+                       } else {
+                               parse_warn(cfile, "Expecting a number.");
+                               skip_to_semi(cfile);
+                               no_semi = 1;
+                       }
+                       break;
+
+                     case RENEW:
+                       token = next_token(&val, NULL, cfile);
+                       if (token == NUMBER) {
+                               ia->renew = atoi(val);
+                       } else {
+                               parse_warn(cfile, "Expecting a number.");
+                               skip_to_semi(cfile);
+                               no_semi = 1;
+                       }
+                       break;
+
+                     case REBIND:
+                       token = next_token(&val, NULL, cfile);
+                       if (token == NUMBER) {
+                               ia->rebind = atoi(val);
+                       } else {
+                               parse_warn(cfile, "Expecting a number.");
+                               skip_to_semi(cfile);
+                               no_semi = 1;
+                       }
+                       break;
+
+                     case IAPREFIX:
+                       *pref = parse_client6_iaprefix_statement(cfile);
+
+                       if (*pref != NULL)
+                               pref = &(*pref)->next;
+
+                       no_semi = 1;
+
+                       break;
+
+                     case OPTION:
+                       if (parse_option_decl(&oc, cfile)) {
+                               save_option(oc->option->universe,
+                                           ia->options, oc);
+                               option_cache_dereference(&oc, MDL);
+                       }
+                       no_semi = 1;
+                       break;
+
+                     default:
+                       parse_warn(cfile, "Unexpected token.");
+                       no_semi = 1;
+                       skip_to_semi(cfile);
+                       break;
+               }
+
+               if (!no_semi)
+                       parse_semi(cfile);
+
+               token = next_token(&val, NULL, cfile);
+
+               if (token == END_OF_FILE) {
+                       parse_warn(cfile, "Unexpected end of file.");
+                       break;
+               }
+       }
+
+       return ia;
+}
+#endif /* DHCPv6 */
+
 /* Parse an iaaddr {} structure. */
 #ifdef DHCPv6
 static struct dhc6_addr *
@@ -1701,6 +1981,114 @@ parse_client6_iaaddr_statement(struct parse *cfile)
 }
 #endif /* DHCPv6 */
 
+/* Parse an iaprefix {} structure. */
+#ifdef DHCPv6
+static struct dhc6_addr *
+parse_client6_iaprefix_statement(struct parse *cfile)
+{
+       struct option_cache *oc = NULL;
+       struct dhc6_addr *pref;
+       const char *val;
+       int token, no_semi;
+
+       pref = dmalloc(sizeof(*pref), MDL);
+       if (pref == NULL) {
+               parse_warn(cfile, "Unable to allocate IAPREFIX state.");
+               skip_to_semi(cfile);
+               return NULL;
+       }
+
+       /* Get IP prefix. */
+       if (!parse_ip6_prefix(cfile, &pref->address, &pref->plen)) {
+               skip_to_semi(cfile);
+               dfree(pref, MDL);
+               return NULL;
+       }
+
+       token = next_token(NULL, NULL, cfile);
+       if (token != LBRACE) {
+               parse_warn(cfile, "Expecting open curly bracket.");
+               skip_to_semi(cfile);
+               dfree(pref, MDL);
+               return NULL;
+       }
+
+       option_state_allocate(&pref->options, MDL);
+       if (pref->options == NULL) {
+               parse_warn(cfile, "Unable to allocate option state.");
+               skip_to_semi(cfile);
+               dfree(pref, MDL);
+               return NULL;
+       }
+
+       token = next_token(&val, NULL, cfile);
+       while (token != RBRACE) {
+               no_semi = 0;
+
+               switch (token) {
+                     case STARTS:
+                       token = next_token(&val, NULL, cfile);
+                       if (token == NUMBER) {
+                               pref->starts = atoi(val);
+                       } else {
+                               parse_warn(cfile, "Expecting a number.");
+                               skip_to_semi(cfile);
+                               no_semi = 1;
+                       }
+                       break;
+
+                     case PREFERRED_LIFE:
+                       token = next_token(&val, NULL, cfile);
+                       if (token == NUMBER) {
+                               pref->preferred_life = atoi(val);
+                       } else {
+                               parse_warn(cfile, "Expecting a number.");
+                               skip_to_semi(cfile);
+                               no_semi = 1;
+                       }
+                       break;
+
+                     case MAX_LIFE:
+                       token = next_token(&val, NULL, cfile);
+                       if (token == NUMBER) {
+                               pref->max_life = atoi(val);
+                       } else {
+                               parse_warn(cfile, "Expecting a number.");
+                               skip_to_semi(cfile);
+                               no_semi = 1;
+                       }
+                       break;
+
+                     case OPTION:
+                       if (parse_option_decl(&oc, cfile)) {
+                               save_option(oc->option->universe,
+                                           pref->options, oc);
+                               option_cache_dereference(&oc, MDL);
+                       }
+                       no_semi = 1;
+                       break;
+
+                     default:
+                       parse_warn(cfile, "Unexpected token.");
+                       skip_to_rbrace(cfile, 1);
+                       no_semi = 1;
+                       break;
+               }
+
+               if (!no_semi)
+                       parse_semi(cfile);
+
+               token = next_token(&val, NULL, cfile);
+               if (token == END_OF_FILE) {
+                       parse_warn(cfile, "Unexpected end of file.");
+                       break;
+               }
+       }
+
+       return pref;
+}
+#endif /* DHCPv6 */
+
 void parse_string_list (cfile, lp, multiple)
        struct parse *cfile;
        struct string_list **lp;
index 8bf89b0989c20592fb66a3563abf15a7d13f1157..c84a8a53c6c8a2a2ab1fe6e2338c37fdc88c2d93 100644 (file)
@@ -34,7 +34,10 @@ struct sockaddr_in6 DHCPv6DestAddr;
 struct option *clientid_option = NULL;
 struct option *elapsed_option = NULL;
 struct option *ia_na_option = NULL;
+struct option *ia_ta_option = NULL;
+struct option *ia_pd_option = NULL;
 struct option *iaaddr_option = NULL;
+struct option *iaprefix_option = NULL;
 struct option *oro_option = NULL;
 
 static struct dhc6_lease *dhc6_dup_lease(struct dhc6_lease *lease,
@@ -47,20 +50,29 @@ static void dhc6_ia_destroy(struct dhc6_ia **src, const char *file, int line);
 static isc_result_t dhc6_parse_ia_na(struct dhc6_ia **pia,
                                     struct packet *packet,
                                     struct option_state *options);
+static isc_result_t dhc6_parse_ia_ta(struct dhc6_ia **pia,
+                                    struct packet *packet,
+                                    struct option_state *options);
+static isc_result_t dhc6_parse_ia_pd(struct dhc6_ia **pia,
+                                    struct packet *packet,
+                                    struct option_state *options);
 static isc_result_t dhc6_parse_addrs(struct dhc6_addr **paddr,
                                     struct packet *packet,
                                     struct option_state *options);
-static struct dhc6_ia *find_ia(struct dhc6_ia *head, const char *id);
+static isc_result_t dhc6_parse_prefs(struct dhc6_addr **ppref,
+                                    struct packet *packet,
+                                    struct option_state *options);
+static struct dhc6_ia *find_ia_na(struct dhc6_ia *head, const char *id);
 static struct dhc6_addr *find_addr(struct dhc6_addr *head,
                                   struct iaddr *address);
 void init_handler(struct packet *packet, struct client_state *client);
 void do_init6(void *input);
 void do_confirm6(void *input);
 void reply_handler(struct packet *packet, struct client_state *client);
-static isc_result_t dhc6_add_ia(struct client_state *client,
-                               struct data_string *packet,
-                               struct dhc6_lease *lease,
-                                u_int8_t message);
+static isc_result_t dhc6_add_ia_na(struct client_state *client,
+                                  struct data_string *packet,
+                                  struct dhc6_lease *lease,
+                                  u_int8_t message);
 static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst);
 void do_select6(void *input);
 void do_refresh6(void *input);
@@ -177,11 +189,27 @@ dhcpv6_client_assignments(void)
                                     &code, 0, MDL))
                log_fatal("Unable to find the IA_NA option definition.");
 
+       code = D6O_IA_TA;
+       if (!option_code_hash_lookup(&ia_ta_option, dhcpv6_universe.code_hash,
+                                    &code, 0, MDL))
+               log_fatal("Unable to find the IA_TA option definition.");
+
+       code = D6O_IA_PD;
+       if (!option_code_hash_lookup(&ia_pd_option, dhcpv6_universe.code_hash,
+                                    &code, 0, MDL))
+               log_fatal("Unable to find the IA_PD option definition.");
+
        code = D6O_IAADDR;
        if (!option_code_hash_lookup(&iaaddr_option, dhcpv6_universe.code_hash,
                                     &code, 0, MDL))
                log_fatal("Unable to find the IAADDR option definition.");
 
+       code = D6O_IAPREFIX;
+       if (!option_code_hash_lookup(&iaprefix_option,
+                                    dhcpv6_universe.code_hash,
+                                    &code, 0, MDL))
+               log_fatal("Unable to find the IAPREFIX option definition.");
+
        code = D6O_ORO;
        if (!option_code_hash_lookup(&oro_option, dhcpv6_universe.code_hash,
                                     &code, 0, MDL))
@@ -409,6 +437,7 @@ dhc6_dup_ia(struct dhc6_ia *ia, const char *file, int line)
 
        memcpy(copy->iaid, ia->iaid, sizeof(copy->iaid));
 
+       copy->ia_type = ia->ia_type;
        copy->starts = ia->starts;
        copy->renew = ia->renew;
        copy->rebind = ia->rebind;
@@ -432,7 +461,7 @@ dhc6_dup_ia(struct dhc6_ia *ia, const char *file, int line)
        return copy;
 }
 
-/* Duplicate an IAADDR structure.
+/* Duplicate an IAADDR or IAPREFIX structure.
  */
 static struct dhc6_addr *
 dhc6_dup_addr(struct dhc6_addr *addr, const char *file, int line)
@@ -446,6 +475,7 @@ dhc6_dup_addr(struct dhc6_addr *addr, const char *file, int line)
 
        memcpy(&copy->address, &addr->address, sizeof(copy->address));
 
+       copy->plen = addr->plen;
        copy->flags = addr->flags;
        copy->starts = addr->starts;
        copy->preferred_life = addr->preferred_life;
@@ -459,7 +489,7 @@ dhc6_dup_addr(struct dhc6_addr *addr, const char *file, int line)
 }
 
 /* Form a DHCPv6 lease structure based upon packet contents.  Creates and
- * populates IA's and any IAADDR's they contain.
+ * populates IA's and any IAADDR/IAPREFIX's they contain.
  */
 static struct dhc6_lease *
 dhc6_leaseify(struct packet *packet)
@@ -508,6 +538,24 @@ dhc6_leaseify(struct packet *packet)
                dhc6_lease_destroy(&lease, MDL);
                return NULL;
        }
+       /* Dig into recursive DHCPv6 pockets for IA_TA and contained IAADDR
+        * options.
+        */
+       if (dhc6_parse_ia_ta(&lease->bindings, packet,
+                            lease->options) != ISC_R_SUCCESS) {
+               /* Error conditions are logged by the caller. */
+               dhc6_lease_destroy(&lease, MDL);
+               return NULL;
+       }
+       /* Dig into recursive DHCPv6 pockets for IA_PD and contained IAPREFIX
+        * options.
+        */
+       if (dhc6_parse_ia_pd(&lease->bindings, packet,
+                            lease->options) != ISC_R_SUCCESS) {
+               /* Error conditions are logged by the caller. */
+               dhc6_lease_destroy(&lease, MDL);
+               return NULL;
+       }
 
        /* This is last because in the future we may want to make a different
         * key based upon additional information from the packet (we may need
@@ -555,6 +603,7 @@ dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet,
                                                 &global_scope, oc, MDL) &&
                           ds.len >= 12) {
                        memcpy(ia->iaid, ds.data, 4);
+                       ia->ia_type = D6O_IA_NA;
                        ia->starts = cur_time;
                        ia->renew = getULong(ds.data + 4);
                        ia->rebind = getULong(ds.data + 8);
@@ -636,6 +685,188 @@ dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet,
        return ISC_R_SUCCESS;
 }
 
+static isc_result_t
+dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet,
+                struct option_state *options)
+{
+       struct data_string ds;
+       struct dhc6_ia *ia;
+       struct option_cache *oc;
+       isc_result_t result;
+
+       memset(&ds, 0, sizeof(ds));
+
+       oc = lookup_option(&dhcpv6_universe, options, D6O_IA_TA);
+       for ( ; oc != NULL ; oc = oc->next) {
+               ia = dmalloc(sizeof(*ia), MDL);
+               if (ia == NULL) {
+                       log_error("Out of memory allocating IA_TA structure.");
+                       return ISC_R_NOMEMORY;
+               } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
+                                                options, NULL,
+                                                &global_scope, oc, MDL) &&
+                          ds.len >= 4) {
+                       memcpy(ia->iaid, ds.data, 4);
+                       ia->ia_type = D6O_IA_TA;
+                       ia->starts = cur_time;
+
+                       log_debug("RCV:  X-- IA_TA %s",
+                                 print_hex_1(4, ia->iaid, 59));
+                       /* XXX: This should be the printed time I think. */
+                       log_debug("RCV:  | X-- starts %u",
+                                 (unsigned)ia->starts);
+
+                       if (ds.len > 4) {
+                               log_debug("RCV:  | X-- [Options]");
+
+                               if (!option_state_allocate(&ia->options,
+                                                          MDL)) {
+                                       log_error("Out of memory allocating "
+                                                 "IA option state.");
+                                       dfree(ia, MDL);
+                                       data_string_forget(&ds, MDL);
+                                       return ISC_R_NOMEMORY;
+                               }
+
+                               if (!parse_option_buffer(ia->options,
+                                                        ds.data + 4,
+                                                        ds.len - 4,
+                                                        &dhcpv6_universe)) {
+                                       log_error("Corrupt IA_TA options.");
+                                       option_state_dereference(&ia->options,
+                                                                MDL);
+                                       dfree(ia, MDL);
+                                       data_string_forget(&ds, MDL);
+                                       return ISC_R_BADPARSE;
+                               }
+                       }
+                       data_string_forget(&ds, MDL);
+
+                       if (ia->options != NULL) {
+                               result = dhc6_parse_addrs(&ia->addrs, packet,
+                                                         ia->options);
+                               if (result != ISC_R_SUCCESS) {
+                                       option_state_dereference(&ia->options,
+                                                                MDL);
+                                       dfree(ia, MDL);
+                                       return result;
+                               }
+                       }
+
+                       *pia = ia;
+                       pia = &ia->next;
+               } else {
+                       log_error("Invalid IA_TA option cache.");
+                       dfree(ia, MDL);
+                       if (ds.len != 0)
+                               data_string_forget(&ds, MDL);
+                       return ISC_R_UNEXPECTED;
+               }
+       }
+
+       return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+dhc6_parse_ia_pd(struct dhc6_ia **pia, struct packet *packet,
+                struct option_state *options)
+{
+       struct data_string ds;
+       struct dhc6_ia *ia;
+       struct option_cache *oc;
+       isc_result_t result;
+
+       memset(&ds, 0, sizeof(ds));
+
+       oc = lookup_option(&dhcpv6_universe, options, D6O_IA_PD);
+       for ( ; oc != NULL ; oc = oc->next) {
+               ia = dmalloc(sizeof(*ia), MDL);
+               if (ia == NULL) {
+                       log_error("Out of memory allocating IA_PD structure.");
+                       return ISC_R_NOMEMORY;
+               } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
+                                                options, NULL,
+                                                &global_scope, oc, MDL) &&
+                          ds.len >= 12) {
+                       memcpy(ia->iaid, ds.data, 4);
+                       ia->ia_type = D6O_IA_PD;
+                       ia->starts = cur_time;
+                       ia->renew = getULong(ds.data + 4);
+                       ia->rebind = getULong(ds.data + 8);
+
+                       log_debug("RCV:  X-- IA_PD %s",
+                                 print_hex_1(4, ia->iaid, 59));
+                       /* XXX: This should be the printed time I think. */
+                       log_debug("RCV:  | X-- starts %u",
+                                 (unsigned)ia->starts);
+                       log_debug("RCV:  | X-- t1 - renew  +%u", ia->renew);
+                       log_debug("RCV:  | X-- t2 - rebind +%u", ia->rebind);
+
+                       /* RFC3315 section 22.4, discard IA_PD's that
+                        * have t1 greater than t2, and both not zero.
+                        * Since RFC3315 defines this behaviour, it is not
+                        * an error - just normal operation.
+                        */
+                       if ((ia->renew > 0) && (ia->rebind > 0) &&
+                           (ia->renew > ia->rebind)) {
+                               log_debug("RCV:  | !-- INVALID renew/rebind "
+                                         "times, IA_PD discarded.");
+                               dfree(ia, MDL);
+                               data_string_forget(&ds, MDL);
+                               continue;
+                       }
+
+                       if (ds.len > 12) {
+                               log_debug("RCV:  | X-- [Options]");
+
+                               if (!option_state_allocate(&ia->options,
+                                                          MDL)) {
+                                       log_error("Out of memory allocating "
+                                                 "IA option state.");
+                                       dfree(ia, MDL);
+                                       data_string_forget(&ds, MDL);
+                                       return ISC_R_NOMEMORY;
+                               }
+
+                               if (!parse_option_buffer(ia->options,
+                                                        ds.data + 12,
+                                                        ds.len - 12,
+                                                        &dhcpv6_universe)) {
+                                       log_error("Corrupt IA_PD options.");
+                                       option_state_dereference(&ia->options,
+                                                                MDL);
+                                       dfree(ia, MDL);
+                                       data_string_forget(&ds, MDL);
+                                       return ISC_R_BADPARSE;
+                               }
+                       }
+                       data_string_forget(&ds, MDL);
+
+                       if (ia->options != NULL) {
+                               result = dhc6_parse_prefs(&ia->addrs, packet,
+                                                         ia->options);
+                               if (result != ISC_R_SUCCESS) {
+                                       option_state_dereference(&ia->options,
+                                                                MDL);
+                                       dfree(ia, MDL);
+                                       return result;
+                               }
+                       }
+
+                       *pia = ia;
+                       pia = &ia->next;
+               } else {
+                       log_error("Invalid IA_PD option cache.");
+                       dfree(ia, MDL);
+                       if (ds.len != 0)
+                               data_string_forget(&ds, MDL);
+                       return ISC_R_UNEXPECTED;
+               }
+       }
+
+       return ISC_R_SUCCESS;
+}
+
 
 static isc_result_t
 dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet,
@@ -730,6 +961,109 @@ dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet,
        return ISC_R_SUCCESS;
 }
 
+static isc_result_t
+dhc6_parse_prefs(struct dhc6_addr **ppref, struct packet *packet,
+                struct option_state *options)
+{
+       struct data_string ds;
+       struct option_cache *oc;
+       struct dhc6_addr *pref;
+
+       memset(&ds, 0, sizeof(ds));
+
+       oc = lookup_option(&dhcpv6_universe, options, D6O_IAPREFIX);
+       for ( ; oc != NULL ; oc = oc->next) {
+               pref = dmalloc(sizeof(*pref), MDL);
+               if (pref == NULL) {
+                       log_error("Out of memory allocating "
+                                 "prefix structure.");
+                       return ISC_R_NOMEMORY;
+               } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
+                                                options, NULL, &global_scope,
+                                                oc, MDL) &&
+                          (ds.len >= 25)) {
+
+                       pref->plen = getUChar(ds.data);
+                       pref->address.len = 16;
+                       memcpy(pref->address.iabuf, ds.data + 1, 16);
+                       pref->starts = cur_time;
+                       pref->preferred_life = getULong(ds.data + 17);
+                       pref->max_life = getULong(ds.data + 21);
+
+                       log_debug("RCV:  | | X-- IAPREFIX %s/%d",
+                                 piaddr(pref->address), (int)pref->plen);
+                       log_debug("RCV:  | | | X-- Preferred lifetime %u.",
+                                 pref->preferred_life);
+                       log_debug("RCV:  | | | X-- Max lifetime %u.",
+                                 pref->max_life);
+
+                       /* Sanity check over the prefix length */
+                       if ((pref->plen < 4) || (pref->plen > 128)) {
+                               log_debug("RCV:  | | | !-- INVALID prefix "
+                                         "length, IAPREFIX discarded.  "
+                                         "Check your server configuration.");
+                               dfree(pref, MDL);
+                               data_string_forget(&ds, MDL);
+                               continue;
+                       }
+                       /* RFC 3315 section 22.6 says we must discard
+                        * prefixes whose pref is later than valid.
+                        */
+                       if ((pref->preferred_life > pref->max_life)) {
+                               log_debug("RCV:  | | | !-- INVALID lifetimes, "
+                                         "IAPREFIX discarded.  Check your "
+                                         "server configuration.");
+                               dfree(pref, MDL);
+                               data_string_forget(&ds, MDL);
+                               continue;
+                       }
+
+                       /* Fortunately this is the last recursion in the
+                        * protocol.
+                        */
+                       if (ds.len > 25) {
+                               if (!option_state_allocate(&pref->options,
+                                                          MDL)) {
+                                       log_error("Out of memory allocating "
+                                                 "IAPREFIX option state.");
+                                       dfree(pref, MDL);
+                                       data_string_forget(&ds, MDL);
+                                       return ISC_R_NOMEMORY;
+                               }
+
+                               if (!parse_option_buffer(pref->options,
+                                                        ds.data + 25,
+                                                        ds.len - 25,
+                                                        &dhcpv6_universe)) {
+                                       log_error("Corrupt IAPREFIX options.");
+                                       option_state_dereference(&pref->options,
+                                                                MDL);
+                                       dfree(pref, MDL);
+                                       data_string_forget(&ds, MDL);
+                                       return ISC_R_BADPARSE;
+                               }
+                       }
+
+                       if (pref->options != NULL)
+                               log_debug("RCV:  | | | X-- "
+                                         "[Options]");
+
+                       data_string_forget(&ds, MDL);
+
+                       *ppref = pref;
+                       ppref = &pref->next;
+               } else {
+                       log_error("Invalid IAPREFIX option cache.");
+                       dfree(pref, MDL);
+                       if (ds.len != 0)
+                               data_string_forget(&ds, MDL);
+                       return ISC_R_UNEXPECTED;
+               }
+       }
+
+       return ISC_R_SUCCESS;
+}
+
 /* Clean up a lease object, deallocate all its parts, and set it to NULL. */
 void
 dhc6_lease_destroy(struct dhc6_lease **src, const char *file, int line)
@@ -1032,8 +1366,8 @@ do_init6(void *input)
        log_debug("XMT:  | X-- Request rebind in +%u", (unsigned)t2);
 
        if ((client->active_lease != NULL) &&
-           ((old_ia = find_ia(client->active_lease->bindings,
-                              (char *)ia.buffer->data)) != NULL)) {
+           ((old_ia = find_ia_na(client->active_lease->bindings,
+                                 (char *)ia.buffer->data)) != NULL)) {
                /* For each address in the old IA, request a binding. */
                memset(&addr, 0, sizeof(addr));
                for (old_addr = old_ia->addrs ; old_addr != NULL ;
@@ -1174,8 +1508,8 @@ do_confirm6(void *input)
                                    &dhcpv6_universe);
 
        /* Append IA's. */
-       if (dhc6_add_ia(client, &ds, client->active_lease,
-                        DHCPV6_CONFIRM) != ISC_R_SUCCESS) {
+       if (dhc6_add_ia_na(client, &ds, client->active_lease,
+                          DHCPV6_CONFIRM) != ISC_R_SUCCESS) {
                data_string_forget(&ds, MDL);
                return;
        }
@@ -1295,8 +1629,8 @@ do_release6(void *input)
                                    &dhcpv6_universe);
 
        /* Append IA's. */
-       if (dhc6_add_ia(client, &ds, client->active_lease,
-                        DHCPV6_RELEASE) != ISC_R_SUCCESS) {
+       if (dhc6_add_ia_na(client, &ds, client->active_lease,
+                          DHCPV6_RELEASE) != ISC_R_SUCCESS) {
                data_string_forget(&ds, MDL);
                return;
        }
@@ -1453,15 +1787,32 @@ dhc6_check_advertise(struct dhc6_lease *lease)
        isc_result_t rval = ISC_R_SUCCESS;
        int have_addrs = ISC_FALSE;
        unsigned code;
+       const char *scope;
 
        rval = dhc6_check_status(rval, lease->options, "message", &code);
 
        for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
-               rval = dhc6_check_status(rval, ia->options, "IA_NA", &code);
+               switch (ia->ia_type) {
+                       case D6O_IA_NA:
+                       default:
+                               scope = "IA_NA";
+                               break;
+                       case D6O_IA_TA:
+                               scope = "IA_TA";
+                               break;
+                       case D6O_IA_PD:
+                               scope = "IA_PD";
+                               break;
+               }
+               rval = dhc6_check_status(rval, ia->options, scope, &code);
 
                for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+                       if (ia->ia_type != D6O_IA_PD)
+                               scope = "IAADDR";
+                       else
+                               scope = "IAPREFIX";
                        rval = dhc6_check_status(rval, addr->options,
-                                                "IAADDR", &code);
+                                                scope, &code);
                        have_addrs = ISC_TRUE;
                }
        }
@@ -1695,6 +2046,7 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
        struct dhc6_addr *addr;
        isc_result_t rval = ISC_R_SUCCESS;
        unsigned code;
+       const char *scope;
        int nscore, sscore;
 
        if ((client == NULL) || (new == NULL))
@@ -1725,15 +2077,31 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
                return ISC_R_CANCELED;
 
        for (ia = new->bindings ; ia != NULL ; ia = ia->next) {
-               rval = dhc6_check_status(rval, ia->options, "IA_NA",
-                                        &code);
+               switch (ia->ia_type) {
+                       case D6O_IA_NA:
+                       default:
+                               scope = "IA_NA";
+                               break;
+                       case D6O_IA_TA:
+                               scope = "IA_TA";
+                               break;
+                       case D6O_IA_PD:
+                               scope = "IA_PD";
+                               break;
+               }
+               rval = dhc6_check_status(rval, ia->options,
+                                        scope, &code);
                if (action(client, rval, code))
                        return ISC_R_CANCELED;
 
                for (addr = ia->addrs ; addr != NULL ;
                     addr = addr->next) {
+                       if (ia->ia_type != D6O_IA_PD)
+                               scope = "IAADDR";
+                       else
+                               scope = "IAPREFIX";
                        rval = dhc6_check_status(rval, addr->options,
-                                                "IAADDR", &code);
+                                                scope, &code);
                        if (action(client, rval, code))
                                return ISC_R_CANCELED;
                }
@@ -2098,8 +2466,9 @@ do_select6(void *input)
                                    NULL, client->sent_options, &global_scope,
                                    &dhcpv6_universe);
 
-       /* Now append any IA_NA's, and within them any IAADDRs. */
-       if (dhc6_add_ia(client, &ds, lease, DHCPV6_REQUEST) != ISC_R_SUCCESS) {
+       /* Now append any IA's, and within them any IAADDR/IAPREFIXs. */
+       if (dhc6_add_ia_na(client, &ds, lease,
+                          DHCPV6_REQUEST) != ISC_R_SUCCESS) {
                data_string_forget(&ds, MDL);
                return;
        }
@@ -2122,12 +2491,12 @@ do_select6(void *input)
        dhc6_retrans_advance(client);
 }
 
-/* For each IA in the lease, for each address in the IA, append that
- * information onto the packet-so-far.
+/* For each IA_NA in the lease, for each address in the IA_NA,
+ * append that information onto the packet-so-far.
  */
 static isc_result_t
-dhc6_add_ia(struct client_state *client, struct data_string *packet,
-           struct dhc6_lease *lease, u_int8_t message)
+dhc6_add_ia_na(struct client_state *client, struct data_string *packet,
+              struct dhc6_lease *lease, u_int8_t message)
 {
        struct data_string iads;
        struct data_string addrds;
@@ -2141,6 +2510,9 @@ dhc6_add_ia(struct client_state *client, struct data_string *packet,
        for (ia = lease->bindings;
              ia != NULL && rval == ISC_R_SUCCESS;
              ia = ia->next) {
+               if (ia->ia_type != D6O_IA_NA)
+                       continue;
+
                if (!buffer_allocate(&iads.buffer, 12, MDL)) {
                        log_error("Unable to allocate memory for IA_NA.");
                        rval = ISC_R_NOMEMORY;
@@ -2469,6 +2841,9 @@ dhc6_check_times(struct client_state *client)
        for(ia = lease->bindings ; ia != NULL ; ia = ia->next) {
                TIME this_ia_lo_expire, this_ia_hi_expire, use_expire;
 
+               if (ia->ia_type != D6O_IA_NA)
+                       continue;
+
                this_ia_lo_expire = MAX_TIME;
                this_ia_hi_expire = 0;
 
@@ -2632,11 +3007,13 @@ dhc6_check_times(struct client_state *client)
 
 /* In a given IA chain, find the IA with the same 'iaid'. */
 static struct dhc6_ia *
-find_ia(struct dhc6_ia *head, const char *id)
+find_ia_na(struct dhc6_ia *head, const char *id)
 {
        struct dhc6_ia *ia;
 
        for (ia = head ; ia != NULL ; ia = ia->next) {
+               if (ia->ia_type != D6O_IA_NA)
+                       continue;
                if (memcmp(ia->iaid, id, 4) == 0)
                        return ia;
        }
@@ -2676,7 +3053,9 @@ dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst)
                return;
 
        for (sia = src->bindings ; sia != NULL ; sia = sia->next) {
-               dia = find_ia(dst->bindings, (char *)sia->iaid);
+               if (sia->ia_type != D6O_IA_NA)
+                       continue;
+               dia = find_ia_na(dst->bindings, (char *)sia->iaid);
 
                if (dia == NULL) {
                        tia = dhc6_dup_ia(sia, MDL);
@@ -2774,8 +3153,11 @@ start_bound(struct client_state *client)
 
        oldia = NULL;
        for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+               if (ia->ia_type != D6O_IA_NA)
+                       continue;
+
                if (old != NULL)
-                       oldia = find_ia(old->bindings, (char *)ia->iaid);
+                       oldia = find_ia_na(old->bindings, (char *)ia->iaid);
                else
                        oldia = NULL;
 
@@ -2983,8 +3365,8 @@ do_refresh6(void *input)
                                    &dhcpv6_universe);
 
         /* Append IA's */
-       if (dhc6_add_ia(client, &ds, lease,
-                        client->refresh_type) != ISC_R_SUCCESS) {
+       if (dhc6_add_ia_na(client, &ds, lease,
+                          client->refresh_type) != ISC_R_SUCCESS) {
                data_string_forget(&ds, MDL);
                return;
        }
@@ -3062,6 +3444,8 @@ do_depref(void *input)
                return;
 
        for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+               if (ia->ia_type != D6O_IA_NA)
+                       continue;
                for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
                        if (addr->flags & DHC6_ADDR_DEPREFFED)
                                continue;
@@ -3110,6 +3494,8 @@ do_expire(void *input)
                return;
 
        for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+               if (ia->ia_type != D6O_IA_NA)
+                       continue;
                for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
                        if (addr->flags & DHC6_ADDR_EXPIRED)
                                continue;
@@ -3174,6 +3560,9 @@ unconfigure6(struct client_state *client, const char *reason)
                return;
 
        for (ia = client->active_lease->bindings ; ia != NULL ; ia = ia->next) {
+               if (ia->ia_type != D6O_IA_NA)
+                       continue;
+
                for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
                        script_init(client, reason, NULL);
                        dhc6_marshall_values("old_", client, 
index 6b0bd0a4830e2d6903c03e8eab8c010ab2a6808d..c2276875ea53b2fa40f78a12388aee4e396fdd0b 100644 (file)
@@ -2487,6 +2487,7 @@ write_client6_lease(struct client_state *client, struct dhc6_lease *lease,
        struct dhc6_ia *ia;
        struct dhc6_addr *addr;
        int stat;
+       const char *ianame;
 
        /* This should include the current lease. */
        if (!rewrite && (leases_written++ > 20)) {
@@ -2516,21 +2517,44 @@ write_client6_lease(struct client_state *client, struct dhc6_lease *lease,
                return ISC_R_IOERROR;
 
        for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
-               stat = fprintf(leaseFile, "  ia-na %s {\n",
-                              print_hex_1(4, ia->iaid, 12));
+               switch (ia->ia_type) {
+                       case D6O_IA_NA:
+                       default:
+                               ianame = "ia-na";
+                               break;
+                       case D6O_IA_TA:
+                               ianame = "ia-ta";
+                               break;
+                       case D6O_IA_PD:
+                               ianame = "ia-pd";
+                               break;
+               }
+               stat = fprintf(leaseFile, "  %s %s {\n",
+                              ianame, print_hex_1(4, ia->iaid, 12));
                if (stat <= 0)
                        return ISC_R_IOERROR;
 
-               stat = fprintf(leaseFile, "    starts %d;\n"
-                                         "    renew %u;\n"
-                                         "    rebind %u;\n",
-                              (int)ia->starts, ia->renew, ia->rebind);
+               if (ia->ia_type != D6O_IA_TA)
+                       stat = fprintf(leaseFile, "    starts %d;\n"
+                                                 "    renew %u;\n"
+                                                 "    rebind %u;\n",
+                                      (int)ia->starts, ia->renew, ia->rebind);
+               else
+                       stat = fprintf(leaseFile, "    starts %d;\n",
+                                      (int)ia->starts);
                if (stat <= 0)
                        return ISC_R_IOERROR;
 
                for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
-                       stat = fprintf(leaseFile, "    iaaddr %s {\n",
-                                      piaddr(addr->address));
+                       if (ia->ia_type != D6O_IA_PD)
+                               stat = fprintf(leaseFile,
+                                              "    iaaddr %s {\n",
+                                              piaddr(addr->address));
+                       else
+                               stat = fprintf(leaseFile,
+                                              "    iaprefix %s/%d {\n",
+                                              piaddr(addr->address),
+                                              (int)addr->plen);
                        if (stat <= 0)
                                return ISC_R_IOERROR;
 
index 25767bf8d13ea9e693d141f32acad3913d046a60..2c3621fee633c5527bce1d19a46f23922aacb25b 100644 (file)
@@ -951,8 +951,14 @@ intern(char *atom, enum dhcp_token dfv) {
              case 'i':
                if (!strcasecmp(atom+1, "a-na")) 
                        return IA_NA;
+               if (!strcasecmp(atom+1, "a-ta")) 
+                       return IA_TA;
+               if (!strcasecmp(atom+1, "a-pd")) 
+                       return IA_PD;
                if (!strcasecmp(atom+1, "aaddr")) 
                        return IAADDR;
+               if (!strcasecmp(atom+1, "aprefix")) 
+                       return IAPREFIX;
                if (!strcasecmp (atom + 1, "nclude"))
                        return INCLUDE;
                if (!strcasecmp (atom + 1, "nteger"))
index ba53584f66c7cfa109339dd4bf97105cf073b9b4..c1c66425b8e6673219f8506272bb47d235d3f2fc 100644 (file)
@@ -434,6 +434,47 @@ parse_ip6_addr_expr(struct expression **expr,
        return make_const_data(expr, addr.iabuf, addr.len, 0, 1, MDL);
 }
 
+/*
+ * ip6-prefix :== ip6-address "/" NUMBER
+ */
+int
+parse_ip6_prefix(struct parse *cfile, struct iaddr *addr, u_int8_t *plen) {
+       enum dhcp_token token;
+       const char *val;
+       int n;
+
+       if (!parse_ip6_addr(cfile, addr)) {
+               return 0;
+       }
+       token = next_token(&val, NULL, cfile);
+       if (token != SLASH) {
+               parse_warn(cfile, "Slash expected.");
+               if (token != SEMI)
+                       skip_to_semi(cfile);
+               return 0;
+       }
+       token = next_token(&val, NULL, cfile);
+       if (token != NUMBER) {
+               parse_warn(cfile, "Number expected.");
+               if (token != SEMI)
+                       skip_to_semi(cfile);
+               return 0;
+       }
+       n = atoi(val);
+       if ((n < 0) || (n > 128)) {
+               parse_warn(cfile, "Invalid IPv6 prefix length.");
+               skip_to_semi(cfile);
+               return 0;
+       }
+       if (!is_cidr_mask_valid(addr, n)) {
+               parse_warn(cfile, "network mask too short.");
+               skip_to_semi(cfile);
+               return 0;
+       }
+       *plen = n;
+       return 1;
+}
+
 /*
  * ip-address-with-subnet :== ip-address |
  *                          ip-address "/" NUMBER
index 1d82eb3397ae48182a1eb78f58953977255ed0e7..243c785752dbfeb47ead8b15d83844f62ea8ffc3 100644 (file)
@@ -889,6 +889,7 @@ struct client_lease {
 struct dhc6_addr {
        struct dhc6_addr *next;
        struct iaddr address;
+       u_int8_t plen;
 
        /* Address state flags. */
        #define DHC6_ADDR_DEPREFFED     0x01
@@ -905,6 +906,7 @@ struct dhc6_addr {
 struct dhc6_ia {
        struct dhc6_ia *next;
        unsigned char iaid[4];
+       u_int16_t ia_type;
 
        TIME starts;
        u_int32_t renew;
@@ -1308,6 +1310,7 @@ typedef struct hash_table iaaddr_hash_t;
 struct iaaddr {
        int refcnt;                             /* reference count */
        struct in6_addr addr;                   /* IPv6 address */
+       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 */
@@ -1326,12 +1329,14 @@ struct iaaddr {
 struct ia_na {
        int refcnt;                     /* reference count */
        struct data_string iaid_duid;   /* from the client */
+       u_int16_t ia_type;              /* IA_NA or IA_TA */
        int num_iaaddr;                 /* number of IAADDR for this IA_NA */
        int max_iaaddr;                 /* space available for IAADDR */
        struct iaaddr **iaaddr;         /* pointers to the various IAADDRs */
 };
 
-extern ia_na_hash_t *ia_active;
+extern ia_na_hash_t *ia_na_active;
+extern ia_na_hash_t *ia_ta_active;
 
 struct ipv6_pool {
        int refcnt;                             /* reference count */
@@ -1350,6 +1355,51 @@ struct ipv6_pool {
 extern struct ipv6_pool **pools;
 extern int num_pools;
 
+/* Sames thing for IA_PDs */
+
+typedef struct hash_table ia_pd_hash_t;
+typedef struct hash_table iaprefix_hash_t;
+
+struct iaprefix {
+       /* Must keep the same layout than iaaddr */
+       int refcnt;                             /* reference count */
+       struct in6_addr pref;                   /* IPv6 prefix */
+       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 */
+       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 
+                                                  (internal use only) */
+};
+
+struct ia_pd {
+       int refcnt;                     /* reference count */
+       struct data_string iaid_duid;   /* from the client */
+       int num_iaprefix;               /* number of IAPREFIX for this IA_PD */
+       int max_iaprefix;               /* space available for IAPREFIX */
+       struct iaprefix **iaprefix;     /* pointers to the various IAPREFIXs */
+};
+
+extern ia_pd_hash_t *ia_pd_active;
+
+struct ipv6_ppool {
+       int refcnt;                             /* reference count */
+       struct in6_addr start_pref;             /* first IPv6 prefix */
+       u_int8_t pool_plen;                     /* pool prefix length */
+       u_int8_t alloc_plen;                    /* allocation prefix length */
+       iaprefix_hash_t *prefs;                 /* non-free IAPREFIX */
+       int num_active;                         /* count of active IAPREFIX */
+       isc_heap_t *active_timeouts;            /* timeouts for active leases */
+       int num_inactive;                       /* count of inactive IAADDR */
+       isc_heap_t *inactive_timeouts;          /* timeouts for expired or 
+                                                  released leases */
+};
+
+extern struct ipv6_ppool **ppools;
+extern int num_ppools;
+
 /* External definitions... */
 
 HASH_FUNCTIONS_DECL (group, const char *, struct group_object, group_hash_t)
@@ -1665,10 +1715,13 @@ int parse_fixed_addr_param PROTO ((struct option_cache **,
 int parse_lease_declaration PROTO ((struct lease **, struct parse *));
 int parse_ip6_addr(struct parse *, struct iaddr *);
 int parse_ip6_addr_expr(struct expression **, struct parse *);
+int parse_ip6_prefix(struct parse *, struct iaddr *, u_int8_t *);
 void parse_address_range PROTO ((struct parse *, struct group *, int,
                                 struct pool *, struct lease **));
 void parse_address_range6(struct parse *cfile, struct group *group);
 void parse_ia_na_declaration(struct parse *);
+void parse_ia_ta_declaration(struct parse *);
+void parse_ia_pd_declaration(struct parse *);
 void parse_server_duid(struct parse *cfile);
 void parse_server_duid_conf(struct parse *cfile);
 
@@ -2487,7 +2540,8 @@ int commit_leases PROTO ((void));
 void db_startup PROTO ((int));
 int new_lease_file PROTO ((void));
 int group_writer (struct group_object *);
-int write_ia_na(const struct ia_na *);
+int write_ia(const struct ia_na *);
+int write_ia_pd(const struct ia_pd *);
 
 /* packet.c */
 u_int32_t checksum PROTO ((unsigned char *, unsigned, u_int32_t));
@@ -3161,7 +3215,10 @@ const char *binding_state_print (enum failover_state);
 
 /* mdb6.c */
 HASH_FUNCTIONS_DECL(ia_na, unsigned char *, struct ia_na, ia_na_hash_t);
+HASH_FUNCTIONS_DECL(ia_pd, unsigned char *, struct ia_pd, ia_pd_hash_t);
 HASH_FUNCTIONS_DECL(iaaddr, struct in6_addr *, struct iaaddr, iaaddr_hash_t);
+HASH_FUNCTIONS_DECL(iaprefix, struct in6_addr *,
+                   struct iaprefix, iaaddr_hash_t);
 
 isc_result_t iaaddr_allocate(struct iaaddr **iaaddr,
                             const char *file, int line);
@@ -3170,9 +3227,17 @@ isc_result_t iaaddr_reference(struct iaaddr **iaaddr, struct iaaddr *src,
 isc_result_t iaaddr_dereference(struct iaaddr **iaaddr,
                                const char *file, int line);
 
-isc_result_t ia_na_make_key(struct data_string *key, u_int32_t iaid,
-                            const char *duid, unsigned int duid_len,
-                           const char *file, int line);
+isc_result_t iaprefix_allocate(struct iaprefix **iaprefix,
+                              const char *file, int line);
+isc_result_t iaprefix_reference(struct iaprefix **iaprefix,
+                               struct iaprefix *src,
+                               const char *file, int line);
+isc_result_t iaprefix_dereference(struct iaprefix **iaprefix,
+                                 const char *file, int line);
+
+isc_result_t ia_make_key(struct data_string *key, u_int32_t iaid,
+                        const char *duid, unsigned int duid_len,
+                        const char *file, int line);
 isc_result_t ia_na_allocate(struct ia_na **ia_na, u_int32_t iaid,
                            const char *duid, unsigned int duid_len,
                            const char *file, int line);
@@ -3186,6 +3251,18 @@ void ia_na_remove_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr,
                         const char *file, int line);
 isc_boolean_t ia_na_equal(const struct ia_na *a, const struct ia_na *b);
 
+isc_result_t ia_pd_allocate(struct ia_pd **ia_pd, u_int32_t iaid,
+                           const char *duid, unsigned int duid_len,
+                           const char *file, int line);
+isc_result_t ia_pd_reference(struct ia_pd **ia_pd, struct ia_pd *src,
+                            const char *file, int line);
+isc_result_t ia_pd_dereference(struct ia_pd **ia_pd,
+                              const char *file, int line);
+isc_result_t ia_pd_add_iaprefix(struct ia_pd *ia_pd, struct iaprefix *iaprefix,
+                               const char *file, int line);
+void ia_pd_remove_iaprefix(struct ia_pd *ia_pd, struct iaprefix *iaprefix,
+                          const char *file, int line);
+
 isc_result_t ipv6_pool_allocate(struct ipv6_pool **pool,
                                const struct in6_addr *start_addr, int bits, 
                                const char *file, int line);
@@ -3194,6 +3271,15 @@ isc_result_t ipv6_pool_reference(struct ipv6_pool **pool,
                                 const char *file, int line);
 isc_result_t ipv6_pool_dereference(struct ipv6_pool **pool,
                                   const char *file, int line);
+isc_result_t ipv6_ppool_allocate(struct ipv6_ppool **ppool,
+                                const struct in6_addr *start_pref,
+                                u_int8_t pool_plen, u_int8_t alloc_plen,
+                                const char *file, int line);
+isc_result_t ipv6_ppool_reference(struct ipv6_ppool **ppool,
+                                 struct ipv6_ppool *src,
+                                 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,
@@ -3212,11 +3298,29 @@ 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 add_prefix6(struct ipv6_ppool *ppool,
+                        struct iaprefix *pref,
+                        time_t valid_lifetime_end_time);
+isc_result_t renew_prefix6(struct ipv6_ppool *ppool, struct iaprefix *pref);
+isc_result_t expire_prefix6(struct iaprefix **pref, 
+                           struct ipv6_ppool *ppool, time_t now);
+isc_result_t release_prefix6(struct ipv6_ppool *ppool, struct iaprefix *pref);
+isc_boolean_t prefix6_exists(const struct ipv6_ppool *ppool, 
+                            const struct in6_addr *pref, u_int8_t plen);
+
 isc_result_t add_ipv6_pool(struct ipv6_pool *pool);
 isc_result_t find_ipv6_pool(struct ipv6_pool **pool, 
                            const struct in6_addr *addr);
 isc_boolean_t ipv6_addr_in_pool(const struct in6_addr *addr, 
                                const struct ipv6_pool *pool);
+isc_result_t add_ipv6_ppool(struct ipv6_ppool *ppool);
+isc_result_t find_ipv6_ppool(struct ipv6_ppool **pool,
+                            const struct in6_addr *pref);
 
 isc_result_t renew_leases(struct ia_na *ia_na);
 isc_result_t release_leases(struct ia_na *ia_na);
@@ -3224,6 +3328,11 @@ isc_result_t decline_leases(struct ia_na *ia_na);
 void schedule_lease_timeout(struct ipv6_pool *pool);
 void schedule_all_ipv6_lease_timeouts();
 
+isc_result_t renew_prefixes(struct ia_pd *ia_pd);
+isc_result_t release_prefixes(struct ia_pd *ia_pd);
+void schedule_prefix_timeout(struct ipv6_ppool *ppool);
+void schedule_all_ipv6_prefix_timeouts();
+
 void mark_hosts_unavailable(void);
 void mark_interfaces_unavailable(void);
 
index 80ad0b3d52eec0a1a537e75201ecb3bf9e692681..dd39a3e62d7845398be42e3ff5604c0b29c4c871 100644 (file)
@@ -333,19 +333,22 @@ enum dhcp_token {
        SUBNET6 = 636,
        HOST_IDENTIFIER = 637,
        IA_NA = 638,
-       IAADDR = 639,
-       LEASE6 = 640,
-       PREFERRED_LIFE = 641,
-       MAX_LIFE = 642,
-       DEFAULT_DUID = 643,
-       SERVER_DUID = 644,
-       LLT = 645,
-       EN = 646,
-       LL = 647,
-       RANGE6 = 648,
-       WHITESPACE = 649,
-       TOKEN_ALSO = 650,
-       AFTER = 651
+       IA_TA = 639,
+       IA_PD = 640,
+       IAADDR = 641,
+       IAPREFIX = 642,
+       LEASE6 = 643,
+       PREFERRED_LIFE = 644,
+       MAX_LIFE = 645,
+       DEFAULT_DUID = 646,
+       SERVER_DUID = 647,
+       LLT = 648,
+       EN = 649,
+       LL = 650,
+       RANGE6 = 651,
+       WHITESPACE = 652,
+       TOKEN_ALSO = 653,
+       AFTER = 654
 };
 
 #define is_identifier(x)       ((x) >= FIRST_TOKEN &&  \
index 715e654241736c9f6669ce84b78461ddc0abb2d4..7a0a3d21e0b084969ce13d9bde87274c9a20b4db 100644 (file)
@@ -266,6 +266,10 @@ isc_result_t lease_file_subparse (struct parse *cfile)
                                            "possibly corrupt lease file");
                } else if (token == IA_NA) {
                        parse_ia_na_declaration(cfile);
+               } else if (token == IA_TA) {
+                       parse_ia_ta_declaration(cfile);
+               } else if (token == IA_PD) {
+                       parse_ia_pd_declaration(cfile);
                } else if (token == CLASS) {
                        parse_class_declaration(0, cfile, root_group,
                                                CLASS_TYPE_CLASS);
@@ -3801,9 +3805,9 @@ parse_ia_na_declaration(struct parse *cfile) {
        skip_to_semi(cfile);
 #else /* defined(DHCPv6) */
        enum dhcp_token token;
-       struct ia_na *ia_na;
+       struct ia_na *ia;
        const char *val;
-       struct ia_na *old_ia_na;
+       struct ia_na *old_ia;
        unsigned int len;
        u_int32_t iaid;
        struct iaddr iaddr;
@@ -3832,10 +3836,11 @@ parse_ia_na_declaration(struct parse *cfile) {
        }
 
        memcpy(&iaid, val, 4);
-       ia_na = NULL;
-       if (ia_na_allocate(&ia_na, iaid, val+4, len-4, MDL) != ISC_R_SUCCESS) {
+       ia = NULL;
+       if (ia_na_allocate(&ia, iaid, val+4, len-4, MDL) != ISC_R_SUCCESS) {
                log_fatal("Out of memory.");
        }
+       ia->ia_type = D6O_IA_NA;
 
        token = next_token(&val, NULL, cfile);
        if (token != LBRACE) {
@@ -4041,8 +4046,8 @@ parse_ia_na_declaration(struct parse *cfile) {
                }
 
                /* add to our various structures */
-               ia_na_add_iaaddr(ia_na, iaaddr, MDL);
-               ia_na_reference(&iaaddr->ia_na, ia_na, MDL);
+               ia_na_add_iaaddr(ia, iaaddr, MDL);
+               ia_na_reference(&iaaddr->ia_na, ia, MDL);
                pool = NULL;
                if (find_ipv6_pool(&pool, &iaaddr->addr) != ISC_R_SUCCESS) {
                        inet_ntop(AF_INET6, &iaaddr->addr, 
@@ -4059,25 +4064,602 @@ parse_ia_na_declaration(struct parse *cfile) {
        /*
         * 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,
-                             (unsigned char *)ia_na->iaid_duid.data,
-                             ia_na->iaid_duid.len, MDL)) {
-               ia_na_hash_delete(ia_active, 
-                                 (unsigned char *)ia_na->iaid_duid.data,
-                                 ia_na->iaid_duid.len, MDL);
-               ia_na_dereference(&old_ia_na, MDL);
+       old_ia = NULL;
+       if (ia_na_hash_lookup(&old_ia, ia_na_active,
+                             (unsigned char *)ia->iaid_duid.data,
+                             ia->iaid_duid.len, MDL)) {
+               ia_na_hash_delete(ia_na_active, 
+                                 (unsigned char *)ia->iaid_duid.data,
+                                 ia->iaid_duid.len, MDL);
+               ia_na_dereference(&old_ia, MDL);
        }
 
        /*
         * If we have addresses, add this, otherwise don't bother.
         */
-       if (ia_na->num_iaaddr > 0) {
-               ia_na_hash_add(ia_active, 
-                              (unsigned char *)ia_na->iaid_duid.data,
-                              ia_na->iaid_duid.len, ia_na, MDL);
+       if (ia->num_iaaddr > 0) {
+               ia_na_hash_add(ia_na_active, 
+                              (unsigned char *)ia->iaid_duid.data,
+                              ia->iaid_duid.len, ia, MDL);
        }
-       ia_na_dereference(&ia_na, MDL);
+       ia_na_dereference(&ia, MDL);
+#endif /* defined(DHCPv6) */
+}
+
+void
+parse_ia_ta_declaration(struct parse *cfile) {
+#if !defined(DHCPv6)
+       parse_warn(cfile, "No DHCPv6 support.");
+       skip_to_semi(cfile);
+#else /* defined(DHCPv6) */
+       enum dhcp_token token;
+       struct ia_na *ia;
+       const char *val;
+       struct ia_na *old_ia;
+       unsigned int len;
+       u_int32_t iaid;
+       struct iaddr iaddr;
+       binding_state_t state;
+       TIME end_time;
+       struct iaaddr *iaaddr;
+       struct ipv6_pool *pool;
+       char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+       isc_boolean_t newbinding;
+       struct binding_scope *scope=NULL;
+       struct binding *bnd;
+       struct binding_value *nv=NULL;
+
+       token = next_token(&val, &len, cfile);
+       if (token != STRING) {
+               parse_warn(cfile, "corrupt lease file; "
+                                 "expecting an iaid+ia_ta string");
+               skip_to_semi(cfile);
+               return;
+       }
+       if (len < 5) {
+               parse_warn(cfile, "corrupt lease file; "
+                                 "iaid+ia_ta string too short");
+               skip_to_semi(cfile);
+               return;
+       }
+
+       memcpy(&iaid, val, 4);
+       ia = NULL;
+       if (ia_na_allocate(&ia, iaid, val+4, len-4, MDL) != ISC_R_SUCCESS) {
+               log_fatal("Out of memory.");
+       }
+       ia->ia_type = D6O_IA_TA;
+
+       token = next_token(&val, NULL, cfile);
+       if (token != LBRACE) {
+               parse_warn(cfile, "corrupt lease file; expecting left brace");
+               skip_to_semi(cfile);
+               return;
+       }
+
+       for (;;) {
+               token = next_token(&val, NULL, cfile);
+               if (token == RBRACE) break;
+
+               if (token != IAADDR) {
+                       parse_warn(cfile, "corrupt lease file; "
+                                         "expecting IAADDR or right brace");
+                       skip_to_semi(cfile);
+                       return;
+               }
+
+               if (!parse_ip6_addr(cfile, &iaddr)) {
+                       parse_warn(cfile, "corrupt lease file; "
+                                         "expecting IPv6 address");
+                       skip_to_semi(cfile);
+                       return;
+               }
+
+               token = next_token(&val, NULL, cfile);
+               if (token != LBRACE) {
+                       parse_warn(cfile, "corrupt lease file; "
+                                         "expecting left brace");
+                       skip_to_semi(cfile);
+                       return;
+               }
+
+               state = FTS_LAST+1;
+               end_time = -1;
+               for (;;) {
+                       token = next_token(&val, NULL, cfile);
+                       if (token == RBRACE) break;
+
+                       switch(token) {
+                               /* Lease binding state. */
+                            case BINDING:
+                               token = next_token(&val, NULL, cfile);
+                               if (token != STATE) {
+                                       parse_warn(cfile, "corrupt lease file; "
+                                                         "expecting state");
+                                       skip_to_semi(cfile);
+                                       return;
+                               }
+                               token = next_token(&val, NULL, cfile);
+                               switch (token) {
+                                       case TOKEN_ABANDONED:
+                                               state = FTS_ABANDONED;
+                                               break;
+                                       case TOKEN_FREE:
+                                               state = FTS_FREE;
+                                               break;
+                                       case TOKEN_ACTIVE:
+                                               state = FTS_ACTIVE;
+                                               break;
+                                       case TOKEN_EXPIRED:
+                                               state = FTS_EXPIRED;
+                                               break;
+                                       case TOKEN_RELEASED:
+                                               state = FTS_RELEASED;
+                                               break;
+                                       default:
+                                               parse_warn(cfile,
+                                                          "corrupt lease "
+                                                          "file; "
+                                                          "expecting a "
+                                                          "binding state.");
+                                               skip_to_semi(cfile);
+                                               return;
+                               }
+
+                               token = next_token(&val, NULL, cfile);
+                               if (token != SEMI) {
+                                       parse_warn(cfile, "corrupt lease file; "
+                                                         "expecting "
+                                                         "semicolon.");
+                               }
+                               break;
+
+                               /* Lease expiration time. */
+                             case ENDS:
+                               end_time = parse_date(cfile);
+                               break;
+
+                               /* Lease binding scopes. */
+                             case TOKEN_SET:
+                               token = next_token(&val, NULL, cfile);
+                               if ((token != NAME) &&
+                                   (token != NUMBER_OR_NAME)) {
+                                       parse_warn(cfile, "%s is not a valid "
+                                                         "variable name",
+                                                  val);
+                                       skip_to_semi(cfile);
+                                       continue;
+                               }
+
+                               if (scope != NULL)
+                                       bnd = find_binding(scope, val);
+                               else {
+                                       if (!binding_scope_allocate(&scope,
+                                                                   MDL)) {
+                                               log_fatal("Out of memory for "
+                                                         "lease binding "
+                                                         "scope.");
+                                       }
+
+                                       bnd = NULL;
+                               }
+
+                               if (bnd == NULL) {
+                                       bnd = dmalloc(sizeof(*bnd),
+                                                         MDL);
+                                       if (bnd == NULL) {
+                                               log_fatal("No memory for "
+                                                         "lease binding.");
+                                       }
+
+                                       bnd->name = dmalloc(strlen(val) + 1,
+                                                           MDL);
+                                       if (bnd->name == NULL) {
+                                               log_fatal("No memory for "
+                                                         "binding name.");
+                                       }
+                                       strcpy(bnd->name, val);
+
+                                       newbinding = ISC_TRUE;
+                               } else {
+                                       newbinding = ISC_FALSE;
+                               }
+
+                               if (!binding_value_allocate(&nv, MDL)) {
+                                       log_fatal("no memory for binding "
+                                                 "value.");
+                               }
+
+                               token = next_token(NULL, NULL, cfile);
+                               if (token != EQUAL) {
+                                       parse_warn(cfile, "expecting '=' in "
+                                                         "set statement.");
+                                       goto binding_err;
+                               }
+
+                               if (!parse_binding_value(cfile, nv)) {
+                                     binding_err:
+                                       binding_value_dereference(&nv, MDL);
+                                       binding_scope_dereference(&scope, MDL);
+                                       return;
+                               }
+
+                               if (newbinding) {
+                                       binding_value_reference(&bnd->value,
+                                                               nv, MDL);
+                                       bnd->next = scope->bindings;
+                                       scope->bindings = bnd;
+                               } else {
+                                       binding_value_dereference(&bnd->value,
+                                                                 MDL);
+                                       binding_value_reference(&bnd->value,
+                                                               nv, MDL);
+                               }
+
+                               binding_value_dereference(&nv, MDL);
+                               parse_semi(cfile);
+                               break;
+
+                             default:
+                               parse_warn(cfile, "corrupt lease file; "
+                                                 "expecting ia_ta contents, "
+                                                 "got '%s'", val);
+                               skip_to_semi(cfile);
+                               continue;
+                       }
+               }
+
+               if (state == FTS_LAST+1) {
+                       parse_warn(cfile, "corrupt lease file; "
+                                         "missing state in iaaddr");
+                       return;
+               }
+               if (end_time == -1) {
+                       parse_warn(cfile, "corrupt lease file; "
+                                         "missing end time in iaaddr");
+                       return;
+               }
+
+               iaaddr = NULL;
+               if (iaaddr_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+                       log_fatal("Out of memory.");
+               }
+               memcpy(&iaaddr->addr, iaddr.iabuf, sizeof(iaaddr->addr));
+               iaaddr->state = state;
+               iaaddr->valid_lifetime_end_time = end_time;
+
+               if (scope != NULL) {
+                       binding_scope_reference(&iaaddr->scope, scope, MDL);
+                       binding_scope_dereference(&scope, MDL);
+               }
+
+               /* add to our various structures */
+               ia_na_add_iaaddr(ia, iaaddr, MDL);
+               ia_na_reference(&iaaddr->ia_na, ia, MDL);
+               pool = NULL;
+               if (find_ipv6_pool(&pool, &iaaddr->addr) != ISC_R_SUCCESS) {
+                       inet_ntop(AF_INET6, &iaaddr->addr, 
+                                 addr_buf, sizeof(addr_buf));
+                       parse_warn(cfile, "no pool found for address %s", 
+                                  addr_buf);
+                       return;
+               }
+               add_lease6(pool, iaaddr, end_time);
+               ipv6_pool_dereference(&pool, MDL);
+               iaaddr_dereference(&iaaddr, MDL);
+       }
+
+       /*
+        * If we have an existing record for this IA_TA, remove it.
+        */
+       old_ia = NULL;
+       if (ia_na_hash_lookup(&old_ia, ia_ta_active,
+                             (unsigned char *)ia->iaid_duid.data,
+                             ia->iaid_duid.len, MDL)) {
+               ia_na_hash_delete(ia_ta_active, 
+                                 (unsigned char *)ia->iaid_duid.data,
+                                 ia->iaid_duid.len, MDL);
+               ia_na_dereference(&old_ia, MDL);
+       }
+
+       /*
+        * If we have addresses, add this, otherwise don't bother.
+        */
+       if (ia->num_iaaddr > 0) {
+               ia_na_hash_add(ia_ta_active, 
+                              (unsigned char *)ia->iaid_duid.data,
+                              ia->iaid_duid.len, ia, MDL);
+       }
+       ia_na_dereference(&ia, MDL);
+#endif /* defined(DHCPv6) */
+}
+
+void
+parse_ia_pd_declaration(struct parse *cfile) {
+#if !defined(DHCPv6)
+       parse_warn(cfile, "No DHCPv6 support.");
+       skip_to_semi(cfile);
+#else /* defined(DHCPv6) */
+       enum dhcp_token token;
+       struct ia_pd *ia_pd;
+       const char *val;
+       struct ia_pd *old_ia_pd;
+       unsigned int len;
+       u_int32_t iaid;
+       struct iaddr iaddr;
+       u_int8_t plen;
+       binding_state_t state;
+       TIME end_time;
+       struct iaprefix *iapref;
+       struct ipv6_ppool *ppool;
+       char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+       isc_boolean_t newbinding;
+       struct binding_scope *scope=NULL;
+       struct binding *bnd;
+       struct binding_value *nv=NULL;
+
+       token = next_token(&val, &len, cfile);
+       if (token != STRING) {
+               parse_warn(cfile, "corrupt lease file; "
+                                 "expecting an iaid+ia_pd string");
+               skip_to_semi(cfile);
+               return;
+       }
+       if (len < 5) {
+               parse_warn(cfile, "corrupt lease file; "
+                                 "iaid+ia_pd string too short");
+               skip_to_semi(cfile);
+               return;
+       }
+
+       memcpy(&iaid, val, 4);
+       ia_pd = NULL;
+       if (ia_pd_allocate(&ia_pd, iaid, val+4, len-4, MDL) != ISC_R_SUCCESS) {
+               log_fatal("Out of memory.");
+       }
+
+       token = next_token(&val, NULL, cfile);
+       if (token != LBRACE) {
+               parse_warn(cfile, "corrupt lease file; expecting left brace");
+               skip_to_semi(cfile);
+               return;
+       }
+
+       for (;;) {
+               token = next_token(&val, NULL, cfile);
+               if (token == RBRACE) break;
+
+               if (token != IAPREFIX) {
+                       parse_warn(cfile, "corrupt lease file; expecting "
+                                  "IAPREFIX or right brace");
+                       skip_to_semi(cfile);
+                       return;
+               }
+
+               if (!parse_ip6_prefix(cfile, &iaddr, &plen)) {
+                       parse_warn(cfile, "corrupt lease file; "
+                                         "expecting IPv6 prefix");
+                       skip_to_semi(cfile);
+                       return;
+               }
+
+               token = next_token(&val, NULL, cfile);
+               if (token != LBRACE) {
+                       parse_warn(cfile, "corrupt lease file; "
+                                         "expecting left brace");
+                       skip_to_semi(cfile);
+                       return;
+               }
+
+               state = FTS_LAST+1;
+               end_time = -1;
+               for (;;) {
+                       token = next_token(&val, NULL, cfile);
+                       if (token == RBRACE) break;
+
+                       switch(token) {
+                               /* Prefix binding state. */
+                            case BINDING:
+                               token = next_token(&val, NULL, cfile);
+                               if (token != STATE) {
+                                       parse_warn(cfile, "corrupt lease file; "
+                                                         "expecting state");
+                                       skip_to_semi(cfile);
+                                       return;
+                               }
+                               token = next_token(&val, NULL, cfile);
+                               switch (token) {
+                                       case TOKEN_ABANDONED:
+                                               state = FTS_ABANDONED;
+                                               break;
+                                       case TOKEN_FREE:
+                                               state = FTS_FREE;
+                                               break;
+                                       case TOKEN_ACTIVE:
+                                               state = FTS_ACTIVE;
+                                               break;
+                                       case TOKEN_EXPIRED:
+                                               state = FTS_EXPIRED;
+                                               break;
+                                       case TOKEN_RELEASED:
+                                               state = FTS_RELEASED;
+                                               break;
+                                       default:
+                                               parse_warn(cfile,
+                                                          "corrupt lease "
+                                                          "file; "
+                                                          "expecting a "
+                                                          "binding state.");
+                                               skip_to_semi(cfile);
+                                               return;
+                               }
+
+                               token = next_token(&val, NULL, cfile);
+                               if (token != SEMI) {
+                                       parse_warn(cfile, "corrupt lease file; "
+                                                         "expecting "
+                                                         "semicolon.");
+                               }
+                               break;
+
+                               /* Prefix expiration time. */
+                             case ENDS:
+                               end_time = parse_date(cfile);
+                               break;
+
+                               /* Prefix binding scopes. */
+                             case TOKEN_SET:
+                               token = next_token(&val, NULL, cfile);
+                               if ((token != NAME) &&
+                                   (token != NUMBER_OR_NAME)) {
+                                       parse_warn(cfile, "%s is not a valid "
+                                                         "variable name",
+                                                  val);
+                                       skip_to_semi(cfile);
+                                       continue;
+                               }
+
+                               if (scope != NULL)
+                                       bnd = find_binding(scope, val);
+                               else {
+                                       if (!binding_scope_allocate(&scope,
+                                                                   MDL)) {
+                                               log_fatal("Out of memory for "
+                                                         "lease binding "
+                                                         "scope.");
+                                       }
+
+                                       bnd = NULL;
+                               }
+
+                               if (bnd == NULL) {
+                                       bnd = dmalloc(sizeof(*bnd),
+                                                         MDL);
+                                       if (bnd == NULL) {
+                                               log_fatal("No memory for "
+                                                         "prefix binding.");
+                                       }
+
+                                       bnd->name = dmalloc(strlen(val) + 1,
+                                                           MDL);
+                                       if (bnd->name == NULL) {
+                                               log_fatal("No memory for "
+                                                         "binding name.");
+                                       }
+                                       strcpy(bnd->name, val);
+
+                                       newbinding = ISC_TRUE;
+                               } else {
+                                       newbinding = ISC_FALSE;
+                               }
+
+                               if (!binding_value_allocate(&nv, MDL)) {
+                                       log_fatal("no memory for binding "
+                                                 "value.");
+                               }
+
+                               token = next_token(NULL, NULL, cfile);
+                               if (token != EQUAL) {
+                                       parse_warn(cfile, "expecting '=' in "
+                                                         "set statement.");
+                                       goto binding_err;
+                               }
+
+                               if (!parse_binding_value(cfile, nv)) {
+                                     binding_err:
+                                       binding_value_dereference(&nv, MDL);
+                                       binding_scope_dereference(&scope, MDL);
+                                       return;
+                               }
+
+                               if (newbinding) {
+                                       binding_value_reference(&bnd->value,
+                                                               nv, MDL);
+                                       bnd->next = scope->bindings;
+                                       scope->bindings = bnd;
+                               } else {
+                                       binding_value_dereference(&bnd->value,
+                                                                 MDL);
+                                       binding_value_reference(&bnd->value,
+                                                               nv, MDL);
+                               }
+
+                               binding_value_dereference(&nv, MDL);
+                               parse_semi(cfile);
+                               break;
+
+                             default:
+                               parse_warn(cfile, "corrupt lease file; "
+                                                 "expecting ia_pd contents, "
+                                                 "got '%s'", val);
+                               skip_to_semi(cfile);
+                               continue;
+                       }
+               }
+
+               if (state == FTS_LAST+1) {
+                       parse_warn(cfile, "corrupt lease file; "
+                                         "missing state in iaprefix");
+                       return;
+               }
+               if (end_time == -1) {
+                       parse_warn(cfile, "corrupt lease file; "
+                                         "missing end time in iaprefix");
+                       return;
+               }
+
+               iapref = NULL;
+               if (iaprefix_allocate(&iapref, MDL) != ISC_R_SUCCESS) {
+                       log_fatal("Out of memory.");
+               }
+               memcpy(&iapref->pref, iaddr.iabuf, sizeof(iapref->pref));
+               iapref->plen = plen;
+               iapref->state = state;
+               iapref->valid_lifetime_end_time = end_time;
+
+               if (scope != NULL) {
+                       binding_scope_reference(&iapref->scope, scope, MDL);
+                       binding_scope_dereference(&scope, MDL);
+               }
+
+               /* add to our various structures */
+               ia_pd_add_iaprefix(ia_pd, iapref, MDL);
+               ia_pd_reference(&iapref->ia_pd, ia_pd, MDL);
+               ppool = NULL;
+               if (find_ipv6_ppool(&ppool, &iapref->pref) != ISC_R_SUCCESS) {
+                       inet_ntop(AF_INET6, &iapref->pref, 
+                                 addr_buf, sizeof(addr_buf));
+                       parse_warn(cfile, "no ppool found for address %s", 
+                                  addr_buf);
+                       return;
+               }
+               add_prefix6(ppool, iapref, end_time);
+               ipv6_ppool_dereference(&ppool, MDL);
+               iaprefix_dereference(&iapref, MDL);
+       }
+
+       /*
+        * If we have an existing record for this IA_PD, remove it.
+        */
+       old_ia_pd = NULL;
+       if (ia_pd_hash_lookup(&old_ia_pd, ia_pd_active,
+                             (unsigned char *)ia_pd->iaid_duid.data,
+                             ia_pd->iaid_duid.len, MDL)) {
+               ia_pd_hash_delete(ia_pd_active,
+                                 (unsigned char *)ia_pd->iaid_duid.data,
+                                 ia_pd->iaid_duid.len, MDL);
+               ia_pd_dereference(&old_ia_pd, MDL);
+       }
+
+       /*
+        * If we have prefixes, add this, otherwise don't bother.
+        */
+       if (ia_pd->num_iaprefix > 0) {
+               ia_pd_hash_add(ia_pd_active, 
+                              (unsigned char *)ia_pd->iaid_duid.data,
+                              ia_pd->iaid_duid.len, ia_pd, MDL);
+       }
+       ia_pd_dereference(&ia_pd, MDL);
 #endif /* defined(DHCPv6) */
 }
 
index 3c92c5fa901ec940c23ace7c197f6088ffb30558..424a3abfe05aa2628a456ee73a6bc27f42c383b6 100644 (file)
@@ -497,10 +497,10 @@ int write_group (group)
 }
 
 /*
- * Write an IA_NA and the options it has.
+ * Write an IA and the options it has.
  */
 int
-write_ia_na(const struct ia_na *ia_na) {
+write_ia(const struct ia_na *ia) {
        struct iaaddr *iaaddr;
        struct binding *bnd;
        int i;
@@ -525,24 +525,32 @@ write_ia_na(const struct ia_na *ia_na) {
        }
 
        
-       s = quotify_buf(ia_na->iaid_duid.data, ia_na->iaid_duid.len, MDL);
+       s = quotify_buf(ia->iaid_duid.data, ia->iaid_duid.len, MDL);
        if (s == NULL) {
                goto error_exit;
        }
-       fprintf_ret = fprintf(db_file, "ia-na \"%s\" {\n", s);
+       if (ia->ia_type == D6O_IA_NA) {
+               fprintf_ret = fprintf(db_file, "ia-na \"%s\" {\n", s);
+       } else if (ia->ia_type == D6O_IA_TA) {
+               fprintf_ret = fprintf(db_file, "ia-ta \"%s\" {\n", s);
+       } else {
+               log_error("Unknown ia type %u at %s:%d",
+                         (unsigned)ia->ia_type, MDL);
+               goto error_exit;
+       }
        dfree(s, MDL);
        if (fprintf_ret < 0) {
                goto error_exit;
        }
-       for (i=0; i<ia_na->num_iaaddr; i++) {
-               iaaddr = ia_na->iaaddr[i];
+       for (i=0; i<ia->num_iaaddr; i++) {
+               iaaddr = ia->iaaddr[i];
 
                inet_ntop(AF_INET6, &iaaddr->addr, addr_buf, sizeof(addr_buf));
                if (fprintf(db_file, "  iaaddr %s {\n", addr_buf) < 0) {
                        goto error_exit;
                }
                if ((iaaddr->state <= 0) || (iaaddr->state > FTS_LAST)) {
-                       log_fatal("Unknown ia_na state %d at %s:%d", 
+                       log_fatal("Unknown iaaddr state %d at %s:%d", 
                                  iaaddr->state, MDL);
                }
                binding_state = binding_state_names[iaaddr->state-1];
@@ -593,7 +601,110 @@ write_ia_na(const struct ia_na *ia_na) {
        return 1;
 
 error_exit:
-       log_info("write_ia_na: unable to write ia-na");
+       log_info("write_ia: unable to write ia-na");
+       lease_file_is_corrupt = 1;
+       return 0;
+}
+
+/*
+ * Write an IA_PD and the options it has.
+ */
+int
+write_ia_pd(const struct ia_pd *ia_pd) {
+       struct iaprefix *iapref;
+       struct binding *bnd;
+       int i;
+       char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff.255.255.255.255")];
+       const char *binding_state;
+       const char *tval;
+       char *s;
+       int fprintf_ret;
+
+       /* 
+        * If the lease file is corrupt, don't try to write any more 
+        * leases until we've written a good lease file. 
+        */
+       if (lease_file_is_corrupt) {
+               if (!new_lease_file()) {
+                       return 0;
+               }
+       }
+
+       if (counting) {
+               ++count;
+       }
+
+       
+       s = quotify_buf(ia_pd->iaid_duid.data, ia_pd->iaid_duid.len, MDL);
+       if (s == NULL) {
+               goto error_exit;
+       }
+       fprintf_ret = fprintf(db_file, "ia-pd \"%s\" {\n", s);
+       dfree(s, MDL);
+       if (fprintf_ret < 0) {
+               goto error_exit;
+       }
+       for (i=0; i<ia_pd->num_iaprefix; i++) {
+               iapref = ia_pd->iaprefix[i];
+
+               inet_ntop(AF_INET6, &iapref->pref, addr_buf, sizeof(addr_buf));
+               if (fprintf(db_file, "  iaprefix %s/%d {\n",
+                           addr_buf, (int)iapref->plen) < 0) {
+                       goto error_exit;
+               }
+               if ((iapref->state <= 0) || (iapref->state > FTS_LAST)) {
+                       log_fatal("Unknown iaprefix state %d at %s:%d", 
+                                 iapref->state, MDL);
+               }
+               binding_state = binding_state_names[iapref->state-1];
+               if (fprintf(db_file, "    binding state %s;\n", 
+                           binding_state) < 0) {
+                       goto error_exit;
+               }
+
+               /* 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 (tval == NULL) {
+                       goto error_exit;
+               }
+               if (fprintf(db_file, "    ends %s", tval) < 0) {
+                       goto error_exit;
+               }
+
+               /* Write out any binding scopes: note that 'ends' above does
+                * not have \n on the end!  We want that.
+                */
+               if (iapref->scope != NULL)
+                       bnd = iapref->scope->bindings;
+               else
+                       bnd = NULL;
+
+               for (; bnd != NULL ; bnd = bnd->next) {
+                       if (bnd->value == NULL)
+                               continue;
+
+                       /* We don't do a regular error_exit because the
+                        * lease db is not corrupt in this case.
+                        */
+                       if (write_binding_scope(db_file, bnd,
+                                               "\n    ") != ISC_R_SUCCESS)
+                               goto error_exit;
+                               
+               }
+
+               if (fprintf(db_file, "\n  }\n") < 0)
+                        goto error_exit;
+       }
+       if (fprintf(db_file, "}\n\n") < 0)
+                goto error_exit;
+
+       fflush(db_file);
+       return 1;
+
+error_exit:
+       log_info("write_ia_pd: unable to write ia-pd");
        lease_file_is_corrupt = 1;
        return 0;
 }
index 904d50d6e72ae8d664c047449357b878a5bcad77..41e3813ad10e35ec23bc82bd9dd335239f6ebf84 100644 (file)
@@ -560,9 +560,15 @@ main(int argc, char **argv) {
 #endif
 
 #ifdef DHCPv6
-       /* set up DHCPv6 hash */
-       if (!ia_na_new_hash(&ia_active, DEFAULT_HASH_SIZE, MDL)) {
-               log_fatal("Out of memory creating hash for active IA.");
+       /* set up DHCPv6 hashes */
+       if (!ia_na_new_hash(&ia_na_active, DEFAULT_HASH_SIZE, MDL)) {
+               log_fatal("Out of memory creating hash for active IA_NA.");
+       }
+       if (!ia_na_new_hash(&ia_ta_active, DEFAULT_HASH_SIZE, MDL)) {
+               log_fatal("Out of memory creating hash for active IA_TA.");
+       }
+       if (!ia_pd_new_hash(&ia_pd_active, DEFAULT_HASH_SIZE, MDL)) {
+               log_fatal("Out of memory creating hash for active IA_PD.");
        }
 #endif /* DHCPv6 */
 
index 6a34a96544f82e40b62983a79827d88e8c3373bc..94dbe4119d3c03ee5e926323526b1f3411e125c0 100644 (file)
@@ -49,11 +49,11 @@ struct reply_state {
 
        /* IA level persistent state */
        unsigned ia_count;
-       unsigned client_addresses;
+       unsigned client_resources;
        isc_boolean_t ia_addrs_included;
        isc_boolean_t static_lease;
        struct ia_na *ia_na;
-       struct ia_na *old_ia;
+       struct ia_na *old_ia_na;
        struct option_state *reply_ia;
        struct data_string fixed;
 
@@ -96,8 +96,8 @@ static void seek_shared_host(struct host_decl **hp,
                             struct shared_network *shared);
 static isc_boolean_t fixed_matches_shared(struct host_decl *host,
                                          struct shared_network *shared);
-static isc_result_t reply_process_ia(struct reply_state *reply,
-                                    struct option_cache *ia);
+static isc_result_t reply_process_ia_na(struct reply_state *reply,
+                                       struct option_cache *ia);
 static isc_result_t reply_process_addr(struct reply_state *reply,
                                       struct option_cache *addr);
 static isc_boolean_t address_is_owned(struct reply_state *reply,
@@ -592,6 +592,14 @@ valid_client_info_req(struct packet *packet, struct data_string *server_id) {
                          piaddr(packet->client_addr), client_id_str);
                goto exit;
        }
+       oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_PD);
+       if (oc != NULL) {
+               log_debug("Discarding %s from %s; "
+                         "IA_PD option present%s", 
+                         dhcpv6_type_names[packet->dhcpv6_msg_type],
+                         piaddr(packet->client_addr), client_id_str);
+               goto exit;
+       }
 
        oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
        if (oc != NULL) {
@@ -649,6 +657,7 @@ static const int required_opts_solicit[] = {
        D6O_SERVERID,
        D6O_IA_NA,
        D6O_IA_TA,
+       D6O_IA_PD,
        D6O_RAPID_COMMIT,
        D6O_STATUS_CODE,
        D6O_VENDOR_OPTS,
@@ -662,6 +671,20 @@ static const int required_opts_IA_NA[] = {
        D6O_VENDOR_OPTS,
        0
 };
+/*
+static const int required_opts_IA_TA[] = {
+       D6O_IAADDR,
+       D6O_STATUS_CODE,
+       D6O_VENDOR_OPTS,
+       0
+};
+static const int required_opts_IA_PD[] = {
+       D6O_IAPREFIX,
+       D6O_STATUS_CODE,
+       D6O_VENDOR_OPTS,
+       0
+};
+*/
 static const int required_opts_STATUS_CODE[] = {
        D6O_STATUS_CODE,
        0
@@ -1090,11 +1113,11 @@ lease_to_client(struct data_string *reply_ret,
        for (; oc != NULL ; oc = oc->next) {
                isc_result_t status;
 
-               /* Start counting addresses offered. */
-               reply.client_addresses = 0;
+               /* Start counting resources (addresses) offered. */
+               reply.client_resources = 0;
                reply.ia_addrs_included = ISC_FALSE;
 
-               status = reply_process_ia(&reply, oc);
+               status = reply_process_ia_na(&reply, oc);
 
                /*
                 * We continue to try other IA's whether we can address
@@ -1108,10 +1131,12 @@ lease_to_client(struct data_string *reply_ret,
                 * If any address can be given to any IA, then do not set the
                 * NoAddrsAvail status code.
                 */
-               if (reply.client_addresses == 0)
+               if (reply.client_resources == 0)
                        no_addrs_avail = ISC_TRUE;
        }
 
+       /* Do IA_TA and IA_PD */
+
        /*
         * Make no reply if we gave no resources and is not
         * for Information-Request.
@@ -1150,7 +1175,7 @@ lease_to_client(struct data_string *reply_ret,
         * INTERPRETATION;
         *
         * Solicit and Request are fairly explicit; we send NoAddrsAvail.
-        * We handle SOLICIT here and REQUEST in the reply_process_ia()
+        * We handle SOLICIT here and REQUEST in the reply_process_ia_na()
         * function (because SOLICIT only counts if we never get around to
         * it).
         *
@@ -1237,7 +1262,7 @@ lease_to_client(struct data_string *reply_ret,
  * the reply packet being built in the reply_state structure.
  */
 static isc_result_t
-reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
+reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
        isc_result_t status = ISC_R_SUCCESS;
        u_int32_t iaid;
        unsigned ia_cursor;
@@ -1257,7 +1282,7 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
 
        /* Make sure there is at least room for the header. */
        if ((reply->cursor + IA_NA_OFFSET + 4) > sizeof(reply->buf)) {
-               log_error("reply_process_ia: Reply too long for IA.");
+               log_error("reply_process_ia_na: Reply too long for IA.");
                return ISC_R_NOSPACE;
        }
 
@@ -1265,7 +1290,7 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
        /* Fetch the IA_NA contents. */
        if (!get_encapsulated_IA_state(&packet_ia, &ia_data, reply->packet,
                                       ia, IA_NA_OFFSET)) {
-               log_error("reply_process_ia: error evaluating ia_na");
+               log_error("reply_process_ia_na: error evaluating ia_na");
                status = ISC_R_FAILURE;
                goto cleanup;
        }
@@ -1282,9 +1307,10 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
                status = ISC_R_NOMEMORY;
                goto cleanup;
        }
+       reply->ia_na->ia_type = D6O_IA_NA;
 
        /* Cache pre-existing IA, if any. */
-       ia_na_hash_lookup(&reply->old_ia, ia_active,
+       ia_na_hash_lookup(&reply->old_ia_na, ia_na_active,
                          (unsigned char *)reply->ia_na->iaid_duid.data,
                          reply->ia_na->iaid_duid.len, MDL);
 
@@ -1302,14 +1328,14 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
                if (!evaluate_option_cache(&reply->fixed, NULL, NULL, NULL,
                                           NULL, NULL, &global_scope,
                                           reply->host->fixed_addr, MDL)) {
-                       log_error("reply_process_ia: unable to evaluate "
+                       log_error("reply_process_ia_na: unable to evaluate "
                                  "fixed address.");
                        status = ISC_R_FAILURE;
                        goto cleanup;
                }
 
                if (reply->fixed.len < 16) {
-                       log_error("reply_process_ia: invalid fixed address.");
+                       log_error("reply_process_ia_na: invalid fixed address.");
                        status = ISC_R_INVALIDARG;
                        goto cleanup;
                }
@@ -1375,7 +1401,7 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
         * If we fell through the above and never gave the client
         * an address, give it one now.
         */
-       if ((status != ISC_R_CANCELED) && (reply->client_addresses == 0)) {
+       if ((status != ISC_R_CANCELED) && (reply->client_resources == 0)) {
                status = find_client_address(reply);
 
                if (status == ISC_R_NORESOURCES) {
@@ -1401,7 +1427,7 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
                                if (!option_state_allocate(&reply->reply_ia,
                                                           MDL))
                                {
-                                       log_error("reply_process_ia: No "
+                                       log_error("reply_process_ia_na: No "
                                                  "memory for option state "
                                                  "wipe.");
                                        status = ISC_R_NOMEMORY;
@@ -1412,7 +1438,7 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
                                                     "No addresses available "
                                                     "for this interface.",
                                                      reply->reply_ia)) {
-                                       log_error("reply_process_ia: Unable "
+                                       log_error("reply_process_ia_na: Unable "
                                                  "to set NoAddrsAvail status "
                                                  "code.");
                                        status = ISC_R_FAILURE;
@@ -1551,20 +1577,20 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
                }
 
                /* Remove any old ia_na from the hash. */
-               if (reply->old_ia != NULL) {
-                       ia_id = &reply->old_ia->iaid_duid;
-                       ia_na_hash_delete(ia_active,
+               if (reply->old_ia_na != NULL) {
+                       ia_id = &reply->old_ia_na->iaid_duid;
+                       ia_na_hash_delete(ia_na_active,
                                          (unsigned char *)ia_id->data,
                                          ia_id->len, MDL);
-                       ia_na_dereference(&reply->old_ia, MDL);
+                       ia_na_dereference(&reply->old_ia_na, MDL);
                }
 
                /* Put new ia_na into the hash. */
                ia_id = &reply->ia_na->iaid_duid;
-               ia_na_hash_add(ia_active, (unsigned char *)ia_id->data,
+               ia_na_hash_add(ia_na_active, (unsigned char *)ia_id->data,
                               ia_id->len, reply->ia_na, MDL);
 
-               write_ia_na(reply->ia_na);
+               write_ia(reply->ia_na);
 
                /* 
                 * Note that we wrote the lease into the database,
@@ -1578,8 +1604,8 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
         * 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)) {
+           (reply->old_ia_na != NULL)) {
+               if (ia_na_equal(reply->old_ia_na, reply->ia_na)) {
                        lease_in_database = ISC_TRUE;
                }
        }
@@ -1595,8 +1621,8 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) {
                data_string_forget(&data, MDL);
        if (reply->ia_na != NULL)
                ia_na_dereference(&reply->ia_na, MDL);
-       if (reply->old_ia != NULL)
-               ia_na_dereference(&reply->old_ia, MDL);
+       if (reply->old_ia_na != NULL)
+               ia_na_dereference(&reply->old_ia_na, MDL);
        if (reply->lease != NULL) {
                if (!lease_in_database) {
                        release_lease6(reply->lease->ipv6_pool, reply->lease);
@@ -1837,12 +1863,12 @@ reply_process_addr(struct reply_state *reply, struct option_cache *addr) {
        }
 
        /*
-        * If client_addresses is nonzero, then the reply_process_is_addressed
+        * If client_resources is nonzero, then the reply_process_is_addressed
         * function has executed configuration state into the reply option
         * cache.  We will use that valid cache to derive configuration for
         * whether or not to engage in additional addresses, and similar.
         */
-       if (reply->client_addresses != 0) {
+       if (reply->client_resources != 0) {
                unsigned limit = 1;
 
                /*
@@ -1859,7 +1885,7 @@ reply_process_addr(struct reply_state *reply, struct option_cache *addr) {
                                                   reply->opt_state,
                                                   scope, oc, MDL) ||
                            (data.len != 4)) {
-                               log_error("reply_process_ia: unable to "
+                               log_error("reply_process_addr: unable to "
                                          "evaluate addrs-per-ia value.");
                                status = ISC_R_FAILURE;
                                goto cleanup;
@@ -1873,7 +1899,7 @@ reply_process_addr(struct reply_state *reply, struct option_cache *addr) {
                 * If we wish to limit the client to a certain number of
                 * addresses, then omit the address from the reply.
                 */
-               if (reply->client_addresses >= limit)
+               if (reply->client_resources >= limit)
                        goto cleanup;
        }
 
@@ -1918,13 +1944,13 @@ address_is_owned(struct reply_state *reply, struct iaddr *addr) {
                return ISC_FALSE;
        }
 
-       if ((reply->old_ia == NULL) || (reply->old_ia->num_iaaddr == 0))
+       if ((reply->old_ia_na == NULL) || (reply->old_ia_na->num_iaaddr == 0))
                return ISC_FALSE;
 
-       for (i = 0 ; i < reply->old_ia->num_iaaddr ; i++) {
+       for (i = 0 ; i < reply->old_ia_na->num_iaaddr ; i++) {
                struct iaaddr *tmp;
 
-               tmp = reply->old_ia->iaaddr[i];
+               tmp = reply->old_ia_na->iaaddr[i];
 
                if (memcmp(addr->iabuf, &tmp->addr, 16) == 0) {
                        iaaddr_reference(&reply->lease, tmp, MDL);
@@ -1998,9 +2024,9 @@ find_client_address(struct reply_state *reply) {
                goto send_addr;
        }
 
-       if (reply->old_ia != NULL)  {
-               for (i = 0 ; i < reply->old_ia->num_iaaddr ; i++) {
-                       lease = reply->old_ia->iaaddr[i];
+       if (reply->old_ia_na != NULL)  {
+               for (i = 0 ; i < reply->old_ia_na->num_iaaddr ; i++) {
+                       lease = reply->old_ia_na->iaaddr[i];
 
                        best_lease = lease_compare(lease, best_lease);
                }
@@ -2082,7 +2108,7 @@ reply_process_is_addressed(struct reply_state *reply,
                                           reply->opt_state,
                                           scope, oc, MDL) ||
                    (data.len != 4)) {
-                       log_error("reply_process_ia: unable to "
+                       log_error("reply_process_is_addressed: unable to "
                                  "evaluate default lease time");
                        status = ISC_R_FAILURE;
                        goto cleanup;
@@ -2109,7 +2135,7 @@ reply_process_is_addressed(struct reply_state *reply,
                                           reply->opt_state,
                                           scope, oc, MDL) ||
                    (data.len != 4)) {
-                       log_error("reply_process_ia: unable to "
+                       log_error("reply_process_is_addressed: unable to "
                                  "evaluate preferred lease time");
                        status = ISC_R_FAILURE;
                        goto cleanup;
@@ -2173,7 +2199,7 @@ reply_process_is_addressed(struct reply_state *reply,
                data_string_forget(&data, MDL);
 
        if (status == ISC_R_SUCCESS)
-               reply->client_addresses++;
+               reply->client_resources++;
 
        return status;
 }
@@ -2189,7 +2215,7 @@ reply_process_send_addr(struct reply_state *reply, struct iaddr *addr) {
        /* Now append the lease. */
        data.len = IAADDR_OFFSET;
        if (!buffer_allocate(&data.buffer, data.len, MDL)) {
-               log_error("reply_process_ia: out of memory allocating "
+               log_error("reply_process_send_addr: out of memory allocating "
                          "new IAADDR buffer.");
                status = ISC_R_NOMEMORY;
                goto cleanup;
@@ -2203,7 +2229,7 @@ reply_process_send_addr(struct reply_state *reply, struct iaddr *addr) {
        if (!append_option_buffer(&dhcpv6_universe, reply->reply_ia,
                                  data.buffer, data.buffer->data,
                                  data.len, D6O_IAADDR, 0)) {
-               log_error("reply_process_ia: unable to save IAADDR "
+               log_error("reply_process_send_addr: unable to save IAADDR "
                          "option");
                status = ISC_R_FAILURE;
                goto cleanup;
@@ -2695,7 +2721,7 @@ ia_na_match_decline(const struct data_string *client_id,
                            tmp_addr, sizeof(tmp_addr)));
        if (lease != NULL) {
                decline_lease6(lease->ipv6_pool, lease);
-               write_ia_na(lease->ia_na);
+               write_ia(lease->ia_na);
        }
 }
 
@@ -2971,16 +2997,16 @@ iterate_over_ia_na(struct data_string *reply_ret,
                        /*
                         * Find existing IA_NA.
                         */
-                       if (ia_na_make_key(&key, iaid, 
-                                          (char *)client_id->data,
-                                          client_id->len, 
-                                          MDL) != ISC_R_SUCCESS) {
+                       if (ia_make_key(&key, iaid, 
+                                       (char *)client_id->data,
+                                       client_id->len, 
+                                       MDL) != ISC_R_SUCCESS) {
                                log_fatal("iterate_over_ia_na: no memory for "
                                          "key.");
                        }
 
                        existing_ia_na = NULL;
-                       if (ia_na_hash_lookup(&existing_ia_na, ia_active, 
+                       if (ia_na_hash_lookup(&existing_ia_na, ia_na_active, 
                                              (unsigned char *)key.data, 
                                              key.len, MDL)) {
                                /* 
@@ -3103,7 +3129,7 @@ ia_na_match_release(const struct data_string *client_id,
                 inet_ntop(AF_INET6, iaaddr->data, tmp_addr, sizeof(tmp_addr)));
        if (lease != NULL) {
                release_lease6(lease->ipv6_pool, lease);
-               write_ia_na(lease->ia_na);
+               write_ia(lease->ia_na);
        }
 }
 
index 70b6c7f2f2cec2b3fe7180c14ffcff3cfd7d876d..0c08ea930d96120eed14a0abb7a54ede2cc66df0 100644 (file)
 HASH_FUNCTIONS(ia_na, unsigned char *, struct ia_na, ia_na_hash_t,
               ia_na_reference, ia_na_dereference, do_string_hash);
 
-ia_na_hash_t *ia_active;
+ia_na_hash_t *ia_na_active;
+ia_na_hash_t *ia_ta_active;
+
+HASH_FUNCTIONS(ia_pd, unsigned char *, struct ia_pd, ia_pd_hash_t,
+              ia_pd_reference, ia_pd_dereference, do_string_hash);
+
+ia_pd_hash_t *ia_pd_active;
 
 HASH_FUNCTIONS(iaaddr, struct in6_addr *, struct iaaddr, iaaddr_hash_t,
               iaaddr_reference, iaaddr_dereference, do_string_hash);
 
+HASH_FUNCTIONS(iaprefix, struct in6_addr *, struct iaprefix, iaprefix_hash_t,
+              iaprefix_reference, iaprefix_dereference, do_string_hash);
+
 struct ipv6_pool **pools;
 int num_pools;
 
+struct ipv6_ppool **ppools;
+int num_ppools;
+
 /*
  * Create a new IAADDR structure.
  *
@@ -138,13 +150,111 @@ iaaddr_dereference(struct iaaddr **iaaddr, const char *file, int line) {
        return ISC_R_SUCCESS;
 }
 
+/*
+ * Create a new IAPREFIX structure.
+ *
+ * - iapref must be a pointer to a (struct iaprefix *) pointer previously
+ *   initialized to NULL
+ */
+isc_result_t
+iaprefix_allocate(struct iaprefix **iapref, const char *file, int line) {
+       struct iaprefix *tmp;
+
+       if (iapref == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       if (*iapref != NULL) {
+               log_error("%s(%d): non-NULL pointer", file, line);
+               return ISC_R_INVALIDARG;
+       }
+
+       tmp = dmalloc(sizeof(*tmp), file, line);
+       if (tmp == NULL) {
+               return ISC_R_NOMEMORY;
+       }
+
+       tmp->refcnt = 1;
+       tmp->state = FTS_FREE;
+       tmp->heap_index = -1;
+
+       *iapref = tmp;
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IAPREFIX structure.
+ *
+ * - iapref must be a pointer to a (struct iaprefix *) pointer previously
+ *   initialized to NULL
+ */
+isc_result_t
+iaprefix_reference(struct iaprefix **iapref, struct iaprefix *src,
+                const char *file, int line) {
+       if (iapref == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       if (*iapref != NULL) {
+               log_error("%s(%d): non-NULL pointer", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       if (src == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       *iapref = src;
+       src->refcnt++;
+       return ISC_R_SUCCESS;
+}
+
+
+/*
+ * Dereference an IAPREFIX structure.
+ *
+ * If it is the last reference, then the memory for the 
+ * structure is freed.
+ */
+isc_result_t
+iaprefix_dereference(struct iaprefix **iapref, const char *file, int line) {
+       struct iaprefix *tmp;
+
+       if ((iapref == NULL) || (*iapref == NULL)) {
+               log_error("%s(%d): NULL pointer", file, line);
+               return ISC_R_INVALIDARG;
+       }
+
+       tmp = *iapref;
+       *iapref = NULL;
+
+       tmp->refcnt--;
+       if (tmp->refcnt < 0) {
+               log_error("%s(%d): negative refcnt", file, line);
+               tmp->refcnt = 0;
+       }
+       if (tmp->refcnt == 0) {
+               if (tmp->ia_pd != NULL) {
+                       ia_pd_dereference(&(tmp->ia_pd), file, line);
+               }
+               if (tmp->ipv6_ppool != NULL) {
+                       ipv6_ppool_dereference(&(tmp->ipv6_ppool), file, line);
+               }
+               if (tmp->scope != NULL) {
+                       binding_scope_dereference(&tmp->scope, file, line);
+               }
+               dfree(tmp, file, line);
+       }
+
+       return ISC_R_SUCCESS;
+}
+
 /* 
- * Make the key that we use for IA_NA.
+ * Make the key that we use for IA.
  */
 isc_result_t
-ia_na_make_key(struct data_string *key, u_int32_t iaid,
-              const char *duid, unsigned int duid_len,
-              const char *file, int line) {
+ia_make_key(struct data_string *key, u_int32_t iaid,
+           const char *duid, unsigned int duid_len,
+           const char *file, int line) {
 
        memset(key, 0, sizeof(*key));
        key->len = duid_len + sizeof(iaid);
@@ -159,9 +269,9 @@ ia_na_make_key(struct data_string *key, u_int32_t iaid,
 }
 
 /*
- * Create a new IA_NA structure.
+ * Create a new IA structure.
  *
- * - ia_na must be a pointer to a (struct ia_na *) pointer previously
+ * - ia must be a pointer to a (struct ia_na *) pointer previously
  *   initialized to NULL
  * - iaid and duid are values from the client
  *
@@ -170,16 +280,16 @@ ia_na_make_key(struct data_string *key, u_int32_t iaid,
  *        between machines of different byte order
  */
 isc_result_t
-ia_na_allocate(struct ia_na **ia_na, u_int32_t iaid, 
+ia_na_allocate(struct ia_na **ia, u_int32_t iaid, 
               const char *duid, unsigned int duid_len,
               const char *file, int line) {
        struct ia_na *tmp;
 
-       if (ia_na == NULL) {
+       if (ia == NULL) {
                log_error("%s(%d): NULL pointer reference", file, line);
                return ISC_R_INVALIDARG;
        }
-       if (*ia_na != NULL) {
+       if (*ia != NULL) {
                log_error("%s(%d): non-NULL pointer", file, line);
                return ISC_R_INVALIDARG;
        }
@@ -189,32 +299,32 @@ ia_na_allocate(struct ia_na **ia_na, u_int32_t iaid,
                return ISC_R_NOMEMORY;
        }
 
-       if (ia_na_make_key(&tmp->iaid_duid, iaid, 
-                          duid, duid_len, file, line) != ISC_R_SUCCESS) {
+       if (ia_make_key(&tmp->iaid_duid, iaid, 
+                       duid, duid_len, file, line) != ISC_R_SUCCESS) {
                dfree(tmp, file, line);
                return ISC_R_NOMEMORY;
        }
 
        tmp->refcnt = 1;
 
-       *ia_na = tmp;
+       *ia = tmp;
        return ISC_R_SUCCESS;
 }
 
 /*
- * Reference an IA_NA structure.
+ * Reference an IA structure.
  *
- * - ia_na must be a pointer to a (struct ia_na *) pointer previously
+ * - ia must be a pointer to a (struct ia_na *) pointer previously
  *   initialized to NULL
  */
 isc_result_t
-ia_na_reference(struct ia_na **ia_na, struct ia_na *src,
+ia_na_reference(struct ia_na **ia, struct ia_na *src,
                const char *file, int line) {
-       if (ia_na == NULL) {
+       if (ia == NULL) {
                log_error("%s(%d): NULL pointer reference", file, line);
                return ISC_R_INVALIDARG;
        }
-       if (*ia_na != NULL) {
+       if (*ia != NULL) {
                log_error("%s(%d): non-NULL pointer", file, line);
                return ISC_R_INVALIDARG;
        }
@@ -222,29 +332,29 @@ ia_na_reference(struct ia_na **ia_na, struct ia_na *src,
                log_error("%s(%d): NULL pointer reference", file, line);
                return ISC_R_INVALIDARG;
        }
-       *ia_na = src;
+       *ia = src;
        src->refcnt++;
        return ISC_R_SUCCESS;
 }
 
 /*
- * Dereference an IA_NA structure.
+ * Dereference an IA structure.
  *
  * If it is the last reference, then the memory for the 
  * structure is freed.
  */
 isc_result_t
-ia_na_dereference(struct ia_na **ia_na, const char *file, int line) {
+ia_na_dereference(struct ia_na **ia, const char *file, int line) {
        struct ia_na *tmp;
        int i;
 
-       if ((ia_na == NULL) || (*ia_na == NULL)) {
+       if ((ia == NULL) || (*ia == NULL)) {
                log_error("%s(%d): NULL pointer", file, line);
                return ISC_R_INVALIDARG;
        }
 
-       tmp = *ia_na;
-       *ia_na = NULL;
+       tmp = *ia;
+       *ia = NULL;
 
        tmp->refcnt--;
        if (tmp->refcnt < 0) {
@@ -267,10 +377,10 @@ ia_na_dereference(struct ia_na **ia_na, const char *file, int line) {
 
 
 /*
- * Add an IAADDR entry to an IA_NA structure.
+ * Add an IAADDR entry to an IA structure.
  */
 isc_result_t
-ia_na_add_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr, 
+ia_na_add_iaaddr(struct ia_na *ia, struct iaaddr *iaaddr, 
                 const char *file, int line) {
        int max;
        struct iaaddr **new;
@@ -282,69 +392,69 @@ ia_na_add_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr,
         *       guess as to how many addresses we might expect on an 
         *       interface.
         */
-       if (ia_na->max_iaaddr <= ia_na->num_iaaddr) {
-               max = ia_na->max_iaaddr + 4;
+       if (ia->max_iaaddr <= ia->num_iaaddr) {
+               max = ia->max_iaaddr + 4;
                new = dmalloc(max * sizeof(struct iaaddr *), file, line);
                if (new == NULL) {
                        return ISC_R_NOMEMORY;
                }
-               memcpy(new, ia_na->iaaddr, 
-                      ia_na->num_iaaddr * sizeof(struct iaaddr *));
-               ia_na->iaaddr = new;
-               ia_na->max_iaaddr = max;
+               memcpy(new, ia->iaaddr, 
+                      ia->num_iaaddr * sizeof(struct iaaddr *));
+               ia->iaaddr = new;
+               ia->max_iaaddr = max;
        }
 
-       iaaddr_reference(&(ia_na->iaaddr[ia_na->num_iaaddr]), iaaddr, 
+       iaaddr_reference(&(ia->iaaddr[ia->num_iaaddr]), iaaddr, 
                         file, line);
-       ia_na->num_iaaddr++;
+       ia->num_iaaddr++;
 
        return ISC_R_SUCCESS;
 }
 
 /*
- * Remove an IAADDR entry to an IA_NA structure.
+ * Remove an IAADDR entry to an IA structure.
  *
  * Note: if an IAADDR appears more than once, then only ONE will be removed.
  */
 void
-ia_na_remove_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr,
+ia_na_remove_iaaddr(struct ia_na *ia, struct iaaddr *iaaddr,
                    const char *file, int line) {
        int i, j;
 
-       for (i=0; i<ia_na->num_iaaddr; i++) {
-               if (ia_na->iaaddr[i] == iaaddr) {
+       for (i=0; i<ia->num_iaaddr; i++) {
+               if (ia->iaaddr[i] == iaaddr) {
                        /* remove this IAADDR */
-                       iaaddr_dereference(&(ia_na->iaaddr[i]), file, line);
+                       iaaddr_dereference(&(ia->iaaddr[i]), file, line);
                        /* move remaining IAADDR pointers down one */
-                       for (j=i+1; j < ia_na->num_iaaddr; j++) {
-                               ia_na->iaaddr[j-1] = ia_na->iaaddr[j];
+                       for (j=i+1; j < ia->num_iaaddr; j++) {
+                               ia->iaaddr[j-1] = ia->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--;
+                       ia->num_iaaddr--;
                        return;
                }
        }
-       log_error("%s(%d): IAADDR not in IA_NA", file, line);
+       log_error("%s(%d): IAADDR not in IA", file, line);
 }
 
 /*
- * Remove all addresses from an IA_NA.
+ * Remove all addresses from an IA.
  */
 void
-ia_na_remove_all_iaaddr(struct ia_na *ia_na, const char *file, int line) {
+ia_na_remove_all_iaaddr(struct ia_na *ia, const char *file, int line) {
        int i;
 
-       for (i=0; i<ia_na->num_iaaddr; i++) {
-               ia_na_dereference(&(ia_na->iaaddr[i]->ia_na), file, line);
-               iaaddr_dereference(&(ia_na->iaaddr[i]), file, line);
+       for (i=0; i<ia->num_iaaddr; i++) {
+               ia_na_dereference(&(ia->iaaddr[i]->ia_na), file, line);
+               iaaddr_dereference(&(ia->iaaddr[i]), file, line);
        }
-       ia_na->num_iaaddr = 0;
+       ia->num_iaaddr = 0;
 }
 
 /*
- * Compare two IA_NA.
+ * Compare two IA.
  */
 isc_boolean_t
 ia_na_equal(const struct ia_na *a, const struct ia_na *b) 
@@ -363,6 +473,13 @@ ia_na_equal(const struct ia_na *a, const struct ia_na *b)
                }
        }       
 
+       /*
+        * Check the type is the same.
+        */
+       if (a->ia_type != b->ia_type) {
+               return ISC_FALSE;
+       }
+
        /*
         * Check the DUID is the same.
         */
@@ -389,7 +506,7 @@ ia_na_equal(const struct ia_na *a, const struct ia_na *b)
                for (j=0; j<a->num_iaaddr; j++) {
                        if (memcmp(&(a->iaaddr[i]->addr),
                                   &(b->iaaddr[j]->addr), 
-                                  sizeof(struct in6_addr) == 0)) {
+                                  sizeof(struct in6_addr)) == 0) {
                                found = ISC_TRUE;
                                break;
                        }
@@ -406,45 +523,27 @@ ia_na_equal(const struct ia_na *a, const struct ia_na *b)
 }
 
 /*
- * Helper function for lease heaps.
- * Makes the top of the heap the oldest lease.
- */
-static isc_boolean_t 
-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;
-}
-
-/*
- * Helper function for lease heaps.
- * Callback when an address's position in the heap changes.
- */
-static void
-lease_address_index_changed(void *iaaddr, unsigned int new_heap_index) {
-       ((struct iaaddr *)iaaddr)-> heap_index = new_heap_index;
-}
-
-
-/*
- * Create a new IPv6 lease pool structure.
+ * Create a new IA_PD structure.
  *
- * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
+ * - ia_pd must be a pointer to a (struct ia_pd *) pointer previously
  *   initialized to NULL
+ * - iaid and duid are values from the client
+ *
+ * XXXsk: we don't concern ourself with the byte order of the IAID, 
+ *        which might be a problem if we transfer this structure 
+ *        between machines of different byte order
  */
 isc_result_t
-ipv6_pool_allocate(struct ipv6_pool **pool,
-                  const struct in6_addr *start_addr, int bits, 
-                  const char *file, int line) {
-       struct ipv6_pool *tmp;
+ia_pd_allocate(struct ia_pd **ia_pd, u_int32_t iaid, 
+              const char *duid, unsigned int duid_len,
+              const char *file, int line) {
+       struct ia_pd *tmp;
 
-       if (pool == NULL) {
+       if (ia_pd == NULL) {
                log_error("%s(%d): NULL pointer reference", file, line);
                return ISC_R_INVALIDARG;
        }
-       if (*pool != NULL) {
+       if (*ia_pd != NULL) {
                log_error("%s(%d): non-NULL pointer", file, line);
                return ISC_R_INVALIDARG;
        }
@@ -454,45 +553,32 @@ ipv6_pool_allocate(struct ipv6_pool **pool,
                return ISC_R_NOMEMORY;
        }
 
-       tmp->refcnt = 1;
-       tmp->start_addr = *start_addr;
-       tmp->bits = bits;
-       if (!iaaddr_new_hash(&tmp->addrs, DEFAULT_HASH_SIZE, file, line)) {
-               dfree(tmp, file, line);
-               return ISC_R_NOMEMORY;
-       }
-       if (isc_heap_create(lease_older, lease_address_index_changed,
-                           0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
-               iaaddr_free_hash_table(&(tmp->addrs), file, line);
-               dfree(tmp, file, line);
-               return ISC_R_NOMEMORY;
-       }
-       if (isc_heap_create(lease_older, lease_address_index_changed,
-                           0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
-               isc_heap_destroy(&(tmp->active_timeouts));
-               iaaddr_free_hash_table(&(tmp->addrs), file, line);
+       if (ia_make_key(&tmp->iaid_duid, iaid, 
+                       duid, duid_len, file, line) != ISC_R_SUCCESS) {
                dfree(tmp, file, line);
                return ISC_R_NOMEMORY;
        }
 
-       *pool = tmp;
+       tmp->refcnt = 1;
+
+       *ia_pd = tmp;
        return ISC_R_SUCCESS;
 }
 
 /*
- * Reference an IPv6 pool structure.
+ * Reference an IA_PD structure.
  *
- * - pool must be a pointer to a (struct pool *) pointer previously
+ * - ia_pd must be a pointer to a (struct ia_pd *) pointer previously
  *   initialized to NULL
  */
 isc_result_t
-ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src,
-                   const char *file, int line) {
-       if (pool == NULL) {
+ia_pd_reference(struct ia_pd **ia_pd, struct ia_pd *src,
+               const char *file, int line) {
+       if (ia_pd == NULL) {
                log_error("%s(%d): NULL pointer reference", file, line);
                return ISC_R_INVALIDARG;
        }
-       if (*pool != NULL) {
+       if (*ia_pd != NULL) {
                log_error("%s(%d): non-NULL pointer", file, line);
                return ISC_R_INVALIDARG;
        }
@@ -500,61 +586,29 @@ ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src,
                log_error("%s(%d): NULL pointer reference", file, line);
                return ISC_R_INVALIDARG;
        }
-       *pool = src;
+       *ia_pd = src;
        src->refcnt++;
        return ISC_R_SUCCESS;
 }
 
-/* 
- * Note: Each IAADDR in a pool is referenced by the pool. This is needed
- * to prevent the IAADDR from being garbage collected out from under the
- * pool.
- *
- * The references are made from the hash and from the heap. The following
- * helper functions dereference these when a pool is destroyed.
- */
-
-/*
- * Helper function for pool cleanup.
- * Dereference each of the hash entries in a pool.
- */
-static isc_result_t 
-dereference_hash_entry(const void *name, unsigned len, void *value) {
-       struct iaaddr *iaaddr = (struct iaaddr *)value;
-
-       iaaddr_dereference(&iaaddr, MDL);
-       return ISC_R_SUCCESS;
-}
-
-/*
- * Helper function for pool cleanup.
- * Dereference each of the heap entries in a pool.
- */
-static void
-dereference_heap_entry(void *value, void *dummy) {
-       struct iaaddr *iaaddr = (struct iaaddr *)value;
-
-       iaaddr_dereference(&iaaddr, MDL);
-}
-
-
 /*
- * Dereference an IPv6 pool structure.
+ * Dereference an IA_PD structure.
  *
  * If it is the last reference, then the memory for the 
  * structure is freed.
  */
 isc_result_t
-ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) {
-       struct ipv6_pool *tmp;
+ia_pd_dereference(struct ia_pd **ia_pd, const char *file, int line) {
+       struct ia_pd *tmp;
+       int i;
 
-       if ((pool == NULL) || (*pool == NULL)) {
+       if ((ia_pd == NULL) || (*ia_pd == NULL)) {
                log_error("%s(%d): NULL pointer", file, line);
                return ISC_R_INVALIDARG;
        }
 
-       tmp = *pool;
-       *pool = NULL;
+       tmp = *ia_pd;
+       *ia_pd = NULL;
 
        tmp->refcnt--;
        if (tmp->refcnt < 0) {
@@ -562,115 +616,1028 @@ ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) {
                tmp->refcnt = 0;
        }
        if (tmp->refcnt == 0) {
-               iaaddr_hash_foreach(tmp->addrs, dereference_hash_entry);
-               iaaddr_free_hash_table(&(tmp->addrs), file, line);
-               isc_heap_foreach(tmp->active_timeouts
-                                dereference_heap_entry, NULL);
-               isc_heap_destroy(&(tmp->active_timeouts));
-               isc_heap_foreach(tmp->inactive_timeouts, 
-                                dereference_heap_entry, NULL);
-               isc_heap_destroy(&(tmp->inactive_timeouts));
+               if (tmp->iaprefix != NULL) {
+                       for (i=0; i<tmp->num_iaprefix; i++) {
+                               iaprefix_dereference(&(tmp->iaprefix[i])
+                                                    file, line);
+                       }
+                       dfree(tmp->iaprefix, file, line);
+               }
+               data_string_forget(&(tmp->iaid_duid), file, line);
                dfree(tmp, file, line);
        }
-
        return ISC_R_SUCCESS;
 }
 
-/* 
- * Create an address by hashing the input, and using that for
- * the non-network part.
+
+/*
+ * Add an IAPREFIX entry to an IA_PD structure.
  */
-static void
-create_address(struct in6_addr *addr, 
-              const struct in6_addr *net_start_addr, int net_bits, 
-              const struct data_string *input) {
-       MD5_CTX ctx;
-       int net_bytes;
-       int i;
-       char *str;
-       const char *net_str;
+isc_result_t
+ia_pd_add_iaprefix(struct ia_pd *ia_pd, struct iaprefix *iapref, 
+                  const char *file, int line) {
+       int max;
+       struct iaprefix **new;
 
        /* 
-        * Use MD5 to get a nice 128 bit hash of the input.
-        * Yes, we know MD5 isn't cryptographically sound. 
-        * No, we don't care.
-        */
-       MD5_Init(&ctx);
-       MD5_Update(&ctx, input->data, input->len);
-       MD5_Final((unsigned char *)addr, &ctx);
-
-       /*
-        * Copy the network bits over.
+        * Grow our array if we need to.
+        * 
+        * Note: we pick 4 as the increment, as that seems a reasonable
+        *       guess as to how many prefixes we might expect on an 
+        *       interface.
         */
-       str = (char *)addr;
-       net_str = (const char *)net_start_addr;
-       net_bytes = net_bits / 8;
-       for (i=0; i<net_bytes; i++) {
-               str[i] = net_str[i];
-       }
-       switch (net_bits % 8) {
-               case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
-               case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
-               case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
-               case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
-               case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
-               case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
-               case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
+       if (ia_pd->max_iaprefix <= ia_pd->num_iaprefix) {
+               max = ia_pd->max_iaprefix + 4;
+               new = dmalloc(max * sizeof(struct iaprefix *), file, line);
+               if (new == NULL) {
+                       return ISC_R_NOMEMORY;
+               }
+               memcpy(new, ia_pd->iaprefix, 
+                      ia_pd->num_iaprefix * sizeof(struct iaprefix *));
+               ia_pd->iaprefix = new;
+               ia_pd->max_iaprefix = max;
        }
-       /* set the 'u' bit to zero for /64s. */
-       if (net_bits == 64)
-               str[8] &= ~0x02;
-}
 
-/* Reserved Subnet Router Anycast ::0:0:0:0. */
+       iaprefix_reference(&(ia_pd->iaprefix[ia_pd->num_iaprefix]), iapref, 
+                          file, line);
+       ia_pd->num_iaprefix++;
+
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * Remove an IAPREFIX entry to an IA_PD structure.
+ *
+ * Note: if an IAPREFIX appears more than once, then only ONE will be removed.
+ */
+void
+ia_pd_remove_iaprefix(struct ia_pd *ia_pd, struct iaprefix *iapref,
+                     const char *file, int line) {
+       int i, j;
+
+       for (i=0; i<ia_pd->num_iaprefix; i++) {
+               if (ia_pd->iaprefix[i] == iapref) {
+                       /* remove this IAPREFIX */
+                       iaprefix_dereference(&(ia_pd->iaprefix[i]),
+                                            file, line);
+                       /* move remaining IAPREFIX pointers down one */
+                       for (j=i+1; j < ia_pd->num_iaprefix; j++) {
+                               ia_pd->iaprefix[j-1] = ia_pd->iaprefix[j];
+                       }
+                       /* decrease our total count */
+                       /* remove the back-reference in the IAPREFIX itself */
+                       ia_pd_dereference(&iapref->ia_pd, file, line);
+                       ia_pd->num_iaprefix--;
+                       return;
+               }
+       }
+       log_error("%s(%d): IAPREFIX not in IA_PD", file, line);
+}
+
+/*
+ * Remove all prefixes from an IA_PD.
+ */
+void
+ia_pd_remove_all_iaprefix(struct ia_pd *ia_pd, const char *file, int line) {
+       int i;
+
+       for (i=0; i<ia_pd->num_iaprefix; i++) {
+               ia_pd_dereference(&(ia_pd->iaprefix[i]->ia_pd), file, line);
+               iaprefix_dereference(&(ia_pd->iaprefix[i]), file, line);
+       }
+       ia_pd->num_iaprefix = 0;
+}
+
+/*
+ * Compare two IA_PD.
+ */
+isc_boolean_t
+ia_pd_equal(const struct ia_pd *a, const struct ia_pd *b) 
+{
+       isc_boolean_t found;
+       int i, j;
+
+       /*
+        * Handle cases where one or both of the inputs is NULL.
+        */
+       if (a == NULL) {
+               if (b == NULL) {
+                       return ISC_TRUE;
+               } else {
+                       return ISC_FALSE;
+               }
+       }       
+
+       /*
+        * Check the DUID is the same.
+        */
+       if (a->iaid_duid.len != b->iaid_duid.len) {
+               return ISC_FALSE;
+       }
+       if (memcmp(a->iaid_duid.data, 
+                  b->iaid_duid.data, a->iaid_duid.len) != 0) {
+               return ISC_FALSE;
+       }
+
+       /*
+        * Make sure we have the same number of prefixes in each.
+        */
+       if (a->num_iaprefix != b->num_iaprefix) {
+               return ISC_FALSE;
+       }
+
+       /*
+        * Check that each prefix is present in both.
+        */
+       for (i=0; i<a->num_iaprefix; i++) {
+               found = ISC_FALSE;
+               for (j=0; j<a->num_iaprefix; j++) {
+                       if (a->iaprefix[i]->plen != b->iaprefix[i]->plen)
+                               continue;
+                       if (memcmp(&(a->iaprefix[i]->pref),
+                                  &(b->iaprefix[j]->pref), 
+                                  sizeof(struct in6_addr)) == 0) {
+                               found = ISC_TRUE;
+                               break;
+                       }
+               }
+               if (!found) {
+                       return ISC_FALSE;
+               }
+       }
+
+       /*
+        * These are the same in every way we care about.
+        */
+       return ISC_TRUE;
+}
+
+/*
+ * Helper function for lease heaps.
+ * Makes the top of the heap the oldest lease.
+ * Note: this relies on the unique layout for leases!
+ */
+static isc_boolean_t 
+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;
+}
+
+/*
+ * Helper function for lease address heaps.
+ * Callback when an address's position in the heap changes.
+ */
+static void
+lease_address_index_changed(void *iaaddr, unsigned int new_heap_index) {
+       ((struct iaaddr *)iaaddr)-> heap_index = new_heap_index;
+}
+
+/*
+ * Helper function for lease prefix heaps.
+ * Callback when a prefix's position in the heap changes.
+ */
+static void
+lease_prefix_index_changed(void *iapref, unsigned int new_heap_index) {
+       ((struct iaprefix *)iapref)-> heap_index = new_heap_index;
+}
+
+
+/*
+ * Create a new IPv6 lease (address) pool structure.
+ *
+ * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
+ *   initialized to NULL
+ */
+isc_result_t
+ipv6_pool_allocate(struct ipv6_pool **pool,
+                  const struct in6_addr *start_addr, int bits, 
+                  const char *file, int line) {
+       struct ipv6_pool *tmp;
+
+       if (pool == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       if (*pool != NULL) {
+               log_error("%s(%d): non-NULL pointer", file, line);
+               return ISC_R_INVALIDARG;
+       }
+
+       tmp = dmalloc(sizeof(*tmp), file, line);
+       if (tmp == NULL) {
+               return ISC_R_NOMEMORY;
+       }
+
+       tmp->refcnt = 1;
+       tmp->start_addr = *start_addr;
+       tmp->bits = bits;
+       if (!iaaddr_new_hash(&tmp->addrs, DEFAULT_HASH_SIZE, file, line)) {
+               dfree(tmp, file, line);
+               return ISC_R_NOMEMORY;
+       }
+       if (isc_heap_create(lease_older, lease_address_index_changed,
+                           0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
+               iaaddr_free_hash_table(&(tmp->addrs), file, line);
+               dfree(tmp, file, line);
+               return ISC_R_NOMEMORY;
+       }
+       if (isc_heap_create(lease_older, lease_address_index_changed,
+                           0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
+               isc_heap_destroy(&(tmp->active_timeouts));
+               iaaddr_free_hash_table(&(tmp->addrs), file, line);
+               dfree(tmp, file, line);
+               return ISC_R_NOMEMORY;
+       }
+
+       *pool = tmp;
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IPv6 pool structure.
+ *
+ * - pool must be a pointer to a (struct pool *) pointer previously
+ *   initialized to NULL
+ */
+isc_result_t
+ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src,
+                   const char *file, int line) {
+       if (pool == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       if (*pool != NULL) {
+               log_error("%s(%d): non-NULL pointer", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       if (src == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       *pool = src;
+       src->refcnt++;
+       return ISC_R_SUCCESS;
+}
+
+/* 
+ * Note: Each IAADDR in a pool is referenced by the pool. This is needed
+ * to prevent the IAADDR from being garbage collected out from under the
+ * pool.
+ *
+ * The references are made from the hash and from the heap. The following
+ * helper functions dereference these when a pool is destroyed.
+ */
+
+/*
+ * Helper function for pool cleanup.
+ * Dereference each of the hash entries in a pool.
+ */
+static isc_result_t 
+dereference_hash_entry(const void *name, unsigned len, void *value) {
+       struct iaaddr *iaaddr = (struct iaaddr *)value;
+
+       iaaddr_dereference(&iaaddr, MDL);
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * Helper function for pool cleanup.
+ * Dereference each of the heap entries in a pool.
+ */
+static void
+dereference_heap_entry(void *value, void *dummy) {
+       struct iaaddr *iaaddr = (struct iaaddr *)value;
+
+       iaaddr_dereference(&iaaddr, MDL);
+}
+
+
+/*
+ * Dereference an IPv6 pool structure.
+ *
+ * If it is the last reference, then the memory for the 
+ * structure is freed.
+ */
+isc_result_t
+ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) {
+       struct ipv6_pool *tmp;
+
+       if ((pool == NULL) || (*pool == NULL)) {
+               log_error("%s(%d): NULL pointer", file, line);
+               return ISC_R_INVALIDARG;
+       }
+
+       tmp = *pool;
+       *pool = NULL;
+
+       tmp->refcnt--;
+       if (tmp->refcnt < 0) {
+               log_error("%s(%d): negative refcnt", file, line);
+               tmp->refcnt = 0;
+       }
+       if (tmp->refcnt == 0) {
+               iaaddr_hash_foreach(tmp->addrs, dereference_hash_entry);
+               iaaddr_free_hash_table(&(tmp->addrs), file, line);
+               isc_heap_foreach(tmp->active_timeouts, 
+                                dereference_heap_entry, NULL);
+               isc_heap_destroy(&(tmp->active_timeouts));
+               isc_heap_foreach(tmp->inactive_timeouts, 
+                                dereference_heap_entry, NULL);
+               isc_heap_destroy(&(tmp->inactive_timeouts));
+               dfree(tmp, file, line);
+       }
+
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * Create a new IPv6 lease (prefix) pool structure.
+ *
+ * - ppool must be a pointer to a (struct ipv6_ppool *) pointer previously
+ *   initialized to NULL
+ */
+isc_result_t
+ipv6_ppool_allocate(struct ipv6_ppool **ppool,
+                   const struct in6_addr *start_pref,
+                   u_int8_t pool_plen, u_int8_t alloc_plen,
+                   const char *file, int line) {
+       struct ipv6_ppool *tmp;
+
+       if (ppool == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       if (*ppool != NULL) {
+               log_error("%s(%d): non-NULL pointer", file, line);
+               return ISC_R_INVALIDARG;
+       }
+
+       tmp = dmalloc(sizeof(*tmp), file, line);
+       if (tmp == NULL) {
+               return ISC_R_NOMEMORY;
+       }
+
+       tmp->refcnt = 1;
+       tmp->start_pref = *start_pref;
+       tmp->pool_plen = pool_plen;
+       tmp->alloc_plen = alloc_plen;
+       if (!iaprefix_new_hash(&tmp->prefs, DEFAULT_HASH_SIZE, file, line)) {
+               dfree(tmp, file, line);
+               return ISC_R_NOMEMORY;
+       }
+       if (isc_heap_create(lease_older, lease_prefix_index_changed,
+                           0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
+               iaprefix_free_hash_table(&(tmp->prefs), file, line);
+               dfree(tmp, file, line);
+               return ISC_R_NOMEMORY;
+       }
+       if (isc_heap_create(lease_older, lease_prefix_index_changed,
+                           0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
+               isc_heap_destroy(&(tmp->active_timeouts));
+               iaprefix_free_hash_table(&(tmp->prefs), file, line);
+               dfree(tmp, file, line);
+               return ISC_R_NOMEMORY;
+       }
+
+       *ppool = tmp;
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IPv6 prefix pool structure.
+ *
+ * - ppool must be a pointer to a (struct ppool *) pointer previously
+ *   initialized to NULL
+ */
+isc_result_t
+ipv6_ppool_reference(struct ipv6_ppool **ppool, struct ipv6_ppool *src,
+                    const char *file, int line) {
+       if (ppool == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       if (*ppool != NULL) {
+               log_error("%s(%d): non-NULL pointer", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       if (src == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return ISC_R_INVALIDARG;
+       }
+       *ppool = src;
+       src->refcnt++;
+       return ISC_R_SUCCESS;
+}
+
+/* 
+ * Note: Each IAPREFIX in a pool is referenced by the pool. This is needed
+ * to prevent the IAPREFIX from being garbage collected out from under the
+ * pool.
+ *
+ * The references are made from the hash and from the heap. The following
+ * helper functions dereference these when a pool is destroyed.
+ */
+
+/*
+ * Helper function for prefix pool cleanup.
+ * Dereference each of the hash entries in a pool.
+ */
+static isc_result_t 
+dereference_phash_entry(const void *name, unsigned len, void *value) {
+       struct iaprefix *iapref = (struct iaprefix *)value;
+
+       iaprefix_dereference(&iapref, MDL);
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * Helper function for prefix pool cleanup.
+ * Dereference each of the heap entries in a pool.
+ */
+static void
+dereference_pheap_entry(void *value, void *dummy) {
+       struct iaprefix *iapref = (struct iaprefix *)value;
+
+       iaprefix_dereference(&iapref, MDL);
+}
+
+
+/*
+ * Dereference an IPv6 prefix pool structure.
+ *
+ * If it is the last reference, then the memory for the 
+ * structure is freed.
+ */
+isc_result_t
+ipv6_ppool_dereference(struct ipv6_ppool **ppool, const char *file, int line) {
+       struct ipv6_ppool *tmp;
+
+       if ((ppool == NULL) || (*ppool == NULL)) {
+               log_error("%s(%d): NULL pointer", file, line);
+               return ISC_R_INVALIDARG;
+       }
+
+       tmp = *ppool;
+       *ppool = NULL;
+
+       tmp->refcnt--;
+       if (tmp->refcnt < 0) {
+               log_error("%s(%d): negative refcnt", file, line);
+               tmp->refcnt = 0;
+       }
+       if (tmp->refcnt == 0) {
+               iaprefix_hash_foreach(tmp->prefs, dereference_phash_entry);
+               iaprefix_free_hash_table(&(tmp->prefs), file, line);
+               isc_heap_foreach(tmp->active_timeouts, 
+                                dereference_pheap_entry, NULL);
+               isc_heap_destroy(&(tmp->active_timeouts));
+               isc_heap_foreach(tmp->inactive_timeouts, 
+                                dereference_pheap_entry, NULL);
+               isc_heap_destroy(&(tmp->inactive_timeouts));
+               dfree(tmp, file, line);
+       }
+
+       return ISC_R_SUCCESS;
+}
+
+/* 
+ * Create an address by hashing the input, and using that for
+ * the non-network part.
+ */
+static void
+create_address(struct in6_addr *addr, 
+              const struct in6_addr *net_start_addr, int net_bits, 
+              const struct data_string *input) {
+       MD5_CTX ctx;
+       int net_bytes;
+       int i;
+       char *str;
+       const char *net_str;
+
+       /* 
+        * Use MD5 to get a nice 128 bit hash of the input.
+        * Yes, we know MD5 isn't cryptographically sound. 
+        * No, we don't care.
+        */
+       MD5_Init(&ctx);
+       MD5_Update(&ctx, input->data, input->len);
+       MD5_Final((unsigned char *)addr, &ctx);
+
+       /*
+        * Copy the network bits over.
+        */
+       str = (char *)addr;
+       net_str = (const char *)net_start_addr;
+       net_bytes = net_bits / 8;
+       for (i=0; i<net_bytes; i++) {
+               str[i] = net_str[i];
+       }
+       switch (net_bits % 8) {
+               case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
+               case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
+               case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
+               case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
+               case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
+               case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
+               case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
+       }
+       /* set the 'u' bit to zero for /64s. */
+       if (net_bits == 64)
+               str[8] &= ~0x02;
+}
+
+/* Reserved Subnet Router Anycast ::0:0:0:0. */
 static struct in6_addr rtany;
 /* Reserved Subnet Anycasts ::fdff:ffff:ffff:ff80-::fdff:ffff:ffff:ffff. */
 static struct in6_addr resany;
 
 /*
- * Create a lease for the given address and client duid.
+ * Create a lease for the given address and client duid.
+ *
+ * - pool must be a pointer to a (struct pool *) pointer previously
+ *   initialized to NULL
+ *
+ * Right now we simply hash the DUID, and if we get a collision, we hash 
+ * again until we find a free address. We try this a fixed number of times,
+ * to avoid getting stuck in a loop (this is important on small pools
+ * where we can run out of space).
+ *
+ * We return the number of attempts that it took to find an available
+ * lease. This tells callers when a pool is are filling up, as
+ * well as an indication of how full the pool is; statistically the 
+ * more full a pool is the more attempts must be made before finding
+ * a free lease. Realistically this will only happen in very full
+ * pools.
+ *
+ * We probably want different algorithms depending on the network size, in
+ * 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) {
+       struct data_string ds;
+       struct in6_addr tmp;
+       struct iaaddr *test_iaaddr;
+       struct data_string new_ds;
+       struct iaaddr *iaaddr;
+       isc_result_t result;
+       isc_boolean_t reserved_iid;
+       static isc_boolean_t init_resiid = ISC_FALSE;
+
+       /*
+        * Fill the reserved IIDs.
+        */
+       if (!init_resiid) {
+               memset(&rtany, 0, 16);
+               memset(&resany, 0, 8);
+               resany.s6_addr[8] = 0xfd;
+               memset(&resany.s6_addr[9], 0xff, 6);
+               init_resiid = ISC_TRUE;
+       }
+
+       /* 
+        * Use the UID as our initial seed for the hash
+        */
+       memset(&ds, 0, sizeof(ds));
+       data_string_copy(&ds, (struct data_string *)uid, MDL);
+
+       *attempts = 0;
+       for (;;) {
+               /*
+                * Give up at some point.
+                */
+               if (++(*attempts) > 100) {
+                       data_string_forget(&ds, MDL);
+                       return ISC_R_NORESOURCES;
+               }
+
+               /* 
+                * Create an address
+                */
+               create_address(&tmp, &pool->start_addr, pool->bits, &ds);
+
+               /*
+                * Avoid reserved interface IDs.
+                * (cf. draft-krishnan-ipv6-reserved-iids-02.txt)
+                */
+               reserved_iid = ISC_FALSE;
+               if (memcmp(&tmp.s6_addr[8], &rtany, 8) == 0) {
+                       reserved_iid = ISC_TRUE;
+               }
+               if (!reserved_iid &&
+                   (memcmp(&tmp.s6_addr[8], &resany, 7) == 0) &&
+                   ((tmp.s6_addr[15] & 0x80) == 0x80)) {
+                       reserved_iid = ISC_TRUE;
+               }
+
+               /*
+                * If this address is not in use, we're happy with it
+                */
+               test_iaaddr = NULL;
+               if (!reserved_iid &&
+                   (iaaddr_hash_lookup(&test_iaaddr, pool->addrs,
+                                       &tmp, sizeof(tmp), MDL) == 0)) {
+                       break;
+               }
+               if (test_iaaddr != NULL)
+                       iaaddr_dereference(&test_iaaddr, MDL);
+
+               /* 
+                * Otherwise, we create a new input, adding the address
+                */
+               memset(&new_ds, 0, sizeof(new_ds));
+               new_ds.len = ds.len + sizeof(tmp);
+               if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
+                       data_string_forget(&ds, MDL);
+                       return ISC_R_NOMEMORY;
+               }
+               new_ds.data = new_ds.buffer->data;
+               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);
+       }
+
+       data_string_forget(&ds, MDL);
+
+       /* 
+        * We're happy with the address, create an IAADDR
+        * to hold it.
+        */
+       iaaddr = NULL;
+       result = iaaddr_allocate(&iaaddr, MDL);
+       if (result != ISC_R_SUCCESS) {
+               return result;
+       }
+       memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
+
+       /*
+        * 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;
+}
+
+/*
+ * Put a lease in the pool directly. This is intended to be used when
+ * loading leases from the file.
+ */
+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;
+
+       /* If a state was not assigned by the caller, assume active. */
+       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);
+
+       /*
+        * 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)) {
+               /* XXX: we should probably ask the iaaddr what heap it is on
+                * (as a consistency check).
+                * XXX: we should probably have one function to "put this lease
+                * on its heap" rather than doing these if's everywhere.  If
+                * you add more states to this list, don't.
+                */
+               if ((test_iaaddr->state == FTS_ACTIVE) ||
+                   (test_iaaddr->state == FTS_ABANDONED)) {
+                       isc_heap_delete(pool->active_timeouts,
+                                       test_iaaddr->heap_index);
+                       pool->num_active--;
+               } else {
+                       isc_heap_delete(pool->inactive_timeouts,
+                                       test_iaaddr->heap_index);
+                       pool->num_inactive--;
+               }
+
+               iaaddr_hash_delete(pool->addrs, &test_iaaddr->addr, 
+                                  sizeof(test_iaaddr->addr), MDL);
+
+               /*
+                * 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.
+        */
+       tmp_iaaddr = NULL;
+       iaaddr_reference(&tmp_iaaddr, iaaddr, MDL);
+       if ((tmp_iaaddr->state == FTS_ACTIVE) ||
+           (tmp_iaaddr->state == FTS_ABANDONED)) {
+               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)
+                       pool->num_active++;
+       } else {
+               insert_result = isc_heap_insert(pool->inactive_timeouts,
+                                               tmp_iaaddr);
+               if (insert_result == ISC_R_SUCCESS)
+                       pool->num_inactive++;
+       }
+       if (insert_result != ISC_R_SUCCESS) {
+               iaaddr_hash_delete(pool->addrs, &iaaddr->addr, 
+                                  sizeof(iaaddr->addr), 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.
+        */
+
+       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;
+
+       test_iaaddr = NULL;
+       if (iaaddr_hash_lookup(&test_iaaddr, pool->addrs, 
+                              (void *)addr, sizeof(*addr), MDL)) {
+               iaaddr_dereference(&test_iaaddr, MDL);
+               return ISC_TRUE;
+       } else {
+               return ISC_FALSE;
+       }
+}
+
+/*
+ * 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.
+ *
+ * To do this, first set the new valid_lifetime_end_time for the address, 
+ * and then invoke renew_lease() on the address.
+ *
+ * WARNING: lease times must only be extended, never reduced!!!
+ */
+isc_result_t
+renew_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
+       /*
+        * 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) {
+               /* Process events upon expiration. */
+               ddns_removals(NULL, addr);
+
+               /* Binding scopes are no longer valid after expiry or
+                * release.
+                */
+               if (addr->scope != NULL) {
+                       binding_scope_dereference(&addr->scope, MDL);
+               }
+
+               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;
+}
+
+/*
+ * Expire the oldest lease if it's lifetime_end_time is 
+ * older than the given time.
  *
- * - pool must be a pointer to a (struct pool *) pointer previously
+ * - iaaddr must be a pointer to a (struct iaaddr *) pointer previously
+ *   initialized to NULL
+ *
+ * On return iaaddr has a reference to the removed entry. It is left
+ * pointing to NULL if the oldest lease has not expired.
+ */
+isc_result_t
+expire_lease6(struct iaaddr **addr, struct ipv6_pool *pool, time_t now) {
+       struct iaaddr *tmp;
+       isc_result_t result;
+
+       if (addr == NULL) {
+               log_error("%s(%d): NULL pointer reference", MDL);
+               return ISC_R_INVALIDARG;
+       }
+       if (*addr != NULL) {
+               log_error("%s(%d): non-NULL pointer", MDL);
+               return ISC_R_INVALIDARG;
+       }
+
+       if (pool->num_active > 0) {
+               tmp = (struct iaaddr *)isc_heap_element(pool->active_timeouts, 
+                                                       1);
+               if (now > tmp->valid_lifetime_end_time) {
+                       result = move_lease_to_inactive(pool, tmp, FTS_EXPIRED);
+                       if (result == ISC_R_SUCCESS) {
+                               iaaddr_reference(addr, tmp, MDL);
+                       }
+                       return result;
+               }
+       }
+       return ISC_R_SUCCESS;
+}
+
+
+/*
+ * For a declined lease, leave it on the "active" pool, but mark
+ * it as declined. Give it an infinite (well, really long) life.
+ */
+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);
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * Put the returned lease on our inactive pool.
+ */
+isc_result_t
+release_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
+       if (addr->state == FTS_ACTIVE) {
+               return move_lease_to_inactive(pool, addr, FTS_RELEASED);
+       } else {
+               return ISC_R_SUCCESS;
+       }
+}
+
+/* 
+ * Create a prefix by hashing the input, and using that for
+ * the part subject to allocation.
+ */
+static void
+create_prefix(struct in6_addr *pref, 
+             const struct in6_addr *net_start_pref,
+             int pool_bits, int pref_bits,
+             const struct data_string *input) {
+       MD5_CTX ctx;
+       int net_bytes;
+       int i;
+       char *str;
+       const char *net_str;
+
+       /* 
+        * Use MD5 to get a nice 128 bit hash of the input.
+        * Yes, we know MD5 isn't cryptographically sound. 
+        * No, we don't care.
+        */
+       MD5_Init(&ctx);
+       MD5_Update(&ctx, input->data, input->len);
+       MD5_Final((unsigned char *)pref, &ctx);
+
+       /*
+        * Copy the network bits over.
+        */
+       str = (char *)pref;
+       net_str = (const char *)net_start_pref;
+       net_bytes = pool_bits / 8;
+       for (i=0; i<net_bytes; i++) {
+               str[i] = net_str[i];
+       }
+       switch (pool_bits % 8) {
+               case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
+               case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
+               case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
+               case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
+               case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
+               case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
+               case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
+       }
+       /*
+        * Zero the remaining bits.
+        */
+       net_bytes = pref_bits / 8;
+       for (i=net_bytes+1; i<16; i++) {
+               str[i] = 0;
+       }
+       switch (pref_bits % 8) {
+               case 0: str[i] &= 0;
+               case 1: str[i] &= 0x80;
+               case 2: str[i] &= 0xC0;
+               case 3: str[i] &= 0xE0;
+               case 4: str[i] &= 0xF0;
+               case 5: str[i] &= 0xF8;
+               case 6: str[i] &= 0xFC;
+               case 7: str[i] &= 0xFE;
+       }
+}
+
+/*
+ * Create a lease for the given prefix and client duid.
+ *
+ * - ppool must be a pointer to a (struct ppool *) pointer previously
  *   initialized to NULL
  *
  * Right now we simply hash the DUID, and if we get a collision, we hash 
- * again until we find a free address. We try this a fixed number of times,
+ * again until we find a free prefix. We try this a fixed number of times,
  * to avoid getting stuck in a loop (this is important on small pools
  * where we can run out of space).
  *
  * We return the number of attempts that it took to find an available
- * lease. This tells callers when a pool is are filling up, as
+ * prefix. This tells callers when a pool is are filling up, as
  * well as an indication of how full the pool is; statistically the 
  * more full a pool is the more attempts must be made before finding
- * a free lease. Realistically this will only happen in very full
+ * a free prefix. Realistically this will only happen in very full
  * pools.
  *
  * We probably want different algorithms depending on the network size, in
  * the long term.
  */
 isc_result_t
-activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr
+activate_prefix(struct ipv6_ppool *ppool, struct iaprefix **pref
                unsigned int *attempts,
-               const struct data_string *uid, time_t valid_lifetime_end_time) {
+               const struct data_string *uid,
+               time_t valid_lifetime_end_time) {
        struct data_string ds;
        struct in6_addr tmp;
-       struct iaaddr *test_iaaddr;
+       struct iaprefix *test_iapref;
        struct data_string new_ds;
-       struct iaaddr *iaaddr;
+       struct iaprefix *iapref;
        isc_result_t result;
-       isc_boolean_t reserved_iid;
-       static isc_boolean_t init_resiid = ISC_FALSE;
-
-       /*
-        * Fill the reserved IIDs.
-        */
-       if (!init_resiid) {
-               memset(&rtany, 0, 16);
-               memset(&resany, 0, 8);
-               resany.s6_addr[8] = 0xfd;
-               memset(&resany.s6_addr[9], 0xff, 6);
-               init_resiid = ISC_TRUE;
-       }
 
        /* 
         * Use the UID as our initial seed for the hash
@@ -683,44 +1650,30 @@ activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
                /*
                 * Give up at some point.
                 */
-               if (++(*attempts) > 100) {
+               if (++(*attempts) > 10) {
                        data_string_forget(&ds, MDL);
                        return ISC_R_NORESOURCES;
                }
 
                /* 
-                * Create an address
-                */
-               create_address(&tmp, &pool->start_addr, pool->bits, &ds);
-
-               /*
-                * Avoid reserved interface IDs.
-                * (cf. draft-krishnan-ipv6-reserved-iids-02.txt)
+                * Create a prefix
                 */
-               reserved_iid = ISC_FALSE;
-               if (memcmp(&tmp.s6_addr[8], &rtany, 8) == 0) {
-                       reserved_iid = ISC_TRUE;
-               }
-               if (!reserved_iid &&
-                   (memcmp(&tmp.s6_addr[8], &resany, 7) == 0) &&
-                   ((tmp.s6_addr[15] & 0x80) == 0x80)) {
-                       reserved_iid = ISC_TRUE;
-               }
+               create_prefix(&tmp, &ppool->start_pref,
+                             (int)ppool->pool_plen, (int)ppool->alloc_plen,
+                             &ds);
 
                /*
-                * If this address is not in use, we're happy with it
+                * If this prefix is not in use, we're happy with it
                 */
-               test_iaaddr = NULL;
-               if (!reserved_iid &&
-                   (iaaddr_hash_lookup(&test_iaaddr, pool->addrs,
-                                       &tmp, sizeof(tmp), MDL) == 0)) {
+               test_iapref = NULL;
+               if (iaprefix_hash_lookup(&test_iapref, ppool->prefs,
+                                        &tmp, sizeof(tmp), MDL) == 0) {
                        break;
                }
-               if (test_iaaddr != NULL)
-                       iaaddr_dereference(&test_iaaddr, MDL);
+               iaprefix_dereference(&test_iapref, MDL);
 
                /* 
-                * Otherwise, we create a new input, adding the address
+                * Otherwise, we create a new input, adding the prefix
                 */
                memset(&new_ds, 0, sizeof(new_ds));
                new_ds.len = ds.len + sizeof(tmp);
@@ -739,113 +1692,113 @@ activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
        data_string_forget(&ds, MDL);
 
        /* 
-        * We're happy with the address, create an IAADDR
+        * We're happy with the prefix, create an IAPREFIX
         * to hold it.
         */
-       iaaddr = NULL;
-       result = iaaddr_allocate(&iaaddr, MDL);
+       iapref = NULL;
+       result = iaprefix_allocate(&iapref, MDL);
        if (result != ISC_R_SUCCESS) {
                return result;
        }
-       memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
+       memcpy(&iapref->pref, &tmp, sizeof(iapref->pref));
 
        /*
-        * Add the lease to the pool.
+        * Add the prefix to the pool.
         */
-       result = add_lease6(pool, iaaddr, valid_lifetime_end_time);
+       result = add_prefix6(ppool, iapref, valid_lifetime_end_time);
        if (result == ISC_R_SUCCESS) {
-               iaaddr_reference(addr, iaaddr, MDL);
+               iaprefix_reference(pref, iapref, MDL);
        }
-       iaaddr_dereference(&iaaddr, MDL);
+       iaprefix_dereference(&iapref, MDL);
        return result;
 }
 
 /*
- * Put a lease in the pool directly. This is intended to be used when
+ * Put a prefix in the pool directly. This is intended to be used when
  * loading leases from the file.
  */
 isc_result_t
-add_lease6(struct ipv6_pool *pool, struct iaaddr *iaaddr,
-          time_t valid_lifetime_end_time) {
+add_prefix6(struct ipv6_ppool *ppool, struct iaprefix *iapref,
+           time_t valid_lifetime_end_time) {
        isc_result_t insert_result;
-       struct iaaddr *test_iaaddr;
-       struct iaaddr *tmp_iaaddr;
+       struct iaprefix *test_iapref;
+       struct iaprefix *tmp_iapref;
 
        /* If a state was not assigned by the caller, assume active. */
-       if (iaaddr->state == 0)
-               iaaddr->state = FTS_ACTIVE;
+       if (iapref->state == 0)
+               iapref->state = FTS_ACTIVE;
 
-       iaaddr->valid_lifetime_end_time = valid_lifetime_end_time;
-       ipv6_pool_reference(&iaaddr->ipv6_pool, pool, MDL);
+       iapref->valid_lifetime_end_time = valid_lifetime_end_time;
+       ipv6_ppool_reference(&iapref->ipv6_ppool, ppool, MDL);
 
        /*
-        * If this IAADDR is already in our structures, remove the 
+        * If this IAPREFIX 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)) {
-               /* XXX: we should probably ask the iaaddr what heap it is on
+       test_iapref = NULL;
+       if (iaprefix_hash_lookup(&test_iapref, ppool->prefs,
+                                &iapref->pref, sizeof(iapref->pref), MDL)) {
+               /* XXX: we should probably ask the iaprefix what heap it is on
                 * (as a consistency check).
-                * XXX: we should probably have one function to "put this lease
-                * on its heap" rather than doing these if's everywhere.  If
-                * you add more states to this list, don't.
+                * XXX: we should probably have one function to "put this
+                * prefix on its heap" rather than doing these if's
+                * everywhere.  If you add more states to this list, don't.
                 */
-               if ((test_iaaddr->state == FTS_ACTIVE) ||
-                   (test_iaaddr->state == FTS_ABANDONED)) {
-                       isc_heap_delete(pool->active_timeouts,
-                                       test_iaaddr->heap_index);
-                       pool->num_active--;
+               if ((test_iapref->state == FTS_ACTIVE) ||
+                   (test_iapref->state == FTS_ABANDONED)) {
+                       isc_heap_delete(ppool->active_timeouts,
+                                       test_iapref->heap_index);
+                       ppool->num_active--;
                } else {
-                       isc_heap_delete(pool->inactive_timeouts,
-                                       test_iaaddr->heap_index);
-                       pool->num_inactive--;
+                       isc_heap_delete(ppool->inactive_timeouts,
+                                       test_iapref->heap_index);
+                       ppool->num_inactive--;
                }
 
-               iaaddr_hash_delete(pool->addrs, &test_iaaddr->addr
-                                  sizeof(test_iaaddr->addr), MDL);
+               iaprefix_hash_delete(ppool->prefs, &test_iapref->pref
+                                    sizeof(test_iapref->pref), MDL);
 
                /*
                 * 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
+                * current reference (in test_iapref), and then one
                 * more time to remove the reference left when the
-                * address was added to the pool before.
+                * prefix was added to the pool before.
                 */
-               tmp_iaaddr = test_iaaddr;
-               iaaddr_dereference(&test_iaaddr, MDL);
-               iaaddr_dereference(&tmp_iaaddr, MDL);
+               tmp_iapref = test_iapref;
+               iaprefix_dereference(&test_iapref, MDL);
+               iaprefix_dereference(&tmp_iapref, MDL);
        }
 
        /* 
-        * Add IAADDR to our structures.
+        * Add IAPREFIX to our structures.
         */
-       tmp_iaaddr = NULL;
-       iaaddr_reference(&tmp_iaaddr, iaaddr, MDL);
-       if ((tmp_iaaddr->state == FTS_ACTIVE) ||
-           (tmp_iaaddr->state == FTS_ABANDONED)) {
-               iaaddr_hash_add(pool->addrs, &tmp_iaaddr->addr
-                               sizeof(tmp_iaaddr->addr), iaaddr, MDL);
-               insert_result = isc_heap_insert(pool->active_timeouts,
-                                               tmp_iaaddr);
+       tmp_iapref = NULL;
+       iaprefix_reference(&tmp_iapref, iapref, MDL);
+       if ((tmp_iapref->state == FTS_ACTIVE) ||
+           (tmp_iapref->state == FTS_ABANDONED)) {
+               iaprefix_hash_add(ppool->prefs, &tmp_iapref->pref
+                                 sizeof(tmp_iapref->pref), iapref, MDL);
+               insert_result = isc_heap_insert(ppool->active_timeouts,
+                                               tmp_iapref);
                if (insert_result == ISC_R_SUCCESS)
-                       pool->num_active++;
+                       ppool->num_active++;
        } else {
-               insert_result = isc_heap_insert(pool->inactive_timeouts,
-                                               tmp_iaaddr);
+               insert_result = isc_heap_insert(ppool->inactive_timeouts,
+                                               tmp_iapref);
                if (insert_result == ISC_R_SUCCESS)
-                       pool->num_inactive++;
+                       ppool->num_inactive++;
        }
        if (insert_result != ISC_R_SUCCESS) {
-               iaaddr_hash_delete(pool->addrs, &iaaddr->addr
-                                  sizeof(iaaddr->addr), MDL);
-               iaaddr_dereference(&tmp_iaaddr, MDL);
+               iaprefix_hash_delete(ppool->prefs, &iapref->pref
+                                    sizeof(iapref->pref), MDL);
+               iaprefix_dereference(&tmp_iapref, MDL);
                return insert_result;
        }
 
        /* 
-        * Note: we intentionally leave tmp_iaaddr referenced; there
+        * Note: we intentionally leave tmp_iapref referenced; there
         * is a reference in the heap/hash, after all.
         */
 
@@ -853,16 +1806,20 @@ add_lease6(struct ipv6_pool *pool, struct iaaddr *iaaddr,
 }
 
 /*
- * Determine if an address is present in a pool or not.
+ * Determine if a prefix 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;
+prefix6_exists(const struct ipv6_ppool *ppool,
+              const struct in6_addr *pref, u_int8_t plen) {
+       struct iaprefix *test_iapref;
 
-       test_iaaddr = NULL;
-       if (iaaddr_hash_lookup(&test_iaaddr, pool->addrs, 
-                              (void *)addr, sizeof(*addr), MDL)) {
-               iaaddr_dereference(&test_iaaddr, MDL);
+       if (plen != ppool->alloc_plen)
+               return ISC_FALSE;
+
+       test_iapref = NULL;
+       if (iaprefix_hash_lookup(&test_iapref, ppool->prefs, 
+                                (void *)pref, sizeof(*pref), MDL)) {
+               iaprefix_dereference(&test_iapref, MDL);
                return ISC_TRUE;
        } else {
                return ISC_FALSE;
@@ -870,36 +1827,36 @@ lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
 }
 
 /*
- * Put the lease on our active pool.
+ * Put the prefix on our active pool.
  */
 static isc_result_t
-move_lease_to_active(struct ipv6_pool *pool, struct iaaddr *addr) {
+move_prefix_to_active(struct ipv6_ppool *ppool, struct iaprefix *pref) {
        isc_result_t insert_result;
        int old_heap_index;
 
-       old_heap_index = addr->heap_index;
-       insert_result = isc_heap_insert(pool->active_timeouts, addr);
+       old_heap_index = pref->heap_index;
+       insert_result = isc_heap_insert(ppool->active_timeouts, pref);
        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;
+                       iaprefix_hash_add(ppool->prefs, &pref->pref
+                                 sizeof(pref->pref), pref, MDL);
+               isc_heap_delete(ppool->inactive_timeouts, old_heap_index);
+               ppool->num_active++;
+               ppool->num_inactive--;
+               pref->state = FTS_ACTIVE;
        }
        return insert_result;
 }
 
 /*
- * Renew an lease in the pool.
+ * Renew a prefix in the pool.
  *
- * To do this, first set the new valid_lifetime_end_time for the address
- * and then invoke renew_lease() on the address.
+ * To do this, first set the new valid_lifetime_end_time for the prefix
+ * and then invoke renew_prefix() on the prefix.
  *
  * WARNING: lease times must only be extended, never reduced!!!
  */
 isc_result_t
-renew_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
+renew_prefix6(struct ipv6_ppool *ppool, struct iaprefix *pref) {
        /*
         * If we're already active, then we can just move our expiration
         * time down the heap. 
@@ -907,77 +1864,78 @@ renew_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
         * 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);
+       if (pref->state == FTS_ACTIVE) {
+               isc_heap_decreased(ppool->active_timeouts, pref->heap_index);
                return ISC_R_SUCCESS;
        } else {
-               return move_lease_to_active(pool, addr);
+               return move_prefix_to_active(ppool, pref);
        }
 }
 
 /*
- * Put the lease on our inactive pool, with the specified state.
+ * Put the prefix 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) {
+move_prefix_to_inactive(struct ipv6_ppool *ppool, struct iaprefix *pref
+                       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);
+       old_heap_index = pref->heap_index;
+       insert_result = isc_heap_insert(ppool->inactive_timeouts, pref);
        if (insert_result == ISC_R_SUCCESS) {
                /* Process events upon expiration. */
-               ddns_removals(NULL, addr);
+               /* No DDNS for prefixes. */
 
                /* Binding scopes are no longer valid after expiry or
                 * release.
                 */
-               if (addr->scope != NULL) {
-                       binding_scope_dereference(&addr->scope, MDL);
+               if (pref->scope != NULL) {
+                       binding_scope_dereference(&pref->scope, MDL);
                }
 
-               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++;
+               iaprefix_hash_delete(ppool->prefs, 
+                                    &pref->pref, sizeof(pref->pref), MDL);
+               isc_heap_delete(ppool->active_timeouts, old_heap_index);
+               pref->state = state;
+               ppool->num_active--;
+               ppool->num_inactive++;
        }
        return insert_result;
 }
 
 /*
- * Expire the oldest lease if it's lifetime_end_time is 
+ * Expire the oldest prefix if it's lifetime_end_time is 
  * older than the given time.
  *
- * - iaaddr must be a pointer to a (struct iaaddr *) pointer previously
+ * - iapref must be a pointer to a (struct iaprefix *) pointer previously
  *   initialized to NULL
  *
- * On return iaaddr has a reference to the removed entry. It is left
- * pointing to NULL if the oldest lease has not expired.
+ * On return iapref has a reference to the removed entry. It is left
+ * pointing to NULL if the oldest prefix has not expired.
  */
 isc_result_t
-expire_lease6(struct iaaddr **addr, struct ipv6_pool *pool, time_t now) {
-       struct iaaddr *tmp;
+expire_prefix6(struct iaprefix **pref, struct ipv6_ppool *ppool, time_t now) {
+       struct iaprefix *tmp;
        isc_result_t result;
 
-       if (addr == NULL) {
+       if (pref == NULL) {
                log_error("%s(%d): NULL pointer reference", MDL);
                return ISC_R_INVALIDARG;
        }
-       if (*addr != NULL) {
+       if (*pref != NULL) {
                log_error("%s(%d): non-NULL pointer", MDL);
                return ISC_R_INVALIDARG;
        }
 
-       if (pool->num_active > 0) {
-               tmp = (struct iaaddr *)isc_heap_element(pool->active_timeouts, 
-                                                       1);
+       if (ppool->num_active > 0) {
+               tmp = (struct iaprefix *)
+                       isc_heap_element(ppool->active_timeouts, 1);
                if (now > tmp->valid_lifetime_end_time) {
-                       result = move_lease_to_inactive(pool, tmp, FTS_EXPIRED);
+                       result = move_prefix_to_inactive(ppool, tmp,
+                                                        FTS_EXPIRED);
                        if (result == ISC_R_SUCCESS) {
-                               iaaddr_reference(addr, tmp, MDL);
+                               iaprefix_reference(pref, tmp, MDL);
                        }
                        return result;
                }
@@ -987,32 +1945,12 @@ expire_lease6(struct iaaddr **addr, struct ipv6_pool *pool, time_t now) {
 
 
 /*
- * For a declined lease, leave it on the "active" pool, but mark
- * it as declined. Give it an infinite (well, really long) life.
- */
-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);
-       return ISC_R_SUCCESS;
-}
-
-/*
- * Put the returned lease on our inactive pool.
+ * Put the returned prefix on our inactive pool.
  */
 isc_result_t
-release_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
-       if (addr->state == FTS_ACTIVE) {
-               return move_lease_to_inactive(pool, addr, FTS_RELEASED);
+release_prefix6(struct ipv6_ppool *ppool, struct iaprefix *pref) {
+       if (pref->state == FTS_ACTIVE) {
+               return move_prefix_to_inactive(ppool, pref, FTS_RELEASED);
        } else {
                return ISC_R_SUCCESS;
        }
@@ -1063,12 +2001,38 @@ add_ipv6_pool(struct ipv6_pool *pool) {
        return ISC_R_SUCCESS;
 }
 
+/* 
+ * Add a prefix pool.
+ */
+isc_result_t
+add_ipv6_ppool(struct ipv6_ppool *ppool) {
+       struct ipv6_ppool **new_ppools;
+
+       new_ppools = dmalloc(sizeof(struct ipv6_ppool *) * (num_ppools + 1),
+                            MDL);
+       if (new_ppools == NULL) {
+               return ISC_R_NOMEMORY;
+       }
+
+       if (num_ppools > 0) {
+               memcpy(new_ppools, ppools, 
+                      sizeof(struct ipv6_ppool *) * num_ppools);
+               dfree(ppools, MDL);
+       }
+       ppools = new_ppools;
+
+       ppools[num_ppools] = NULL;
+       ipv6_ppool_reference(&ppools[num_ppools], ppool, MDL);
+       num_ppools++;
+       return ISC_R_SUCCESS;
+}
+
 
 static void
 cleanup_old_expired(struct ipv6_pool *pool) {
        struct iaaddr *tmp;
-       struct ia_na *ia_na;
-       struct ia_na *ia_na_active;
+       struct ia_na *ia;
+       struct ia_na *ia_active;
        unsigned char *tmpd;
        
        while (pool->num_inactive > 0) {
@@ -1084,24 +2048,34 @@ cleanup_old_expired(struct ipv6_pool *pool) {
 
                if (tmp->ia_na != NULL) {
                        /*
-                        * Check to see if this IA_NA is in the active list,
+                        * Check to see if this IA is in the active list,
                         * but has no remaining addresses. If so, remove it
                         * from the active list.
                         */
-                       ia_na = NULL;
-                       ia_na_reference(&ia_na, tmp->ia_na, MDL);
-                       ia_na_remove_iaaddr(ia_na, tmp, MDL);
-                       ia_na_active = NULL;
-                       tmpd = (unsigned char *)ia_na->iaid_duid.data;
-                       if ((ia_na->num_iaaddr <= 0) &&
-                           (ia_na_hash_lookup(&ia_na_active, ia_active, tmpd,
-                                              ia_na->iaid_duid.len,
+                       ia = NULL;
+                       ia_na_reference(&ia, tmp->ia_na, MDL);
+                       ia_na_remove_iaaddr(ia, tmp, MDL);
+                       ia_active = NULL;
+                       tmpd = (unsigned char *)ia->iaid_duid.data;
+                       if ((ia->ia_type == D6O_IA_NA) &&
+                           (ia->num_iaaddr <= 0) &&
+                           (ia_na_hash_lookup(&ia_active, ia_na_active, tmpd,
+                                              ia->iaid_duid.len,
+                                              MDL) == 0) &&
+                           (ia_active == ia)) {
+                               ia_na_hash_delete(ia_na_active, tmpd, 
+                                                 ia->iaid_duid.len, MDL);
+                       }
+                       if ((ia->ia_type == D6O_IA_TA) &&
+                           (ia->num_iaaddr <= 0) &&
+                           (ia_na_hash_lookup(&ia_active, ia_ta_active, tmpd,
+                                              ia->iaid_duid.len,
                                               MDL) == 0) &&
-                           (ia_na_active == ia_na)) {
-                               ia_na_hash_delete(ia_active, tmpd, 
-                                                 ia_na->iaid_duid.len, MDL);
+                           (ia_active == ia)) {
+                               ia_na_hash_delete(ia_ta_active, tmpd, 
+                                                 ia->iaid_duid.len, MDL);
                        }
-                       ia_na_dereference(&ia_na, MDL);
+                       ia_na_dereference(&ia, MDL);
                }
                iaaddr_dereference(&tmp, MDL);
        }
@@ -1137,7 +2111,7 @@ lease_timeout_support(void *vpool) {
                 */
                ddns_removals(NULL, addr);
 
-               write_ia_na(addr->ia_na);
+               write_ia(addr->ia_na);
 
                iaaddr_dereference(&addr, MDL);
        }
@@ -1202,6 +2176,138 @@ schedule_all_ipv6_lease_timeouts(void) {
        }
 }
 
+static void
+cleanup_old_pexpired(struct ipv6_ppool *ppool) {
+       struct iaprefix *tmp;
+       struct ia_pd *ia_pd;
+       struct ia_pd *ia_active;
+       unsigned char *tmpd;
+       
+       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) {
+                       break;
+               }
+
+               isc_heap_delete(ppool->inactive_timeouts, tmp->heap_index);
+               ppool->num_inactive--;
+
+               if (tmp->ia_pd != NULL) {
+                       /*
+                        * Check to see if this IA_PD is in the active list,
+                        * but has no remaining prefixes. If so, remove it
+                        * from the active list.
+                        */
+                       ia_pd = NULL;
+                       ia_pd_reference(&ia_pd, tmp->ia_pd, MDL);
+                       ia_pd_remove_iaprefix(ia_pd, tmp, MDL);
+                       ia_active = NULL;
+                       tmpd = (unsigned char *)ia_pd->iaid_duid.data;
+                       if ((ia_pd->num_iaprefix <= 0) &&
+                           (ia_pd_hash_lookup(&ia_active, ia_pd_active,
+                                              tmpd, ia_pd->iaid_duid.len,
+                                              MDL) == 0) &&
+                           (ia_active == ia_pd)) {
+                               ia_pd_hash_delete(ia_pd_active, tmpd, 
+                                                 ia_pd->iaid_duid.len, MDL);
+                       }
+                       ia_pd_dereference(&ia_pd, MDL);
+               }
+               iaprefix_dereference(&tmp, MDL);
+       }
+}
+
+static void
+prefix_timeout_support(void *vppool) {
+       struct ipv6_ppool *ppool;
+       struct iaprefix *pref;
+       
+       ppool = (struct ipv6_ppool *)vppool;
+       for (;;) {
+               /*
+                * Get the next prefix scheduled to expire.
+                *
+                * Note that if there are no prefixes in the pool, 
+                * expire_prefix6() will return ISC_R_SUCCESS with 
+                * a NULL prefix.
+                */
+               pref = NULL;
+               if (expire_prefix6(&pref, ppool, cur_time) != ISC_R_SUCCESS) {
+                       break;
+               }
+               if (pref == NULL) {
+                       break;
+               }
+
+               /* No DDNS for prefixes. */
+
+               write_ia_pd(pref->ia_pd);
+
+               iaprefix_dereference(&pref, MDL);
+       }
+
+       /*
+        * Do some cleanup of our expired prefixes.
+        */
+       cleanup_old_pexpired(ppool);
+
+       /*
+        * Schedule next round of expirations.
+        */
+       schedule_prefix_timeout(ppool);
+}
+
+/*
+ * For a given prefix pool, add a timer that will remove the next
+ * prefix to expire.
+ */
+void 
+schedule_prefix_timeout(struct ipv6_ppool *ppool) {
+       struct iaprefix *tmp;
+       time_t timeout;
+       time_t next_timeout;
+
+       next_timeout = MAX_TIME;
+
+       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 (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 (timeout < next_timeout) {
+                       next_timeout = timeout;
+               }
+       }
+
+       if (next_timeout < MAX_TIME) {
+               add_timeout(next_timeout, prefix_timeout_support, ppool,
+                           (tvref_t)ipv6_ppool_reference, 
+                           (tvunref_t)ipv6_ppool_dereference);
+       }
+}
+
+/*
+ * Schedule timeouts across all pools.
+ */
+void
+schedule_all_ipv6_prefix_timeouts(void) {
+       int i;
+
+       for (i=0; i<num_ppools; i++) {
+               schedule_prefix_timeout(ppools[i]);
+       }
+}
+
 /* 
  * Given an address and the length of the network mask, return
  * only the network portion.
@@ -1303,7 +2409,7 @@ find_ipv6_pool(struct ipv6_pool **pool, const struct in6_addr *addr) {
  * pools.
  */
 static isc_result_t 
-change_leases(struct ia_na *ia_na
+change_leases(struct ia_na *ia, 
              isc_result_t (*change_func)(struct ipv6_pool *, struct iaaddr*)) {
        isc_result_t retval;
        isc_result_t renew_retval;
@@ -1312,11 +2418,11 @@ change_leases(struct ia_na *ia_na,
        int i;
 
        retval = ISC_R_SUCCESS;
-       for (i=0; i<ia_na->num_iaaddr; i++) {
+       for (i=0; i<ia->num_iaaddr; i++) {
                pool = NULL;
-               addr = &ia_na->iaaddr[i]->addr;
+               addr = &ia->iaaddr[i]->addr;
                if (find_ipv6_pool(&pool, addr) == ISC_R_SUCCESS) {
-                       renew_retval =  change_func(pool, ia_na->iaaddr[i]);
+                       renew_retval =  change_func(pool, ia->iaaddr[i]);
                        if (renew_retval != ISC_R_SUCCESS) {
                                retval = renew_retval;
                        }
@@ -1327,31 +2433,124 @@ change_leases(struct ia_na *ia_na,
 }
 
 /*
- * Renew all leases in an IA_NA from all pools.
+ * Renew all leases in an IA from all pools.
  *
  * The new valid_lifetime_end_time should be updated for the addresses.
  *
  * WARNING: lease times must only be extended, never reduced!!!
  */
 isc_result_t 
-renew_leases(struct ia_na *ia_na) {
-       return change_leases(ia_na, renew_lease6);
+renew_leases(struct ia_na *ia) {
+       return change_leases(ia, renew_lease6);
+}
+
+/*
+ * Release all leases in an IA from all pools.
+ */
+isc_result_t 
+release_leases(struct ia_na *ia) {
+       return change_leases(ia, release_lease6);
+}
+
+/*
+ * Decline all leases in an IA from all pools.
+ */
+isc_result_t 
+decline_leases(struct ia_na *ia) {
+       return change_leases(ia, decline_lease6);
+}
+
+/*
+ * Determine if the given prefix is in the pool.
+ */
+isc_boolean_t
+ipv6_prefix_in_ppool(const struct in6_addr *pref,
+                    const struct ipv6_ppool *ppool) {
+       struct in6_addr tmp;
+       
+       ipv6_network_portion(&tmp, pref, (int)ppool->pool_plen);
+       if (memcmp(&tmp, &ppool->start_pref, sizeof(tmp)) == 0) {
+               return ISC_TRUE;
+       } else {
+               return ISC_FALSE;
+       }
+}
+
+/*
+ * Find the pool that contains the given prefix.
+ *
+ * - pool must be a pointer to a (struct ipv6_ppool *) pointer previously
+ *   initialized to NULL
+ */
+isc_result_t
+find_ipv6_ppool(struct ipv6_ppool **ppool, const struct in6_addr *pref) {
+       int i;
+
+       if (ppool == NULL) {
+               log_error("%s(%d): NULL pointer reference", MDL);
+               return ISC_R_INVALIDARG;
+       }
+       if (*ppool != NULL) {
+               log_error("%s(%d): non-NULL pointer", MDL);
+               return ISC_R_INVALIDARG;
+       }
+
+       for (i=0; i<num_ppools; i++) {
+               if (ipv6_prefix_in_ppool(pref, ppools[i])) { 
+                       ipv6_ppool_reference(ppool, ppools[i], MDL);
+                       return ISC_R_SUCCESS;
+               }
+       }
+       return ISC_R_NOTFOUND;
+}
+
+/*
+ * Helper function for the various functions that act across all
+ * prefix pools.
+ */
+static isc_result_t 
+change_prefixes(struct ia_pd *ia_pd, 
+               isc_result_t (*change_func)(struct ipv6_ppool *,
+                                           struct iaprefix*)) {
+       isc_result_t retval;
+       isc_result_t renew_retval;
+       struct ipv6_ppool *ppool;
+       struct in6_addr *pref;
+       int i;
+
+       retval = ISC_R_SUCCESS;
+       for (i=0; i<ia_pd->num_iaprefix; i++) {
+               ppool = NULL;
+               pref = &ia_pd->iaprefix[i]->pref;
+               if (find_ipv6_ppool(&ppool, pref) == ISC_R_SUCCESS) {
+                       renew_retval = change_func(ppool, ia_pd->iaprefix[i]);
+                       if (renew_retval != ISC_R_SUCCESS) {
+                               retval = renew_retval;
+                       }
+               }
+               /* XXXsk: should we warn if we don't find a pool? */
+       }
+       return retval;
 }
 
 /*
- * Release all leases in an IA_NA from all pools.
+ * Renew all prefixes in an IA_PD from all pools.
+ *
+ * The new valid_lifetime_end_time should be updated for the addresses.
+ *
+ * WARNING: lease times must only be extended, never reduced!!!
  */
 isc_result_t 
-release_leases(struct ia_na *ia_na) {
-       return change_leases(ia_na, release_lease6);
+renew_prefixes(struct ia_pd *ia_pd) {
+       return change_prefixes(ia_pd, renew_prefix6);
 }
 
 /*
- * Decline all leases in an IA_NA from all pools.
+ * Release all prefixes in an IA_PD from all pools.
  */
 isc_result_t 
-decline_leases(struct ia_na *ia_na) {
-       return change_leases(ia_na, decline_lease6);
+release_prefixes(struct ia_pd *ia_pd) {
+       return change_prefixes(ia_pd, release_prefix6);
 }
 
 #ifdef DHCPv6
@@ -1361,11 +2560,26 @@ decline_leases(struct ia_na *ia_na) {
 static int write_error;
 
 static isc_result_t 
-write_ia_na_leases(const void *name, unsigned len, void *value) {
-       struct ia_na *ia_na = (struct ia_na *)value;
+write_ia_leases(const void *name, unsigned len, void *value) {
+       struct ia_na *ia = (struct ia_na *)value;
+       
+       if (!write_error) { 
+               if (!write_ia(ia)) {
+                       write_error = 1;
+               }
+       }
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * Helper function to output prefixes.
+ */
+static isc_result_t 
+write_ia_pd_prefixes(const void *name, unsigned len, void *value) {
+       struct ia_pd *ia_pd = (struct ia_pd *)value;
        
        if (!write_error) { 
-               if (!write_ia_na(ia_na)) {
+               if (!write_ia_pd(ia_pd)) {
                        write_error = 1;
                }
        }
@@ -1379,7 +2593,15 @@ int
 write_leases6(void) {
        write_error = 0;
        write_server_duid();
-       ia_na_hash_foreach(ia_active, write_ia_na_leases);
+       ia_na_hash_foreach(ia_na_active, write_ia_leases);
+       if (write_error) {
+               return 0;
+       }
+       ia_na_hash_foreach(ia_ta_active, write_ia_leases);
+       if (write_error) {
+               return 0;
+       }
+       ia_pd_hash_foreach(ia_pd_active, write_ia_pd_prefixes);
        if (write_error) {
                return 0;
        }
@@ -2070,6 +3292,26 @@ main(int argc, char *argv[]) {
                return 1;
        }*/
 
+       {
+               struct in6_addr r;
+               struct data_string ds;
+               char buf[64];
+               int i, j;
+
+               ds.len = 16;
+               ds.data = &addr;
+
+               inet_pton(AF_INET6, "3ffe:501:ffff:100::", &addr);
+               for (i = 32; i < 42; i++)
+                       for (j = i + 1; j < 49; j++) {
+                               memset(&r, 0, sizeof(r));
+                               memset(buf, 0, 64);
+                               create_prefix(&r, &addr, i, j, &ds);
+                               inet_ntop(AF_INET6, &r, buf, 64);
+                               printf("%d,%d-> %s/%d\n", i, j, buf, j);
+                       }
+       }
+       
        printf("SUCCESS: all tests passed (ignore any warning messages)\n");
        return 0;
 }