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.
This commit is contained in:
David van Moolenbroek 2016-09-29 19:31:22 +00:00 committed by sg
parent 9ba9dee2aa
commit 0d7805a86a

View File

@ -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;