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,
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);
&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))
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;
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)
memcpy(©->address, &addr->address, sizeof(copy->address));
+ copy->plen = addr->plen;
copy->flags = addr->flags;
copy->starts = addr->starts;
copy->preferred_life = addr->preferred_life;
}
/* 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)
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
&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);
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,
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)
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 ;
&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;
}
&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;
}
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;
}
}
struct dhc6_addr *addr;
isc_result_t rval = ISC_R_SUCCESS;
unsigned code;
+ const char *scope;
int nscore, sscore;
if ((client == NULL) || (new == NULL))
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;
}
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;
}
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;
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;
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;
/* 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;
}
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);
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;
&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;
}
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;
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;
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,
"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);
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;
}
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) {
}
/* 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,
/*
* 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) */
}
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.
*
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);
}
/*
- * 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
*
* 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;
}
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;
}
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) {
/*
- * 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;
* 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)
}
}
+ /*
+ * Check the type is the same.
+ */
+ if (a->ia_type != b->ia_type) {
+ return ISC_FALSE;
+ }
+
/*
* Check the DUID is the same.
*/
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;
}
}
/*
- * Helper function for lease heaps.
- * Makes the top of the heap the oldest lease.
- */
-static isc_boolean_t
-lease_older(void *a, void *b) {
- struct iaaddr *ia = (struct iaaddr *)a;
- struct iaaddr *ib = (struct iaaddr *)b;
-
- return difftime(ia->valid_lifetime_end_time,
- ib->valid_lifetime_end_time) < 0;
-}
-
-/*
- * Helper function for lease heaps.
- * Callback when an address's position in the heap changes.
- */
-static void
-lease_address_index_changed(void *iaaddr, unsigned int new_heap_index) {
- ((struct iaaddr *)iaaddr)-> heap_index = new_heap_index;
-}
-
-
-/*
- * Create a new IPv6 lease pool structure.
+ * Create a new IA_PD structure.
*
- * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
+ * - ia_pd must be a pointer to a (struct ia_pd *) pointer previously
* initialized to NULL
+ * - iaid and duid are values from the client
+ *
+ * XXXsk: we don't concern ourself with the byte order of the IAID,
+ * which might be a problem if we transfer this structure
+ * between machines of different byte order
*/
isc_result_t
-ipv6_pool_allocate(struct ipv6_pool **pool,
- const struct in6_addr *start_addr, int bits,
- const char *file, int line) {
- struct ipv6_pool *tmp;
+ia_pd_allocate(struct ia_pd **ia_pd, u_int32_t iaid,
+ const char *duid, unsigned int duid_len,
+ const char *file, int line) {
+ struct ia_pd *tmp;
- if (pool == NULL) {
+ if (ia_pd == NULL) {
log_error("%s(%d): NULL pointer reference", file, line);
return ISC_R_INVALIDARG;
}
- if (*pool != NULL) {
+ if (*ia_pd != NULL) {
log_error("%s(%d): non-NULL pointer", file, line);
return ISC_R_INVALIDARG;
}
return ISC_R_NOMEMORY;
}
- tmp->refcnt = 1;
- tmp->start_addr = *start_addr;
- tmp->bits = bits;
- if (!iaaddr_new_hash(&tmp->addrs, DEFAULT_HASH_SIZE, file, line)) {
- dfree(tmp, file, line);
- return ISC_R_NOMEMORY;
- }
- if (isc_heap_create(lease_older, lease_address_index_changed,
- 0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
- iaaddr_free_hash_table(&(tmp->addrs), file, line);
- dfree(tmp, file, line);
- return ISC_R_NOMEMORY;
- }
- if (isc_heap_create(lease_older, lease_address_index_changed,
- 0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
- isc_heap_destroy(&(tmp->active_timeouts));
- iaaddr_free_hash_table(&(tmp->addrs), file, line);
+ if (ia_make_key(&tmp->iaid_duid, iaid,
+ duid, duid_len, file, line) != ISC_R_SUCCESS) {
dfree(tmp, file, line);
return ISC_R_NOMEMORY;
}
- *pool = tmp;
+ tmp->refcnt = 1;
+
+ *ia_pd = tmp;
return ISC_R_SUCCESS;
}
/*
- * Reference an IPv6 pool structure.
+ * Reference an IA_PD structure.
*
- * - pool must be a pointer to a (struct pool *) pointer previously
+ * - ia_pd must be a pointer to a (struct ia_pd *) pointer previously
* initialized to NULL
*/
isc_result_t
-ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src,
- const char *file, int line) {
- if (pool == NULL) {
+ia_pd_reference(struct ia_pd **ia_pd, struct ia_pd *src,
+ const char *file, int line) {
+ if (ia_pd == NULL) {
log_error("%s(%d): NULL pointer reference", file, line);
return ISC_R_INVALIDARG;
}
- if (*pool != NULL) {
+ if (*ia_pd != NULL) {
log_error("%s(%d): non-NULL pointer", file, line);
return ISC_R_INVALIDARG;
}
log_error("%s(%d): NULL pointer reference", file, line);
return ISC_R_INVALIDARG;
}
- *pool = src;
+ *ia_pd = src;
src->refcnt++;
return ISC_R_SUCCESS;
}
-/*
- * Note: Each IAADDR in a pool is referenced by the pool. This is needed
- * to prevent the IAADDR from being garbage collected out from under the
- * pool.
- *
- * The references are made from the hash and from the heap. The following
- * helper functions dereference these when a pool is destroyed.
- */
-
-/*
- * Helper function for pool cleanup.
- * Dereference each of the hash entries in a pool.
- */
-static isc_result_t
-dereference_hash_entry(const void *name, unsigned len, void *value) {
- struct iaaddr *iaaddr = (struct iaaddr *)value;
-
- iaaddr_dereference(&iaaddr, MDL);
- return ISC_R_SUCCESS;
-}
-
-/*
- * Helper function for pool cleanup.
- * Dereference each of the heap entries in a pool.
- */
-static void
-dereference_heap_entry(void *value, void *dummy) {
- struct iaaddr *iaaddr = (struct iaaddr *)value;
-
- iaaddr_dereference(&iaaddr, MDL);
-}
-
-
/*
- * Dereference an IPv6 pool structure.
+ * Dereference an IA_PD structure.
*
* If it is the last reference, then the memory for the
* structure is freed.
*/
isc_result_t
-ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) {
- struct ipv6_pool *tmp;
+ia_pd_dereference(struct ia_pd **ia_pd, const char *file, int line) {
+ struct ia_pd *tmp;
+ int i;
- if ((pool == NULL) || (*pool == NULL)) {
+ if ((ia_pd == NULL) || (*ia_pd == NULL)) {
log_error("%s(%d): NULL pointer", file, line);
return ISC_R_INVALIDARG;
}
- tmp = *pool;
- *pool = NULL;
+ tmp = *ia_pd;
+ *ia_pd = NULL;
tmp->refcnt--;
if (tmp->refcnt < 0) {
tmp->refcnt = 0;
}
if (tmp->refcnt == 0) {
- iaaddr_hash_foreach(tmp->addrs, dereference_hash_entry);
- iaaddr_free_hash_table(&(tmp->addrs), file, line);
- isc_heap_foreach(tmp->active_timeouts,
- dereference_heap_entry, NULL);
- isc_heap_destroy(&(tmp->active_timeouts));
- isc_heap_foreach(tmp->inactive_timeouts,
- dereference_heap_entry, NULL);
- isc_heap_destroy(&(tmp->inactive_timeouts));
+ if (tmp->iaprefix != NULL) {
+ for (i=0; i<tmp->num_iaprefix; i++) {
+ iaprefix_dereference(&(tmp->iaprefix[i]),
+ file, line);
+ }
+ dfree(tmp->iaprefix, file, line);
+ }
+ data_string_forget(&(tmp->iaid_duid), file, line);
dfree(tmp, file, line);
}
-
return ISC_R_SUCCESS;
}
-/*
- * Create an address by hashing the input, and using that for
- * the non-network part.
+
+/*
+ * Add an IAPREFIX entry to an IA_PD structure.
*/
-static void
-create_address(struct in6_addr *addr,
- const struct in6_addr *net_start_addr, int net_bits,
- const struct data_string *input) {
- MD5_CTX ctx;
- int net_bytes;
- int i;
- char *str;
- const char *net_str;
+isc_result_t
+ia_pd_add_iaprefix(struct ia_pd *ia_pd, struct iaprefix *iapref,
+ const char *file, int line) {
+ int max;
+ struct iaprefix **new;
/*
- * Use MD5 to get a nice 128 bit hash of the input.
- * Yes, we know MD5 isn't cryptographically sound.
- * No, we don't care.
- */
- MD5_Init(&ctx);
- MD5_Update(&ctx, input->data, input->len);
- MD5_Final((unsigned char *)addr, &ctx);
-
- /*
- * Copy the network bits over.
+ * Grow our array if we need to.
+ *
+ * Note: we pick 4 as the increment, as that seems a reasonable
+ * guess as to how many prefixes we might expect on an
+ * interface.
*/
- str = (char *)addr;
- net_str = (const char *)net_start_addr;
- net_bytes = net_bits / 8;
- for (i=0; i<net_bytes; i++) {
- str[i] = net_str[i];
- }
- switch (net_bits % 8) {
- case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
- case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
- case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
- case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
- case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
- case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
- case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
+ if (ia_pd->max_iaprefix <= ia_pd->num_iaprefix) {
+ max = ia_pd->max_iaprefix + 4;
+ new = dmalloc(max * sizeof(struct iaprefix *), file, line);
+ if (new == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+ memcpy(new, ia_pd->iaprefix,
+ ia_pd->num_iaprefix * sizeof(struct iaprefix *));
+ ia_pd->iaprefix = new;
+ ia_pd->max_iaprefix = max;
}
- /* set the 'u' bit to zero for /64s. */
- if (net_bits == 64)
- str[8] &= ~0x02;
-}
-/* Reserved Subnet Router Anycast ::0:0:0:0. */
+ iaprefix_reference(&(ia_pd->iaprefix[ia_pd->num_iaprefix]), iapref,
+ file, line);
+ ia_pd->num_iaprefix++;
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Remove an IAPREFIX entry to an IA_PD structure.
+ *
+ * Note: if an IAPREFIX appears more than once, then only ONE will be removed.
+ */
+void
+ia_pd_remove_iaprefix(struct ia_pd *ia_pd, struct iaprefix *iapref,
+ const char *file, int line) {
+ int i, j;
+
+ for (i=0; i<ia_pd->num_iaprefix; i++) {
+ if (ia_pd->iaprefix[i] == iapref) {
+ /* remove this IAPREFIX */
+ iaprefix_dereference(&(ia_pd->iaprefix[i]),
+ file, line);
+ /* move remaining IAPREFIX pointers down one */
+ for (j=i+1; j < ia_pd->num_iaprefix; j++) {
+ ia_pd->iaprefix[j-1] = ia_pd->iaprefix[j];
+ }
+ /* decrease our total count */
+ /* remove the back-reference in the IAPREFIX itself */
+ ia_pd_dereference(&iapref->ia_pd, file, line);
+ ia_pd->num_iaprefix--;
+ return;
+ }
+ }
+ log_error("%s(%d): IAPREFIX not in IA_PD", file, line);
+}
+
+/*
+ * Remove all prefixes from an IA_PD.
+ */
+void
+ia_pd_remove_all_iaprefix(struct ia_pd *ia_pd, const char *file, int line) {
+ int i;
+
+ for (i=0; i<ia_pd->num_iaprefix; i++) {
+ ia_pd_dereference(&(ia_pd->iaprefix[i]->ia_pd), file, line);
+ iaprefix_dereference(&(ia_pd->iaprefix[i]), file, line);
+ }
+ ia_pd->num_iaprefix = 0;
+}
+
+/*
+ * Compare two IA_PD.
+ */
+isc_boolean_t
+ia_pd_equal(const struct ia_pd *a, const struct ia_pd *b)
+{
+ isc_boolean_t found;
+ int i, j;
+
+ /*
+ * Handle cases where one or both of the inputs is NULL.
+ */
+ if (a == NULL) {
+ if (b == NULL) {
+ return ISC_TRUE;
+ } else {
+ return ISC_FALSE;
+ }
+ }
+
+ /*
+ * Check the DUID is the same.
+ */
+ if (a->iaid_duid.len != b->iaid_duid.len) {
+ return ISC_FALSE;
+ }
+ if (memcmp(a->iaid_duid.data,
+ b->iaid_duid.data, a->iaid_duid.len) != 0) {
+ return ISC_FALSE;
+ }
+
+ /*
+ * Make sure we have the same number of prefixes in each.
+ */
+ if (a->num_iaprefix != b->num_iaprefix) {
+ return ISC_FALSE;
+ }
+
+ /*
+ * Check that each prefix is present in both.
+ */
+ for (i=0; i<a->num_iaprefix; i++) {
+ found = ISC_FALSE;
+ for (j=0; j<a->num_iaprefix; j++) {
+ if (a->iaprefix[i]->plen != b->iaprefix[i]->plen)
+ continue;
+ if (memcmp(&(a->iaprefix[i]->pref),
+ &(b->iaprefix[j]->pref),
+ sizeof(struct in6_addr)) == 0) {
+ found = ISC_TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ return ISC_FALSE;
+ }
+ }
+
+ /*
+ * These are the same in every way we care about.
+ */
+ return ISC_TRUE;
+}
+
+/*
+ * Helper function for lease heaps.
+ * Makes the top of the heap the oldest lease.
+ * Note: this relies on the unique layout for leases!
+ */
+static isc_boolean_t
+lease_older(void *a, void *b) {
+ struct iaaddr *ia = (struct iaaddr *)a;
+ struct iaaddr *ib = (struct iaaddr *)b;
+
+ return difftime(ia->valid_lifetime_end_time,
+ ib->valid_lifetime_end_time) < 0;
+}
+
+/*
+ * Helper function for lease address heaps.
+ * Callback when an address's position in the heap changes.
+ */
+static void
+lease_address_index_changed(void *iaaddr, unsigned int new_heap_index) {
+ ((struct iaaddr *)iaaddr)-> heap_index = new_heap_index;
+}
+
+/*
+ * Helper function for lease prefix heaps.
+ * Callback when a prefix's position in the heap changes.
+ */
+static void
+lease_prefix_index_changed(void *iapref, unsigned int new_heap_index) {
+ ((struct iaprefix *)iapref)-> heap_index = new_heap_index;
+}
+
+
+/*
+ * Create a new IPv6 lease (address) pool structure.
+ *
+ * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+ipv6_pool_allocate(struct ipv6_pool **pool,
+ const struct in6_addr *start_addr, int bits,
+ const char *file, int line) {
+ struct ipv6_pool *tmp;
+
+ if (pool == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (*pool != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+
+ tmp = dmalloc(sizeof(*tmp), file, line);
+ if (tmp == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+
+ tmp->refcnt = 1;
+ tmp->start_addr = *start_addr;
+ tmp->bits = bits;
+ if (!iaaddr_new_hash(&tmp->addrs, DEFAULT_HASH_SIZE, file, line)) {
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+ if (isc_heap_create(lease_older, lease_address_index_changed,
+ 0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
+ iaaddr_free_hash_table(&(tmp->addrs), file, line);
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+ if (isc_heap_create(lease_older, lease_address_index_changed,
+ 0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
+ isc_heap_destroy(&(tmp->active_timeouts));
+ iaaddr_free_hash_table(&(tmp->addrs), file, line);
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+
+ *pool = tmp;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IPv6 pool structure.
+ *
+ * - pool must be a pointer to a (struct pool *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src,
+ const char *file, int line) {
+ if (pool == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (*pool != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (src == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ *pool = src;
+ src->refcnt++;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Note: Each IAADDR in a pool is referenced by the pool. This is needed
+ * to prevent the IAADDR from being garbage collected out from under the
+ * pool.
+ *
+ * The references are made from the hash and from the heap. The following
+ * helper functions dereference these when a pool is destroyed.
+ */
+
+/*
+ * Helper function for pool cleanup.
+ * Dereference each of the hash entries in a pool.
+ */
+static isc_result_t
+dereference_hash_entry(const void *name, unsigned len, void *value) {
+ struct iaaddr *iaaddr = (struct iaaddr *)value;
+
+ iaaddr_dereference(&iaaddr, MDL);
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Helper function for pool cleanup.
+ * Dereference each of the heap entries in a pool.
+ */
+static void
+dereference_heap_entry(void *value, void *dummy) {
+ struct iaaddr *iaaddr = (struct iaaddr *)value;
+
+ iaaddr_dereference(&iaaddr, MDL);
+}
+
+
+/*
+ * Dereference an IPv6 pool structure.
+ *
+ * If it is the last reference, then the memory for the
+ * structure is freed.
+ */
+isc_result_t
+ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) {
+ struct ipv6_pool *tmp;
+
+ if ((pool == NULL) || (*pool == NULL)) {
+ log_error("%s(%d): NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+
+ tmp = *pool;
+ *pool = NULL;
+
+ tmp->refcnt--;
+ if (tmp->refcnt < 0) {
+ log_error("%s(%d): negative refcnt", file, line);
+ tmp->refcnt = 0;
+ }
+ if (tmp->refcnt == 0) {
+ iaaddr_hash_foreach(tmp->addrs, dereference_hash_entry);
+ iaaddr_free_hash_table(&(tmp->addrs), file, line);
+ isc_heap_foreach(tmp->active_timeouts,
+ dereference_heap_entry, NULL);
+ isc_heap_destroy(&(tmp->active_timeouts));
+ isc_heap_foreach(tmp->inactive_timeouts,
+ dereference_heap_entry, NULL);
+ isc_heap_destroy(&(tmp->inactive_timeouts));
+ dfree(tmp, file, line);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Create a new IPv6 lease (prefix) pool structure.
+ *
+ * - ppool must be a pointer to a (struct ipv6_ppool *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+ipv6_ppool_allocate(struct ipv6_ppool **ppool,
+ const struct in6_addr *start_pref,
+ u_int8_t pool_plen, u_int8_t alloc_plen,
+ const char *file, int line) {
+ struct ipv6_ppool *tmp;
+
+ if (ppool == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (*ppool != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+
+ tmp = dmalloc(sizeof(*tmp), file, line);
+ if (tmp == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+
+ tmp->refcnt = 1;
+ tmp->start_pref = *start_pref;
+ tmp->pool_plen = pool_plen;
+ tmp->alloc_plen = alloc_plen;
+ if (!iaprefix_new_hash(&tmp->prefs, DEFAULT_HASH_SIZE, file, line)) {
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+ if (isc_heap_create(lease_older, lease_prefix_index_changed,
+ 0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
+ iaprefix_free_hash_table(&(tmp->prefs), file, line);
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+ if (isc_heap_create(lease_older, lease_prefix_index_changed,
+ 0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
+ isc_heap_destroy(&(tmp->active_timeouts));
+ iaprefix_free_hash_table(&(tmp->prefs), file, line);
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+
+ *ppool = tmp;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IPv6 prefix pool structure.
+ *
+ * - ppool must be a pointer to a (struct ppool *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+ipv6_ppool_reference(struct ipv6_ppool **ppool, struct ipv6_ppool *src,
+ const char *file, int line) {
+ if (ppool == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (*ppool != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ if (src == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return ISC_R_INVALIDARG;
+ }
+ *ppool = src;
+ src->refcnt++;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Note: Each IAPREFIX in a pool is referenced by the pool. This is needed
+ * to prevent the IAPREFIX from being garbage collected out from under the
+ * pool.
+ *
+ * The references are made from the hash and from the heap. The following
+ * helper functions dereference these when a pool is destroyed.
+ */
+
+/*
+ * Helper function for prefix pool cleanup.
+ * Dereference each of the hash entries in a pool.
+ */
+static isc_result_t
+dereference_phash_entry(const void *name, unsigned len, void *value) {
+ struct iaprefix *iapref = (struct iaprefix *)value;
+
+ iaprefix_dereference(&iapref, MDL);
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Helper function for prefix pool cleanup.
+ * Dereference each of the heap entries in a pool.
+ */
+static void
+dereference_pheap_entry(void *value, void *dummy) {
+ struct iaprefix *iapref = (struct iaprefix *)value;
+
+ iaprefix_dereference(&iapref, MDL);
+}
+
+
+/*
+ * Dereference an IPv6 prefix pool structure.
+ *
+ * If it is the last reference, then the memory for the
+ * structure is freed.
+ */
+isc_result_t
+ipv6_ppool_dereference(struct ipv6_ppool **ppool, const char *file, int line) {
+ struct ipv6_ppool *tmp;
+
+ if ((ppool == NULL) || (*ppool == NULL)) {
+ log_error("%s(%d): NULL pointer", file, line);
+ return ISC_R_INVALIDARG;
+ }
+
+ tmp = *ppool;
+ *ppool = NULL;
+
+ tmp->refcnt--;
+ if (tmp->refcnt < 0) {
+ log_error("%s(%d): negative refcnt", file, line);
+ tmp->refcnt = 0;
+ }
+ if (tmp->refcnt == 0) {
+ iaprefix_hash_foreach(tmp->prefs, dereference_phash_entry);
+ iaprefix_free_hash_table(&(tmp->prefs), file, line);
+ isc_heap_foreach(tmp->active_timeouts,
+ dereference_pheap_entry, NULL);
+ isc_heap_destroy(&(tmp->active_timeouts));
+ isc_heap_foreach(tmp->inactive_timeouts,
+ dereference_pheap_entry, NULL);
+ isc_heap_destroy(&(tmp->inactive_timeouts));
+ dfree(tmp, file, line);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Create an address by hashing the input, and using that for
+ * the non-network part.
+ */
+static void
+create_address(struct in6_addr *addr,
+ const struct in6_addr *net_start_addr, int net_bits,
+ const struct data_string *input) {
+ MD5_CTX ctx;
+ int net_bytes;
+ int i;
+ char *str;
+ const char *net_str;
+
+ /*
+ * Use MD5 to get a nice 128 bit hash of the input.
+ * Yes, we know MD5 isn't cryptographically sound.
+ * No, we don't care.
+ */
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, input->data, input->len);
+ MD5_Final((unsigned char *)addr, &ctx);
+
+ /*
+ * Copy the network bits over.
+ */
+ str = (char *)addr;
+ net_str = (const char *)net_start_addr;
+ net_bytes = net_bits / 8;
+ for (i=0; i<net_bytes; i++) {
+ str[i] = net_str[i];
+ }
+ switch (net_bits % 8) {
+ case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
+ case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
+ case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
+ case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
+ case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
+ case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
+ case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
+ }
+ /* set the 'u' bit to zero for /64s. */
+ if (net_bits == 64)
+ str[8] &= ~0x02;
+}
+
+/* Reserved Subnet Router Anycast ::0:0:0:0. */
static struct in6_addr rtany;
/* Reserved Subnet Anycasts ::fdff:ffff:ffff:ff80-::fdff:ffff:ffff:ffff. */
static struct in6_addr resany;
/*
- * Create a lease for the given address and client duid.
+ * Create a lease for the given address and client duid.
+ *
+ * - pool must be a pointer to a (struct pool *) pointer previously
+ * initialized to NULL
+ *
+ * Right now we simply hash the DUID, and if we get a collision, we hash
+ * again until we find a free address. We try this a fixed number of times,
+ * to avoid getting stuck in a loop (this is important on small pools
+ * where we can run out of space).
+ *
+ * We return the number of attempts that it took to find an available
+ * lease. This tells callers when a pool is are filling up, as
+ * well as an indication of how full the pool is; statistically the
+ * more full a pool is the more attempts must be made before finding
+ * a free lease. Realistically this will only happen in very full
+ * pools.
+ *
+ * We probably want different algorithms depending on the network size, in
+ * the long term.
+ */
+isc_result_t
+activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
+ unsigned int *attempts,
+ const struct data_string *uid, time_t valid_lifetime_end_time) {
+ struct data_string ds;
+ struct in6_addr tmp;
+ struct iaaddr *test_iaaddr;
+ struct data_string new_ds;
+ struct iaaddr *iaaddr;
+ isc_result_t result;
+ isc_boolean_t reserved_iid;
+ static isc_boolean_t init_resiid = ISC_FALSE;
+
+ /*
+ * Fill the reserved IIDs.
+ */
+ if (!init_resiid) {
+ memset(&rtany, 0, 16);
+ memset(&resany, 0, 8);
+ resany.s6_addr[8] = 0xfd;
+ memset(&resany.s6_addr[9], 0xff, 6);
+ init_resiid = ISC_TRUE;
+ }
+
+ /*
+ * Use the UID as our initial seed for the hash
+ */
+ memset(&ds, 0, sizeof(ds));
+ data_string_copy(&ds, (struct data_string *)uid, MDL);
+
+ *attempts = 0;
+ for (;;) {
+ /*
+ * Give up at some point.
+ */
+ if (++(*attempts) > 100) {
+ data_string_forget(&ds, MDL);
+ return ISC_R_NORESOURCES;
+ }
+
+ /*
+ * Create an address
+ */
+ create_address(&tmp, &pool->start_addr, pool->bits, &ds);
+
+ /*
+ * Avoid reserved interface IDs.
+ * (cf. draft-krishnan-ipv6-reserved-iids-02.txt)
+ */
+ reserved_iid = ISC_FALSE;
+ if (memcmp(&tmp.s6_addr[8], &rtany, 8) == 0) {
+ reserved_iid = ISC_TRUE;
+ }
+ if (!reserved_iid &&
+ (memcmp(&tmp.s6_addr[8], &resany, 7) == 0) &&
+ ((tmp.s6_addr[15] & 0x80) == 0x80)) {
+ reserved_iid = ISC_TRUE;
+ }
+
+ /*
+ * If this address is not in use, we're happy with it
+ */
+ test_iaaddr = NULL;
+ if (!reserved_iid &&
+ (iaaddr_hash_lookup(&test_iaaddr, pool->addrs,
+ &tmp, sizeof(tmp), MDL) == 0)) {
+ break;
+ }
+ if (test_iaaddr != NULL)
+ iaaddr_dereference(&test_iaaddr, MDL);
+
+ /*
+ * Otherwise, we create a new input, adding the address
+ */
+ memset(&new_ds, 0, sizeof(new_ds));
+ new_ds.len = ds.len + sizeof(tmp);
+ if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
+ data_string_forget(&ds, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ new_ds.data = new_ds.buffer->data;
+ memcpy(new_ds.buffer->data, ds.data, ds.len);
+ memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp));
+ data_string_forget(&ds, MDL);
+ data_string_copy(&ds, &new_ds, MDL);
+ data_string_forget(&new_ds, MDL);
+ }
+
+ data_string_forget(&ds, MDL);
+
+ /*
+ * We're happy with the address, create an IAADDR
+ * to hold it.
+ */
+ iaaddr = NULL;
+ result = iaaddr_allocate(&iaaddr, MDL);
+ if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
+
+ /*
+ * Add the lease to the pool.
+ */
+ result = add_lease6(pool, iaaddr, valid_lifetime_end_time);
+ if (result == ISC_R_SUCCESS) {
+ iaaddr_reference(addr, iaaddr, MDL);
+ }
+ iaaddr_dereference(&iaaddr, MDL);
+ return result;
+}
+
+/*
+ * Put a lease in the pool directly. This is intended to be used when
+ * loading leases from the file.
+ */
+isc_result_t
+add_lease6(struct ipv6_pool *pool, struct iaaddr *iaaddr,
+ time_t valid_lifetime_end_time) {
+ isc_result_t insert_result;
+ struct iaaddr *test_iaaddr;
+ struct iaaddr *tmp_iaaddr;
+
+ /* If a state was not assigned by the caller, assume active. */
+ if (iaaddr->state == 0)
+ iaaddr->state = FTS_ACTIVE;
+
+ iaaddr->valid_lifetime_end_time = valid_lifetime_end_time;
+ ipv6_pool_reference(&iaaddr->ipv6_pool, pool, MDL);
+
+ /*
+ * If this IAADDR is already in our structures, remove the
+ * old one.
+ */
+ test_iaaddr = NULL;
+ if (iaaddr_hash_lookup(&test_iaaddr, pool->addrs,
+ &iaaddr->addr, sizeof(iaaddr->addr), MDL)) {
+ /* XXX: we should probably ask the iaaddr what heap it is on
+ * (as a consistency check).
+ * XXX: we should probably have one function to "put this lease
+ * on its heap" rather than doing these if's everywhere. If
+ * you add more states to this list, don't.
+ */
+ if ((test_iaaddr->state == FTS_ACTIVE) ||
+ (test_iaaddr->state == FTS_ABANDONED)) {
+ isc_heap_delete(pool->active_timeouts,
+ test_iaaddr->heap_index);
+ pool->num_active--;
+ } else {
+ isc_heap_delete(pool->inactive_timeouts,
+ test_iaaddr->heap_index);
+ pool->num_inactive--;
+ }
+
+ iaaddr_hash_delete(pool->addrs, &test_iaaddr->addr,
+ sizeof(test_iaaddr->addr), MDL);
+
+ /*
+ * We're going to do a bit of evil trickery here.
+ *
+ * We need to dereference the entry once to remove our
+ * current reference (in test_iaaddr), and then one
+ * more time to remove the reference left when the
+ * address was added to the pool before.
+ */
+ tmp_iaaddr = test_iaaddr;
+ iaaddr_dereference(&test_iaaddr, MDL);
+ iaaddr_dereference(&tmp_iaaddr, MDL);
+ }
+
+ /*
+ * Add IAADDR to our structures.
+ */
+ tmp_iaaddr = NULL;
+ iaaddr_reference(&tmp_iaaddr, iaaddr, MDL);
+ if ((tmp_iaaddr->state == FTS_ACTIVE) ||
+ (tmp_iaaddr->state == FTS_ABANDONED)) {
+ iaaddr_hash_add(pool->addrs, &tmp_iaaddr->addr,
+ sizeof(tmp_iaaddr->addr), iaaddr, MDL);
+ insert_result = isc_heap_insert(pool->active_timeouts,
+ tmp_iaaddr);
+ if (insert_result == ISC_R_SUCCESS)
+ pool->num_active++;
+ } else {
+ insert_result = isc_heap_insert(pool->inactive_timeouts,
+ tmp_iaaddr);
+ if (insert_result == ISC_R_SUCCESS)
+ pool->num_inactive++;
+ }
+ if (insert_result != ISC_R_SUCCESS) {
+ iaaddr_hash_delete(pool->addrs, &iaaddr->addr,
+ sizeof(iaaddr->addr), MDL);
+ iaaddr_dereference(&tmp_iaaddr, MDL);
+ return insert_result;
+ }
+
+ /*
+ * Note: we intentionally leave tmp_iaaddr referenced; there
+ * is a reference in the heap/hash, after all.
+ */
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Determine if an address is present in a pool or not.
+ */
+isc_boolean_t
+lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
+ struct iaaddr *test_iaaddr;
+
+ test_iaaddr = NULL;
+ if (iaaddr_hash_lookup(&test_iaaddr, pool->addrs,
+ (void *)addr, sizeof(*addr), MDL)) {
+ iaaddr_dereference(&test_iaaddr, MDL);
+ return ISC_TRUE;
+ } else {
+ return ISC_FALSE;
+ }
+}
+
+/*
+ * Put the lease on our active pool.
+ */
+static isc_result_t
+move_lease_to_active(struct ipv6_pool *pool, struct iaaddr *addr) {
+ isc_result_t insert_result;
+ int old_heap_index;
+
+ old_heap_index = addr->heap_index;
+ insert_result = isc_heap_insert(pool->active_timeouts, addr);
+ if (insert_result == ISC_R_SUCCESS) {
+ iaaddr_hash_add(pool->addrs, &addr->addr,
+ sizeof(addr->addr), addr, MDL);
+ isc_heap_delete(pool->inactive_timeouts, old_heap_index);
+ pool->num_active++;
+ pool->num_inactive--;
+ addr->state = FTS_ACTIVE;
+ }
+ return insert_result;
+}
+
+/*
+ * Renew an lease in the pool.
+ *
+ * To do this, first set the new valid_lifetime_end_time for the address,
+ * and then invoke renew_lease() on the address.
+ *
+ * WARNING: lease times must only be extended, never reduced!!!
+ */
+isc_result_t
+renew_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
+ /*
+ * If we're already active, then we can just move our expiration
+ * time down the heap.
+ *
+ * Otherwise, we have to move from the inactive heap to the
+ * active heap.
+ */
+ if (addr->state == FTS_ACTIVE) {
+ isc_heap_decreased(pool->active_timeouts, addr->heap_index);
+ return ISC_R_SUCCESS;
+ } else {
+ return move_lease_to_active(pool, addr);
+ }
+}
+
+/*
+ * Put the lease on our inactive pool, with the specified state.
+ */
+static isc_result_t
+move_lease_to_inactive(struct ipv6_pool *pool, struct iaaddr *addr,
+ binding_state_t state) {
+ isc_result_t insert_result;
+ int old_heap_index;
+
+ old_heap_index = addr->heap_index;
+ insert_result = isc_heap_insert(pool->inactive_timeouts, addr);
+ if (insert_result == ISC_R_SUCCESS) {
+ /* Process events upon expiration. */
+ ddns_removals(NULL, addr);
+
+ /* Binding scopes are no longer valid after expiry or
+ * release.
+ */
+ if (addr->scope != NULL) {
+ binding_scope_dereference(&addr->scope, MDL);
+ }
+
+ iaaddr_hash_delete(pool->addrs,
+ &addr->addr, sizeof(addr->addr), MDL);
+ isc_heap_delete(pool->active_timeouts, old_heap_index);
+ addr->state = state;
+ pool->num_active--;
+ pool->num_inactive++;
+ }
+ return insert_result;
+}
+
+/*
+ * Expire the oldest lease if it's lifetime_end_time is
+ * older than the given time.
*
- * - pool must be a pointer to a (struct pool *) pointer previously
+ * - iaaddr must be a pointer to a (struct iaaddr *) pointer previously
+ * initialized to NULL
+ *
+ * On return iaaddr has a reference to the removed entry. It is left
+ * pointing to NULL if the oldest lease has not expired.
+ */
+isc_result_t
+expire_lease6(struct iaaddr **addr, struct ipv6_pool *pool, time_t now) {
+ struct iaaddr *tmp;
+ isc_result_t result;
+
+ if (addr == NULL) {
+ log_error("%s(%d): NULL pointer reference", MDL);
+ return ISC_R_INVALIDARG;
+ }
+ if (*addr != NULL) {
+ log_error("%s(%d): non-NULL pointer", MDL);
+ return ISC_R_INVALIDARG;
+ }
+
+ if (pool->num_active > 0) {
+ tmp = (struct iaaddr *)isc_heap_element(pool->active_timeouts,
+ 1);
+ if (now > tmp->valid_lifetime_end_time) {
+ result = move_lease_to_inactive(pool, tmp, FTS_EXPIRED);
+ if (result == ISC_R_SUCCESS) {
+ iaaddr_reference(addr, tmp, MDL);
+ }
+ return result;
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+
+/*
+ * For a declined lease, leave it on the "active" pool, but mark
+ * it as declined. Give it an infinite (well, really long) life.
+ */
+isc_result_t
+decline_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
+ isc_result_t result;
+
+ if (addr->state != FTS_ACTIVE) {
+ result = move_lease_to_active(pool, addr);
+ if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ }
+ addr->state = FTS_ABANDONED;
+ addr->valid_lifetime_end_time = MAX_TIME;
+ isc_heap_decreased(pool->active_timeouts, addr->heap_index);
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Put the returned lease on our inactive pool.
+ */
+isc_result_t
+release_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
+ if (addr->state == FTS_ACTIVE) {
+ return move_lease_to_inactive(pool, addr, FTS_RELEASED);
+ } else {
+ return ISC_R_SUCCESS;
+ }
+}
+
+/*
+ * Create a prefix by hashing the input, and using that for
+ * the part subject to allocation.
+ */
+static void
+create_prefix(struct in6_addr *pref,
+ const struct in6_addr *net_start_pref,
+ int pool_bits, int pref_bits,
+ const struct data_string *input) {
+ MD5_CTX ctx;
+ int net_bytes;
+ int i;
+ char *str;
+ const char *net_str;
+
+ /*
+ * Use MD5 to get a nice 128 bit hash of the input.
+ * Yes, we know MD5 isn't cryptographically sound.
+ * No, we don't care.
+ */
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, input->data, input->len);
+ MD5_Final((unsigned char *)pref, &ctx);
+
+ /*
+ * Copy the network bits over.
+ */
+ str = (char *)pref;
+ net_str = (const char *)net_start_pref;
+ net_bytes = pool_bits / 8;
+ for (i=0; i<net_bytes; i++) {
+ str[i] = net_str[i];
+ }
+ switch (pool_bits % 8) {
+ case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
+ case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
+ case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
+ case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
+ case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
+ case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
+ case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
+ }
+ /*
+ * Zero the remaining bits.
+ */
+ net_bytes = pref_bits / 8;
+ for (i=net_bytes+1; i<16; i++) {
+ str[i] = 0;
+ }
+ switch (pref_bits % 8) {
+ case 0: str[i] &= 0;
+ case 1: str[i] &= 0x80;
+ case 2: str[i] &= 0xC0;
+ case 3: str[i] &= 0xE0;
+ case 4: str[i] &= 0xF0;
+ case 5: str[i] &= 0xF8;
+ case 6: str[i] &= 0xFC;
+ case 7: str[i] &= 0xFE;
+ }
+}
+
+/*
+ * Create a lease for the given prefix and client duid.
+ *
+ * - ppool must be a pointer to a (struct ppool *) pointer previously
* initialized to NULL
*
* Right now we simply hash the DUID, and if we get a collision, we hash
- * again until we find a free address. We try this a fixed number of times,
+ * again until we find a free prefix. We try this a fixed number of times,
* to avoid getting stuck in a loop (this is important on small pools
* where we can run out of space).
*
* We return the number of attempts that it took to find an available
- * lease. This tells callers when a pool is are filling up, as
+ * prefix. This tells callers when a pool is are filling up, as
* well as an indication of how full the pool is; statistically the
* more full a pool is the more attempts must be made before finding
- * a free lease. Realistically this will only happen in very full
+ * a free prefix. Realistically this will only happen in very full
* pools.
*
* We probably want different algorithms depending on the network size, in
* the long term.
*/
isc_result_t
-activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
+activate_prefix(struct ipv6_ppool *ppool, struct iaprefix **pref,
unsigned int *attempts,
- const struct data_string *uid, time_t valid_lifetime_end_time) {
+ const struct data_string *uid,
+ time_t valid_lifetime_end_time) {
struct data_string ds;
struct in6_addr tmp;
- struct iaaddr *test_iaaddr;
+ struct iaprefix *test_iapref;
struct data_string new_ds;
- struct iaaddr *iaaddr;
+ struct iaprefix *iapref;
isc_result_t result;
- isc_boolean_t reserved_iid;
- static isc_boolean_t init_resiid = ISC_FALSE;
-
- /*
- * Fill the reserved IIDs.
- */
- if (!init_resiid) {
- memset(&rtany, 0, 16);
- memset(&resany, 0, 8);
- resany.s6_addr[8] = 0xfd;
- memset(&resany.s6_addr[9], 0xff, 6);
- init_resiid = ISC_TRUE;
- }
/*
* Use the UID as our initial seed for the hash
/*
* Give up at some point.
*/
- if (++(*attempts) > 100) {
+ if (++(*attempts) > 10) {
data_string_forget(&ds, MDL);
return ISC_R_NORESOURCES;
}
/*
- * Create an address
- */
- create_address(&tmp, &pool->start_addr, pool->bits, &ds);
-
- /*
- * Avoid reserved interface IDs.
- * (cf. draft-krishnan-ipv6-reserved-iids-02.txt)
+ * Create a prefix
*/
- reserved_iid = ISC_FALSE;
- if (memcmp(&tmp.s6_addr[8], &rtany, 8) == 0) {
- reserved_iid = ISC_TRUE;
- }
- if (!reserved_iid &&
- (memcmp(&tmp.s6_addr[8], &resany, 7) == 0) &&
- ((tmp.s6_addr[15] & 0x80) == 0x80)) {
- reserved_iid = ISC_TRUE;
- }
+ create_prefix(&tmp, &ppool->start_pref,
+ (int)ppool->pool_plen, (int)ppool->alloc_plen,
+ &ds);
/*
- * If this address is not in use, we're happy with it
+ * If this prefix is not in use, we're happy with it
*/
- test_iaaddr = NULL;
- if (!reserved_iid &&
- (iaaddr_hash_lookup(&test_iaaddr, pool->addrs,
- &tmp, sizeof(tmp), MDL) == 0)) {
+ test_iapref = NULL;
+ if (iaprefix_hash_lookup(&test_iapref, ppool->prefs,
+ &tmp, sizeof(tmp), MDL) == 0) {
break;
}
- if (test_iaaddr != NULL)
- iaaddr_dereference(&test_iaaddr, MDL);
+ iaprefix_dereference(&test_iapref, MDL);
/*
- * Otherwise, we create a new input, adding the address
+ * Otherwise, we create a new input, adding the prefix
*/
memset(&new_ds, 0, sizeof(new_ds));
new_ds.len = ds.len + sizeof(tmp);
data_string_forget(&ds, MDL);
/*
- * We're happy with the address, create an IAADDR
+ * We're happy with the prefix, create an IAPREFIX
* to hold it.
*/
- iaaddr = NULL;
- result = iaaddr_allocate(&iaaddr, MDL);
+ iapref = NULL;
+ result = iaprefix_allocate(&iapref, MDL);
if (result != ISC_R_SUCCESS) {
return result;
}
- memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
+ memcpy(&iapref->pref, &tmp, sizeof(iapref->pref));
/*
- * Add the lease to the pool.
+ * Add the prefix to the pool.
*/
- result = add_lease6(pool, iaaddr, valid_lifetime_end_time);
+ result = add_prefix6(ppool, iapref, valid_lifetime_end_time);
if (result == ISC_R_SUCCESS) {
- iaaddr_reference(addr, iaaddr, MDL);
+ iaprefix_reference(pref, iapref, MDL);
}
- iaaddr_dereference(&iaaddr, MDL);
+ iaprefix_dereference(&iapref, MDL);
return result;
}
/*
- * Put a lease in the pool directly. This is intended to be used when
+ * Put a prefix in the pool directly. This is intended to be used when
* loading leases from the file.
*/
isc_result_t
-add_lease6(struct ipv6_pool *pool, struct iaaddr *iaaddr,
- time_t valid_lifetime_end_time) {
+add_prefix6(struct ipv6_ppool *ppool, struct iaprefix *iapref,
+ time_t valid_lifetime_end_time) {
isc_result_t insert_result;
- struct iaaddr *test_iaaddr;
- struct iaaddr *tmp_iaaddr;
+ struct iaprefix *test_iapref;
+ struct iaprefix *tmp_iapref;
/* If a state was not assigned by the caller, assume active. */
- if (iaaddr->state == 0)
- iaaddr->state = FTS_ACTIVE;
+ if (iapref->state == 0)
+ iapref->state = FTS_ACTIVE;
- iaaddr->valid_lifetime_end_time = valid_lifetime_end_time;
- ipv6_pool_reference(&iaaddr->ipv6_pool, pool, MDL);
+ iapref->valid_lifetime_end_time = valid_lifetime_end_time;
+ ipv6_ppool_reference(&iapref->ipv6_ppool, ppool, MDL);
/*
- * If this IAADDR is already in our structures, remove the
+ * If this IAPREFIX is already in our structures, remove the
* old one.
*/
- test_iaaddr = NULL;
- if (iaaddr_hash_lookup(&test_iaaddr, pool->addrs,
- &iaaddr->addr, sizeof(iaaddr->addr), MDL)) {
- /* XXX: we should probably ask the iaaddr what heap it is on
+ test_iapref = NULL;
+ if (iaprefix_hash_lookup(&test_iapref, ppool->prefs,
+ &iapref->pref, sizeof(iapref->pref), MDL)) {
+ /* XXX: we should probably ask the iaprefix what heap it is on
* (as a consistency check).
- * XXX: we should probably have one function to "put this lease
- * on its heap" rather than doing these if's everywhere. If
- * you add more states to this list, don't.
+ * XXX: we should probably have one function to "put this
+ * prefix on its heap" rather than doing these if's
+ * everywhere. If you add more states to this list, don't.
*/
- if ((test_iaaddr->state == FTS_ACTIVE) ||
- (test_iaaddr->state == FTS_ABANDONED)) {
- isc_heap_delete(pool->active_timeouts,
- test_iaaddr->heap_index);
- pool->num_active--;
+ if ((test_iapref->state == FTS_ACTIVE) ||
+ (test_iapref->state == FTS_ABANDONED)) {
+ isc_heap_delete(ppool->active_timeouts,
+ test_iapref->heap_index);
+ ppool->num_active--;
} else {
- isc_heap_delete(pool->inactive_timeouts,
- test_iaaddr->heap_index);
- pool->num_inactive--;
+ isc_heap_delete(ppool->inactive_timeouts,
+ test_iapref->heap_index);
+ ppool->num_inactive--;
}
- iaaddr_hash_delete(pool->addrs, &test_iaaddr->addr,
- sizeof(test_iaaddr->addr), MDL);
+ iaprefix_hash_delete(ppool->prefs, &test_iapref->pref,
+ sizeof(test_iapref->pref), MDL);
/*
* We're going to do a bit of evil trickery here.
*
* We need to dereference the entry once to remove our
- * current reference (in test_iaaddr), and then one
+ * current reference (in test_iapref), and then one
* more time to remove the reference left when the
- * address was added to the pool before.
+ * prefix was added to the pool before.
*/
- tmp_iaaddr = test_iaaddr;
- iaaddr_dereference(&test_iaaddr, MDL);
- iaaddr_dereference(&tmp_iaaddr, MDL);
+ tmp_iapref = test_iapref;
+ iaprefix_dereference(&test_iapref, MDL);
+ iaprefix_dereference(&tmp_iapref, MDL);
}
/*
- * Add IAADDR to our structures.
+ * Add IAPREFIX to our structures.
*/
- tmp_iaaddr = NULL;
- iaaddr_reference(&tmp_iaaddr, iaaddr, MDL);
- if ((tmp_iaaddr->state == FTS_ACTIVE) ||
- (tmp_iaaddr->state == FTS_ABANDONED)) {
- iaaddr_hash_add(pool->addrs, &tmp_iaaddr->addr,
- sizeof(tmp_iaaddr->addr), iaaddr, MDL);
- insert_result = isc_heap_insert(pool->active_timeouts,
- tmp_iaaddr);
+ tmp_iapref = NULL;
+ iaprefix_reference(&tmp_iapref, iapref, MDL);
+ if ((tmp_iapref->state == FTS_ACTIVE) ||
+ (tmp_iapref->state == FTS_ABANDONED)) {
+ iaprefix_hash_add(ppool->prefs, &tmp_iapref->pref,
+ sizeof(tmp_iapref->pref), iapref, MDL);
+ insert_result = isc_heap_insert(ppool->active_timeouts,
+ tmp_iapref);
if (insert_result == ISC_R_SUCCESS)
- pool->num_active++;
+ ppool->num_active++;
} else {
- insert_result = isc_heap_insert(pool->inactive_timeouts,
- tmp_iaaddr);
+ insert_result = isc_heap_insert(ppool->inactive_timeouts,
+ tmp_iapref);
if (insert_result == ISC_R_SUCCESS)
- pool->num_inactive++;
+ ppool->num_inactive++;
}
if (insert_result != ISC_R_SUCCESS) {
- iaaddr_hash_delete(pool->addrs, &iaaddr->addr,
- sizeof(iaaddr->addr), MDL);
- iaaddr_dereference(&tmp_iaaddr, MDL);
+ iaprefix_hash_delete(ppool->prefs, &iapref->pref,
+ sizeof(iapref->pref), MDL);
+ iaprefix_dereference(&tmp_iapref, MDL);
return insert_result;
}
/*
- * Note: we intentionally leave tmp_iaaddr referenced; there
+ * Note: we intentionally leave tmp_iapref referenced; there
* is a reference in the heap/hash, after all.
*/
}
/*
- * Determine if an address is present in a pool or not.
+ * Determine if a prefix is present in a pool or not.
*/
isc_boolean_t
-lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
- struct iaaddr *test_iaaddr;
+prefix6_exists(const struct ipv6_ppool *ppool,
+ const struct in6_addr *pref, u_int8_t plen) {
+ struct iaprefix *test_iapref;
- test_iaaddr = NULL;
- if (iaaddr_hash_lookup(&test_iaaddr, pool->addrs,
- (void *)addr, sizeof(*addr), MDL)) {
- iaaddr_dereference(&test_iaaddr, MDL);
+ if (plen != ppool->alloc_plen)
+ return ISC_FALSE;
+
+ test_iapref = NULL;
+ if (iaprefix_hash_lookup(&test_iapref, ppool->prefs,
+ (void *)pref, sizeof(*pref), MDL)) {
+ iaprefix_dereference(&test_iapref, MDL);
return ISC_TRUE;
} else {
return ISC_FALSE;
}
/*
- * Put the lease on our active pool.
+ * Put the prefix on our active pool.
*/
static isc_result_t
-move_lease_to_active(struct ipv6_pool *pool, struct iaaddr *addr) {
+move_prefix_to_active(struct ipv6_ppool *ppool, struct iaprefix *pref) {
isc_result_t insert_result;
int old_heap_index;
- old_heap_index = addr->heap_index;
- insert_result = isc_heap_insert(pool->active_timeouts, addr);
+ old_heap_index = pref->heap_index;
+ insert_result = isc_heap_insert(ppool->active_timeouts, pref);
if (insert_result == ISC_R_SUCCESS) {
- iaaddr_hash_add(pool->addrs, &addr->addr,
- sizeof(addr->addr), addr, MDL);
- isc_heap_delete(pool->inactive_timeouts, old_heap_index);
- pool->num_active++;
- pool->num_inactive--;
- addr->state = FTS_ACTIVE;
+ iaprefix_hash_add(ppool->prefs, &pref->pref,
+ sizeof(pref->pref), pref, MDL);
+ isc_heap_delete(ppool->inactive_timeouts, old_heap_index);
+ ppool->num_active++;
+ ppool->num_inactive--;
+ pref->state = FTS_ACTIVE;
}
return insert_result;
}
/*
- * Renew an lease in the pool.
+ * Renew a prefix in the pool.
*
- * To do this, first set the new valid_lifetime_end_time for the address,
- * and then invoke renew_lease() on the address.
+ * To do this, first set the new valid_lifetime_end_time for the prefix,
+ * and then invoke renew_prefix() on the prefix.
*
* WARNING: lease times must only be extended, never reduced!!!
*/
isc_result_t
-renew_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
+renew_prefix6(struct ipv6_ppool *ppool, struct iaprefix *pref) {
/*
* If we're already active, then we can just move our expiration
* time down the heap.
* Otherwise, we have to move from the inactive heap to the
* active heap.
*/
- if (addr->state == FTS_ACTIVE) {
- isc_heap_decreased(pool->active_timeouts, addr->heap_index);
+ if (pref->state == FTS_ACTIVE) {
+ isc_heap_decreased(ppool->active_timeouts, pref->heap_index);
return ISC_R_SUCCESS;
} else {
- return move_lease_to_active(pool, addr);
+ return move_prefix_to_active(ppool, pref);
}
}
/*
- * Put the lease on our inactive pool, with the specified state.
+ * Put the prefix on our inactive pool, with the specified state.
*/
static isc_result_t
-move_lease_to_inactive(struct ipv6_pool *pool, struct iaaddr *addr,
- binding_state_t state) {
+move_prefix_to_inactive(struct ipv6_ppool *ppool, struct iaprefix *pref,
+ binding_state_t state) {
isc_result_t insert_result;
int old_heap_index;
- old_heap_index = addr->heap_index;
- insert_result = isc_heap_insert(pool->inactive_timeouts, addr);
+ old_heap_index = pref->heap_index;
+ insert_result = isc_heap_insert(ppool->inactive_timeouts, pref);
if (insert_result == ISC_R_SUCCESS) {
/* Process events upon expiration. */
- ddns_removals(NULL, addr);
+ /* No DDNS for prefixes. */
/* Binding scopes are no longer valid after expiry or
* release.
*/
- if (addr->scope != NULL) {
- binding_scope_dereference(&addr->scope, MDL);
+ if (pref->scope != NULL) {
+ binding_scope_dereference(&pref->scope, MDL);
}
- iaaddr_hash_delete(pool->addrs,
- &addr->addr, sizeof(addr->addr), MDL);
- isc_heap_delete(pool->active_timeouts, old_heap_index);
- addr->state = state;
- pool->num_active--;
- pool->num_inactive++;
+ iaprefix_hash_delete(ppool->prefs,
+ &pref->pref, sizeof(pref->pref), MDL);
+ isc_heap_delete(ppool->active_timeouts, old_heap_index);
+ pref->state = state;
+ ppool->num_active--;
+ ppool->num_inactive++;
}
return insert_result;
}
/*
- * Expire the oldest lease if it's lifetime_end_time is
+ * Expire the oldest prefix if it's lifetime_end_time is
* older than the given time.
*
- * - iaaddr must be a pointer to a (struct iaaddr *) pointer previously
+ * - iapref must be a pointer to a (struct iaprefix *) pointer previously
* initialized to NULL
*
- * On return iaaddr has a reference to the removed entry. It is left
- * pointing to NULL if the oldest lease has not expired.
+ * On return iapref has a reference to the removed entry. It is left
+ * pointing to NULL if the oldest prefix has not expired.
*/
isc_result_t
-expire_lease6(struct iaaddr **addr, struct ipv6_pool *pool, time_t now) {
- struct iaaddr *tmp;
+expire_prefix6(struct iaprefix **pref, struct ipv6_ppool *ppool, time_t now) {
+ struct iaprefix *tmp;
isc_result_t result;
- if (addr == NULL) {
+ if (pref == NULL) {
log_error("%s(%d): NULL pointer reference", MDL);
return ISC_R_INVALIDARG;
}
- if (*addr != NULL) {
+ if (*pref != NULL) {
log_error("%s(%d): non-NULL pointer", MDL);
return ISC_R_INVALIDARG;
}
- if (pool->num_active > 0) {
- tmp = (struct iaaddr *)isc_heap_element(pool->active_timeouts,
- 1);
+ if (ppool->num_active > 0) {
+ tmp = (struct iaprefix *)
+ isc_heap_element(ppool->active_timeouts, 1);
if (now > tmp->valid_lifetime_end_time) {
- result = move_lease_to_inactive(pool, tmp, FTS_EXPIRED);
+ result = move_prefix_to_inactive(ppool, tmp,
+ FTS_EXPIRED);
if (result == ISC_R_SUCCESS) {
- iaaddr_reference(addr, tmp, MDL);
+ iaprefix_reference(pref, tmp, MDL);
}
return result;
}
/*
- * For a declined lease, leave it on the "active" pool, but mark
- * it as declined. Give it an infinite (well, really long) life.
- */
-isc_result_t
-decline_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
- isc_result_t result;
-
- if (addr->state != FTS_ACTIVE) {
- result = move_lease_to_active(pool, addr);
- if (result != ISC_R_SUCCESS) {
- return result;
- }
- }
- addr->state = FTS_ABANDONED;
- addr->valid_lifetime_end_time = MAX_TIME;
- isc_heap_decreased(pool->active_timeouts, addr->heap_index);
- return ISC_R_SUCCESS;
-}
-
-/*
- * Put the returned lease on our inactive pool.
+ * Put the returned prefix on our inactive pool.
*/
isc_result_t
-release_lease6(struct ipv6_pool *pool, struct iaaddr *addr) {
- if (addr->state == FTS_ACTIVE) {
- return move_lease_to_inactive(pool, addr, FTS_RELEASED);
+release_prefix6(struct ipv6_ppool *ppool, struct iaprefix *pref) {
+ if (pref->state == FTS_ACTIVE) {
+ return move_prefix_to_inactive(ppool, pref, FTS_RELEASED);
} else {
return ISC_R_SUCCESS;
}
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) {
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);
}
*/
ddns_removals(NULL, addr);
- write_ia_na(addr->ia_na);
+ write_ia(addr->ia_na);
iaaddr_dereference(&addr, MDL);
}
}
}
+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.
* 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;
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;
}
}
/*
- * Renew all leases in an IA_NA from all pools.
+ * Renew all leases in an IA from all pools.
*
* The new valid_lifetime_end_time should be updated for the addresses.
*
* WARNING: lease times must only be extended, never reduced!!!
*/
isc_result_t
-renew_leases(struct ia_na *ia_na) {
- return change_leases(ia_na, renew_lease6);
+renew_leases(struct ia_na *ia) {
+ return change_leases(ia, renew_lease6);
+}
+
+/*
+ * Release all leases in an IA from all pools.
+ */
+isc_result_t
+release_leases(struct ia_na *ia) {
+ return change_leases(ia, release_lease6);
+}
+
+/*
+ * Decline all leases in an IA from all pools.
+ */
+isc_result_t
+decline_leases(struct ia_na *ia) {
+ return change_leases(ia, decline_lease6);
+}
+
+/*
+ * Determine if the given prefix is in the pool.
+ */
+isc_boolean_t
+ipv6_prefix_in_ppool(const struct in6_addr *pref,
+ const struct ipv6_ppool *ppool) {
+ struct in6_addr tmp;
+
+ ipv6_network_portion(&tmp, pref, (int)ppool->pool_plen);
+ if (memcmp(&tmp, &ppool->start_pref, sizeof(tmp)) == 0) {
+ return ISC_TRUE;
+ } else {
+ return ISC_FALSE;
+ }
+}
+
+/*
+ * Find the pool that contains the given prefix.
+ *
+ * - pool must be a pointer to a (struct ipv6_ppool *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+find_ipv6_ppool(struct ipv6_ppool **ppool, const struct in6_addr *pref) {
+ int i;
+
+ if (ppool == NULL) {
+ log_error("%s(%d): NULL pointer reference", MDL);
+ return ISC_R_INVALIDARG;
+ }
+ if (*ppool != NULL) {
+ log_error("%s(%d): non-NULL pointer", MDL);
+ return ISC_R_INVALIDARG;
+ }
+
+ for (i=0; i<num_ppools; i++) {
+ if (ipv6_prefix_in_ppool(pref, ppools[i])) {
+ ipv6_ppool_reference(ppool, ppools[i], MDL);
+ return ISC_R_SUCCESS;
+ }
+ }
+ return ISC_R_NOTFOUND;
+}
+
+/*
+ * Helper function for the various functions that act across all
+ * prefix pools.
+ */
+static isc_result_t
+change_prefixes(struct ia_pd *ia_pd,
+ isc_result_t (*change_func)(struct ipv6_ppool *,
+ struct iaprefix*)) {
+ isc_result_t retval;
+ isc_result_t renew_retval;
+ struct ipv6_ppool *ppool;
+ struct in6_addr *pref;
+ int i;
+
+ retval = ISC_R_SUCCESS;
+ for (i=0; i<ia_pd->num_iaprefix; i++) {
+ ppool = NULL;
+ pref = &ia_pd->iaprefix[i]->pref;
+ if (find_ipv6_ppool(&ppool, pref) == ISC_R_SUCCESS) {
+ renew_retval = change_func(ppool, ia_pd->iaprefix[i]);
+ if (renew_retval != ISC_R_SUCCESS) {
+ retval = renew_retval;
+ }
+ }
+ /* XXXsk: should we warn if we don't find a pool? */
+ }
+ return retval;
}
/*
- * Release all leases in an IA_NA from all pools.
+ * Renew all prefixes in an IA_PD from all pools.
+ *
+ * The new valid_lifetime_end_time should be updated for the addresses.
+ *
+ * WARNING: lease times must only be extended, never reduced!!!
*/
isc_result_t
-release_leases(struct ia_na *ia_na) {
- return change_leases(ia_na, release_lease6);
+renew_prefixes(struct ia_pd *ia_pd) {
+ return change_prefixes(ia_pd, renew_prefix6);
}
/*
- * Decline all leases in an IA_NA from all pools.
+ * Release all prefixes in an IA_PD from all pools.
*/
isc_result_t
-decline_leases(struct ia_na *ia_na) {
- return change_leases(ia_na, decline_lease6);
+release_prefixes(struct ia_pd *ia_pd) {
+ return change_prefixes(ia_pd, release_prefix6);
}
#ifdef DHCPv6
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;
}
}
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;
}
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;
}