From f79eabd24b0b266d0263b331afdc4df1fc545e22 Mon Sep 17 00:00:00 2001 From: Joel Cunningham Date: Mon, 14 Nov 2016 15:31:57 -0600 Subject: [PATCH] bug #49533: start persist timer when unsent seg can't fit in window This commit returns LwIP to previous behavior where if the next unsent segment can't be sent due to the current send window, we start the persist timer. This is done to engage window probing in the case that the subsequent window update from the receiver is dropped, thus preventing connection deadlock This commit refines the previous logic to only target the following case: 1) Next unsent segment doesn't fit within the send window (not congestion) and there is some room in the window 2) Unacked queue is empty (otherwise data is inflight and the RTO timer will take care of any dropped window updates) See commit d8f090a7595a79050c435545d00efc1261b9691c (which removed this behavior) to reference the old logic. The old logic falsely started the persit timer when the RTO timer was already running. --- src/core/tcp_out.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c index 3c2bc81a..28d17441 100644 --- a/src/core/tcp_out.c +++ b/src/core/tcp_out.c @@ -1033,6 +1033,24 @@ tcp_output(struct tcp_pcb *pcb) lwip_ntohl(seg->tcphdr->seqno), pcb->lastack)); } #endif /* TCP_CWND_DEBUG */ + /* Check if we need to start the persistent timer when the next unsent segment + * does not fit within the remaining send window and RTO timer is not running (we + * have no in-flight data). A traditional approach would fill the remaining window + * with part of the unsent segment (which will engage zero-window probing upon + * reception of the zero window update from the receiver). This ensures the + * subsequent window update is reliably received. With the goal of being lightweight, + * we avoid splitting the unsent segment and treat the window as already zero. + */ + if (seg != NULL && + lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd && + wnd > 0 && wnd == pcb->snd_wnd && pcb->unacked == NULL) { + /* Start the persist timer */ + if (pcb->persist_backoff == 0) { + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + goto output_done; + } /* data available and window allows it to be sent? */ while (seg != NULL && lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { @@ -1112,6 +1130,7 @@ tcp_output(struct tcp_pcb *pcb) } seg = pcb->unsent; } +output_done: #if TCP_OVERSIZE if (pcb->unsent == NULL) { /* last unsent has been removed, reset unsent_oversize */