diff options
-rw-r--r-- | RELNOTES | 4 | ||||
-rw-r--r-- | client/dhc6.c | 866 | ||||
-rw-r--r-- | client/dhclient.8 | 16 | ||||
-rw-r--r-- | client/dhclient.c | 12 | ||||
-rw-r--r-- | client/dhclient.conf.5 | 40 | ||||
-rw-r--r-- | includes/dhcpd.h | 1 | ||||
-rw-r--r-- | includes/site.h | 7 |
7 files changed, 761 insertions, 185 deletions
@@ -121,6 +121,10 @@ by Eric Young (eay@cryptsoft.com). compile DHCP and its bind library dependency). [ISC-Bugs #38836] +- Update the client code to better support getting IA_NAs and IA_PDs + in the same packet, see RFC7550 for some discussion. + [ISC-Bugs #40190] + Changes since 4.3.3b1 - None diff --git a/client/dhc6.c b/client/dhc6.c index 37ce7394..b3a7d5c2 100644 --- a/client/dhc6.c +++ b/client/dhc6.c @@ -52,13 +52,16 @@ static struct dhc6_addr *dhc6_dup_addr(struct dhc6_addr *addr, static void dhc6_ia_destroy(struct dhc6_ia **src, const char *file, int line); static isc_result_t dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet, - struct option_state *options); + struct option_state *options, + unsigned code); static isc_result_t dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet, - struct option_state *options); + struct option_state *options, + unsigned code); static isc_result_t dhc6_parse_ia_pd(struct dhc6_ia **pia, struct packet *packet, - struct option_state *options); + struct option_state *options, + unsigned code); static isc_result_t dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet, struct option_state *options); @@ -78,18 +81,34 @@ void do_init6(void *input); void do_info_request6(void *input); void do_confirm6(void *input); void reply_handler(struct packet *packet, struct client_state *client); +static isc_result_t dhc6_create_iaid(struct client_state *client, + struct data_string *ia, + int idx, + unsigned len); +static int dhc6_count_ia(struct dhc6_lease *lease, + u_int16_t ia_type); +static isc_result_t dhc6_bare_ia_xx(struct client_state *client, + struct data_string *packet, + int wanted, + u_int16_t ia_type); static isc_result_t dhc6_add_ia_na(struct client_state *client, struct data_string *packet, struct dhc6_lease *lease, - u_int8_t message); + u_int8_t message, + int wanted, + int *added); static isc_result_t dhc6_add_ia_ta(struct client_state *client, struct data_string *packet, struct dhc6_lease *lease, - u_int8_t message); + u_int8_t message, + int wanted, + int *added); static isc_result_t dhc6_add_ia_pd(struct client_state *client, struct data_string *packet, struct dhc6_lease *lease, - u_int8_t message); + u_int8_t message, + int wanted, + int *added); static isc_boolean_t stopping_finished(void); static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst); void do_select6(void *input); @@ -112,9 +131,16 @@ static void script_write_params6(struct client_state *client, static void script_write_requested6(struct client_state *client); static isc_boolean_t active_prefix(struct client_state *client); -static int check_timing6(struct client_state *client, u_int8_t msg_type, - char *msg_str, struct dhc6_lease *lease, - struct data_string *ds); +static int check_timing6(struct client_state *client, u_int8_t msg_type, + char *msg_str, struct dhc6_lease *lease, + struct data_string *ds); +static isc_result_t dhc6_get_status_code(struct option_state *options, + unsigned *code, + struct data_string *msg); +static isc_result_t dhc6_check_status(isc_result_t rval, + struct option_state *options, + const char *scope, + unsigned *code); extern int onetry; extern int stateless; @@ -362,7 +388,7 @@ dhc6_retrans_advance(struct client_state *client) if (elapsed.tv_sec >= client->MRD) { /* * The desired RT is the time that will be remaining in MRD - * when the current timeout finishes. We then have + * when the current timeout finishes. We then have * desired RT = MRD - (elapsed time + previous RT); or * desired RT = MRD - elapsed_plut_rt; */ @@ -538,6 +564,12 @@ dhc6_dup_addr(struct dhc6_addr *addr, const char *file, int line) * Form a DHCPv6 lease structure based upon packet contents. Creates and * populates IA's and any IAADDR/IAPREFIX's they contain. * Parsed options are deleted in order to not save them in the lease file. + * + * If we get a status code of NoAddrs or NoPrefix we toss the affected + * IAs. If it as at the top level we toss all IAs of that type. If it + * is in an IA we only toss that one. According to the spec we shouldn't + * get a NoPrefix status at the top level but we will allow it. + * */ static struct dhc6_lease * dhc6_leaseify(struct packet *packet) @@ -545,6 +577,7 @@ dhc6_leaseify(struct packet *packet) struct data_string ds; struct dhc6_lease *lease; struct option_cache *oc; + unsigned code; lease = dmalloc(sizeof(*lease), MDL); if (lease == NULL) { @@ -578,12 +611,28 @@ dhc6_leaseify(struct packet *packet) } delete_option(&dhcpv6_universe, lease->options, D6O_PREFERENCE); + /* Get the top level status code. If the code is NoAddrsAvail + * or NoPrefixAvail strip it from the options as we don't + * want it to show up in check_[advertise reply]. We + * pass it along to the parse_ia_xx routines and they + * will drop the affected IAs for NoAddrs or NoPrefix, + * other status codes will be ignored and handled by + * the check_[advertise reply] routines. + */ + code = STATUS_Success; + if ((dhc6_get_status_code(lease->options, &code, NULL) == ISC_R_SUCCESS) + && + ((code == STATUS_NoAddrsAvail) || (code == STATUS_NoPrefixAvail))) { + delete_option(&dhcpv6_universe, lease->options, + D6O_STATUS_CODE); + } + /* * Dig into recursive DHCPv6 pockets for IA_NA and contained IAADDR * options. */ if (dhc6_parse_ia_na(&lease->bindings, packet, - lease->options) != ISC_R_SUCCESS) { + lease->options, code) != ISC_R_SUCCESS) { /* Error conditions are logged by the caller. */ dhc6_lease_destroy(&lease, MDL); return NULL; @@ -593,7 +642,7 @@ dhc6_leaseify(struct packet *packet) * options. */ if (dhc6_parse_ia_ta(&lease->bindings, packet, - lease->options) != ISC_R_SUCCESS) { + lease->options, code) != ISC_R_SUCCESS) { /* Error conditions are logged by the caller. */ dhc6_lease_destroy(&lease, MDL); return NULL; @@ -603,7 +652,7 @@ dhc6_leaseify(struct packet *packet) * options. */ if (dhc6_parse_ia_pd(&lease->bindings, packet, - lease->options) != ISC_R_SUCCESS) { + lease->options, code) != ISC_R_SUCCESS) { /* Error conditions are logged by the caller. */ dhc6_lease_destroy(&lease, MDL); return NULL; @@ -637,12 +686,13 @@ dhc6_leaseify(struct packet *packet) static isc_result_t dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet, - struct option_state *options) + struct option_state *options, unsigned code) { struct data_string ds; struct dhc6_ia *ia; struct option_cache *oc; isc_result_t result; + unsigned ia_code; memset(&ds, 0, sizeof(ds)); @@ -726,6 +776,23 @@ dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet, } } + /* If we have no addresses or the top level status code + * or the status code in this IA indicate no addresses + * toss the IA. + */ + ia_code = STATUS_Success; + if ((ia->addrs == NULL) || + (code == STATUS_NoAddrsAvail) || + ((ia->options != NULL) && + (dhc6_get_status_code(ia->options, &ia_code, NULL) + == ISC_R_SUCCESS) && + (ia_code == STATUS_NoAddrsAvail))) { + log_debug("RCV: | !-- Status code of " + "no addrs, IA_NA discarded."); + dhc6_ia_destroy(&ia, MDL); + continue; + } + while (*pia != NULL) pia = &(*pia)->next; *pia = ia; @@ -745,12 +812,13 @@ dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet, static isc_result_t dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet, - struct option_state *options) + struct option_state *options, unsigned code) { struct data_string ds; struct dhc6_ia *ia; struct option_cache *oc; isc_result_t result; + unsigned ia_code; memset(&ds, 0, sizeof(ds)); @@ -811,6 +879,23 @@ dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet, } } + /* If we have no addresses or the top level status code + * or the status code in this IA indicate no addresses + * toss the IA. + */ + ia_code = STATUS_Success; + if ((ia->addrs == NULL) || + (code == STATUS_NoAddrsAvail) || + ((ia->options != NULL) && + (dhc6_get_status_code(ia->options, &ia_code, NULL) + == ISC_R_SUCCESS) && + (ia_code == STATUS_NoAddrsAvail))) { + log_debug("RCV: | !-- Status code of " + "no addrs, IA_TA discarded."); + dhc6_ia_destroy(&ia, MDL); + continue; + } + while (*pia != NULL) pia = &(*pia)->next; *pia = ia; @@ -830,12 +915,13 @@ dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet, static isc_result_t dhc6_parse_ia_pd(struct dhc6_ia **pia, struct packet *packet, - struct option_state *options) + struct option_state *options, unsigned code) { struct data_string ds; struct dhc6_ia *ia; struct option_cache *oc; isc_result_t result; + unsigned ia_code; memset(&ds, 0, sizeof(ds)); @@ -916,6 +1002,23 @@ dhc6_parse_ia_pd(struct dhc6_ia **pia, struct packet *packet, } } + /* If we have no prefixes or the top level status code + * or the status code in this IA indicate no prefixes + * toss the IA. + */ + ia_code = STATUS_Success; + if ((ia->addrs == NULL) || + (code == STATUS_NoPrefixAvail) || + ((ia->options != NULL) && + (dhc6_get_status_code(ia->options, &ia_code, NULL) + == ISC_R_SUCCESS) && + (ia_code == STATUS_NoPrefixAvail))) { + log_debug("RCV: | !-- Status code of " + "no prefix, IA_PD discarded."); + dhc6_ia_destroy(&ia, MDL); + continue; + } + while (*pia != NULL) pia = &(*pia)->next; *pia = ia; @@ -941,6 +1044,8 @@ dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet, struct data_string ds; struct option_cache *oc; struct dhc6_addr *addr; + isc_result_t rval = ISC_R_SUCCESS; + unsigned code; memset(&ds, 0, sizeof(ds)); @@ -1009,12 +1114,29 @@ dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet, } } - if (addr->options != NULL) - log_debug("RCV: | | | X-- " - "[Options]"); - data_string_forget(&ds, MDL); + if (addr->options != NULL) { + log_debug("RCV: | | | X-- [Options]"); + + /* Get the status code if the return value + * indicates an error or the status code + * indicates no address toss the address + */ + code = STATUS_Success; + rval = dhc6_check_status(ISC_R_SUCCESS, + addr->options, + "IAADDR", &code); + if (rval != ISC_R_SUCCESS) { + log_debug("RCV: | | | X-- Status code" + " issue, IAADDR discarded."); + option_state_dereference(&addr->options, + MDL); + dfree(addr, MDL); + continue; + } + } + *paddr = addr; paddr = &addr->next; } else { @@ -1037,6 +1159,8 @@ dhc6_parse_prefixes(struct dhc6_addr **ppfx, struct packet *packet, struct data_string ds; struct option_cache *oc; struct dhc6_addr *pfx; + isc_result_t rval = ISC_R_SUCCESS; + unsigned code; memset(&ds, 0, sizeof(ds)); @@ -1115,12 +1239,29 @@ dhc6_parse_prefixes(struct dhc6_addr **ppfx, struct packet *packet, } } - if (pfx->options != NULL) - log_debug("RCV: | | | X-- " - "[Options]"); - data_string_forget(&ds, MDL); + if (pfx->options != NULL) { + log_debug("RCV: | | | X-- [Options]"); + + /* Get the status code if the return value + * indicates an error or the status code + * indicates no prefix toss the prefix + */ + code = STATUS_Success; + rval = dhc6_check_status(ISC_R_SUCCESS, + pfx->options, + "IAPREFIX", &code); + if (rval != ISC_R_SUCCESS) { + log_debug("RCV: | | | X-- Status code" + " issue IAPREFIX discarded."); + option_state_dereference(&pfx->options, + MDL); + dfree(pfx, MDL); + continue; + } + } + *ppfx = pfx; ppfx = &pfx->next; } else { @@ -1220,9 +1361,40 @@ insert_lease(struct dhc6_lease **head, struct dhc6_lease *new) return; } -/* - * Not really clear what to do here yet. +/*! + * + * \brief Determine a score for a lease. We use this to + * compare and choose leases if we receive multiple candidates. + * + * We originally started with scores of 50 for a binding and 100 for + * an address. This would select multiple adresses over multiple + * bindings. As part of the 7550 work I've changed this to be + * 10000 for a binding, 100 for an address and 1 for an option. + * This will cause us to choose a lease with more bindings over + * a lease with less bindings but more addresses which seems + * to be the best selection criteria to me. + * In theory we could end up with a lease with enough addresses + * or options being better but at 100 to 1 I don't think it's likely. + * + * \param client = the state of the entire client + * \param lease = the lease to score. + * + * \retrun the score of the lease */ + +/* The scores for individual items. */ +#ifdef USE_ORIGINAL_CLIENT_LEASE_WEIGHTS +#define SCORE_BINDING 50 +#define SCORE_ADDRESS 100 +#else +#define SCORE_BINDING 10000 +#define SCORE_ADDRESS 100 +#endif + +#define SCORE_OPTION 1 +/* We need a lease with at least 1 binding and 1 address */ +#define SCORE_MIN (SCORE_BINDING + SCORE_ADDRESS) + static int dhc6_score_lease(struct client_state *client, struct dhc6_lease *lease) { @@ -1234,7 +1406,7 @@ dhc6_score_lease(struct client_state *client, struct dhc6_lease *lease) if (lease->score) return lease->score; - lease->score = 1; + lease->score = SCORE_OPTION; /* If this lease lacks a required option, dump it. */ /* XXX: we should be able to cache the failure... */ @@ -1255,15 +1427,15 @@ dhc6_score_lease(struct client_state *client, struct dhc6_lease *lease) for (i = 0 ; req[i] != NULL ; i++) { if (lookup_option(&dhcpv6_universe, lease->options, req[i]->code) != NULL) - lease->score++; + lease->score += SCORE_OPTION; } } for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { - lease->score += 50; + lease->score += SCORE_BINDING; for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { - lease->score += 100; + lease->score += SCORE_ADDRESS; } } @@ -1413,7 +1585,18 @@ start_confirm6(struct client_state *client) tv.tv_sec += 1; tv.tv_usec -= 1000000; } - if (wanted_ia_pd != 0) { + + /* We do a rebind instead of a confirm if the user + * is requesting PDs or previously requesed PDs or + * increased the number of NAs or TAs they want + * Confirms don't tell us if PDs are still on-link and + * we won't add new IAs on a confirm. + */ + + if ((wanted_ia_pd != 0) || + (dhc6_count_ia(client->active_lease, D6O_IA_PD) != 0) || + (dhc6_count_ia(client->active_lease, D6O_IA_NA) < wanted_ia_na) || + (dhc6_count_ia(client->active_lease, D6O_IA_TA) < wanted_ia_ta)) { client->state = S_REBINDING; client->refresh_type = DHCPV6_REBIND; add_timeout(&tv, do_refresh6, client, NULL, NULL); @@ -1431,7 +1614,7 @@ start_confirm6(struct client_state *client) #define CHK_TIM_ALLOC_FAILURE 3 int -check_timing6 (struct client_state *client, u_int8_t msg_type, +check_timing6 (struct client_state *client, u_int8_t msg_type, char *msg_str, struct dhc6_lease *lease, struct data_string *ds) { @@ -1496,6 +1679,154 @@ check_timing6 (struct client_state *client, u_int8_t msg_type, return(CHK_TIM_SUCCESS); } +/*! + * + * \brief Create an iaid from information from the client. + * + * \param client = the state of the entire client + * \param ia = the ia to fill in + * \param idx = index of the ia in case we are doing multiples + * \param len = length of the base IA (4 for TA, 12 for NA & PD) + * + * \return ISC_R_SUCCESS - all is well continue, any other return indicates + * an error and the packet should be tossed + */ + +static isc_result_t +dhc6_create_iaid(struct client_state *client, + struct data_string *ia, + int idx, + unsigned len) +{ + int start_idx, copy_len; + + memset(ia, 0, sizeof(*ia)); + if (!buffer_allocate(&ia->buffer, 12, MDL)) { + return (ISC_R_NOMEMORY); + } + ia->data = ia->buffer->data; + ia->len = len; + + /* + * A simple IAID is the last 4 bytes + * of the hardware address. + */ + if (client->interface->hw_address.hlen > 4) { + start_idx = client->interface->hw_address.hlen - 4; + copy_len = 4; + } else { + start_idx = 0; + copy_len = client->interface->hw_address.hlen; + } + memcpy(ia->buffer->data, + client->interface->hw_address.hbuf + start_idx, + copy_len); + if (idx) + ia->buffer->data[3] += idx; + + return (ISC_R_SUCCESS); +} + +/*! + * + * \brief Add bare IA_NAs, IA_TAs or IA_PDs to the packet we are building. + * + * Attempt to add the number of bare IAs indicated by wanted to + * the packet. As we have already added a number of IAs based + * on what is in the current lease after we create an IAID we check + * it against the current lease and skip any that are already in use. + * + * \param client = the state of the entire client + * \param packet = the packet we are building and where we + * shall append the IA_NA, IA_TA or IA_PDs we create + * \param wanted = the number of IA_NA, IA_TA or IA_PDs we want to create + * \param ia_type = the type of the IAs we want to create: NA, TA or PD. + * + * \return ISC_R_SUCCESS - all is well continue, any other return indicates + * an error and the packet should be tossed + */ +static isc_result_t +dhc6_bare_ia_xx(struct client_state *client, + struct data_string *packet, + int wanted, + u_int16_t ia_type) +{ + struct dhc6_ia *old_ia; + struct data_string ia; + u_int32_t t1, t2; + int i, len; + isc_result_t rval; + char *type_string; + struct option *type_option; + + /* figure out what type of option we are working with */ + switch (ia_type) { + case D6O_IA_NA: + type_string = "IA_NA"; + type_option = ia_na_option; + len = 12; + break; + case D6O_IA_TA: + type_string = "IA_TA"; + type_option = ia_ta_option; + len = 4; + break; + case D6O_IA_PD: + type_string = "IA_PD"; + type_option = ia_pd_option; + len = 12; + break; + default: + return (ISC_R_FAILURE); + } + + for (i = 0; wanted != 0; i++) { + rval = dhc6_create_iaid(client, &ia, i, len); + if (rval != ISC_R_SUCCESS) { + log_error("Unable to allocate memory for %s.", + type_string); + return (rval); + } + + /* If we are already using this IAID, skip it and try again */ + if ((client->active_lease != NULL) && + ((old_ia = find_ia(client->active_lease->bindings, + ia_type, + (char *)ia.buffer->data)) != NULL)) { + data_string_forget(&ia, MDL); + continue; + } + + /* We have a good IAID, log it */ + log_debug("XMT: X-- %s %s", + type_string, print_hex_1(4, ia.buffer->data, 55)); + + /* If we are requesting an NA or a PD we also want to add + * the renew and rebind times we are requesting. + */ + if (len == 12) { + t1 = client->config->requested_lease / 2; + t2 = t1 + (t1 / 2); + putULong(ia.buffer->data + 4, t1); + putULong(ia.buffer->data + 8, t2); + + log_debug("XMT: | X-- Request renew in +%u", + (unsigned)t1); + log_debug("XMT: | X-- Request rebind in +%u", + (unsigned)t2); + } + + /* and append it to the packet */ + append_option(packet, &dhcpv6_universe, type_option, &ia); + data_string_forget(&ia, MDL); + + /* decrement the number of IAs we want */ + wanted--; + } + + return (ISC_R_SUCCESS); +} + /* * do_init6() marshals and transmits a solicit. */ @@ -1510,7 +1841,7 @@ do_init6(void *input) struct data_string addr; struct timeval tv; u_int32_t t1, t2; - int i, idx, len, send_ret; + int i, send_ret; client = input; @@ -1559,31 +1890,11 @@ do_init6(void *input) * cache. They'd have to be pulled down as they also contain * different option caches in the same universe... */ - memset(&ia, 0, sizeof(ia)); - if (!buffer_allocate(&ia.buffer, 12, MDL)) { + if (dhc6_create_iaid(client, &ia, i, 12) != ISC_R_SUCCESS) { log_error("Unable to allocate memory for IA_NA."); data_string_forget(&ds, MDL); return; } - ia.data = ia.buffer->data; - ia.len = 12; - - /* - * A simple IAID is the last 4 bytes - * of the hardware address. - */ - if (client->interface->hw_address.hlen > 4) { - idx = client->interface->hw_address.hlen - 4; - len = 4; - } else { - idx = 0; - len = client->interface->hw_address.hlen; - } - memcpy(ia.buffer->data, - client->interface->hw_address.hbuf + idx, - len); - if (i) - ia.buffer->data[3] += i; t1 = client->config->requested_lease / 2; t2 = t1 + (t1 / 2); @@ -1662,31 +1973,11 @@ do_init6(void *input) * cache. They'd have to be pulled down as they also contain * different option caches in the same universe... */ - memset(&ia, 0, sizeof(ia)); - if (!buffer_allocate(&ia.buffer, 4, MDL)) { + if (dhc6_create_iaid(client, &ia, i, 4) != ISC_R_SUCCESS) { log_error("Unable to allocate memory for IA_TA."); data_string_forget(&ds, MDL); return; } - ia.data = ia.buffer->data; - ia.len = 4; - - /* - * A simple IAID is the last 4 bytes - * of the hardware address. - */ - if (client->interface->hw_address.hlen > 4) { - idx = client->interface->hw_address.hlen - 4; - len = 4; - } else { - idx = 0; - len = client->interface->hw_address.hlen; - } - memcpy(ia.buffer->data, - client->interface->hw_address.hbuf + idx, - len); - if (i) - ia.buffer->data[3] += i; log_debug("XMT: X-- IA_TA %s", print_hex_1(4, ia.buffer->data, 55)); @@ -1759,30 +2050,11 @@ do_init6(void *input) * different option caches in the same universe... */ memset(&ia, 0, sizeof(ia)); - if (!buffer_allocate(&ia.buffer, 12, MDL)) { + if (dhc6_create_iaid(client, &ia, i, 12) != ISC_R_SUCCESS) { log_error("Unable to allocate memory for IA_PD."); data_string_forget(&ds, MDL); return; } - ia.data = ia.buffer->data; - ia.len = 12; - - /* - * A simple IAID is the last 4 bytes - * of the hardware address. - */ - if (client->interface->hw_address.hlen > 4) { - idx = client->interface->hw_address.hlen - 4; - len = 4; - } else { - idx = 0; - len = client->interface->hw_address.hlen; - } - memcpy(ia.buffer->data, - client->interface->hw_address.hbuf + idx, - len); - if (i) - ia.buffer->data[3] += i; t1 = client->config->requested_lease / 2; t2 = t1 + (t1 / 2); @@ -1945,7 +2217,7 @@ do_confirm6(void *input) { struct client_state *client; struct data_string ds; - int send_ret; + int send_ret, added; struct timeval tv; client = input; @@ -1988,13 +2260,13 @@ do_confirm6(void *input) /* Append IA's. */ if (wanted_ia_na && dhc6_add_ia_na(client, &ds, client->active_lease, - DHCPV6_CONFIRM) != ISC_R_SUCCESS) { + DHCPV6_CONFIRM, 0, &added) != ISC_R_SUCCESS) { data_string_forget(&ds, MDL); return; } if (wanted_ia_ta && dhc6_add_ia_ta(client, &ds, client->active_lease, - DHCPV6_CONFIRM) != ISC_R_SUCCESS) { + DHCPV6_CONFIRM, 0, &added) != ISC_R_SUCCESS) { data_string_forget(&ds, MDL); return; } @@ -2071,7 +2343,7 @@ do_release6(void *input) { struct client_state *client; struct data_string ds; - int send_ret; + int send_ret, added; struct timeval tv; client = input; @@ -2079,7 +2351,7 @@ do_release6(void *input) if ((client->active_lease == NULL) || !active_prefix(client)) return; - switch(check_timing6(client, DHCPV6_RELEASE, "Release", + switch(check_timing6(client, DHCPV6_RELEASE, "Release", client->active_lease, &ds)) { case CHK_TIM_MRC_EXCEEDED: case CHK_TIM_ALLOC_FAILURE: @@ -2101,13 +2373,13 @@ do_release6(void *input) /* Append IA's (but don't release temporary addresses). */ if (wanted_ia_na && dhc6_add_ia_na(client, &ds, client->active_lease, - DHCPV6_RELEASE) != ISC_R_SUCCESS) { + DHCPV6_RELEASE, 0, &added) != ISC_R_SUCCESS) { data_string_forget(&ds, MDL); goto release_done; } if (wanted_ia_pd && dhc6_add_ia_pd(client, &ds, client->active_lease, - DHCPV6_RELEASE) != ISC_R_SUCCESS) { + DHCPV6_RELEASE, 0, &added) != ISC_R_SUCCESS) { data_string_forget(&ds, MDL); goto release_done; } @@ -2272,18 +2544,24 @@ dhc6_check_status(isc_result_t rval, struct option_state *options, return rval; } -/* Look in the packet, any IA's, and any IAADDR's within those IA's to find - * status code options that are not SUCCESS. +/* Determine if this packet could provide usable information. + * We check the status codes at the top level and at the IA level, + * IAADDRS have already been checked in the leaseify step and any with + * a bad format or status code that wasn't success have been dropped. + * + * leaseify has also already removed any IAs for which the top level status + * code or the IA status code indicated no addresses or prefixes were + * available. */ static isc_result_t dhc6_check_advertise(struct dhc6_lease *lease) { struct dhc6_ia *ia; - struct dhc6_addr *addr; isc_result_t rval = ISC_R_SUCCESS; int have_addrs = ISC_FALSE; unsigned code; const char *scope; + int got_na = 0, got_ta = 0, got_pd = 0; rval = dhc6_check_status(rval, lease->options, "message", &code); @@ -2291,31 +2569,45 @@ dhc6_check_advertise(struct dhc6_lease *lease) switch (ia->ia_type) { case D6O_IA_NA: scope = "IA_NA"; + got_na++; break; case D6O_IA_TA: scope = "IA_TA"; + got_ta++; break; case D6O_IA_PD: scope = "IA_PD"; + got_pd++; break; default: log_error("dhc6_check_advertise: no type."); return ISC_R_FAILURE; } + /* Currently we toss packets if we have an error getting a + * status code or if the status code isn't success, so + * no need to loop through the addresses */ rval = dhc6_check_status(rval, ia->options, scope, &code); + if (rval != ISC_R_SUCCESS) + continue; - 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, - scope, &code); + /* We don't need to check status on IAADDRS here as we already + * did it as part of the leaseify step and tossed bad IAADDRS. + * We are just checking to see if we have any addrs. + * Should we check the addr itself for usability? + */ + if (ia->addrs != NULL) { have_addrs = ISC_TRUE; } } - if (have_addrs != ISC_TRUE) + /* If we didn't get some addrs or the user required us to + * get all of the requested IAs and we didn't return an error + */ + if ((have_addrs != ISC_TRUE) || + ((require_all_ias != 0) && + ((got_na < wanted_ia_na) || + (got_ta < wanted_ia_ta) || + (got_pd < wanted_ia_pd)))) rval = ISC_R_ADDRNOTAVAIL; return rval; @@ -2645,11 +2937,12 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new) isc_boolean_t (*action)(struct client_state *, isc_result_t *, unsigned); struct dhc6_ia *ia; - struct dhc6_addr *addr; isc_result_t rval = ISC_R_SUCCESS; unsigned code; const char *scope; int nscore, sscore; + int have_addrs = ISC_FALSE; + int got_na = 0, got_ta = 0, got_pd = 0; if ((client == NULL) || (new == NULL)) return DHCP_R_INVALIDARG; @@ -2690,32 +2983,27 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new) switch (ia->ia_type) { case D6O_IA_NA: scope = "IA_NA"; + got_na++; break; case D6O_IA_TA: scope = "IA_TA"; + got_ta++; break; case D6O_IA_PD: scope = "IA_PD"; + got_pd++; break; default: log_error("dhc6_check_reply: no type."); return DHCP_R_INVALIDARG; } - rval = dhc6_check_status(rval, ia->options, - scope, &code); + 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, - scope, &code); - if (action(client, &rval, code)) - return ISC_R_CANCELED; + if (ia->addrs != NULL) { + have_addrs = ISC_TRUE; } } @@ -2723,6 +3011,26 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new) if (client->state == S_REBOOTING) return rval; + /* We expect the lease to have at least one address and if + * required all of the requested IAs if not flag it as + * NoAddrs and call the action routine to try again. + * + * Currently we don't completely handle TAs in all cases + * so we don't check them for requires. I've left the + * check in and commented it as I eventually do want + * us to check for TAs as well. SAR + */ + if ((have_addrs != ISC_TRUE) || + ((require_all_ias != 0) && + ((got_na < wanted_ia_na) || + /*(got_ta < wanted_ia_ta) ||*/ + (got_pd < wanted_ia_pd)))) { + rval = ISC_R_FAILURE; + if (action(client, &rval, STATUS_NoAddrsAvail) == ISC_TRUE) { + return ISC_R_CANCELED; + } + } + /* No old lease in rapid-commit. */ if (client->state == S_INIT) return rval; @@ -2834,7 +3142,7 @@ init_handler(struct packet *packet, struct client_state *client) */ if ((client->txcount > 1) || ((lease->pref == 255) && - (dhc6_score_lease(client, lease) > 150))) { + (dhc6_score_lease(client, lease) > SCORE_MIN))) { log_debug("RCV: Advertisement immediately selected."); cancel_timeout(do_init6, client); start_selecting6(client); @@ -2863,6 +3171,7 @@ info_request_handler(struct packet *packet, struct client_state *client) check_status = dhc6_check_status(ISC_R_SUCCESS, packet->options, "message", &code); + if (check_status != ISC_R_SUCCESS) { /* If no action was taken, but there is an error, then * we wait for a retransmission. @@ -3036,8 +3345,8 @@ dhc6_best_lease(struct client_state *client, struct dhc6_lease **head) * * The third clause tells us to give up on leases that * have no bindings even if their preference is better. - * So where our 'selected' lease's score is less than 150 - * (1 ia + 1 addr), choose any candidate >= 150. + * So where our 'selected' lease's score is less than + * SCORE_MIN (1 ia + 1 addr), choose any candidate >= SCORE_MIN. * * The first clause tells us to make preference the primary * deciding factor. So if it's lower, reject, if it's @@ -3054,7 +3363,7 @@ dhc6_best_lease(struct client_state *client, struct dhc6_lease **head) * Since server id's are unique in this list, there is * no further tie to break. */ - if ((rscore < 150) && (cscore >= 150)) { + if ((rscore < SCORE_MIN) && (cscore >= SCORE_MIN)) { log_debug("PRC: | X-- Selected, has bindings."); } else if (cand->pref < rval->pref) { log_debug("PRC: | X-- Rejected, lower preference."); @@ -3141,7 +3450,7 @@ do_select6(void *input) struct dhc6_lease *lease; struct data_string ds; struct timeval tv; - int send_ret; + int send_ret, added; client = input; @@ -3193,22 +3502,32 @@ do_select6(void *input) NULL, client->sent_options, &global_scope, &dhcpv6_universe); - /* Now append any IA's, and within them any IAADDR/IAPREFIXs. */ + /* Now append any IA's, and within them any IAADDR/IAPREFIXs. + * For each type of IA (na, ta, pd) we start with the ones for + * which we already have addresses (dhc6_add_ia_xx) and then + * if we still want more we add aditional IAs (dhc6_bare_ia_xx) + */ if (wanted_ia_na && - dhc6_add_ia_na(client, &ds, lease, - DHCPV6_REQUEST) != ISC_R_SUCCESS) { + ((dhc6_add_ia_na(client, &ds, lease, DHCPV6_REQUEST, + wanted_ia_na, &added) != ISC_R_SUCCESS) || + (dhc6_bare_ia_xx(client, &ds, wanted_ia_na - added, + D6O_IA_NA) != ISC_R_SUCCESS))) { data_string_forget(&ds, MDL); return; } if (wanted_ia_ta && - dhc6_add_ia_ta(client, &ds, lease, - DHCPV6_REQUEST) != ISC_R_SUCCESS) { + ((dhc6_add_ia_ta(client, &ds, lease, DHCPV6_REQUEST, + wanted_ia_ta, &added) != ISC_R_SUCCESS) || + (dhc6_bare_ia_xx(client, &ds, wanted_ia_ta - added, + D6O_IA_TA) != ISC_R_SUCCESS))) { data_string_forget(&ds, MDL); return; } if (wanted_ia_pd && - dhc6_add_ia_pd(client, &ds, lease, - DHCPV6_REQUEST) != ISC_R_SUCCESS) { + ((dhc6_add_ia_pd(client, &ds, lease, DHCPV6_REQUEST, + wanted_ia_pd, &added) != ISC_R_SUCCESS) || + (dhc6_bare_ia_xx(client, &ds, wanted_ia_pd - added, + D6O_IA_PD) != ISC_R_SUCCESS))) { data_string_forget(&ds, MDL); return; } @@ -3238,12 +3557,59 @@ do_select6(void *input) dhc6_retrans_advance(client); } -/* For each IA_NA in the lease, for each address in the IA_NA, - * append that information onto the packet-so-far. +/*! + * + * \brief Count the number of IAs in the bindings + * + * \param lease the lease to count + * \param ia_type the type of the IA we wish to count + * + * \return The number of IAs of the specified type we found + */ +static int +dhc6_count_ia(struct dhc6_lease *lease, u_int16_t ia_type) +{ + struct dhc6_ia *ia; + int i = 0; + + for (ia = lease->bindings; ia != NULL; ia = ia->next) { + if (ia->ia_type == ia_type) + /* bump the counter for the correct types */ + i++; + } + + return (i); +} + +/*! + * + * \brief Add IA_NA information from the lease to the packet + * we are building. + * + * Walk through the lease and for each IA_NA in the lease + * and for each address in the IA_NA append that information + * onto the packet-so-far. If wanted is 0 include all IA_NAs + * in the lease if wanted is non-zero include only that many + * IA_NAs (this may occur if sommebody restarts a client with + * arugments for a smaller number of NAs than before). + * + * \param client = the state of the entire client + * \param packet = the packet we are building and where we + * shall append the IA_NAs we create + * \param lease = the current lease + * \param message = the type of the packet + * \param wanted = the number of IA_NAs to include in the packet + * 0 means include all + * \param added = the number of IA_NAs that were added to the packet + * + * \return ISC_R_SUCCESS - all is well continue, any other return + * indicates an error (most likely memory issues) + * and the packet should be tossed. */ static isc_result_t dhc6_add_ia_na(struct client_state *client, struct data_string *packet, - struct dhc6_lease *lease, u_int8_t message) + struct dhc6_lease *lease, u_int8_t message, + int wanted, int *added) { struct data_string iads; struct data_string addrds; @@ -3251,15 +3617,20 @@ dhc6_add_ia_na(struct client_state *client, struct data_string *packet, struct dhc6_ia *ia; isc_result_t rval = ISC_R_SUCCESS; TIME t1, t2; + int i; + *added = 0; memset(&iads, 0, sizeof(iads)); memset(&addrds, 0, sizeof(addrds)); - for (ia = lease->bindings; - ia != NULL && rval == ISC_R_SUCCESS; + for (ia = lease->bindings, i = 0; + ia != NULL && rval == ISC_R_SUCCESS && (wanted == 0 || i < wanted); ia = ia->next) { if (ia->ia_type != D6O_IA_NA) continue; + /* Now that we know this is an NA bump the counter */ + i++; + if (!buffer_allocate(&iads.buffer, 12, MDL)) { log_error("Unable to allocate memory for IA_NA."); rval = ISC_R_NOMEMORY; @@ -3407,15 +3778,41 @@ dhc6_add_ia_na(struct client_state *client, struct data_string *packet, data_string_forget(&iads, MDL); } - return rval; + if (rval == ISC_R_SUCCESS) + *added = i; + + return (rval); } -/* For each IA_TA in the lease, for each address in the IA_TA, - * append that information onto the packet-so-far. +/*! + * + * \brief Add IA_TA information from the lease to the packet + * we are building. + * + * Walk through the lease and for each IA_TA in the lease + * and for each address in the IA_TA append that information + * onto the packet-so-far. If wanted is 0 include all IA_TAs + * in the lease if wanted is non-zero include only that many + * IA_TAs (this may occur if sommebody restarts a client with + * arugments for a smaller number of TAs than before). + * + * \param client = the state of the entire client + * \param packet = the packet we are building and where we + * shall append the IA_TAs we create + * \param lease = the current lease + * \param message = the type of the packet + * \param wanted = the number of IA_TAs to include in the packet + * 0 means include all + * \param added = the number of IA_TAs that were added to the packet + * + * \return ISC_R_SUCCESS - all is well continue, any other return + * indicates an error (most likely memory issues) + * and the packet should be tossed. */ static isc_result_t dhc6_add_ia_ta(struct client_state *client, struct data_string *packet, - struct dhc6_lease *lease, u_int8_t message) + struct dhc6_lease *lease, u_int8_t message, + int wanted, int *added) { struct data_string iads; struct data_string addrds; @@ -3423,15 +3820,20 @@ dhc6_add_ia_ta(struct client_state *client, struct data_string *packet, struct dhc6_ia *ia; isc_result_t rval = ISC_R_SUCCESS; TIME t1, t2; + int i; + *added = 0; memset(&iads, 0, sizeof(iads)); memset(&addrds, 0, sizeof(addrds)); - for (ia = lease->bindings; - ia != NULL && rval == ISC_R_SUCCESS; + for (ia = lease->bindings, i = 0; + ia != NULL && rval == ISC_R_SUCCESS && (wanted == 0 || i < wanted); ia = ia->next) { if (ia->ia_type != D6O_IA_TA) continue; + /* Now that we know this is an TA bump the counter */ + i++; + if (!buffer_allocate(&iads.buffer, 4, MDL)) { log_error("Unable to allocate memory for IA_TA."); rval = ISC_R_NOMEMORY; @@ -3537,15 +3939,41 @@ dhc6_add_ia_ta(struct client_state *client, struct data_string *packet, data_string_forget(&iads, MDL); } - return rval; + if (rval == ISC_R_SUCCESS) + *added = i; + + return (rval); } -/* For each IA_PD in the lease, for each prefix in the IA_PD, - * append that information onto the packet-so-far. +/*! + * + * \brief Add IA_PD information from the lease to the packet + * we are building. + * + * Walk through the lease and for each IA_PD in the lease + * and for each address in the IA_PD append that information + * onto the packet-so-far. If wanted is 0 include all IA_PDs + * in the lease if wanted is non-zero include only that many + * IA_PDs (this may occur if sommebody restarts a client with + * arugments for a smaller number of PDs than before). + * + * \param client = the state of the entire client + * \param packet = the packet we are building and where we + * shall append the IA_PDs we create + * \param lease = the current lease + * \param message = the type of the packet + * \param wanted = the number of IA_PDs to include in the packet + * 0 means include all + * \param added = the number of IA_PDs that were added to the packet + * + * \return ISC_R_SUCCESS - all is well continue, any other return + * indicates an error (most likely memory issues) + * and the packet should be tossed. */ static isc_result_t dhc6_add_ia_pd(struct client_state *client, struct data_string *packet, - struct dhc6_lease *lease, u_int8_t message) + struct dhc6_lease *lease, u_int8_t message, + int wanted, int *added) { struct data_string iads; struct data_string prefds; @@ -3553,15 +3981,20 @@ dhc6_add_ia_pd(struct client_state *client, struct data_string *packet, struct dhc6_ia *ia; isc_result_t rval = ISC_R_SUCCESS; TIME t1, t2; + int i; + *added = 0; memset(&iads, 0, sizeof(iads)); memset(&prefds, 0, sizeof(prefds)); - for (ia = lease->bindings; - ia != NULL && rval == ISC_R_SUCCESS; + for (ia = lease->bindings, i = 0; + ia != NULL && rval == ISC_R_SUCCESS && (wanted == 0 || i < wanted); ia = ia->next) { if (ia->ia_type != D6O_IA_PD) continue; + /* Now that we know this is an PD bump the counter */ + i++; + if (!buffer_allocate(&iads.buffer, 12, MDL)) { log_error("Unable to allocate memory for IA_PD."); rval = ISC_R_NOMEMORY; @@ -3701,7 +4134,10 @@ dhc6_add_ia_pd(struct client_state *client, struct data_string *packet, data_string_forget(&iads, MDL); } - return rval; + if (rval == ISC_R_SUCCESS) + *added = i; + + return (rval); } /* stopping_finished() checks if there is a remaining work to do. @@ -3905,7 +4341,7 @@ dhc6_check_times(struct client_state *client) struct dhc6_ia *ia; struct dhc6_addr *addr; TIME renew=MAX_TIME, rebind=MAX_TIME, depref=MAX_TIME, - lo_expire=MAX_TIME, hi_expire=0, tmp; + lo_expire=MAX_TIME, hi_expire=0, max_ia_starts = 0, tmp; int has_addrs = ISC_FALSE; struct timeval tv; @@ -3972,26 +4408,37 @@ dhc6_check_times(struct client_state *client) use_expire /= 2; /* Don't renew/rebind temporary addresses. */ + /* For NA and PD we find the most recent IA and the smallest + * values for the renew and rebind then base the timer on + * the sum of the them. + * Normally all the IAs will have the same time as they + * are requested and served as a group but in some cases the + * client isn't asking for all of the IAs (for example + * restarted with a different set of arguments) or the server + * isn't updating the client on all of them (probably a + * broken server). + */ if (ia->ia_type != D6O_IA_TA) { + if (ia->starts > max_ia_starts) + max_ia_starts = ia->starts; if (ia->renew == 0) { - tmp = ia->starts + use_expire; + tmp = use_expire; } else if (ia->renew == 0xffffffff) tmp = MAX_TIME; else - tmp = ia->starts + ia->renew; + tmp = ia->renew; if (tmp < renew) renew = tmp; if (ia->rebind == 0) { /* Set rebind to 3/4 expiration interval. */ - tmp = ia->starts; - tmp += use_expire + (use_expire / 2); + tmp = use_expire + (use_expire / 2); } else if (ia->rebind == 0xffffffff) tmp = MAX_TIME; else - tmp = ia->starts + ia->rebind; + tmp = ia->rebind; if (tmp < rebind) rebind = tmp; @@ -4028,6 +4475,15 @@ dhc6_check_times(struct client_state *client) return; } + /* Second part of calculating the renew and rebind times. + * We have the start time and the desired periods for renew + * and rebind, just add them to get the desired end time. + */ + if (renew != MAX_TIME) + renew += max_ia_starts; + if (rebind != MAX_TIME) + rebind += max_ia_starts; + switch(client->state) { case S_BOUND: /* We'd like to hit renewing, but if rebinding has already @@ -4145,15 +4601,29 @@ find_pref(struct dhc6_addr *head, struct iaddr *prefix, u_int8_t plen) return NULL; } -/* Merge the bindings from the source lease into the destination lease - * structure, where they are missing. We have to copy the stateful - * objects rather than move them over, because later code needs to be - * able to compare new versus old if they contain any bindings. +/* + * + * \brief Merge the bindings from the source lease into the destination + * lease structure, where they are missing. + * + * This is used to merge any extra information we have in the current + * (older, src) lease into the lease we have just received. For example + * the src lease might include a binding for an NA that is still usable + * but that we didn't request or that the server is no longer serving. + * We want to keep that information until we toss the binding (expire, + * release) so we move it to the new lease. + * + * We have to copy the stateful objects rather than move them over, + * because later code needs to be able to compare new versus old if + * they contain any bindings. + * + * \param src The older lease to copy the objects from + * \param dst The newer lease to copy the objects to */ static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst) { - struct dhc6_ia *sia, *dia, *tia; + struct dhc6_ia *sia, *dia, *tia, **eia; struct dhc6_addr *saddr, *daddr, *taddr; int changes = 0; @@ -4171,9 +4641,20 @@ dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst) "Unable to continue without losing " "state! (%s:%d)", MDL); - /* XXX: consider sorting? */ - tia->next = dst->bindings; - dst->bindings = tia; + /* Put any bindings that aren't in the new lease at the + * end of the list. If the user or server reduces the + * number of IAs the ones in use will be at the front + * and will be used when building the next requests + * We could be more efficient by finding the end + * of the list once but we don't expect to do this + * often. + */ + for (eia = &dst->bindings; + *eia != NULL; + eia = &(*eia)->next) { + ; /* no work just find the end */ + } + *eia = tia; changes = 1; } else { for (saddr = sia->addrs ; saddr != NULL ; @@ -4408,7 +4889,7 @@ do_refresh6(void *input) struct client_state *client; struct dhc6_lease *lease; struct timeval elapsed, tv; - int send_ret; + int send_ret, added; client = (struct client_state *)input; memset(&ds, 0, sizeof(ds)); @@ -4515,16 +4996,24 @@ do_refresh6(void *input) client->sent_options, &global_scope, &dhcpv6_universe); - /* Append IA's */ + /* Now append any IA's, and within them any IAADDR/IAPREFIXs. + * For each type of IA (na, ta, pd) we start with the ones for + * which we already have addresses (dhc6_add_ia_xx) and then + * if we still want more we add aditional IAs (dhc6_bare_ia_xx) + */ if (wanted_ia_na && - dhc6_add_ia_na(client, &ds, lease, - client->refresh_type) != ISC_R_SUCCESS) { + ((dhc6_add_ia_na(client, &ds, lease, client->refresh_type, + wanted_ia_na, &added) != ISC_R_SUCCESS) || + (dhc6_bare_ia_xx(client, &ds, wanted_ia_na - added, + D6O_IA_NA) != ISC_R_SUCCESS))) { data_string_forget(&ds, MDL); return; } if (wanted_ia_pd && - dhc6_add_ia_pd(client, &ds, lease, - client->refresh_type) != ISC_R_SUCCESS) { + ((dhc6_add_ia_pd(client, &ds, lease, client->refresh_type, + wanted_ia_pd, &added) != ISC_R_SUCCESS) || + (dhc6_bare_ia_xx(client, &ds, wanted_ia_pd - added, + D6O_IA_PD) != ISC_R_SUCCESS))) { data_string_forget(&ds, MDL); return; } @@ -4634,7 +5123,7 @@ do_depref(void *input) /* Remove DDNS bindings at depref time. */ if ((ia->ia_type == D6O_IA_NA) && client->config->do_forward_update) - client_dns_remove(client, + client_dns_remove(client, &addr->address); #endif } @@ -4652,9 +5141,10 @@ do_expire(void *input) { struct client_state *client; struct dhc6_lease *lease; - struct dhc6_ia *ia; + struct dhc6_ia *ia, **tia; struct dhc6_addr *addr; int has_addrs = ISC_FALSE; + int ia_has_addrs = ISC_FALSE; client = (struct client_state *)input; @@ -4662,7 +5152,8 @@ do_expire(void *input) if (lease == NULL) return; - for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { + for (ia = lease->bindings, tia = &lease->bindings; ia != NULL ; ) { + ia_has_addrs = ISC_FALSE; for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { if (addr->flags & DHC6_ADDR_EXPIRED) continue; @@ -4699,8 +5190,25 @@ do_expire(void *input) continue; } + ia_has_addrs = ISC_TRUE; has_addrs = ISC_TRUE; } + + /* Update to the next ia and git rid of this ia + * if it doesn't have any leases. + */ + if (ia_has_addrs == ISC_TRUE) { + /* leases, just advance the list pointer */ + tia = &(*tia)->next; + } else { + /* no leases, update the list pointer + * and free the ia + */ + *tia = ia->next; + dhc6_ia_destroy(&ia, MDL); + } + /* lastly update the ia pointer to our new ia */ + ia = *tia; } /* Clean up empty leases. */ diff --git a/client/dhclient.8 b/client/dhclient.8 index 5b056981..83260e61 100644 --- a/client/dhclient.8 +++ b/client/dhclient.8 @@ -55,6 +55,8 @@ dhclient - Dynamic Host Configuration Protocol Client .B -P... ] ] +.B -R +] [ .B -i ] @@ -364,8 +366,21 @@ See \fB\-N\fR to restore it. .BI \-P Enable IPv6 prefix delegation. This implies \fB\-6\fR and also disables the normal address query. See \fB\-N\fR to restore it. +Multiple prefixes can be requested with multiple \fB\-P\fR flags. Note only one requested interface is allowed. .TP +.BI \-R +Require that responses include all of the items requested by any +\fB\-N\fR, \fB\-T\fR, or \fB\-P\fR options. Normally even if +the command line includes a number of these the client will be willing +to accept the best lease it can even if the lease doesn't include all +of the requested items. This option causes the client to only +accept leases that include all of the requested items. + +Note well: enabling this may prevent the client from using any +leases it receives if the servers aren't configured to supply +all of the items. +.TP .BI \-D \ LL\ or\ LLT Override the default when selecting the type of DUID to use. By default, DHCPv6 \fBdhclient\fR creates an identifier based on the link-layer address @@ -381,6 +396,7 @@ overrides these default, with a value of either \fILL\fR or \fILLT\fR. .\" TODO: is this for telling an already running dhclient? Restore normal address query for IPv6. This implies \fB-6\fR. It is used to restore normal operation after using \fB-T\fR or \fB-P\fR. +Multiple addresses can be requested with multiple \fB\-N\fR flags. .PP .I Modifying default file locations: The following options can be used to modify the locations a client uses diff --git a/client/dhclient.c b/client/dhclient.c index 3a0c0611..5583ca17 100644 --- a/client/dhclient.c +++ b/client/dhclient.c @@ -88,6 +88,9 @@ int stateless = 0; int wanted_ia_na = -1; /* the absolute value is the real one. */ int wanted_ia_ta = 0; int wanted_ia_pd = 0; +int require_all_ias = 0; /* If the user requires all of the IAs to + be available before accepting a lease + 0 = no, 1 = requries */ char *mockup_relay = NULL; char *progname = NULL; @@ -308,6 +311,13 @@ main(int argc, char **argv) { wanted_ia_na = 0; } wanted_ia_pd++; + } else if (!strcmp(argv[i], "-R")) { + if (local_family_set && (local_family == AF_INET)) { + usage(); + } + local_family_set = 1; + local_family = AF_INET6; + require_all_ias = 1; #endif /* DHCPv6 */ } else if (!strcmp(argv[i], "-D")) { duid_v4 = 1; @@ -761,7 +771,7 @@ static void usage() log_fatal("Usage: %s " #ifdef DHCPv6 - "[-4|-6] [-SNTPI1dvrxi] [-nw] [-p <port>] [-D LL|LLT] \n" + "[-4|-6] [-SNTPRI1dvrxi] [-nw] [-p <port>] [-D LL|LLT] \n" #else /* DHCPv6 */ "[-I1dvrxi] [-nw] [-p <port>] [-D LL|LLT] \n" #endif /* DHCPv6 */ diff --git a/client/dhclient.conf.5 b/client/dhclient.conf.5 index 93b16d3c..3082df6c 100644 --- a/client/dhclient.conf.5 +++ b/client/dhclient.conf.5 @@ -55,16 +55,15 @@ reasonable timing behaviour will be used by default - one which results in fairly timely updates without placing an inordinate load on the server. .PP -The following statements can be used to adjust the timing behaviour of -the DHCP client if required, however: +If required the following statements can be used to adjust the timing +behaviour of the DHCPv4 client. The DHCPv6 protocol provides values +to use and they are not currently configurable. .PP .I The .B timeout .I statement .PP -.B timeout -.I time -.B ; + \fBtimeout \fItime\fR\fB;\fR .PP The .I timeout @@ -176,6 +175,37 @@ client used to wait random time up to 5 seconds, but that was unwanted due to impact on startup time. As such, new versions have the default initial delay set to 0. To restore old behavior, please set initial-delay to 5. +.SH DHCPv6 LEASE SELECTION +In the DHCPv6 protocol the client will wait a small amount of time to +allow ADVERTISE messages from multiple servers to arrive. It will then +need to choose from all of the messages that may have arrived before +proceeding to making a request of the selected server. + +The first selection criteria is the set of options and addresses +in the message. Messages that don't include an option specified +as required will be given a score of 0 and not used. If the +\fI-R\fR option is given on the command line then messages that +don't include the correct number of bindings (IA-NA, IA-TA or +IA-PD) will be discarded. + +The next criteria is the preference value from the message. With +the highest preference value being used even if leases with better +addresses or options are available. + +Finally the lease is scored and the lease with the highest score +is selected. A lease's score is based on the number of bindings, +number of addresses and number of options it contains: +.nf + bindings * X + addresses * Y + options +.fi +By default X = 10000 and Y = 100, this will cause the client to +select a lease with more bindings over a lease with less bindings +but more addresses. The weightings were changed as part of +implementing RFC 7550. Previously they were X = 50 and Y = 100 +meaning more addresses were preferred over more bindings. If +you wish to continue using the old style you may do so by editing +the file includes/site.h and uncommenting the define for +USE_ORIGINAL_CLIENT_LEASE_WEIGHTS. .SH LEASE REQUIREMENTS AND REQUESTS The DHCP protocol allows the client to request that the server send it specific information, and not send it other information that it is not diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 02cdc5c0..1cf66d33 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -2863,6 +2863,7 @@ extern int nowait; extern int wanted_ia_na; extern int wanted_ia_ta; extern int wanted_ia_pd; +extern int require_all_ias; extern const char *path_dhclient_conf; extern const char *path_dhclient_db; diff --git a/includes/site.h b/includes/site.h index ce7876ba..7c6d8431 100644 --- a/includes/site.h +++ b/includes/site.h @@ -298,6 +298,13 @@ this option will be removed at some time. */ /* #define INCLUDE_OLD_DHCP_ISC_ERROR_CODES */ +/* Use the older factors for scoring a lease in the v6 client code. + The new factors cause the client to choose more bindings (IAs) + over more addresse within a binding. Most uses will get a + single address in a single binding and only get an adverstise + from a single server and there won't be a difference. */ +/* #define USE_ORIGINAL_CLIENT_LEASE_WEIGHTS */ + /* Include definitions for various options. In general these should be left as is, but if you have already defined one of these and prefer your definition you can comment the |