--- embedaddon/dhcp/server/dhcpv6.c 2012/02/21 22:30:18 1.1 +++ embedaddon/dhcp/server/dhcpv6.c 2012/10/09 09:06:55 1.1.1.1 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2011 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2006-2012 by Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1091,7 +1091,8 @@ try_client_v6_prefix(struct iasubopt **pref, return ISC_R_INVALIDARG; } tmp_plen = (int) requested_pref->data[0]; - if ((tmp_plen < 3) || (tmp_plen > 128)) { + if ((tmp_plen < 3) || (tmp_plen > 128) || + ((int)tmp_plen != pool->units)) { return ISC_R_FAILURE; } memcpy(&tmp_pref, requested_pref->data + 1, sizeof(tmp_pref)); @@ -1104,9 +1105,8 @@ try_client_v6_prefix(struct iasubopt **pref, return ISC_R_FAILURE; } - if (((int)tmp_plen != pool->units) || - !ipv6_in_pool(&tmp_pref, pool)) { - return ISC_R_FAILURE; + if (!ipv6_in_pool(&tmp_pref, pool)) { + return ISC_R_ADDRNOTAVAIL; } if (prefix6_exists(pool, &tmp_pref, tmp_plen)) { @@ -1206,6 +1206,10 @@ pick_v6_prefix(struct iasubopt **pref, int plen, } /* + *! \file server/dhcpv6.c + * + * \brief construct a reply containing information about a client's lease + * * lease_to_client() is called from several messages to construct a * reply that contains all that we know about the client's correct lease * (or projected lease). @@ -1227,8 +1231,15 @@ pick_v6_prefix(struct iasubopt **pref, int plen, * validate and echo back any contents that can be. If the client-supplied * data does not error out (on renew/rebind as above), but we did not send * any addresses, attempt to allocate one. + * + * At the end of the this function we call commit_leases_timed() to + * fsync and rotate the file as necessary. commit_leases_timed() will + * check that we have written at least one lease to the file and that + * some time has passed before doing any fsync or file rewrite so we + * don't bother tracking if we did a write_ia during this function. */ /* TODO: look at client hints for lease times */ + static void lease_to_client(struct data_string *reply_ret, struct packet *packet, @@ -1238,8 +1249,12 @@ lease_to_client(struct data_string *reply_ret, static struct reply_state reply; struct option_cache *oc; struct data_string packet_oro; - isc_boolean_t no_resources_avail; +#if defined (RFC3315_PRE_ERRATA_2010_08) + isc_boolean_t no_resources_avail = ISC_FALSE; +#endif + memset(&packet_oro, 0, sizeof(packet_oro)); + /* Locate the client. */ if (shared_network_from_packet6(&reply.shared, packet) != ISC_R_SUCCESS) @@ -1262,7 +1277,6 @@ lease_to_client(struct data_string *reply_ret, * Get the ORO from the packet, if any. */ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_ORO); - memset(&packet_oro, 0, sizeof(packet_oro)); if (oc != NULL) { if (!evaluate_option_cache(&packet_oro, packet, NULL, NULL, @@ -1290,7 +1304,7 @@ lease_to_client(struct data_string *reply_ret, /* Process the client supplied IA's onto the reply buffer. */ reply.ia_count = 0; oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA); - no_resources_avail = ISC_FALSE; + for (; oc != NULL ; oc = oc->next) { isc_result_t status; @@ -1308,12 +1322,14 @@ lease_to_client(struct data_string *reply_ret, (status != ISC_R_NORESOURCES)) goto exit; +#if defined (RFC3315_PRE_ERRATA_2010_08) /* * If any address cannot be given to any IA, then set the * NoAddrsAvail status code. */ if (reply.client_resources == 0) no_resources_avail = ISC_TRUE; +#endif } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_TA); for (; oc != NULL ; oc = oc->next) { @@ -1333,12 +1349,14 @@ lease_to_client(struct data_string *reply_ret, (status != ISC_R_NORESOURCES)) goto exit; +#if defined (RFC3315_PRE_ERRATA_2010_08) /* * If any address cannot be given to any IA, then set the * NoAddrsAvail status code. */ if (reply.client_resources == 0) no_resources_avail = ISC_TRUE; +#endif } /* Same for IA_PD's. */ @@ -1360,13 +1378,6 @@ lease_to_client(struct data_string *reply_ret, if ((status != ISC_R_SUCCESS) && (status != ISC_R_NORESOURCES)) goto exit; - - /* - * If any prefix cannot be given to any IA_PD, then - * set the NoPrefixAvail status code. - */ - if (reply.client_resources == 0) - no_resources_avail = ISC_TRUE; } /* @@ -1422,6 +1433,7 @@ lease_to_client(struct data_string *reply_ret, * the server. * Sends a Renew/Rebind if the IA is not in the Reply message. */ +#if defined (RFC3315_PRE_ERRATA_2010_08) if (no_resources_avail && (reply.ia_count != 0) && (reply.packet->dhcpv6_msg_type == DHCPV6_SOLICIT)) { @@ -1452,36 +1464,6 @@ lease_to_client(struct data_string *reply_ret, reply.opt_state, reply.packet, required_opts_NAA, NULL); - } else if (no_resources_avail && (reply.ia_count == 0) && - (reply.packet->dhcpv6_msg_type == DHCPV6_SOLICIT)) - { - /* Set the NoPrefixAvail status code. */ - if (!set_status_code(STATUS_NoPrefixAvail, - "No prefixes available for this " - "interface.", reply.opt_state)) { - log_error("lease_to_client: Unable to set " - "NoPrefixAvail status code."); - goto exit; - } - - /* Rewind the cursor to the start. */ - reply.cursor = REPLY_OPTIONS_INDEX; - - /* - * Produce an advertise that includes only: - * - * Status code. - * Server DUID. - * Client DUID. - */ - reply.buf.reply.msg_type = DHCPV6_ADVERTISE; - reply.cursor += store_options6((char *)reply.buf.data + - reply.cursor, - sizeof(reply.buf) - - reply.cursor, - reply.opt_state, reply.packet, - required_opts_NAA, - NULL); } else { /* * Having stored the client's IA's, store any options that @@ -1495,6 +1477,17 @@ lease_to_client(struct data_string *reply_ret, required_opts_solicit, &packet_oro); } +#else /* defined (RFC3315_PRE_ERRATA_2010_08) */ + /* + * Having stored the client's IA's, store any options that + * will fit in the remaining space. + */ + reply.cursor += store_options6((char *)reply.buf.data + reply.cursor, + sizeof(reply.buf) - reply.cursor, + reply.opt_state, reply.packet, + required_opts_solicit, + &packet_oro); +#endif /* defined (RFC3315_PRE_ERRATA_2010_08) */ /* Return our reply to the caller. */ reply_ret->len = reply.cursor; @@ -1505,6 +1498,9 @@ lease_to_client(struct data_string *reply_ret, memcpy(reply_ret->buffer->data, reply.buf.data, reply.cursor); reply_ret->data = reply_ret->buffer->data; + /* If appropriate commit and rotate the lease file */ + (void) commit_leases_timed(); + exit: /* Cleanup. */ if (reply.shared != NULL) @@ -1517,6 +1513,8 @@ lease_to_client(struct data_string *reply_ret, packet_dereference(&reply.packet, MDL); if (reply.client_id.data != NULL) data_string_forget(&reply.client_id, MDL); + if (packet_oro.buffer != NULL) + data_string_forget(&packet_oro, MDL); reply.renew = reply.rebind = reply.prefer = reply.valid = 0; reply.cursor = 0; } @@ -1832,9 +1830,6 @@ reply_process_ia_na(struct reply_state *reply, struct ia_reference(&tmp->ia, reply->ia, MDL); /* Commit 'hard' bindings. */ - tmp->hard_lifetime_end_time = - tmp->soft_lifetime_end_time; - tmp->soft_lifetime_end_time = 0; renew_lease6(tmp->ipv6_pool, tmp); schedule_lease_timeout(tmp->ipv6_pool); @@ -2219,13 +2214,13 @@ address_is_owned(struct reply_state *reply, struct iad log_fatal("Impossible condition at %s:%d.", MDL); if (memcmp(addr->iabuf, reply->fixed.data, 16) == 0) - return ISC_TRUE; + return (ISC_TRUE); - return ISC_FALSE; + return (ISC_FALSE); } if ((reply->old_ia == NULL) || (reply->old_ia->num_iasubopt == 0)) - return ISC_FALSE; + return (ISC_FALSE); for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) { struct iasubopt *tmp; @@ -2233,12 +2228,15 @@ address_is_owned(struct reply_state *reply, struct iad tmp = reply->old_ia->iasubopt[i]; if (memcmp(addr->iabuf, &tmp->addr, 16) == 0) { + if (lease6_usable(tmp) == ISC_FALSE) { + return (ISC_FALSE); + } iasubopt_reference(&reply->lease, tmp, MDL); - return ISC_TRUE; + return (ISC_TRUE); } } - return ISC_FALSE; + return (ISC_FALSE); } /* Process a client-supplied IA_TA. This may append options to the tail of @@ -2488,9 +2486,6 @@ reply_process_ia_ta(struct reply_state *reply, struct ia_reference(&tmp->ia, reply->ia, MDL); /* Commit 'hard' bindings. */ - tmp->hard_lifetime_end_time = - tmp->soft_lifetime_end_time; - tmp->soft_lifetime_end_time = 0; renew_lease6(tmp->ipv6_pool, tmp); schedule_lease_timeout(tmp->ipv6_pool); @@ -2692,16 +2687,18 @@ find_client_temporaries(struct reply_state *reply) { */ static isc_result_t reply_process_try_addr(struct reply_state *reply, struct iaddr *addr) { - isc_result_t status = ISC_R_NORESOURCES; + isc_result_t status = ISC_R_ADDRNOTAVAIL; struct ipv6_pool *pool; int i; struct data_string data_addr; if ((reply == NULL) || (reply->shared == NULL) || - (reply->shared->ipv6_pools == NULL) || (addr == NULL) || - (reply->lease != NULL)) + (addr == NULL) || (reply->lease != NULL)) return ISC_R_INVALIDARG; + if (reply->shared->ipv6_pools == NULL) + return (ISC_R_ADDRNOTAVAIL); + memset(&data_addr, 0, sizeof(data_addr)); data_addr.len = addr->len; data_addr.data = addr->iabuf; @@ -2718,7 +2715,7 @@ reply_process_try_addr(struct reply_state *reply, stru /* Note that this is just pedantry. There is no allocation to free. */ data_string_forget(&data_addr, MDL); /* Return just the most recent status... */ - return status; + return (status); } /* Look around for an address to give the client. First, look through the @@ -2758,7 +2755,8 @@ find_client_address(struct reply_state *reply) { * Look for the best lease on the client's shared * network. */ - if (candidate_shared == reply->shared) { + if ((candidate_shared == reply->shared) && + (lease6_usable(lease) == ISC_TRUE)) { best_lease = lease_compare(lease, best_lease); } } @@ -2769,7 +2767,7 @@ find_client_address(struct reply_state *reply) { */ if ((best_lease == NULL) || (best_lease->state == FTS_ABANDONED)) { status = pick_v6_address(&reply->lease, reply->shared, - &reply->client_id); + &reply->ia->iaid_duid); } else if (best_lease != NULL) { iasubopt_reference(&reply->lease, best_lease, MDL); status = ISC_R_SUCCESS; @@ -3213,7 +3211,9 @@ reply_process_ia_pd(struct reply_state *reply, struct if (status == ISC_R_CANCELED) break; - if ((status != ISC_R_SUCCESS) && (status != ISC_R_ADDRINUSE)) + if ((status != ISC_R_SUCCESS) && + (status != ISC_R_ADDRINUSE) && + (status != ISC_R_ADDRNOTAVAIL)) goto cleanup; } @@ -3353,9 +3353,6 @@ reply_process_ia_pd(struct reply_state *reply, struct ia_reference(&tmp->ia, reply->ia, MDL); /* Commit 'hard' bindings. */ - tmp->hard_lifetime_end_time = - tmp->soft_lifetime_end_time; - tmp->soft_lifetime_end_time = 0; renew_lease6(tmp->ipv6_pool, tmp); schedule_lease_timeout(tmp->ipv6_pool); } @@ -3492,8 +3489,9 @@ reply_process_prefix(struct reply_state *reply, struct status = reply_process_try_prefix(reply, &tmp_pref); /* Either error out or skip this prefix. */ - if ((status != ISC_R_SUCCESS) && - (status != ISC_R_ADDRINUSE)) + if ((status != ISC_R_SUCCESS) && + (status != ISC_R_ADDRINUSE) && + (status != ISC_R_ADDRNOTAVAIL)) goto cleanup; if (reply->lease == NULL) { @@ -3641,14 +3639,14 @@ prefix_is_owned(struct reply_state *reply, struct iadd if ((pref->bits == l->cidrnet.bits) && (memcmp(pref->lo_addr.iabuf, l->cidrnet.lo_addr.iabuf, 16) == 0)) - return ISC_TRUE; + return (ISC_TRUE); } - return ISC_FALSE; + return (ISC_FALSE); } if ((reply->old_ia == NULL) || (reply->old_ia->num_iasubopt == 0)) - return ISC_FALSE; + return (ISC_FALSE); for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) { struct iasubopt *tmp; @@ -3656,13 +3654,16 @@ prefix_is_owned(struct reply_state *reply, struct iadd tmp = reply->old_ia->iasubopt[i]; if ((pref->bits == (int) tmp->plen) && - memcmp(pref->lo_addr.iabuf, &tmp->addr, 16) == 0) { + (memcmp(pref->lo_addr.iabuf, &tmp->addr, 16) == 0)) { + if (lease6_usable(tmp) == ISC_FALSE) { + return (ISC_FALSE); + } iasubopt_reference(&reply->lease, tmp, MDL); - return ISC_TRUE; + return (ISC_TRUE); } } - return ISC_FALSE; + return (ISC_FALSE); } /* @@ -3672,21 +3673,23 @@ prefix_is_owned(struct reply_state *reply, struct iadd static isc_result_t reply_process_try_prefix(struct reply_state *reply, struct iaddrcidrnet *pref) { - isc_result_t status = ISC_R_NORESOURCES; + isc_result_t status = ISC_R_ADDRNOTAVAIL; struct ipv6_pool *pool; int i; struct data_string data_pref; if ((reply == NULL) || (reply->shared == NULL) || - (reply->shared->ipv6_pools == NULL) || (pref == NULL) || - (reply->lease != NULL)) + (pref == NULL) || (reply->lease != NULL)) return ISC_R_INVALIDARG; + if (reply->shared->ipv6_pools == NULL) + return (ISC_R_ADDRNOTAVAIL); + memset(&data_pref, 0, sizeof(data_pref)); data_pref.len = 17; if (!buffer_allocate(&data_pref.buffer, data_pref.len, MDL)) { log_error("reply_process_try_prefix: out of memory."); - return ISC_R_NOMEMORY; + return (ISC_R_NOMEMORY); } data_pref.data = data_pref.buffer->data; data_pref.buffer->data[0] = (u_int8_t) pref->bits; @@ -3705,7 +3708,7 @@ reply_process_try_prefix(struct reply_state *reply, data_string_forget(&data_pref, MDL); /* Return just the most recent status... */ - return status; + return (status); } /* Look around for a prefix to give the client. First, look through the old @@ -3756,8 +3759,9 @@ find_client_prefix(struct reply_state *reply) { * if it is scoped in a pool under the client's shared * network. */ - if (candidate_shared == NULL || - candidate_shared == reply->shared) { + if (((candidate_shared == NULL) || + (candidate_shared == reply->shared)) && + (lease6_usable(prefix) == ISC_TRUE)) { best_prefix = prefix_compare(reply, prefix, best_prefix); } @@ -4606,7 +4610,6 @@ iterate_over_ia_na(struct data_string *reply_ret, struct option_state *host_opt_state; struct data_string iaaddr; struct data_string fixed_addr; - int iaaddr_is_found; char reply_data[65536]; struct dhcpv6_packet *reply = (struct dhcpv6_packet *)reply_data; int reply_ofs = (int)(offsetof(struct dhcpv6_packet, options)); @@ -4709,7 +4712,6 @@ iterate_over_ia_na(struct data_string *reply_ret, */ for (ia = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA); ia != NULL; ia = ia->next) { - iaaddr_is_found = 0; if (!get_encapsulated_IA_state(&cli_enc_opt_state, &cli_enc_opt_data, @@ -5120,7 +5122,6 @@ iterate_over_ia_pd(struct data_string *reply_ret, struct host_decl *host; struct option_state *host_opt_state; struct data_string iaprefix; - int iaprefix_is_found; char reply_data[65536]; int reply_ofs; struct iasubopt *prefix; @@ -5183,7 +5184,6 @@ iterate_over_ia_pd(struct data_string *reply_ret, */ for (ia = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_PD); ia != NULL; ia = ia->next) { - iaprefix_is_found = 0; if (!get_encapsulated_IA_state(&cli_enc_opt_state, &cli_enc_opt_data, @@ -5510,6 +5510,7 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struc msg_type = enc_opt_data.data[0]; if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) { + int relaylen = (int)(offsetof(struct dhcpv6_relay_packet, options)); relay = (struct dhcpv6_relay_packet *)enc_opt_data.data; enc_packet->dhcpv6_msg_type = relay->msg_type; @@ -5522,13 +5523,14 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struc if (!parse_option_buffer(enc_packet->options, relay->options, - enc_opt_data.len-sizeof(*relay), + enc_opt_data.len - relaylen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */ goto exit; } } else { + int msglen = (int)(offsetof(struct dhcpv6_packet, options)); msg = (struct dhcpv6_packet *)enc_opt_data.data; enc_packet->dhcpv6_msg_type = msg->msg_type; @@ -5539,7 +5541,7 @@ dhcpv6_relay_forw(struct data_string *reply_ret, struc if (!parse_option_buffer(enc_packet->options, msg->options, - enc_opt_data.len-sizeof(*msg), + enc_opt_data.len - msglen, &dhcpv6_universe)) { /* no logging here, as parse_option_buffer() logs all cases where it fails */