From 0d7805a86a538404ea89a51f85dd16cd435cecbe Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Thu, 29 Sep 2016 19:31:22 +0000 Subject: [PATCH] tcp: fix FIN ACK handling with unsent data TCP's snd_nxt represents the next sequence number after sent data, and as such does not cover any unsent data queued on the connection. The current implementation does not take the latter point into account when processing FIN acknowledgments, mistakenly assuming that an outgoing FIN is ACK'ed when the acknowledgment covers up to snd_nxt while there is still unsent data. This patch adds a check for unsent data to correct this, effectively preventing that TCP connections are closed prematurely. --- src/core/tcp_in.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core/tcp_in.c b/src/core/tcp_in.c index dd88e1e5..1ee0e127 100644 --- a/src/core/tcp_in.c +++ b/src/core/tcp_in.c @@ -886,7 +886,8 @@ tcp_process(struct tcp_pcb *pcb) case FIN_WAIT_1: tcp_receive(pcb); if (recv_flags & TF_GOT_FIN) { - if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) && + pcb->unsent == NULL) { LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); tcp_ack_now(pcb); @@ -898,7 +899,8 @@ tcp_process(struct tcp_pcb *pcb) tcp_ack_now(pcb); pcb->state = CLOSING; } - } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) && + pcb->unsent == NULL) { pcb->state = FIN_WAIT_2; } break; @@ -915,7 +917,7 @@ tcp_process(struct tcp_pcb *pcb) break; case CLOSING: tcp_receive(pcb); - if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + if (flags & TCP_ACK && ackno == pcb->snd_nxt && pcb->unsent == NULL) { LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); tcp_pcb_purge(pcb); TCP_RMV_ACTIVE(pcb); @@ -925,7 +927,7 @@ tcp_process(struct tcp_pcb *pcb) break; case LAST_ACK: tcp_receive(pcb); - if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + if (flags & TCP_ACK && ackno == pcb->snd_nxt && pcb->unsent == NULL) { LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */ recv_flags |= TF_CLOSED;