From 1192edf394aaebde666c6aafda1c8b782b08a845 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 11 Mar 2021 15:54:26 +0000 Subject: [PATCH] [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 Signed-off-by: Michael Brown --- src/net/udp/dhcp.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 4ebd38bbb..a335a778a 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -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 )