dhcp_fine_tmr() was called every 10ms, but lwIP expects it to be
called every DHCP_FINE_TIMER_MSECS (500ms).
Since the fine timer ticks are currently 50 times faster than lwIP
expects, the client burns through the bounded exponential backoff
sequence for the DHCPREQUEST messages in 2.44 seconds rather than 122
seconds, after which it uses a new xid and returns to sending
DHCPDISCOVER messages. If there is enough latency in the server, the
client could receive an ACK response within DHCP_TIMEOUT_MS (10
seconds), but reject it because it has already moved on with another
xid after 2.44 seconds.
We have seen this occur with our boards. When our rack of 16 boards
get powered on together, they all request for an address from the
network switch's DHCP server in near lock-step and we see that only a
few of the boards actually obtain a lease. Fixing the timing to 500ms
allowed all of the boards to obtain a lease consistently.
Fixes: 98ad145db6 ("net: lwip: add DHCP support and dhcp commmand")
Signed-off-by: Luca Jones <ljones@t0.technology>
163 lines
3.5 KiB
C
163 lines
3.5 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/* Copyright (C) 2024 Linaro Ltd. */
|
|
|
|
#include <command.h>
|
|
#include <console.h>
|
|
#include <env.h>
|
|
#include <log.h>
|
|
#include <dm/device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/errno.h>
|
|
#include <lwip/apps/sntp.h>
|
|
#include <lwip/dhcp.h>
|
|
#include <lwip/dns.h>
|
|
#include <lwip/timeouts.h>
|
|
#include <net.h>
|
|
#include <time.h>
|
|
|
|
#define DHCP_TIMEOUT_MS 10000
|
|
|
|
#ifdef CONFIG_CMD_TFTPBOOT
|
|
/* Boot file obtained from DHCP (if present) */
|
|
static char boot_file_name[DHCP_BOOT_FILE_LEN];
|
|
#endif
|
|
|
|
static void call_lwip_dhcp_fine_tmr(void *ctx)
|
|
{
|
|
dhcp_fine_tmr();
|
|
sys_timeout(DHCP_FINE_TIMER_MSECS, call_lwip_dhcp_fine_tmr, NULL);
|
|
}
|
|
|
|
static int dhcp_loop(struct udevice *udev)
|
|
{
|
|
char ipstr[] = "ipaddr\0\0\0";
|
|
char maskstr[] = "netmask\0\0\0";
|
|
char gwstr[] = "gatewayip\0\0\0";
|
|
const ip_addr_t *ntpserverip;
|
|
unsigned long start;
|
|
struct netif *netif;
|
|
struct dhcp *dhcp;
|
|
bool bound;
|
|
int idx;
|
|
|
|
idx = dev_seq(udev);
|
|
if (idx < 0 || idx > 99) {
|
|
log_err("unexpected idx %d\n", idx);
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
|
|
netif = net_lwip_new_netif_noip(udev);
|
|
if (!netif)
|
|
return CMD_RET_FAILURE;
|
|
|
|
/*
|
|
* Request the DHCP stack to parse and store the NTP servers for
|
|
* eventual use by the SNTP command
|
|
*/
|
|
if (CONFIG_IS_ENABLED(CMD_SNTP))
|
|
sntp_servermode_dhcp(1);
|
|
|
|
start = get_timer(0);
|
|
|
|
if (dhcp_start(netif))
|
|
return CMD_RET_FAILURE;
|
|
|
|
call_lwip_dhcp_fine_tmr(NULL);
|
|
|
|
/* Wait for DHCP to complete */
|
|
do {
|
|
net_lwip_rx(udev, netif);
|
|
bound = dhcp_supplied_address(netif);
|
|
if (bound)
|
|
break;
|
|
if (ctrlc()) {
|
|
printf("Abort\n");
|
|
break;
|
|
}
|
|
mdelay(1);
|
|
} while (get_timer(start) < DHCP_TIMEOUT_MS);
|
|
|
|
sys_untimeout(call_lwip_dhcp_fine_tmr, NULL);
|
|
|
|
if (!bound) {
|
|
net_lwip_remove_netif(netif);
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
|
|
dhcp = netif_dhcp_data(netif);
|
|
|
|
env_set("bootfile", dhcp->boot_file_name);
|
|
|
|
if (idx > 0) {
|
|
sprintf(ipstr, "ipaddr%d", idx);
|
|
sprintf(maskstr, "netmask%d", idx);
|
|
sprintf(gwstr, "gatewayip%d", idx);
|
|
} else {
|
|
net_ip.s_addr = ip_addr_get_ip4_u32(&dhcp->offered_ip_addr);
|
|
}
|
|
|
|
env_set(ipstr, ip4addr_ntoa(&dhcp->offered_ip_addr));
|
|
env_set(maskstr, ip4addr_ntoa(&dhcp->offered_sn_mask));
|
|
env_set("serverip", ip4addr_ntoa(&dhcp->server_ip_addr));
|
|
if (!ip4_addr_isany(&dhcp->offered_gw_addr))
|
|
env_set(gwstr, ip4addr_ntoa(&dhcp->offered_gw_addr));
|
|
if (!ip4_addr_isany(&dhcp->offered_si_addr) &&
|
|
!ip4_addr_eq(&dhcp->offered_si_addr, &dhcp->server_ip_addr))
|
|
env_set("tftpserverip", ip4addr_ntoa(&dhcp->offered_si_addr));
|
|
|
|
#ifdef CONFIG_PROT_DNS_LWIP
|
|
env_set("dnsip", ip4addr_ntoa(dns_getserver(0)));
|
|
env_set("dnsip2", ip4addr_ntoa(dns_getserver(1)));
|
|
#endif
|
|
#ifdef CONFIG_CMD_TFTPBOOT
|
|
if (dhcp->boot_file_name[0] != '\0')
|
|
strncpy(boot_file_name, dhcp->boot_file_name,
|
|
sizeof(boot_file_name));
|
|
#endif
|
|
if (CONFIG_IS_ENABLED(CMD_SNTP)) {
|
|
ntpserverip = sntp_getserver(1);
|
|
if (ntpserverip != IP_ADDR_ANY)
|
|
env_set("ntpserverip", ip4addr_ntoa(ntpserverip));
|
|
}
|
|
|
|
printf("DHCP client bound to address %pI4 (%lu ms)\n",
|
|
&dhcp->offered_ip_addr, get_timer(start));
|
|
|
|
net_lwip_remove_netif(netif);
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
int ret;
|
|
struct udevice *dev;
|
|
|
|
if (net_lwip_eth_start() < 0)
|
|
return CMD_RET_FAILURE;
|
|
|
|
dev = eth_get_dev();
|
|
if (!dev) {
|
|
log_err("No network device\n");
|
|
ret = CMD_RET_FAILURE;
|
|
goto out;
|
|
}
|
|
|
|
ret = dhcp_loop(dev);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (argc > 1) {
|
|
struct cmd_tbl cmdtp = {};
|
|
|
|
ret = do_tftpb(&cmdtp, 0, argc, argv);
|
|
goto out;
|
|
}
|
|
|
|
ret = CMD_RET_SUCCESS;
|
|
|
|
out:
|
|
net_lwip_eth_stop();
|
|
|
|
return ret;
|
|
}
|