diff --git a/RELNOTES b/RELNOTES index dd71a851..40a3897f 100644 --- a/RELNOTES +++ b/RELNOTES @@ -94,6 +94,10 @@ suggested fixes to . to put dhcpd.leases and dhclient.leases in /usr/local/var/db, which no one ever has. +- Regression fix for bug where server advertised a IPv6 address in + response to a SOLICIT but would not return the address in response + to a REQUEST. + Changes since 4.0.0b2 - Clarified error message when lease limit exceeded diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 3d380653..a426d9ec 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -3184,6 +3184,7 @@ isc_result_t ia_na_add_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr, const char *file, int line); void ia_na_remove_iaaddr(struct ia_na *ia_na, struct iaaddr *iaaddr, const char *file, int line); +isc_boolean_t ia_na_equal(const struct ia_na *a, const struct ia_na *b); isc_result_t ipv6_pool_allocate(struct ipv6_pool **pool, const struct in6_addr *start_addr, int bits, diff --git a/includes/tree.h b/includes/tree.h index 8171ce1b..ef8f3baf 100644 --- a/includes/tree.h +++ b/includes/tree.h @@ -75,6 +75,8 @@ struct buffer { XXX ephemeral by default and be made a persistent reference explicitly. */ /* XXX on the other hand, it seems to work pretty nicely, so maybe the XXX above comment is meshuggenah. */ +/* XXX I think the above comment tries to say this: + XXX http://tinyurl.com/2tjqre */ /* A string of data bytes, possibly accompanied by a larger buffer. */ struct data_string { diff --git a/server/dhcpv6.c b/server/dhcpv6.c index ac59b284..69c3f6d1 100644 --- a/server/dhcpv6.c +++ b/server/dhcpv6.c @@ -1130,7 +1130,7 @@ lease_to_client(struct data_string *reply_ret, reply.cursor = 0; } -/* Process a client-supplied IA_NA. This may append options ot the tail of +/* Process a client-supplied IA_NA. This may append options to the tail of * the reply packet being built in the reply_state structure. */ static isc_result_t @@ -1141,12 +1141,16 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) { struct option_state *packet_ia; struct option_cache *oc; struct data_string ia_data, data; + isc_boolean_t lease_in_database; /* Initialize values that will get cleaned up on return. */ packet_ia = NULL; memset(&ia_data, 0, sizeof(ia_data)); memset(&data, 0, sizeof(data)); - /* Note that find_client_address() may set reply->lease. */ + lease_in_database = ISC_FALSE; + /* + * Note that find_client_address() may set reply->lease. + */ /* Make sure there is at least room for the header. */ if ((reply->cursor + IA_NA_OFFSET + 4) > sizeof(reply->buf)) { @@ -1395,7 +1399,7 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) { * pool timers for each (if any). */ if ((status != ISC_R_CANCELED) && !reply->static_lease && - (reply->packet->dhcpv6_msg_type != DHCPV6_SOLICIT) && + (reply->buf.reply.msg_type == DHCPV6_REPLY) && (reply->ia_na->num_iaaddr != 0)) { struct iaaddr *tmp; struct data_string *ia_id; @@ -1443,6 +1447,23 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) { ia_id->len, reply->ia_na, MDL); write_ia_na(reply->ia_na); + + /* + * Note that we wrote the lease into the database, + * so that we know not to release it when we're done + * with this function. + */ + lease_in_database = ISC_TRUE; + + /* + * If this is a soft binding, we will check to see if we are + * suggesting the existing database entry to the client. + */ + } else if ((status != ISC_R_CANCELED) && !reply->static_lease && + (reply->old_ia != NULL)) { + if (ia_na_equal(reply->old_ia, reply->ia_na)) { + lease_in_database = ISC_TRUE; + } } cleanup: @@ -1458,8 +1479,12 @@ reply_process_ia(struct reply_state *reply, struct option_cache *ia) { ia_na_dereference(&reply->ia_na, MDL); if (reply->old_ia != NULL) ia_na_dereference(&reply->old_ia, MDL); - if (reply->lease != NULL) + if (reply->lease != NULL) { + if (!lease_in_database) { + release_lease6(reply->lease->ipv6_pool, reply->lease); + } iaaddr_dereference(&reply->lease, MDL); + } if (reply->fixed.data != NULL) data_string_forget(&reply->fixed, MDL); diff --git a/server/mdb6.c b/server/mdb6.c index 398e6c67..5e48a29e 100644 --- a/server/mdb6.c +++ b/server/mdb6.c @@ -343,6 +343,68 @@ ia_na_remove_all_iaaddr(struct ia_na *ia_na, const char *file, int line) { ia_na->num_iaaddr = 0; } +/* + * Compare two IA_NA. + */ +isc_boolean_t +ia_na_equal(const struct ia_na *a, const struct ia_na *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 addresses in each. + */ + if (a->num_iaaddr != b->num_iaaddr) { + return ISC_FALSE; + } + + /* + * Check that each address is present in both. + */ + for (i=0; inum_iaaddr; i++) { + found = ISC_FALSE; + for (j=0; jnum_iaaddr; j++) { + if (memcmp(&(a->iaaddr[i]->addr), + &(b->iaaddr[j]->addr), + 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.