[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>
diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c
index 4ebd38b..a335a77 100644
--- a/src/net/udp/dhcp.c
+++ b/src/net/udp/dhcp.c
@@ -444,6 +444,26 @@
}
/**
+ * 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
*
* @v dhcp DHCP session
@@ -462,14 +482,8 @@
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 @@
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 )