diff mbox series

[v12,18/21] net: lwip: tftp: add support of blksize option to client

Message ID 6de92bad17c86e300e3ae4d1b8b67569b350410c.1728482473.git.jerome.forissier@linaro.org
State Superseded
Headers show
Series Introduce the lwIP network stack | expand

Commit Message

Jerome Forissier Oct. 9, 2024, 2:49 p.m. UTC
The TFTP protocol uses a default block size of 512 bytes. This value is
sub-optimal for ethernet devices, which have a MTU (Maximum Transmission
Unit) of 1500 bytes. When taking into acount the overhead of the IP and
UDP layers, this leaves 1468 bytes for the TFTP payload.

This patch introduces a new function: tftp_client_set_blksize() which
may be used to change the block size from the default. It has to be
called after tftp_client_init() and before tftp_get(). If the server
does not support the option, the client will still accept to receive
512-byte blocks.

Submitted upstream: https://savannah.nongnu.org/patch/index.php?10462

Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
 lib/lwip/lwip/src/apps/tftp/tftp.c            | 94 +++++++++++++++++--
 .../lwip/src/include/lwip/apps/tftp_client.h  |  1 +
 2 files changed, 89 insertions(+), 6 deletions(-)

Comments

Ilias Apalodimas Oct. 9, 2024, 3:26 p.m. UTC | #1
Hi Jerome,

