mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-08-22 01:49:35 +00:00
[master] Client (-6) DECLINEs and resolicits when client script DAD check fails
Merges in rt21237
This commit is contained in:
parent
d60573340f
commit
0f69ff73f0
9
RELNOTES
9
RELNOTES
@ -218,6 +218,15 @@ dhcp-users@lists.isc.org.
|
||||
completion of the test.
|
||||
[ISC-Bugs #22267]
|
||||
|
||||
- dhclient will now generate a DHCPv6 DECLINE message containing all IA_NA
|
||||
addresses which for which the client script indicates a DAD failure. After
|
||||
receiving the DECLINE reply, dhclient will restart the solicit process.
|
||||
Note, the client script must exit with a value of 3 to signify that the
|
||||
address failed DAD. Thanks to Jiri Popelka of Red Hat for submitting the
|
||||
patch that was the foundation for this change.
|
||||
[ISC-Bugs #21297]
|
||||
[ISC-Bugs #23357]
|
||||
|
||||
Changes since 4.3.0 (bug fixes)
|
||||
|
||||
- Tidy up several small tickets.
|
||||
|
412
client/dhc6.c
412
client/dhc6.c
@ -115,6 +115,8 @@ void do_select6(void *input);
|
||||
void do_refresh6(void *input);
|
||||
static void do_release6(void *input);
|
||||
static void start_bound(struct client_state *client);
|
||||
static void start_decline6(struct client_state *client);
|
||||
static void do_decline6(void *input);
|
||||
static void start_informed(struct client_state *client);
|
||||
void informed_handler(struct packet *packet, struct client_state *client);
|
||||
void bound_handler(struct packet *packet, struct client_state *client);
|
||||
@ -144,6 +146,11 @@ static isc_result_t dhc6_check_status(isc_result_t rval,
|
||||
static int dhc6_score_lease(struct client_state *client,
|
||||
struct dhc6_lease *lease);
|
||||
|
||||
static isc_result_t dhc6_add_ia_na_decline(struct client_state *client,
|
||||
struct data_string *packet,
|
||||
struct dhc6_lease *lease);
|
||||
static int drop_declined_addrs(struct dhc6_lease *lease);
|
||||
|
||||
extern int onetry;
|
||||
extern int stateless;
|
||||
extern int prefix_len_hint;
|
||||
@ -2353,6 +2360,7 @@ start_release6(struct client_state *client)
|
||||
cancel_timeout(do_select6, client);
|
||||
cancel_timeout(do_refresh6, client);
|
||||
cancel_timeout(do_release6, client);
|
||||
cancel_timeout(do_decline6, client);
|
||||
client->state = S_STOPPED;
|
||||
|
||||
/*
|
||||
@ -2972,6 +2980,47 @@ dhc6_stop_action(struct client_state *client, isc_result_t *rvalp,
|
||||
return ISC_TRUE;
|
||||
}
|
||||
|
||||
static isc_boolean_t
|
||||
dhc6_decline_action(struct client_state *client, isc_result_t *rvalp,
|
||||
unsigned code)
|
||||
{
|
||||
isc_result_t rval;
|
||||
|
||||
if (rvalp == NULL)
|
||||
log_fatal("Impossible condition at %s:%d.", MDL);
|
||||
|
||||
if (client == NULL) {
|
||||
*rvalp = DHCP_R_INVALIDARG;
|
||||
return ISC_FALSE;
|
||||
}
|
||||
rval = *rvalp;
|
||||
|
||||
if (rval == ISC_R_SUCCESS) {
|
||||
return ISC_FALSE;
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
case STATUS_UseMulticast:
|
||||
/* The server is telling us to use a multicast address, so
|
||||
* we have to delete the unicast option from the active
|
||||
* lease, then allow retransmission to occur normally.
|
||||
* (XXX: It might be preferable in this case to retransmit
|
||||
* sooner than the current interval, but for now we don't.)
|
||||
*/
|
||||
if (client->active_lease != NULL)
|
||||
delete_option(&dhcp_universe,
|
||||
client->active_lease->options,
|
||||
D6O_UNICAST);
|
||||
return ISC_FALSE;
|
||||
default:
|
||||
/* Anything else is basically meaningless */
|
||||
break;
|
||||
}
|
||||
|
||||
return ISC_TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Look at a new and old lease, and make sure the new information is not
|
||||
* losing us any state.
|
||||
*/
|
||||
@ -3010,6 +3059,10 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
|
||||
action = dhc6_stop_action;
|
||||
break;
|
||||
|
||||
case S_DECLINING:
|
||||
action = dhc6_decline_action;
|
||||
break;
|
||||
|
||||
default:
|
||||
log_fatal("Impossible condition at %s:%d.", MDL);
|
||||
return ISC_R_CANCELED;
|
||||
@ -3123,6 +3176,7 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
|
||||
break;
|
||||
|
||||
case S_STOPPED:
|
||||
case S_DECLINING:
|
||||
/* Nothing critical to do at this stage. */
|
||||
break;
|
||||
|
||||
@ -4261,17 +4315,35 @@ reply_handler(struct packet *packet, struct client_state *client)
|
||||
cancel_timeout(do_select6, client);
|
||||
cancel_timeout(do_refresh6, client);
|
||||
cancel_timeout(do_release6, client);
|
||||
cancel_timeout(do_decline6, client);
|
||||
|
||||
/* If this is in response to a Release/Decline, clean up and return. */
|
||||
/* If this is in response to a Release, clean up and return. */
|
||||
if (client->state == S_STOPPED) {
|
||||
if (client->active_lease == NULL)
|
||||
return;
|
||||
if (client->active_lease != NULL) {
|
||||
dhc6_lease_destroy(&client->active_lease, MDL);
|
||||
client->active_lease = NULL;
|
||||
/* We should never wait for nothing!? */
|
||||
if (stopping_finished()) {
|
||||
finish(0);
|
||||
}
|
||||
}
|
||||
|
||||
dhc6_lease_destroy(&client->active_lease, MDL);
|
||||
client->active_lease = NULL;
|
||||
/* We should never wait for nothing!? */
|
||||
if (stopping_finished())
|
||||
finish(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state == S_DECLINING) {
|
||||
/* Weed thru the lease and delete all declined addresses.
|
||||
* Toss the lease if there aren't any addresses left */
|
||||
int live_cnt = drop_declined_addrs(client->active_lease);
|
||||
if (live_cnt == 0) {
|
||||
dhc6_lease_destroy(&client->active_lease, MDL);
|
||||
client->active_lease = NULL;
|
||||
}
|
||||
|
||||
/* Solicit with any live addresses we have so far, and
|
||||
* add additional empty NA iasubopts for those we had
|
||||
* to decline. */
|
||||
start_init6(client);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -4770,6 +4842,7 @@ start_bound(struct client_state *client)
|
||||
struct dhc6_addr *addr, *oldaddr;
|
||||
struct dhc6_lease *lease, *old;
|
||||
const char *reason;
|
||||
int decline_cnt = 0;
|
||||
#if defined (NSUPDATE)
|
||||
TIME dns_update_offset = 1;
|
||||
#endif
|
||||
@ -4853,7 +4926,22 @@ start_bound(struct client_state *client)
|
||||
dhc6_marshall_values("new_", client, lease, ia, addr);
|
||||
script_write_requested6(client);
|
||||
|
||||
script_go(client);
|
||||
/* When script returns 3, DAD failed */
|
||||
if (script_go(client) == 3) {
|
||||
if (ia->ia_type == D6O_IA_NA) {
|
||||
addr->flags |= DHC6_ADDR_DECLINED;
|
||||
log_debug ("Flag address declined:%s",
|
||||
piaddr(addr->address));
|
||||
decline_cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the client script DAD failed any addresses we need
|
||||
* build and issue a DECLINE */
|
||||
if (decline_cnt) {
|
||||
start_decline6(client);
|
||||
return;
|
||||
}
|
||||
|
||||
/* XXX: maybe we should loop on the old values instead? */
|
||||
@ -4915,6 +5003,153 @@ bound_handler(struct packet *packet, struct client_state *client)
|
||||
log_debug("RCV: Input packets are ignored once bound.");
|
||||
}
|
||||
|
||||
/*
|
||||
* start_decline6() kicks off the decline process, transmitting
|
||||
* an decline packet and scheduling a retransmission event.
|
||||
*/
|
||||
void
|
||||
start_decline6(struct client_state *client)
|
||||
{
|
||||
/* Cancel any pending transmissions */
|
||||
cancel_timeout(do_confirm6, client);
|
||||
cancel_timeout(do_select6, client);
|
||||
cancel_timeout(do_refresh6, client);
|
||||
cancel_timeout(do_release6, client);
|
||||
cancel_timeout(do_decline6, client);
|
||||
client->state = S_DECLINING;
|
||||
|
||||
if (client->active_lease == NULL)
|
||||
return;
|
||||
|
||||
/* Set timers per RFC3315 section 18.1.7. */
|
||||
client->IRT = DEC_TIMEOUT * 100;
|
||||
client->MRT = 0;
|
||||
client->MRC = DEC_MAX_RC;
|
||||
client->MRD = 0;
|
||||
|
||||
dhc6_retrans_init(client);
|
||||
client->v6_handler = reply_handler;
|
||||
|
||||
client->refresh_type = DHCPV6_DECLINE;
|
||||
do_decline6(client);
|
||||
}
|
||||
|
||||
/*
|
||||
* do_decline6() creates a Decline packet and transmits it.
|
||||
* The decline will contain an IA_NA with iasubopt(s) for
|
||||
* each IA_NA containing declined address(es) in the active
|
||||
* lease.
|
||||
*/
|
||||
static void
|
||||
do_decline6(void *input)
|
||||
{
|
||||
struct client_state *client;
|
||||
struct data_string ds;
|
||||
int send_ret;
|
||||
struct timeval elapsed, tv;
|
||||
|
||||
client = input;
|
||||
if (client == NULL || client->active_lease == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((client->MRC != 0) && (client->txcount > client->MRC)) {
|
||||
log_info("Max retransmission count exceeded.");
|
||||
goto decline_done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start_time starts at the first transmission.
|
||||
*/
|
||||
if (client->txcount == 0) {
|
||||
client->start_time.tv_sec = cur_tv.tv_sec;
|
||||
client->start_time.tv_usec = cur_tv.tv_usec;
|
||||
}
|
||||
|
||||
/* elapsed = cur - start */
|
||||
elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec;
|
||||
elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec;
|
||||
if (elapsed.tv_usec < 0) {
|
||||
elapsed.tv_sec -= 1;
|
||||
elapsed.tv_usec += 1000000;
|
||||
}
|
||||
|
||||
memset(&ds, 0, sizeof(ds));
|
||||
if (!buffer_allocate(&ds.buffer, 4, MDL)) {
|
||||
log_error("Unable to allocate memory for Decline.");
|
||||
goto decline_done;
|
||||
}
|
||||
|
||||
ds.data = ds.buffer->data;
|
||||
ds.len = 4;
|
||||
ds.buffer->data[0] = DHCPV6_DECLINE;
|
||||
memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3);
|
||||
|
||||
/* Form an elapsed option. */
|
||||
/* Maximum value is 65535 1/100s coded as 0xffff. */
|
||||
if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) ||
|
||||
((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) {
|
||||
client->elapsed = 0xffff;
|
||||
} else {
|
||||
client->elapsed = elapsed.tv_sec * 100;
|
||||
client->elapsed += elapsed.tv_usec / 10000;
|
||||
}
|
||||
|
||||
client->elapsed = htons(client->elapsed);
|
||||
|
||||
log_debug("XMT: Forming Decline.");
|
||||
make_client6_options(client, &client->sent_options,
|
||||
client->active_lease, DHCPV6_DECLINE);
|
||||
dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL,
|
||||
client->sent_options, &global_scope,
|
||||
&dhcpv6_universe);
|
||||
|
||||
/* Append IA_NA's. */
|
||||
if (dhc6_add_ia_na_decline(client, &ds, client->active_lease)
|
||||
!= ISC_R_SUCCESS) {
|
||||
data_string_forget(&ds, MDL);
|
||||
goto decline_done;
|
||||
}
|
||||
|
||||
/* Transmit and wait. */
|
||||
log_info("XMT: Decline on %s, interval %ld0ms.",
|
||||
client->name ? client->name : client->interface->name,
|
||||
(long int)client->RT);
|
||||
|
||||
send_ret = send_packet6(client->interface, ds.data, ds.len,
|
||||
&DHCPv6DestAddr);
|
||||
if (send_ret != ds.len) {
|
||||
log_error("dhc6: sendpacket6() sent %d of %d bytes",
|
||||
send_ret, ds.len);
|
||||
}
|
||||
|
||||
data_string_forget(&ds, MDL);
|
||||
|
||||
/* Wait RT */
|
||||
tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
|
||||
tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
|
||||
if (tv.tv_usec >= 1000000) {
|
||||
tv.tv_sec += 1;
|
||||
tv.tv_usec -= 1000000;
|
||||
}
|
||||
add_timeout(&tv, do_decline6, client, NULL, NULL);
|
||||
dhc6_retrans_advance(client);
|
||||
return;
|
||||
|
||||
decline_done:
|
||||
/* We here because we've exhausted our retry limits or
|
||||
* something else has gone wrong with the decline process.
|
||||
* So let's just toss the existing lease and start over.
|
||||
*/
|
||||
if (client->active_lease != NULL) {
|
||||
dhc6_lease_destroy(&client->active_lease, MDL);
|
||||
client->active_lease = NULL;
|
||||
}
|
||||
|
||||
start_init6(client);
|
||||
return;
|
||||
}
|
||||
|
||||
/* start_renew6() gets us all ready to go to start transmitting Renew packets.
|
||||
* Note that client->next_MRD must be set before entering this function -
|
||||
* it must be set to the time at which the client should start Rebinding.
|
||||
@ -5716,4 +5951,163 @@ active_prefix(struct client_state *client)
|
||||
}
|
||||
return ISC_TRUE;
|
||||
}
|
||||
|
||||
/* Adds a leases's declined addreses to the outbound packet
|
||||
*
|
||||
* For each IA_NA in the lease that contains one or more declined
|
||||
* addresses, an IA_NA option with an iasubopt for each declined
|
||||
* address is added to the outbound packet.
|
||||
*
|
||||
* We skip PDs and TAs as declines are undefined for them.
|
||||
*/
|
||||
static isc_result_t
|
||||
dhc6_add_ia_na_decline(struct client_state *client, struct data_string *packet,
|
||||
struct dhc6_lease *lease) {
|
||||
struct data_string iads;
|
||||
struct data_string addrds;
|
||||
struct dhc6_addr *addr;
|
||||
struct dhc6_ia *ia;
|
||||
isc_result_t rval = ISC_R_SUCCESS;
|
||||
|
||||
memset(&iads, 0, sizeof(iads));
|
||||
memset(&addrds, 0, sizeof(addrds));
|
||||
for (ia = lease->bindings; ia != NULL && rval == ISC_R_SUCCESS;
|
||||
ia = ia->next) {
|
||||
if (ia->ia_type != D6O_IA_NA)
|
||||
continue;
|
||||
|
||||
int start_new_ia = 1;
|
||||
for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
|
||||
/*
|
||||
* Do not confirm expired addresses, do not request
|
||||
* expired addresses (but we keep them around for
|
||||
* solicit).
|
||||
*/
|
||||
if (!(addr->flags & DHC6_ADDR_DECLINED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (start_new_ia) {
|
||||
if (!buffer_allocate(&iads.buffer, 12, MDL)) {
|
||||
log_error("Unable to allocate memory"
|
||||
" for IA_NA.");
|
||||
rval = ISC_R_NOMEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy the IAID into the packet buffer. */
|
||||
memcpy(iads.buffer->data, ia->iaid, 4);
|
||||
iads.data = iads.buffer->data;
|
||||
iads.len = 12;
|
||||
|
||||
/* Set t1/t2 to zero; server will ignore them */
|
||||
memset(iads.buffer->data + 4, 0, 8);
|
||||
log_debug("XMT: X-- IA_NA %s",
|
||||
print_hex_1(4, iads.buffer->data, 55));
|
||||
start_new_ia = 0;
|
||||
}
|
||||
|
||||
if (addr->address.len != 16) {
|
||||
log_error("Illegal IPv6 address length (%d), "
|
||||
"ignoring. (%s:%d)",
|
||||
addr->address.len, MDL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!buffer_allocate(&addrds.buffer, 24, MDL)) {
|
||||
log_error("Unable to allocate memory for "
|
||||
"IAADDR.");
|
||||
rval = ISC_R_NOMEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
addrds.data = addrds.buffer->data;
|
||||
addrds.len = 24;
|
||||
|
||||
/* Copy the address into the packet buffer. */
|
||||
memcpy(addrds.buffer->data, addr->address.iabuf, 16);
|
||||
|
||||
/* Preferred and max life are irrelevant */
|
||||
memset(addrds.buffer->data + 16, 0, 8);
|
||||
log_debug("XMT: | X-- Decline Address %s",
|
||||
piaddr(addr->address));
|
||||
|
||||
append_option(&iads, &dhcpv6_universe, iaaddr_option,
|
||||
&addrds);
|
||||
data_string_forget(&addrds, MDL);
|
||||
}
|
||||
|
||||
/*
|
||||
* It doesn't make sense to make a request without an
|
||||
* address.
|
||||
*/
|
||||
if (ia->addrs == NULL) {
|
||||
log_debug("!!!: V IA_NA has no IAADDRs - removed.");
|
||||
rval = ISC_R_FAILURE;
|
||||
} else if (rval == ISC_R_SUCCESS) {
|
||||
log_debug("XMT: V IA_NA appended.");
|
||||
append_option(packet, &dhcpv6_universe, ia_na_option,
|
||||
&iads);
|
||||
}
|
||||
|
||||
data_string_forget(&iads, MDL);
|
||||
}
|
||||
|
||||
return (rval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove any declined NA addresses from the lease.
|
||||
*
|
||||
* Returns zero if the all of the bindings on the lease
|
||||
* were removed, non-zero if there are PD, TA, or usuable NA
|
||||
* bindings
|
||||
*/
|
||||
int drop_declined_addrs(struct dhc6_lease *lease) {
|
||||
struct dhc6_ia *ia;
|
||||
int live_cnt = 0;
|
||||
|
||||
for (ia = lease->bindings; ia != NULL; ia = ia->next) {
|
||||
struct dhc6_addr* prev_addr;
|
||||
struct dhc6_addr* addr;
|
||||
struct dhc6_addr* next;
|
||||
|
||||
/* If it's a PD or TA, we assume it has at least
|
||||
* one usuable binding */
|
||||
if (ia->ia_type != D6O_IA_NA) {
|
||||
live_cnt++;
|
||||
continue;
|
||||
}
|
||||
|
||||
prev_addr = NULL;
|
||||
for (addr = ia->addrs ; addr != NULL ; ) {
|
||||
if (!(addr->flags & DHC6_ADDR_DECLINED)) {
|
||||
live_cnt++;
|
||||
addr = addr->next;
|
||||
prev_addr = addr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we're deleting head, move it up one */
|
||||
if (ia->addrs == addr) {
|
||||
ia->addrs = addr->next;
|
||||
prev_addr = addr->next;
|
||||
} else {
|
||||
prev_addr->next = addr->next;
|
||||
}
|
||||
|
||||
if (addr->options != NULL) {
|
||||
option_state_dereference(&addr->options, MDL);
|
||||
}
|
||||
|
||||
next = addr->next;
|
||||
dfree(addr, MDL);
|
||||
addr = next;
|
||||
}
|
||||
}
|
||||
|
||||
return (live_cnt);
|
||||
}
|
||||
|
||||
|
||||
#endif /* DHCPv6 */
|
||||
|
@ -4508,6 +4508,7 @@ void client_location_changed ()
|
||||
case S_INIT:
|
||||
case S_REBINDING:
|
||||
case S_STOPPED:
|
||||
case S_DECLINING:
|
||||
break;
|
||||
}
|
||||
client -> state = S_INIT;
|
||||
|
@ -1134,6 +1134,7 @@ struct dhc6_addr {
|
||||
/* Address state flags. */
|
||||
#define DHC6_ADDR_DEPREFFED 0x01
|
||||
#define DHC6_ADDR_EXPIRED 0x02
|
||||
#define DHC6_ADDR_DECLINED 0x04
|
||||
u_int8_t flags;
|
||||
|
||||
TIME starts;
|
||||
@ -1179,7 +1180,8 @@ enum dhcp_state {
|
||||
S_BOUND = 5,
|
||||
S_RENEWING = 6,
|
||||
S_REBINDING = 7,
|
||||
S_STOPPED = 8
|
||||
S_DECLINING = 8,
|
||||
S_STOPPED = 9
|
||||
};
|
||||
|
||||
/* Possible pending client operations. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user