diff --git a/net/tcp/tcp_send_unbuffered.c b/net/tcp/tcp_send_unbuffered.c index d2e0aa16ee5..01f505529bf 100644 --- a/net/tcp/tcp_send_unbuffered.c +++ b/net/tcp/tcp_send_unbuffered.c @@ -80,14 +80,23 @@ struct send_s { - FAR struct socket *snd_sock; /* Points to the parent socket structure */ - FAR struct devif_callback_s *snd_cb; /* Reference to callback instance */ - sem_t snd_sem; /* Used to wake up the waiting thread */ - FAR const uint8_t *snd_buffer; /* Points to the buffer of data to send */ - size_t snd_buflen; /* Number of bytes in the buffer to send */ - ssize_t snd_sent; /* The number of bytes sent */ - uint32_t snd_isn; /* Initial sequence number */ - uint32_t snd_acked; /* The number of bytes acked */ + FAR struct socket *snd_sock; /* Points to the parent socket structure */ + FAR struct devif_callback_s *snd_cb; /* Reference to callback instance */ + sem_t snd_sem; /* Used to wake up the waiting thread */ + FAR const uint8_t *snd_buffer; /* Points to the buffer of data to send */ + size_t snd_buflen; /* Number of bytes in the buffer to send */ + ssize_t snd_sent; /* The number of bytes sent */ + uint32_t snd_isn; /* Initial sequence number */ + uint32_t snd_acked; /* The number of bytes acked */ + uint32_t snd_prev_ack; /* The previous ACKed seq number */ +#ifdef CONFIG_NET_TCP_WINDOW_SCALE + uint32_t snd_prev_wnd; /* The advertised window in the last + * incoming acknowledgment + */ +#else + uint16_t snd_prev_wnd; +#endif + int snd_dup_acks; /* Duplicate ACK counter */ }; /**************************************************************************** @@ -183,6 +192,7 @@ static uint16_t tcpsend_eventhandler(FAR struct net_driver_s *dev, if ((flags & TCP_ACKDATA) != 0) { + uint32_t ackno; FAR struct tcp_hdr_s *tcp; /* Get the offset address of the TCP header */ @@ -213,7 +223,8 @@ static uint16_t tcpsend_eventhandler(FAR struct net_driver_s *dev, * of bytes to be acknowledged. */ - pstate->snd_acked = TCP_SEQ_SUB(tcp_getsequence(tcp->ackno), + ackno = tcp_getsequence(tcp->ackno); + pstate->snd_acked = TCP_SEQ_SUB(ackno, pstate->snd_isn); ninfo("ACK: acked=%" PRId32 " sent=%zd buflen=%zd\n", pstate->snd_acked, pstate->snd_sent, pstate->snd_buflen); @@ -229,12 +240,39 @@ static uint16_t tcpsend_eventhandler(FAR struct net_driver_s *dev, goto end_wait; } - /* No.. fall through to send more data if necessary */ + /* Fast Retransmit (RFC 5681): an acknowledgment is considered a + * "duplicate" when (a) the receiver of the ACK has outstanding data, + * (b) the incoming acknowledgment carries no data, (c) the SYN and + * FIN bits are both off, (d) the acknowledgment number is equal to + * the greatest acknowledgment received on the given connection + * and (e) the advertised window in the incoming acknowledgment equals + * the advertised window in the last incoming acknowledgment. + */ + + if (pstate->snd_acked < pstate->snd_sent && + (flags & TCP_NEWDATA) == 0 && + (tcp->flags & (TCP_SYN | TCP_FIN)) == 0 && + ackno == pstate->snd_prev_ack && + conn->snd_wnd == pstate->snd_prev_wnd) + { + if (++pstate->snd_dup_acks >= + CONFIG_NET_TCP_FAST_RETRANSMIT_WATERMARK) + { + flags |= TCP_REXMIT; + } + } + else + { + pstate->snd_dup_acks = 0; + } + + pstate->snd_prev_ack = ackno; + pstate->snd_prev_wnd = conn->snd_wnd; } /* Check if we are being asked to retransmit data */ - else if ((flags & TCP_REXMIT) != 0) + if ((flags & TCP_REXMIT) != 0) { /* According to RFC 6298 (5.4), retransmit the earliest segment * that has not been acknowledged by the TCP receiver.