From 7a1a9a7946044afa19b9754e6b47bbeee9b45d28 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 10 May 2017 12:46:46 +0300 Subject: [PATCH] net: tcp: Handle retransmitted packets from peer. When we receive a packet with the sequence we already seen (and processed), the most likely cause of it is that our ACK was lost, and peer has to retransmit that packet. Then, we should just ACK it, because otherwise peer will retransmit it again and again, falling into exponential backoff and hosing the entire TCP connection. This makes changes to send_ack(), adding a flag to force sending an ACK regardless of its cached status, and remove inline modifier, as the function is big and called from many places. Signed-off-by: Paul Sokolovsky --- subsys/net/ip/net_context.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index 0619e865497..2f8a2952db0 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -734,8 +734,8 @@ static inline int send_syn_ack(struct net_context *context, "SYN_ACK"); } -static inline int send_ack(struct net_context *context, - struct sockaddr *remote) +static int send_ack(struct net_context *context, + struct sockaddr *remote, bool force) { struct net_pkt *pkt = NULL; int ret; @@ -743,7 +743,7 @@ static inline int send_ack(struct net_context *context, /* Something (e.g. a data transmission under the user * callback) already sent the ACK, no need */ - if (context->tcp->send_ack == context->tcp->sent_ack) { + if (!force && context->tcp->send_ack == context->tcp->sent_ack) { return 0; } @@ -838,6 +838,18 @@ NET_CONN_CB(tcp_established) return NET_DROP; } + if (net_tcp_seq_cmp(sys_get_be32(NET_TCP_HDR(pkt)->seq), context->tcp->send_ack) < 0) { + /* Peer sent us packet we've already seen. Apparently, + * our ack was lost. + */ + + /* RFC793 specifies that "highest" (i.e. current from our PoV) + * ack # value can/should be sent, so we just force resend. + */ + send_ack(context, &conn->remote_addr, true); + return NET_DROP; + } + if (sys_get_be32(NET_TCP_HDR(pkt)->seq) - context->tcp->send_ack) { /* Don't try to reorder packets. If it doesn't * match the next segment exactly, drop and wait for @@ -869,7 +881,7 @@ NET_CONN_CB(tcp_established) } } - send_ack(context, &conn->remote_addr); + send_ack(context, &conn->remote_addr, false); if (sys_slist_is_empty(&context->tcp->sent_list) && context->tcp->fin_rcvd @@ -995,7 +1007,7 @@ NET_CONN_CB(tcp_synack_received) net_tcp_change_state(context->tcp, NET_TCP_ESTABLISHED); net_context_set_state(context, NET_CONTEXT_CONNECTED); - send_ack(context, raddr); + send_ack(context, raddr, false); k_sem_give(&context->tcp->connect_wait);