net/dhcp: Introduce per-interface timeout

Currently we have a global timeout for all network cards in the BOOTP/DHCP
discovery process.

Make this timeout a per-interface one, so better accommodate the upcoming
4-way DHCP handshake and to also cover the lease time limit a DHCP offer
will come with.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
This commit is contained in:
Andrei Borzenkov 2019-03-07 15:14:12 +00:00 committed by Daniel Kiper
parent 5459243465
commit 12e1b6e604
2 changed files with 36 additions and 4 deletions

View file

@ -33,6 +33,9 @@ enum
#define GRUB_BOOTP_MAX_OPTIONS_SIZE 64 #define GRUB_BOOTP_MAX_OPTIONS_SIZE 64
/* Max timeout when waiting for BOOTP/DHCP reply */
#define GRUB_DHCP_MAX_PACKET_TIMEOUT 32
static const void * static const void *
find_dhcp_option (const struct grub_net_bootp_packet *bp, grub_size_t size, find_dhcp_option (const struct grub_net_bootp_packet *bp, grub_size_t size,
grub_uint8_t opt_code, grub_uint8_t *opt_len) grub_uint8_t opt_code, grub_uint8_t *opt_len)
@ -548,7 +551,6 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
struct grub_net_network_level_interface *ifaces; struct grub_net_network_level_interface *ifaces;
grub_size_t ncards = 0; grub_size_t ncards = 0;
unsigned j = 0; unsigned j = 0;
int interval;
grub_err_t err; grub_err_t err;
unsigned i; unsigned i;
@ -587,6 +589,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
ifaces[j].address.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV; ifaces[j].address.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV;
grub_memcpy (&ifaces[j].hwaddress, &card->default_address, grub_memcpy (&ifaces[j].hwaddress, &card->default_address,
sizeof (ifaces[j].hwaddress)); sizeof (ifaces[j].hwaddress));
ifaces[j].dhcp_tmo = ifaces[j].dhcp_tmo_left = 1;
j++; j++;
} }
ifaces[ncards - 1].next = grub_net_network_level_interfaces; ifaces[ncards - 1].next = grub_net_network_level_interfaces;
@ -594,25 +597,52 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
grub_net_network_level_interfaces->prev = & ifaces[ncards - 1].next; grub_net_network_level_interfaces->prev = & ifaces[ncards - 1].next;
grub_net_network_level_interfaces = &ifaces[0]; grub_net_network_level_interfaces = &ifaces[0];
ifaces[0].prev = &grub_net_network_level_interfaces; ifaces[0].prev = &grub_net_network_level_interfaces;
for (interval = 200; interval < 10000; interval *= 2)
/*
* Running DHCP restransmission timer is kept per interface in dhcp_tmo_left.
* When it runs off, dhcp_tmo is increased exponentionally and dhcp_tmo_left
* initialized to it. Max value is 32 which gives approximately 12s total per
* packet timeout assuming 200ms poll tick. Timeout is reset when DHCP OFFER
* is received, so total timeout is 25s in the worst case.
*
* DHCP NAK also resets timer and transaction starts again.
*
* Total wait time is limited to ~25s to prevent endless loop in case of
* permanent NAK
*/
for (i = 0; i < GRUB_DHCP_MAX_PACKET_TIMEOUT * 4; i++)
{ {
int need_poll = 0; int need_poll = 0;
for (j = 0; j < ncards; j++) for (j = 0; j < ncards; j++)
{ {
if (!ifaces[j].prev) if (!ifaces[j].prev ||
ifaces[j].dhcp_tmo > GRUB_DHCP_MAX_PACKET_TIMEOUT)
continue;
if (--ifaces[j].dhcp_tmo_left)
{
need_poll = 1;
continue;
}
ifaces[j].dhcp_tmo *= 2;
if (ifaces[j].dhcp_tmo > GRUB_DHCP_MAX_PACKET_TIMEOUT)
continue; continue;
err = send_dhcp_packet (&ifaces[j]); err = send_dhcp_packet (&ifaces[j]);
if (err) if (err)
{ {
grub_print_error (); grub_print_error ();
/* To ignore it during next poll */
ifaces[j].dhcp_tmo = GRUB_DHCP_MAX_PACKET_TIMEOUT + 1;
continue; continue;
} }
ifaces[j].dhcp_tmo_left = ifaces[j].dhcp_tmo;
need_poll = 1; need_poll = 1;
} }
if (!need_poll) if (!need_poll)
break; break;
grub_net_poll_cards (interval, 0); grub_net_poll_cards (200, 0);
} }
err = GRUB_ERR_NONE; err = GRUB_ERR_NONE;

View file

@ -292,6 +292,8 @@ struct grub_net_network_level_interface
struct grub_net_bootp_packet *dhcp_ack; struct grub_net_bootp_packet *dhcp_ack;
grub_size_t dhcp_acklen; grub_size_t dhcp_acklen;
grub_uint16_t vlantag; grub_uint16_t vlantag;
unsigned dhcp_tmo_left; /* DHCPv4 running retransmission timeout */
unsigned dhcp_tmo; /* DHCPv4 current retransmission timeout */
void *data; void *data;
}; };