summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/clparse.c394
-rw-r--r--client/dhc6.c447
-rw-r--r--client/dhclient.c40
-rw-r--r--common/conflex.c6
-rw-r--r--common/parse.c41
-rw-r--r--includes/dhcpd.h119
-rw-r--r--includes/dhctoken.h29
-rw-r--r--server/confpars.c620
-rw-r--r--server/db.c127
-rw-r--r--server/dhcpd.c12
-rw-r--r--server/dhcpv6.c126
-rw-r--r--server/mdb6.c1414
12 files changed, 3151 insertions, 224 deletions
diff --git a/client/clparse.c b/client/clparse.c
index a4921f57..6adad35f 100644
--- a/client/clparse.c
+++ b/client/clparse.c
@@ -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;
diff --git a/client/dhc6.c b/client/dhc6.c
index 8bf89b09..c84a8a53 100644
--- a/client/dhc6.c
+++ b/client/dhc6.c
@@ -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,
diff --git a/client/dhclient.c b/client/dhclient.c
index 6b0bd0a4..c2276875 100644
--- a/client/dhclient.c
+++ b/client/dhclient.c
@@ -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;
diff --git a/common/conflex.c b/common/conflex.c
index 25767bf8..2c3621fe 100644
--- a/common/conflex.c
+++ b/common/conflex.c
@@ -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"))
diff --git a/common/parse.c b/common/parse.c
index ba53584f..c1c66425 100644
--- a/common/parse.c
+++ b/common/parse.c
@@ -435,6 +435,47 @@ parse_ip6_addr_expr(struct expression **expr,
}
/*
+ * 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
*/
diff --git a/includes/dhcpd.h b/includes/dhcpd.h
index 1d82eb33..243c7857 100644
--- a/includes/dhcpd.h
+++ b/includes/dhcpd.h
@@ -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);
diff --git a/includes/dhctoken.h b/includes/dhctoken.h
index 80ad0b3d..dd39a3e6 100644
--- a/includes/dhctoken.h
+++ b/includes/dhctoken.h
@@ -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 && \
diff --git a/server/confpars.c b/server/confpars.c
index 715e6542..7a0a3d21 100644
--- a/server/confpars.c
+++ b/server/confpars.c
@@ -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) */
}
diff --git a/server/db.c b/server/db.c
index 3c92c5fa..424a3abf 100644
--- a/server/db.c
+++ b/server/db.c
@@ -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;
}
diff --git a/server/dhcpd.c b/server/dhcpd.c
index 904d50d6..41e3813a 100644
--- a/server/dhcpd.c
+++ b/server/dhcpd.c
@@ -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 */
diff --git a/server/dhcpv6.c b/server/dhcpv6.c
index 6a34a965..94dbe411 100644
--- a/server/dhcpv6.c
+++ b/server/dhcpv6.c
@@ -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);
}
}
diff --git a/server/mdb6.c b/server/mdb6.c
index 70b6c7f2..0c08ea93 100644
--- a/server/mdb6.c
+++ b/server/mdb6.c
@@ -32,14 +32,26 @@
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)
@@ -364,6 +474,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.
*/
if (a->iaid_duid.len != b->iaid_duid.len) {
@@ -389,7 +506,257 @@ 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;
+ }
+ }
+ if (!found) {
+ return ISC_FALSE;
+ }
+ }
+
+ /*
+ * These are the same in every way we care about.
+ */
+ return ISC_TRUE;
+}
+
+/*
+ * Create a new IA_PD structure.
+ *
+ * - 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
+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 (ia_pd == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (*ia_pd != 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;
+ }
+
+ 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_pd = tmp;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IA_PD structure.
+ *
+ * - ia_pd must be a pointer to a (struct ia_pd *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+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 (*ia_pd != 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;
+ }
+ *ia_pd = src;
+ src->refcnt++;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Dereference an IA_PD structure.
+ *
+ * If it is the last reference, then the memory for the
+ * structure is freed.
+ */
+isc_result_t
+ia_pd_dereference(struct ia_pd **ia_pd, const char *file, int line) {
+ struct ia_pd *tmp;
+ int i;
+
+ if ((ia_pd == NULL) || (*ia_pd == NULL)) {
+ log_error("%s(%d): NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+
+ tmp = *ia_pd;
+ *ia_pd = NULL;
+
+ tmp->refcnt--;
+ if (tmp->refcnt < 0) {
+ log_error("%s(%d): negative refcnt", file, line);
+ tmp->refcnt = 0;
+ }
+ if (tmp->refcnt == 0) {
+ 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;
+}
+
+
+/*
+ * Add an IAPREFIX entry to an IA_PD structure.
+ */
+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;
+
+ /*
+ * 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.
+ */
+ 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;
+ }
+
+ 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;
}
@@ -408,6 +775,7 @@ 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.
+ * Note: this relies on the unique layout for leases!
*/
static isc_boolean_t
lease_older(void *a, void *b) {
@@ -419,7 +787,7 @@ lease_older(void *a, void *b) {
}
/*
- * Helper function for lease heaps.
+ * Helper function for lease address heaps.
* Callback when an address's position in the heap changes.
*/
static void
@@ -427,9 +795,18 @@ 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 pool structure.
+ * Create a new IPv6 lease (address) pool structure.
*
* - pool must be a pointer to a (struct ipv6_pool *) pointer previously
* initialized to NULL
@@ -576,6 +953,156 @@ ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int 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.
@@ -1018,6 +1545,417 @@ release_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
}
}
+/*
+ * 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 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
+ * 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 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_prefix(struct ipv6_ppool *ppool, struct iaprefix **pref,
+ unsigned int *attempts,
+ const struct data_string *uid,
+ time_t valid_lifetime_end_time) {
+ struct data_string ds;
+ struct in6_addr tmp;
+ struct iaprefix *test_iapref;
+ struct data_string new_ds;
+ struct iaprefix *iapref;
+ isc_result_t result;
+
+ /*
+ * 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) > 10) {
+ data_string_forget(&ds, MDL);
+ return ISC_R_NORESOURCES;
+ }
+
+ /*
+ * Create a prefix
+ */
+ create_prefix(&tmp, &ppool->start_pref,
+ (int)ppool->pool_plen, (int)ppool->alloc_plen,
+ &ds);
+
+ /*
+ * If this prefix is not in use, we're happy with it
+ */
+ test_iapref = NULL;
+ if (iaprefix_hash_lookup(&test_iapref, ppool->prefs,
+ &tmp, sizeof(tmp), MDL) == 0) {
+ break;
+ }
+ iaprefix_dereference(&test_iapref, MDL);
+
+ /*
+ * Otherwise, we create a new input, adding the prefix
+ */
+ 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 prefix, create an IAPREFIX
+ * to hold it.
+ */
+ iapref = NULL;
+ result = iaprefix_allocate(&iapref, MDL);
+ if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ memcpy(&iapref->pref, &tmp, sizeof(iapref->pref));
+
+ /*
+ * Add the prefix to the pool.
+ */
+ result = add_prefix6(ppool, iapref, valid_lifetime_end_time);
+ if (result == ISC_R_SUCCESS) {
+ iaprefix_reference(pref, iapref, MDL);
+ }
+ iaprefix_dereference(&iapref, MDL);
+ return result;
+}
+
+/*
+ * Put a prefix in the pool directly. This is intended to be used when
+ * loading leases from the file.
+ */
+isc_result_t
+add_prefix6(struct ipv6_ppool *ppool, struct iaprefix *iapref,
+ time_t valid_lifetime_end_time) {
+ isc_result_t insert_result;
+ struct iaprefix *test_iapref;
+ struct iaprefix *tmp_iapref;
+
+ /* If a state was not assigned by the caller, assume active. */
+ if (iapref->state == 0)
+ iapref->state = FTS_ACTIVE;
+
+ iapref->valid_lifetime_end_time = valid_lifetime_end_time;
+ ipv6_ppool_reference(&iapref->ipv6_ppool, ppool, MDL);
+
+ /*
+ * If this IAPREFIX is already in our structures, remove the
+ * old one.
+ */
+ 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
+ * prefix on its heap" rather than doing these if's
+ * everywhere. If you add more states to this list, don't.
+ */
+ 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(ppool->inactive_timeouts,
+ test_iapref->heap_index);
+ ppool->num_inactive--;
+ }
+
+ 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_iapref), and then one
+ * more time to remove the reference left when the
+ * prefix was added to the pool before.
+ */
+ tmp_iapref = test_iapref;
+ iaprefix_dereference(&test_iapref, MDL);
+ iaprefix_dereference(&tmp_iapref, MDL);
+ }
+
+ /*
+ * Add IAPREFIX to our structures.
+ */
+ 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)
+ ppool->num_active++;
+ } else {
+ insert_result = isc_heap_insert(ppool->inactive_timeouts,
+ tmp_iapref);
+ if (insert_result == ISC_R_SUCCESS)
+ ppool->num_inactive++;
+ }
+ if (insert_result != ISC_R_SUCCESS) {
+ iaprefix_hash_delete(ppool->prefs, &iapref->pref,
+ sizeof(iapref->pref), MDL);
+ iaprefix_dereference(&tmp_iapref, MDL);
+ return insert_result;
+ }
+
+ /*
+ * Note: we intentionally leave tmp_iapref referenced; there
+ * is a reference in the heap/hash, after all.
+ */
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Determine if a prefix is present in a pool or not.
+ */
+isc_boolean_t
+prefix6_exists(const struct ipv6_ppool *ppool,
+ const struct in6_addr *pref, u_int8_t plen) {
+ struct iaprefix *test_iapref;
+
+ 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;
+ }
+}
+
+/*
+ * Put the prefix on our active pool.
+ */
+static isc_result_t
+move_prefix_to_active(struct ipv6_ppool *ppool, struct iaprefix *pref) {
+ isc_result_t insert_result;
+ int old_heap_index;
+
+ old_heap_index = pref->heap_index;
+ insert_result = isc_heap_insert(ppool->active_timeouts, pref);
+ if (insert_result == ISC_R_SUCCESS) {
+ 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 a prefix in the pool.
+ *
+ * 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_prefix6(struct ipv6_ppool *ppool, struct iaprefix *pref) {
+ /*
+ * 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 (pref->state == FTS_ACTIVE) {
+ isc_heap_decreased(ppool->active_timeouts, pref->heap_index);
+ return ISC_R_SUCCESS;
+ } else {
+ return move_prefix_to_active(ppool, pref);
+ }
+}
+
+/*
+ * Put the prefix on our inactive pool, with the specified state.
+ */
+static isc_result_t
+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 = pref->heap_index;
+ insert_result = isc_heap_insert(ppool->inactive_timeouts, pref);
+ if (insert_result == ISC_R_SUCCESS) {
+ /* Process events upon expiration. */
+ /* No DDNS for prefixes. */
+
+ /* Binding scopes are no longer valid after expiry or
+ * release.
+ */
+ if (pref->scope != NULL) {
+ binding_scope_dereference(&pref->scope, MDL);
+ }
+
+ 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 prefix if it's lifetime_end_time is
+ * older than the given time.
+ *
+ * - iapref must be a pointer to a (struct iaprefix *) pointer previously
+ * initialized to NULL
+ *
+ * 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_prefix6(struct iaprefix **pref, struct ipv6_ppool *ppool, time_t now) {
+ struct iaprefix *tmp;
+ isc_result_t result;
+
+ if (pref == NULL) {
+ log_error("%s(%d): NULL pointer reference", MDL);
+ return ISC_R_INVALIDARG;
+ }
+ if (*pref != NULL) {
+ log_error("%s(%d): non-NULL pointer", MDL);
+ return ISC_R_INVALIDARG;
+ }
+
+ if (ppool->num_active > 0) {
+ tmp = (struct iaprefix *)
+ isc_heap_element(ppool->active_timeouts, 1);
+ if (now > tmp->valid_lifetime_end_time) {
+ result = move_prefix_to_inactive(ppool, tmp,
+ FTS_EXPIRED);
+ if (result == ISC_R_SUCCESS) {
+ iaprefix_reference(pref, tmp, MDL);
+ }
+ return result;
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+
+/*
+ * Put the returned prefix on our inactive pool.
+ */
+isc_result_t
+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;
+ }
+}
+
/*
* Mark an IPv6 address as unavailable from a pool.
*
@@ -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_NA from all pools.
+ * Release all leases in an IA from all pools.
*/
isc_result_t
-release_leases(struct ia_na *ia_na) {
- return change_leases(ia_na, release_lease6);
+release_leases(struct ia_na *ia) {
+ return change_leases(ia, release_lease6);
}
/*
- * Decline all leases in an IA_NA from all pools.
+ * Decline all leases in an IA from all pools.
*/
isc_result_t
-decline_leases(struct ia_na *ia_na) {
- return change_leases(ia_na, decline_lease6);
+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;
+}
+
+/*
+ * 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
+renew_prefixes(struct ia_pd *ia_pd) {
+ return change_prefixes(ia_pd, renew_prefix6);
+}
+
+/*
+ * Release all prefixes in an IA_PD from all pools.
+ */
+isc_result_t
+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;
}