]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
net: bootp: BOOTP/DHCPv4 retransmission improvements
authorSean Edmond <seanedmond@microsoft.com>
Thu, 9 May 2024 02:39:02 +0000 (19:39 -0700)
committerTom Rini <trini@konsulko.com>
Fri, 23 May 2025 17:28:51 +0000 (11:28 -0600)
This patch introduces 3 improvements to align with RFC 951:
- retransmission backoff interval maximum is configurable
- initial retranmission backoff interval is configurable
- transaction ID is kept the same for each BOOTP/DHCPv4 request

In applications where thousands of nodes are serviced by a single DHCP
server, maximizing the retransmission backoff interval at 2 seconds (the
current u-boot default) exerts high pressure on the DHCP server and
network layer.

RFC 951 “7.2. Client Retransmission Strategy” states that the
retransmission backoff interval should be limited to 60 seconds.  This
patch allows the interval to be configurable using the environment
variable "bootpretransmitperiodmax"

The initial retranmission backoff period defaults to 250ms, which is
also too small for these scenarios with many clients.  This patch makes
the initial retransmission interval to be configurable using the
environment variable "bootpretransmitperiodinit".

Also, on a retransmission it is not expected for the transaction ID to
change (only the 'secs' field should be updated). Let's save the
transaction ID and use the same transaction ID for each BOOTP/DHCPv4
exchange.

Signed-off-by: Sean Edmond <seanedmond@microsoft.com>
lib/Kconfig
net/bootp.c

index 189e6eb31aa188d9211df51d171d56330fae0a7d..9b62b3bbea70e7abaed87061c748147405dfcad3 100644 (file)
@@ -265,7 +265,8 @@ config REGEX
 choice
        prompt "Pseudo-random library support type"
        depends on NET_RANDOM_ETHADDR || RANDOM_UUID || CMD_UUID || \
-                  RNG_SANDBOX || UT_LIB && AES || FAT_WRITE
+                  RNG_SANDBOX || UT_LIB && AES || FAT_WRITE || CMD_BOOTP || \
+                  CMD_DHCP || CMD_DHCP6
        default LIB_RAND
        help
          Select the library to provide pseudo-random number generator
index b9e3cccb4f95429d6384ff2080e9317cb06f747c..c34ffe27854a95a757534a7576f667d9659adf78 100644 (file)
  */
 #define TIMEOUT_MS     ((3 + (CONFIG_NET_RETRY_COUNT * 5)) * 1000)
 
+/*
+ * According to rfc951 : 7.2. Client Retransmission Strategy
+ * "After the 'average' backoff reaches about 60 seconds, it should be
+ * increased no further, but still randomized."
+ *
+ * U-Boot has saturated this backoff at 2 seconds for a long time.
+ * To modify, set the environment variable "bootpretransmitperiodmax"
+ */
+#define RETRANSMIT_PERIOD_MAX_MS       60000
+
+/* Retransmission timeout for the initial packet (in milliseconds).
+ * This timeout will double on each retry.  To modify, set the
+ * environment variable bootpretransmitperiodinit.
+ */
+#define RETRANSMIT_PERIOD_INIT_MS      250
+
 #ifndef CFG_DHCP_MIN_EXT_LEN           /* minimal length of extension list */
 #define CFG_DHCP_MIN_EXT_LEN 64
 #endif
@@ -53,6 +69,7 @@
 u32            bootp_ids[CFG_BOOTP_ID_CACHE_SIZE];
 unsigned int   bootp_num_ids;
 int            bootp_try;
+u32            bootp_id;
 ulong          bootp_start;
 ulong          bootp_timeout;
 char net_nis_domain[32] = {0,}; /* Our NIS domain */
@@ -60,6 +77,7 @@ char net_hostname[32] = {0,}; /* Our hostname */
 char net_root_path[CONFIG_BOOTP_MAX_ROOT_PATH_LEN] = {0,}; /* Our bootpath */
 
 static ulong time_taken_max;