On Wed, 9 Oct 2024 at 17:50, Jerome Forissier
<jerome.forissier@linaro.org> wrote:
>
> The TFTP protocol uses a default block size of 512 bytes. This value is
> sub-optimal for ethernet devices, which have a MTU (Maximum Transmission
> Unit) of 1500 bytes. When taking into acount the overhead of the IP and
> UDP layers, this leaves 1468 bytes for the TFTP payload.
>
> This patch introduces a new function: tftp_client_set_blksize() which
> may be used to change the block size from the default. It has to be
> called after tftp_client_init() and before tftp_get(). If the server
> does not support the option, the client will still accept to receive
> 512-byte blocks.
>
> Submitted upstream: https://savannah.nongnu.org/patch/index.php?10462
>
> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
> Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> ---
>  lib/lwip/lwip/src/apps/tftp/tftp.c            | 94 +++++++++++++++++--
>  .../lwip/src/include/lwip/apps/tftp_client.h  |  1 +
>  2 files changed, 89 insertions(+), 6 deletions(-)
>
> diff --git a/lib/lwip/lwip/src/apps/tftp/tftp.c b/lib/lwip/lwip/src/apps/tftp/tftp.c
> index 74fc1fbe586..8e0c071772a 100644
> --- a/lib/lwip/lwip/src/apps/tftp/tftp.c
> +++ b/lib/lwip/lwip/src/apps/tftp/tftp.c
> @@ -57,7 +57,7 @@
>  #include "lwip/timeouts.h"
>  #include "lwip/debug.h"
>
> -#define TFTP_MAX_PAYLOAD_SIZE 512
> +#define TFTP_DEFAULT_BLOCK_SIZE 512
>  #define TFTP_HEADER_LENGTH    4
>
>  #define TFTP_RRQ   1
> @@ -65,6 +65,7 @@
>  #define TFTP_DATA  3
>  #define TFTP_ACK   4
>  #define TFTP_ERROR 5
> +#define TFTP_OACK  6
>
>  enum tftp_error {
>    TFTP_ERROR_FILE_NOT_FOUND    = 1,
> @@ -88,9 +89,11 @@ struct tftp_state {
>    int timer;
>    int last_pkt;
>    u16_t blknum;
> +  u16_t blksize;
>    u8_t retries;
>    u8_t mode_write;
>    u8_t tftp_mode;
> +  bool wait_oack;
>  };
>
>  static struct tftp_state tftp_state;
> @@ -137,10 +140,24 @@ send_request(const ip_addr_t *addr, u16_t port, u16_t opcode, const char* fname,
>  {
>    size_t fname_length = strlen(fname)+1;
>    size_t mode_length = strlen(mode)+1;
> -  struct pbuf* p = init_packet(opcode, 0, fname_length + mode_length - 2);
> +  size_t blksize_length = 0;
> +  struct pbuf* p;
>    char* payload;
>    err_t ret;
>
> +  if (tftp_state.blksize) {
> +    blksize_length = 14; /* maximum (blksize is a u16_t): 'blksize\0XXXXX\0' */
> +    if (tftp_state.blksize < 10000)
> +           blksize_length--;
> +    if (tftp_state.blksize < 1000)
> +           blksize_length--;
> +    if (tftp_state.blksize < 100)
> +           blksize_length--;
> +    if (tftp_state.blksize < 10)
> +           blksize_length--;
> +  }

I'd really prefer the alternative for this [0]. Since you havent
changed it, make sure you queue it up if v12 gets merged

> +
> +  p = init_packet(opcode, 0, fname_length + mode_length + blksize_length - 2);
>    if (p == NULL) {
>      return ERR_MEM;

[...]

>
>
> --
> 2.40.1
>

[0] https://lore.kernel.org/u-boot/CAC_iWjJjsOZYZHB+019G8xs33+Bj2FeV8MB7FfhJ-C8v8uBNng@mail.gmail.com/

Thanks
/Ilias
Jerome Forissier Oct. 9, 2024, 4:29 p.m. UTC | #2
On 10/9/24 17:26, Ilias Apalodimas wrote:
> Hi Jerome,
> 
> On Wed, 9 Oct 2024 at 17:50, Jerome Forissier
> <jerome.forissier@linaro.org> wrote:
>>
>> The TFTP protocol uses a default block size of 512 bytes. This value is
>> sub-optimal for ethernet devices, which have a MTU (Maximum Transmission
>> Unit) of 1500 bytes. When taking into acount the overhead of the IP and
>> UDP layers, this leaves 1468 bytes for the TFTP payload.
>>
>> This patch introduces a new function: tftp_client_set_blksize() which
>> may be used to change the block size from the default. It has to be
>> called after tftp_client_init() and before tftp_get(). If the server
>> does not support the option, the client will still accept to receive
>> 512-byte blocks.
>>
>> Submitted upstream: https://savannah.nongnu.org/patch/index.php?10462
>>
>> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
>> Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
>> ---
>>  lib/lwip/lwip/src/apps/tftp/tftp.c            | 94 +++++++++++++++++--
>>  .../lwip/src/include/lwip/apps/tftp_client.h  |  1 +
>>  2 files changed, 89 insertions(+), 6 deletions(-)
>>
>> diff --git a/lib/lwip/lwip/src/apps/tftp/tftp.c b/lib/lwip/lwip/src/apps/tftp/tftp.c
>> index 74fc1fbe586..8e0c071772a 100644
>> --- a/lib/lwip/lwip/src/apps/tftp/tftp.c
>> +++ b/lib/lwip/lwip/src/apps/tftp/tftp.c
>> @@ -57,7 +57,7 @@
>>  #include "lwip/timeouts.h"
>>  #include "lwip/debug.h"
>>
>> -#define TFTP_MAX_PAYLOAD_SIZE 512
>> +#define TFTP_DEFAULT_BLOCK_SIZE 512
>>  #define TFTP_HEADER_LENGTH    4
>>
>>  #define TFTP_RRQ   1
>> @@ -65,6 +65,7 @@
>>  #define TFTP_DATA  3
>>  #define TFTP_ACK   4
>>  #define TFTP_ERROR 5
>> +#define TFTP_OACK  6
>>
>>  enum tftp_error {
>>    TFTP_ERROR_FILE_NOT_FOUND    = 1,
>> @@ -88,9 +89,11 @@ struct tftp_state {
>>    int timer;
>>    int last_pkt;
>>    u16_t blknum;
>> +  u16_t blksize;
>>    u8_t retries;
>>    u8_t mode_write;
>>    u8_t tftp_mode;
>> +  bool wait_oack;
>>  };
>>
>>  static struct tftp_state tftp_state;
>> @@ -137,10 +140,24 @@ send_request(const ip_addr_t *addr, u16_t port, u16_t opcode, const char* fname,
>>  {
>>    size_t fname_length = strlen(fname)+1;
>>    size_t mode_length = strlen(mode)+1;
>> -  struct pbuf* p = init_packet(opcode, 0, fname_length + mode_length - 2);
>> +  size_t blksize_length = 0;
>> +  struct pbuf* p;
>>    char* payload;
>>    err_t ret;
>>
>> +  if (tftp_state.blksize) {
>> +    blksize_length = 14; /* maximum (blksize is a u16_t): 'blksize\0XXXXX\0' */
>> +    if (tftp_state.blksize < 10000)
>> +           blksize_length--;
>> +    if (tftp_state.blksize < 1000)
>> +           blksize_length--;
>> +    if (tftp_state.blksize < 100)
>> +           blksize_length--;
>> +    if (tftp_state.blksize < 10)
>> +           blksize_length--;
>> +  }
> 
> I'd really prefer the alternative for this [0]. Since you havent
> changed it, make sure you queue it up if v12 gets merged

Sorry about that, I forgot. The code you propose in [0] needs a few
adjustments: >= not >, initialize cnt to 1 to account for one digit at
least, and do nothing if tftp_state.blksize == 0. We can get rid of
cnt and it still looks good and even better IMO. So how about this?

diff --git a/lib/lwip/lwip/src/apps/tftp/tftp.c b/lib/lwip/lwip/src/apps/tftp/tftp.c
index 8e0c071772a..7012aad9d91 100644
--- a/lib/lwip/lwip/src/apps/tftp/tftp.c
+++ b/lib/lwip/lwip/src/apps/tftp/tftp.c
@@ -141,20 +141,18 @@ send_request(const ip_addr_t *addr, u16_t port, u16_t opcode, const char* fname,
   size_t fname_length = strlen(fname)+1;
   size_t mode_length = strlen(mode)+1;
   size_t blksize_length = 0;
+  int blksize = tftp_state.blksize;
   struct pbuf* p;
   char* payload;
   err_t ret;
 
-  if (tftp_state.blksize) {
-    blksize_length = 14; /* maximum (blksize is a u16_t): 'blksize\0XXXXX\0' */
-    if (tftp_state.blksize < 10000)
-	    blksize_length--;
-    if (tftp_state.blksize < 1000)
-	    blksize_length--;
-    if (tftp_state.blksize < 100)
-	    blksize_length--;
-    if (tftp_state.blksize < 10)
-	    blksize_length--;
+  if (blksize) {
+    /* "blksize\0.\0" with . = 1 digit */
+    blksize_length = strlen("blksize") + 1 + 1 + 1;
+    while (blksize >= 10) {
+      blksize /= 10;
+      blksize_length++;
+    }

> 
>> +
>> +  p = init_packet(opcode, 0, fname_length + mode_length + blksize_length - 2);
>>    if (p == NULL) {
>>      return ERR_MEM;
> 
> [...]
> 
>>
>>
>> --
>> 2.40.1
>>
> 
> [0] https://lore.kernel.org/u-boot/CAC_iWjJjsOZYZHB+019G8xs33+Bj2FeV8MB7FfhJ-C8v8uBNng@mail.gmail.com/
> 
> Thanks
> /Ilias

Thanks,
Ilias Apalodimas Oct. 9, 2024, 5:39 p.m. UTC | #3
On Wed, 9 Oct 2024 at 19:30, Jerome Forissier
<jerome.forissier@linaro.org> wrote:
>
>
>
> On 10/9/24 17:26, Ilias Apalodimas wrote:
> > Hi Jerome,
> >
> > On Wed, 9 Oct 2024 at 17:50, Jerome Forissier
> > <jerome.forissier@linaro.org> wrote:
> >>
> >> The TFTP protocol uses a default block size of 512 bytes. This value is
> >> sub-optimal for ethernet devices, which have a MTU (Maximum Transmission
> >> Unit) of 1500 bytes. When taking into acount the overhead of the IP and
> >> UDP layers, this leaves 1468 bytes for the TFTP payload.
> >>
> >> This patch introduces a new function: tftp_client_set_blksize() which
> >> may be used to change the block size from the default. It has to be
> >> called after tftp_client_init() and before tftp_get(). If the server
> >> does not support the option, the client will still accept to receive
> >> 512-byte blocks.
> >>
> >> Submitted upstream: https://savannah.nongnu.org/patch/index.php?10462
> >>
> >> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
> >> Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> >> ---
> >>  lib/lwip/lwip/src/apps/tftp/tftp.c            | 94 +++++++++++++++++--
> >>  .../lwip/src/include/lwip/apps/tftp_client.h  |  1 +
> >>  2 files changed, 89 insertions(+), 6 deletions(-)
> >>
> >> diff --git a/lib/lwip/lwip/src/apps/tftp/tftp.c b/lib/lwip/lwip/src/apps/tftp/tftp.c
> >> index 74fc1fbe586..8e0c071772a 100644
> >> --- a/lib/lwip/lwip/src/apps/tftp/tftp.c
> >> +++ b/lib/lwip/lwip/src/apps/tftp/tftp.c
> >> @@ -57,7 +57,7 @@
> >>  #include "lwip/timeouts.h"
> >>  #include "lwip/debug.h"
> >>
> >> -#define TFTP_MAX_PAYLOAD_SIZE 512
> >> +#define TFTP_DEFAULT_BLOCK_SIZE 512
> >>  #define TFTP_HEADER_LENGTH    4
> >>
> >>  #define TFTP_RRQ   1
> >> @@ -65,6 +65,7 @@
> >>  #define TFTP_DATA  3
> >>  #define TFTP_ACK   4
> >>  #define TFTP_ERROR 5
> >> +#define TFTP_OACK  6
> >>
> >>  enum tftp_error {
> >>    TFTP_ERROR_FILE_NOT_FOUND    = 1,
> >> @@ -88,9 +89,11 @@ struct tftp_state {
> >>    int timer;
> >>    int last_pkt;
> >>    u16_t blknum;
> >> +  u16_t blksize;
> >>    u8_t retries;
> >>    u8_t mode_write;
> >>    u8_t tftp_mode;
> >> +  bool wait_oack;
> >>  };
> >>
> >>  static struct tftp_state tftp_state;
> >> @@ -137,10 +140,24 @@ send_request(const ip_addr_t *addr, u16_t port, u16_t opcode, const char* fname,
> >>  {
> >>    size_t fname_length = strlen(fname)+1;
> >>    size_t mode_length = strlen(mode)+1;
> >> -  struct pbuf* p = init_packet(opcode, 0, fname_length + mode_length - 2);
> >> +  size_t blksize_length = 0;
> >> +  struct pbuf* p;
> >>    char* payload;
> >>    err_t ret;
> >>
> >> +  if (tftp_state.blksize) {
> >> +    blksize_length = 14; /* maximum (blksize is a u16_t): 'blksize\0XXXXX\0' */
> >> +    if (tftp_state.blksize < 10000)
> >> +           blksize_length--;
> >> +    if (tftp_state.blksize < 1000)
> >> +           blksize_length--;
> >> +    if (tftp_state.blksize < 100)
> >> +           blksize_length--;
> >> +    if (tftp_state.blksize < 10)
> >> +           blksize_length--;
> >> +  }
> >
> > I'd really prefer the alternative for this [0]. Since you havent
> > changed it, make sure you queue it up if v12 gets merged
>
> Sorry about that, I forgot. The code you propose in [0] needs a few
> adjustments: >= not >,

There's a cnt++ in that code after the loop, so it's either with > and ++ or >=

> initialize cnt to 1 to account for one digit at
> least, and do nothing if tftp_state.blksize == 0. We can get rid of
> cnt and it still looks good and even better IMO. So how about this?
>
> diff --git a/lib/lwip/lwip/src/apps/tftp/tftp.c b/lib/lwip/lwip/src/apps/tftp/tftp.c
> index 8e0c071772a..7012aad9d91 100644
> --- a/lib/lwip/lwip/src/apps/tftp/tftp.c
> +++ b/lib/lwip/lwip/src/apps/tftp/tftp.c
> @@ -141,20 +141,18 @@ send_request(const ip_addr_t *addr, u16_t port, u16_t opcode, const char* fname,
>    size_t fname_length = strlen(fname)+1;
>    size_t mode_length = strlen(mode)+1;
>    size_t blksize_length = 0;
> +  int blksize = tftp_state.blksize;
>    struct pbuf* p;
>    char* payload;
>    err_t ret;
>
> -  if (tftp_state.blksize) {
> -    blksize_length = 14; /* maximum (blksize is a u16_t): 'blksize\0XXXXX\0' */
> -    if (tftp_state.blksize < 10000)
> -           blksize_length--;
> -    if (tftp_state.blksize < 1000)
> -           blksize_length--;
> -    if (tftp_state.blksize < 100)
> -           blksize_length--;
> -    if (tftp_state.blksize < 10)
> -           blksize_length--;
> +  if (blksize) {
> +    /* "blksize\0.\0" with . = 1 digit */
> +    blksize_length = strlen("blksize") + 1 + 1 + 1;
> +    while (blksize >= 10) {
> +      blksize /= 10;
> +      blksize_length++;
> +    }

Yea that looks ok


Thanks
/Ilias
>
> >
> >> +
> >> +  p = init_packet(opcode, 0, fname_length + mode_length + blksize_length - 2);
> >>    if (p == NULL) {
> >>      return ERR_MEM;
> >
> > [...]
> >
> >>
> >>
> >> --
> >> 2.40.1
> >>
> >
> > [0] https://lore.kernel.org/u-boot/CAC_iWjJjsOZYZHB+019G8xs33+Bj2FeV8MB7FfhJ-C8v8uBNng@mail.gmail.com/
> >
> > Thanks
> > /Ilias
>
> Thanks,
> --
> Jerome
diff mbox series

Patch

diff --git a/lib/lwip/lwip/src/apps/tftp/tftp.c b/lib/lwip/lwip/src/apps/tftp/tftp.c
index 74fc1fbe586..8e0c071772a 100644
--- a/lib/lwip/lwip/src/apps/tftp/tftp.c
+++ b/lib/lwip/lwip/src/apps/tftp/tftp.c
@@ -57,7 +57,7 @@ 
 #include "lwip/timeouts.h"
 #include "lwip/debug.h"
 
-#define TFTP_MAX_PAYLOAD_SIZE 512
+#define TFTP_DEFAULT_BLOCK_SIZE 512
 #define TFTP_HEADER_LENGTH    4
 
 #define TFTP_RRQ   1
@@ -65,6 +65,7 @@ 
 #define TFTP_DATA  3
 #define TFTP_ACK   4
 #define TFTP_ERROR 5
+#define TFTP_OACK  6
 
 enum tftp_error {
   TFTP_ERROR_FILE_NOT_FOUND    = 1,
@@ -88,9 +89,11 @@  struct tftp_state {
   int timer;
   int last_pkt;
   u16_t blknum;
+  u16_t blksize;
   u8_t retries;
   u8_t mode_write;
   u8_t tftp_mode;
+  bool wait_oack;
 };
 
 static struct tftp_state tftp_state;
@@ -137,10 +140,24 @@  send_request(const ip_addr_t *addr, u16_t port, u16_t opcode, const char* fname,
 {
   size_t fname_length = strlen(fname)+1;
   size_t mode_length = strlen(mode)+1;
-  struct pbuf* p = init_packet(opcode, 0, fname_length + mode_length - 2);
+  size_t blksize_length = 0;
+  struct pbuf* p;
   char* payload;
   err_t ret;
 
+  if (tftp_state.blksize) {
+    blksize_length = 14; /* maximum (blksize is a u16_t): 'blksize\0XXXXX\0' */
+    if (tftp_state.blksize < 10000)
+	    blksize_length--;
+    if (tftp_state.blksize < 1000)
+	    blksize_length--;
+    if (tftp_state.blksize < 100)
+	    blksize_length--;
+    if (tftp_state.blksize < 10)
+	    blksize_length--;
+  }
+
+  p = init_packet(opcode, 0, fname_length + mode_length + blksize_length - 2);
   if (p == NULL) {
     return ERR_MEM;
   }
@@ -148,7 +165,10 @@  send_request(const ip_addr_t *addr, u16_t port, u16_t opcode, const char* fname,
   payload = (char*) p->payload;
   MEMCPY(payload+2,              fname, fname_length);
   MEMCPY(payload+2+fname_length, mode,  mode_length);
+  if (tftp_state.blksize)
+    sprintf(payload+2+fname_length+mode_length, "blksize%c%d%c", 0, tftp_state.blksize, 0);
 
+  tftp_state.wait_oack = true;
   ret = udp_sendto(tftp_state.upcb, p, addr, port);
   pbuf_free(p);
   return ret;
@@ -221,14 +241,14 @@  send_data(const ip_addr_t *addr, u16_t port)
     pbuf_free(tftp_state.last_data);
   }
 
-  tftp_state.last_data = init_packet(TFTP_DATA, tftp_state.blknum, TFTP_MAX_PAYLOAD_SIZE);
+  tftp_state.last_data = init_packet(TFTP_DATA, tftp_state.blknum, TFTP_DEFAULT_BLOCK_SIZE);
   if (tftp_state.last_data == NULL) {
     return;
   }
 
   payload = (u16_t *) tftp_state.last_data->payload;
 
-  ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
+  ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_DEFAULT_BLOCK_SIZE);
   if (ret < 0) {
     send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Error occurred while reading the file.");
     close_handle();
@@ -239,6 +259,28 @@  send_data(const ip_addr_t *addr, u16_t port)
   resend_data(addr, port);
 }
 
+static u16_t payload_size(void)
+{
+  if (tftp_state.blksize)
+    return tftp_state.blksize;
+  return TFTP_DEFAULT_BLOCK_SIZE;
+}
+
+static const char *
+find_option(struct pbuf *p, const char *option)
+{
+  int i;
+  u16_t optlen = strlen(option);
+  const char *b = p->payload;
+
+  for (i = 0; i + optlen + 1 < p->len; i++) {
+    if (lwip_strnstr(b + i, option, optlen))
+      return b + i + optlen + 2;
+  }
+
+  return NULL;
+}
+
 static void
 tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
 {
@@ -338,6 +380,15 @@  tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr
       }
 
       blknum = lwip_ntohs(sbuf[1]);
+      if (tftp_state.blksize && tftp_state.wait_oack) {
+        /*
+         * Data received while we are expecting an OACK for our blksize option.
+         * This means the server doesn't support it, let's switch back to the
+         * default block size.
+         */
+       tftp_state.blksize = 0;
+       tftp_state.wait_oack = false;
+      }
       if (blknum == tftp_state.blknum) {
         pbuf_remove_header(p, TFTP_HEADER_LENGTH);
 
@@ -349,7 +400,7 @@  tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr
           send_ack(addr, port, blknum);
         }
 
-        if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
+        if (p->tot_len < payload_size()) {
           close_handle();
         } else {
           tftp_state.blknum++;
@@ -386,7 +437,7 @@  tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr
       lastpkt = 0;
 
       if (tftp_state.last_data != NULL) {
-        lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
+        lastpkt = tftp_state.last_data->tot_len != (TFTP_DEFAULT_BLOCK_SIZE + TFTP_HEADER_LENGTH);
       }
 
       if (!lastpkt) {
@@ -405,6 +456,25 @@  tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr
         close_handle();
       }
       break;
+    case PP_HTONS(TFTP_OACK): {
+      const char *optval = find_option(p, "blksize");
+      u16_t srv_blksize = 0;
+      tftp_state.wait_oack = false;
+      if (optval) {
+	if (!tftp_state.blksize) {
+	  /* We did not request this option */
+          send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "blksize unexpected");
+	}
+	srv_blksize = atoi(optval);
+	if (srv_blksize <= 0 || srv_blksize > tftp_state.blksize) {
+	  send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Invalid blksize");
+	}
+	LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: accepting blksize=%d\n", srv_blksize));
+	tftp_state.blksize = srv_blksize;
+      }
+      send_ack(addr, port, 0);
+      break;
+    }
     default:
       send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
       break;
@@ -495,6 +565,18 @@  tftp_init_client(const struct tftp_context *ctx)
   return tftp_init_common(LWIP_TFTP_MODE_CLIENT, ctx);
 }
 
+/** @ingroup tftp
+ * Set the block size to be used by the TFTP client. The server may choose to
+ * accept a lower value.
+ * @param blksize Block size in bytes
+ */
+void
+tftp_client_set_blksize(u16_t blksize)
+{
+  if (blksize != TFTP_DEFAULT_BLOCK_SIZE)
+    tftp_state.blksize = blksize;
+}
+
 /** @ingroup tftp
  * Deinitialize ("turn off") TFTP client/server.
  */
diff --git a/lib/lwip/lwip/src/include/lwip/apps/tftp_client.h b/lib/lwip/lwip/src/include/lwip/apps/tftp_client.h
index 24dbda6a8c9..e1e21d06b67 100644
--- a/lib/lwip/lwip/src/include/lwip/apps/tftp_client.h
+++ b/lib/lwip/lwip/src/include/lwip/apps/tftp_client.h
@@ -44,6 +44,7 @@  enum tftp_transfer_mode {
 };
 
 err_t tftp_init_client(const struct tftp_context* ctx);
+void tftp_client_set_blksize(u16_t blksize);
 err_t tftp_get(void* handle, const ip_addr_t *addr, u16_t port, const char* fname, enum tftp_transfer_mode mode);
 err_t tftp_put(void* handle, const ip_addr_t *addr, u16_t port, const char* fname, enum tftp_transfer_mode mode);