]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
net: tftp: Add client support for RFC 7440
authorRamon Fried <rfried.dev@gmail.com>
Sat, 18 Jul 2020 20:31:46 +0000 (23:31 +0300)
committerTom Rini <trini@konsulko.com>
Wed, 5 Aug 2020 03:30:02 +0000 (23:30 -0400)
Add support for RFC 7440: "TFTP Windowsize Option".

This optional feature allows the client and server
to negotiate a window size of consecutive blocks to send as an
alternative for replacing the single-block lockstep schema.

windowsize can be defined statically during compilation by
setting CONFIG_TFTP_WINDOWSIZE, or defined in runtime by
setting an environment variable: "tftpwindowsize"
If not defined, the windowsize is set to 1, meaning that it
behaves as it was never defined.

Choosing the appropriate windowsize depends on the specific
network topology, underlying NIC.
You should test various windowsize scenarios and see which
best work for you.

Setting a windowsize too big can actually decreases performance.

Signed-off-by: Ramon Fried <rfried.dev@gmail.com>
Reviewed-by: Marek Vasut <marex@denx.de>
README
net/Kconfig
net/tftp.c

diff --git a/README b/README
index d4bf74ce75d7aee50d360aa2f18553f04fcfb7b3..121dc4966af22bbeb110a874765cfedbd222754f 100644 (file)
--- a/README
+++ b/README
@@ -3444,6 +3444,11 @@ List of environment variables (most likely not complete):
                  downloads succeed with high packet loss rates, or with
                  unreliable TFTP servers or client hardware.
 
+  tftpwindowsize       - if this is set, the value is used for TFTP's
+                 window size as described by RFC 7440.
+                 This means the count of blocks we can receive before
+                 sending ack to server.
+
   vlan         - When set to a value < 4095 the traffic over
                  Ethernet is encapsulated/received over 802.1q
                  VLAN tagged frames.
index 6c47b7d69c5a5a58b0a3886d6bf718b1ecd1f40a..6874b55aacb5afbef76b45fbb0e9deba34d1dc09 100644 (file)
@@ -59,4 +59,13 @@ config TFTP_BLOCKSIZE
          almost-MTU block sizes.
          You can also activate CONFIG_IP_DEFRAG to set a larger block.
 
+config TFTP_WINDOWSIZE
+       int "TFTP window size"
+       default 1
+       help
+         Default TFTP window size.
+         RFC7440 defines an optional window size of transmits,
+         before an ack response is required.
+         The default TFTP implementation implies a window size of 1.
+
 endif   # if NET
index c05b7b5532b9c5454cebc5a85417f84ff87caaad..84e970bec1348a6c28bb602dc9444c4386607556 100644 (file)
@@ -5,7 +5,6 @@
  * Copyright 2011 Comelit Group SpA,
  *                Luca Ceresoli <luca.ceresoli@comelit.it>
  */
-
 #include <common.h>
 #include <command.h>
 #include <efi_loader.h>
@@ -98,6 +97,12 @@ static int   tftp_tsize;
 /* The number of hashes we printed */
 static short   tftp_tsize_num_hash;
 #endif
+/* The window size negotiated */
+static ushort  tftp_windowsize;
+/* Next block to send ack to */
+static ushort  tftp_next_ack;
+/* Last nack block we send */
+static ushort  tftp_last_nack;
 #ifdef CONFIG_CMD_TFTPPUT
 /* 1 if writing, else 0 */
 static int     tftp_put_active;
@@ -138,8 +143,19 @@ static char tftp_filename[MAX_LEN];
  * (but those using CONFIG_IP_DEFRAG may want to set a larger block in cfg file)
  */
 
+/* When windowsize is defined to 1,
+ * tftp behaves the same way as it was
+ * never declared
+ */
+#ifdef CONFIG_TFTP_WINDOWSIZE
+#define TFTP_WINDOWSIZE CONFIG_TFTP_WINDOWSIZE
+#else
+#define TFTP_WINDOWSIZE 1
+#endif
+
 static unsigned short tftp_block_size = TFTP_BLOCK_SIZE;
 static unsigned short tftp_block_size_option = CONFIG_TFTP_BLOCKSIZE;