+static u32   retransmit_period_max_ms;
 
 #if defined(CONFIG_CMD_DHCP)
 static dhcp_state_t dhcp_state = INIT;
@@ -396,6 +414,7 @@ static void bootp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
 static void bootp_timeout_handler(void)
 {
        ulong time_taken = get_timer(bootp_start);
+       int rand_minus_plus_100;
 
        if (time_taken >= time_taken_max) {
 #ifdef CONFIG_BOOTP_MAY_FAIL
@@ -414,8 +433,17 @@ static void bootp_timeout_handler(void)
                }
        } else {
                bootp_timeout *= 2;
-               if (bootp_timeout > 2000)
-                       bootp_timeout = 2000;
+               if (bootp_timeout > retransmit_period_max_ms)
+                       bootp_timeout = retransmit_period_max_ms;
+
+               /* Randomize by adding bootp_timeout*RAND, where RAND
+                * is a randomization factor between -0.1..+0.1
+                */
+               srand(get_ticks() + rand());
+               rand_minus_plus_100 = ((rand() % 200) - 100);
+               bootp_timeout = bootp_timeout +
+                               (((int)bootp_timeout * rand_minus_plus_100) / 1000);
+
                net_set_timeout_handler(bootp_timeout, bootp_timeout_handler);
                bootp_request();
        }
@@ -714,7 +742,8 @@ void bootp_reset(void)
        bootp_num_ids = 0;
        bootp_try = 0;
        bootp_start = get_timer(0);
-       bootp_timeout = 250;
+
+       bootp_timeout = env_get_ulong("bootpretransmitperiodinit", 10, RETRANSMIT_PERIOD_INIT_MS);
 }
 
 void bootp_request(void)
@@ -726,7 +755,6 @@ void bootp_request(void)
 #ifdef CONFIG_BOOTP_RANDOM_DELAY
        ulong rand_ms;
 #endif
-       u32 bootp_id;
        struct in_addr zero_ip;
        struct in_addr bcast_ip;
        char *ep;  /* Environment pointer */
@@ -742,6 +770,9 @@ void bootp_request(void)
        else
                time_taken_max = TIMEOUT_MS;
 
+       retransmit_period_max_ms = env_get_ulong("bootpretransmitperiodmax", 10,
+                                                RETRANSMIT_PERIOD_MAX_MS);
+
 #ifdef CONFIG_BOOTP_RANDOM_DELAY               /* Random BOOTP delay */
        if (bootp_try == 0)
                srand_mac();
@@ -801,18 +832,23 @@ void bootp_request(void)
        extlen = bootp_extended((u8 *)bp->bp_vend);
 #endif
 
-       /*
-        *      Bootp ID is the lower 4 bytes of our ethernet address
-        *      plus the current time in ms.
-        */
-       bootp_id = ((u32)net_ethaddr[2] << 24)
-               | ((u32)net_ethaddr[3] << 16)
-               | ((u32)net_ethaddr[4] << 8)
-               | (u32)net_ethaddr[5];
-       bootp_id += get_timer(0);
-       bootp_id = htonl(bootp_id);
-       bootp_add_id(bootp_id);
-       net_copy_u32(&bp->bp_id, &bootp_id);
+       /* Only generate a new transaction ID for each new BOOTP request */
+       if (bootp_try == 1) {
+               /*
+                *      Bootp ID is the lower 4 bytes of our ethernet address
+                *      plus the current time in ms.
+                */
+               bootp_id = ((u32)net_ethaddr[2] << 24)
+                       | ((u32)net_ethaddr[3] << 16)
+                       | ((u32)net_ethaddr[4] << 8)
+                       | (u32)net_ethaddr[5];
+               bootp_id += get_timer(0);
+               bootp_id = htonl(bootp_id);
+               bootp_add_id(bootp_id);
+               net_copy_u32(&bp->bp_id, &bootp_id);
+       } else {
+               net_copy_u32(&bp->bp_id, &bootp_id);
+       }
 
        /*
         * Calculate proper packet lengths taking into account the