[PATCH v4] net: tftp: Add client support for RFC 7440
Matthias Brugger
matthias.bgg at gmail.com
Sat May 23 19:39:58 CEST 2020
On 19/05/2020 21:25, Ramon Fried wrote:
> 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 at gmail.com>
> Reviewed-by: Marek Vasut <marex at denx.de>
> ---
> v2:
> * Don't send windowsize option on tftpput, as it's not implemented yet.
> * Don't send NACK for every out of order block that arrives, one nack
> is enough.
> v3:
> * Add option CONFIG_TFTP_WINDOWSIZE to kconfig with default 1.
> * Fixed some spelling errors.
> * Took assignment out of a loop.
> * simplified variable increment.
> v4:
> * send ack for last packet, so the server can finish
> the tranfer gracefully and not in timeout.
>
> README | 5 ++++
> net/Kconfig | 9 ++++++
> net/tftp.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++------
> 3 files changed, 87 insertions(+), 8 deletions(-)
>
> diff --git a/README b/README
> index be9e6391d6..686474a2f1 100644
> --- a/README
> +++ b/README
> @@ -3522,6 +3522,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.
> diff --git a/net/Kconfig b/net/Kconfig
> index ac6d0cf8a6..7916ae305f 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -49,4 +49,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
> diff --git a/net/tftp.c b/net/tftp.c
> index be24e63075..72d23e1574 100644
> --- a/net/tftp.c
> +++ b/net/tftp.c
> @@ -5,7 +5,6 @@
> * Copyright 2011 Comelit Group SpA,
> * Luca Ceresoli <luca.ceresoli at comelit.it>
> */
> -
> #include <common.h>
> #include <command.h>
> #include <efi_loader.h>
> @@ -95,6 +94,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;
> @@ -134,8 +139,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)
> {
> @@ -348,6 +364,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)
I think it makes more sense to check:
if (tftp_window_size_option > 1 && tftp_state == STATE_SEND_RRQ)
Because I understand that the tftp_state will change while the
tftp_window_size_option is set or at compile time or through the environment. So
we can save the check of the tftp_state if we have the default value.
Regards,
Matthias
> + pkt += sprintf((char *)pkt, "windowsize%c%d%c",
> + 0, tftp_window_size_option, 0);
> len = pkt - xp;
> break;
>
> @@ -500,7 +524,18 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
> (char *)pkt + i + 6, tftp_tsize);
> }
> #endif
> + if (strcmp((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) {
> /* Get ready to send the first block */
> @@ -514,12 +549,32 @@ 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++;
>
> update_block_number();
>
> if (tftp_state == STATE_SEND_RRQ)
> - debug("Server did not acknowledge timeout option!\n");
> + debug("Server did not acknowledge the options!\n");
>
> if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK ||
> tftp_state == STATE_RECV_WRQ) {
> @@ -557,10 +612,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:
> @@ -634,6 +694,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);
> @@ -655,8 +719,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)) {
> @@ -752,7 +816,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 */
>
More information about the U-Boot
mailing list