+static unsigned short tftp_window_size_option = TFTP_WINDOWSIZE;
 
 static inline int store_block(int block, uchar *src, unsigned int len)
 {
@@ -356,6 +372,14 @@ static void tftp_send(void)
                /* try for more effic. blk size */
                pkt += sprintf((char *)pkt, "blksize%c%d%c",
                                0, tftp_block_size_option, 0);
+
+               /* try for more effic. window size.
+                * Implemented only for tftp get.
+                * Don't bother sending if it's 1
+                */
+               if (tftp_state == STATE_SEND_RRQ && tftp_window_size_option > 1)
+                       pkt += sprintf((char *)pkt, "windowsize%c%d%c",
+                                       0, tftp_window_size_option, 0);
                len = pkt - xp;
                break;
 
@@ -550,7 +574,17 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
                                      (char *)pkt + i + 6, tftp_tsize);
                        }
 #endif
+                       if (strcasecmp((char *)pkt + i,  "windowsize") == 0) {
+                               tftp_windowsize =
+                                       simple_strtoul((char *)pkt + i + 11,
+                                                      NULL, 10);
+                               debug("windowsize = %s, %d\n",
+                                     (char *)pkt + i + 11, tftp_windowsize);
+                       }
                }
+
+               tftp_next_ack = tftp_windowsize;
+
 #ifdef CONFIG_CMD_TFTPPUT
                if (tftp_put_active && tftp_state == STATE_OACK) {
                        /* Get ready to send the first block */
@@ -564,7 +598,28 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
                if (len < 2)
                        return;
                len -= 2;
-               tftp_cur_block = ntohs(*(__be16 *)pkt);
+
+               if (ntohs(*(__be16 *)pkt) != (ushort)(tftp_cur_block + 1)) {
+                       debug("Received unexpected block: %d, expected: %d\n",
+                             ntohs(*(__be16 *)pkt),
+                             (ushort)(tftp_cur_block + 1));
+                       /*
+                        * If one packet is dropped most likely
+                        * all other buffers in the window
+                        * that will arrive will cause a sending NACK.
+                        * This just overwellms the server, let's just send one.
+                        */
+                       if (tftp_last_nack != tftp_cur_block) {
+                               tftp_send();
+                               tftp_last_nack = tftp_cur_block;
+                               tftp_next_ack = (ushort)(tftp_cur_block +
+                                                        tftp_windowsize);
+                       }
+                       break;
+               }
+
+               tftp_cur_block++;
+               tftp_cur_block %= TFTP_SEQUENCE_SIZE;
 
                if (tftp_state == STATE_SEND_RRQ)
                        debug("Server did not acknowledge any options!\n");
@@ -606,10 +661,15 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
                 *      Acknowledge the block just received, which will prompt
                 *      the remote for the next one.
                 */
-               tftp_send();
+               if (tftp_cur_block == tftp_next_ack) {
+                       tftp_send();
+                       tftp_next_ack += tftp_windowsize;
+               }
 
-               if (len < tftp_block_size)
+               if (len < tftp_block_size) {
+                       tftp_send();
                        tftp_complete();
+               }
                break;
 
        case TFTP_ERROR:
@@ -683,6 +743,10 @@ void tftp_start(enum proto_t protocol)
        if (ep != NULL)
                tftp_block_size_option = simple_strtol(ep, NULL, 10);
 
+       ep = env_get("tftpwindowsize");
+       if (ep != NULL)
+               tftp_window_size_option = simple_strtol(ep, NULL, 10);
+
        ep = env_get("tftptimeout");
        if (ep != NULL)
                timeout_ms = simple_strtol(ep, NULL, 10);
@@ -704,8 +768,8 @@ void tftp_start(enum proto_t protocol)
        }
 #endif
 
-       debug("TFTP blocksize = %i, timeout = %ld ms\n",
-             tftp_block_size_option, timeout_ms);
+       debug("TFTP blocksize = %i, TFTP windowsize = %d timeout = %ld ms\n",
+             tftp_block_size_option, tftp_window_size_option, timeout_ms);
 
        tftp_remote_ip = net_server_ip;
        if (!net_parse_bootfile(&tftp_remote_ip, tftp_filename, MAX_LEN)) {
@@ -801,7 +865,8 @@ void tftp_start(enum proto_t protocol)
                tftp_our_port = simple_strtol(ep, NULL, 10);
 #endif
        tftp_cur_block = 0;
-
+       tftp_windowsize = 1;
+       tftp_last_nack = 0;
        /* zero out server ether in case the server ip has changed */
        memset(net_server_ethaddr, 0, 6);
        /* Revert tftp_block_size to dflt */