[dhcp] Handle DHCPNAK by returning to discovery state

Handle a DHCPNAK by returning to the discovery state to allow iPXE to
attempt to obtain a replacement IPv4 address.

Reuse the existing logic for deferring discovery when the link is
blocked: this avoids hammering a misconfigured DHCP server with a
non-stop stream of requests and allows the DHCP process to eventually
time out and fail.

Originally-implemented-by: Blake Rouse <blake.rouse@canonical.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/310/head
Michael Brown 2021-03-11 15:54:26 +00:00
parent 7c8fc2cae8
commit 1192edf394
1 changed files with 31 additions and 9 deletions

View File

@ -443,6 +443,26 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
dhcp_set_state ( dhcp, &dhcp_state_request );
}
/**
* Defer DHCP discovery
*
* @v dhcp DHCP session
*/
static void dhcp_defer ( struct dhcp_session *dhcp ) {
/* Do nothing if we have reached the deferral limit */
if ( dhcp->count > DHCP_DISC_MAX_DEFERRALS )
return;
/* Return to discovery state */
DBGC ( dhcp, "DHCP %p deferring discovery\n", dhcp );
dhcp_set_state ( dhcp, &dhcp_state_discover );
/* Delay first DHCPDISCOVER */
start_timer_fixed ( &dhcp->timer,
( DHCP_DISC_START_TIMEOUT_SEC * TICKS_PER_SEC ) );
}
/**
* Handle timer expiry during DHCP discovery
*
@ -462,14 +482,8 @@ static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) {
dhcp_tx ( dhcp );
/* If link is blocked, defer DHCP discovery timeout */
if ( netdev_link_blocked ( dhcp->netdev ) &&
( dhcp->count <= DHCP_DISC_MAX_DEFERRALS ) ) {
DBGC ( dhcp, "DHCP %p deferring discovery timeout\n", dhcp );
dhcp->start = currticks();
start_timer_fixed ( &dhcp->timer,
( DHCP_DISC_START_TIMEOUT_SEC *
TICKS_PER_SEC ) );
}
if ( netdev_link_blocked ( dhcp->netdev ) )
dhcp_defer ( dhcp );
}
/** DHCP discovery state operations */
@ -553,9 +567,17 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
DBGC ( dhcp, "\n" );
/* Filter out unacceptable responses */
/* Filter out invalid port */
if ( peer->sin_port != htons ( BOOTPS_PORT ) )
return;
/* Handle DHCPNAK */
if ( msgtype == DHCPNAK ) {
dhcp_defer ( dhcp );
return;
}
/* Filter out unacceptable responses */
if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
return;
if ( server_id.s_addr != dhcp->server.s_addr )