From 46fc56559933777051bcc7b58d06424f1f4af504 Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Thu, 24 Jul 2025 18:08:24 +0200 Subject: [PATCH] lwip: tftp: resend initial request The TFTP implementation does not resend the initial request if there is no response from the server. Since TFTP is based on UDP, there should be a mechanism to deal with unreliable transmissions at this point, similar to what we have for data packets. Therefore, introduce request retransmission. Signed-off-by: Jerome Forissier CC: Venkatesh Abbarapu CC: Michal Simek CC: Ilias Apalodimas --- lib/lwip/lwip/src/apps/tftp/tftp.c | 74 +++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/lib/lwip/lwip/src/apps/tftp/tftp.c b/lib/lwip/lwip/src/apps/tftp/tftp.c index 63b1e0e0e20..ecb6c55ae11 100644 --- a/lib/lwip/lwip/src/apps/tftp/tftp.c +++ b/lib/lwip/lwip/src/apps/tftp/tftp.c @@ -79,6 +79,14 @@ enum tftp_error { #include +struct tftp_req { + ip_addr_t addr; + u16_t port; + u16_t opcode; + enum tftp_transfer_mode mode; + char* fname; +}; + struct tftp_state { const struct tftp_context *ctx; void *handle; @@ -97,14 +105,33 @@ struct tftp_state { }; static struct tftp_state tftp_state; +static struct tftp_req tftp_req; static void tftp_tmr(void *arg); +static void tftp_req_tmr(void *arg); +static const char *mode_to_string(enum tftp_transfer_mode mode); + +static void +clear_req(void) +{ + ip_addr_set_any(0, &tftp_req.addr); + tftp_req.port = 0; + tftp_req.opcode = 0; + free(tftp_req.fname); + tftp_req.fname = NULL; + tftp_req.mode = 0; + + sys_untimeout(tftp_req_tmr, NULL); +} static void close_handle(void) { + clear_req(); + tftp_state.port = 0; ip_addr_set_any(0, &tftp_state.addr); + tftp_state.retries = 0; if (tftp_state.last_data != NULL) { pbuf_free(tftp_state.last_data); @@ -209,6 +236,12 @@ send_ack(const ip_addr_t *addr, u16_t port, u16_t blknum) return ret; } +static err_t +resend_request(void) +{ + return send_request(&tftp_req.addr, tftp_req.port, tftp_req.opcode, tftp_req.fname, mode_to_string(tftp_req.mode)); +} + static err_t resend_data(const ip_addr_t *addr, u16_t port) { @@ -336,6 +369,9 @@ tftp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr tftp_state.last_pkt = tftp_state.timer; tftp_state.retries = 0; + if (tftp_req.fname) + clear_req(); + switch (opcode) { case PP_HTONS(TFTP_RRQ): /* fall through */ case PP_HTONS(TFTP_WRQ): { @@ -542,6 +578,26 @@ tftp_tmr(void *arg) } } +static void +tftp_req_tmr(void *arg) +{ + if (tftp_state.handle == NULL) { + return; + } + + sys_timeout(TFTP_TIMER_MSECS, tftp_req_tmr, NULL); + + if (tftp_state.retries < TFTP_MAX_RETRIES) { + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: req timeout, retrying\n")); + resend_request(); + tftp_state.retries++; + } else { + LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: req timeout\n")); + tftp_state.ctx->error(tftp_state.handle, -1, "Request timeout", strlen("Request timeout")); + close_handle(); + } +} + /** * Initialize TFTP client/server. * @param mode TFTP mode (client/server) @@ -637,6 +693,20 @@ mode_to_string(enum tftp_transfer_mode mode) return NULL; } +err_t +start_send_requests(const ip_addr_t *addr, u16_t port, u16_t opcode, const char* fname, enum tftp_transfer_mode mode) +{ + tftp_req.addr = *addr; + tftp_req.port = port; + tftp_req.opcode = opcode; + tftp_req.fname = strdup(fname); + tftp_req.mode = mode; + if (!tftp_req.fname) + return ERR_MEM; + sys_timeout(TFTP_TIMER_MSECS, tftp_req_tmr, NULL); + return resend_request(); +} + err_t tftp_get(void* handle, const ip_addr_t *addr, u16_t port, const char* fname, enum tftp_transfer_mode mode) { @@ -647,7 +717,7 @@ tftp_get(void* handle, const ip_addr_t *addr, u16_t port, const char* fname, enu tftp_state.handle = handle; tftp_state.blknum = 1; tftp_state.mode_write = 1; /* We want to receive data */ - return send_request(addr, port, TFTP_RRQ, fname, mode_to_string(mode)); + return start_send_requests(addr, port, TFTP_RRQ, fname, mode); } err_t @@ -660,7 +730,7 @@ tftp_put(void* handle, const ip_addr_t *addr, u16_t port, const char* fname, enu tftp_state.handle = handle; tftp_state.blknum = 1; tftp_state.mode_write = 0; /* We want to send data */ - return send_request(addr, port, TFTP_WRQ, fname, mode_to_string(mode)); + return start_send_requests(addr, port, TFTP_WRQ, fname, mode); } #endif /* LWIP_UDP */ -- 2.47.2