diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c index 12af1e7c2b..dcd9827028 100644 --- a/net/tcp/tcp_input.c +++ b/net/tcp/tcp_input.c @@ -385,11 +385,58 @@ found: dev->d_len -= (len + iplen); - /* First, check if the sequence number of the incoming packet is - * what we're expecting next. If not, we send out an ACK with the - * correct numbers in, unless we are in the SYN_RCVD state and - * receive a SYN, in which case we should retransmit our SYNACK - * (which is done further down). +#ifdef CONFIG_NET_TCP_KEEPALIVE + /* Check for a to KeepAlive probes. These packets have these properties: + * + * - TCP_ACK flag is set. SYN/FIN/RST never appear in a Keepalive probe. + * - Sequence number is the sequence number of previously ACKed data, i.e., + * the expected sequence number minus one. + * - The data payload is one or two bytes. + * + * We would expect a KeepAlive only in the ESTABLISHED state and only after + * some time has elapsed with no network activity. If there is un-ACKed data, + * then we will let the normal TCP re-transmission logic handle that case. + */ + + if ((tcp->flags & TCP_ACK) != 0 && + (tcp->flags & (TCP_SYN | TCP_FIN | TCP_RST)) == 0 && + (conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED && + (dev->d_len == 0 || dev->d_len == 1) && + conn->unacked <= 0) + { + uint32_t ackseq; + uint32_t rcvseq; + + /* Get the sequence number of that has just been acknowledged by this + * incoming packet. + */ + + ackseq = tcp_getsequence(tcp->seqno); + rcvseq = tcp_getsequence(conn->rcvseq); + + if (ackseq < rcvseq) + { + if (dev->d_len > 0) + { + /* Increment the received sequence number (perhaps including the + * discarded dummy byte in the probe). + */ + + net_incr32(conn->rcvseq, dev->d_len); + } + + /* And send a "normal" acknowledgment of the KeepAlive probe */ + + tcp_send(dev, conn, TCP_ACK, tcpiplen); + return; + } + } +#endif + + /* Check if the sequence number of the incoming packet is what we are + * expecting next. If not, we send out an ACK with the correct numbers + * in, unless we are in the SYN_RCVD state and receive a SYN, in which + * case we should retransmit our SYNACK (which is done further down). */ if (!((((conn->tcpstateflags & TCP_STATE_MASK) == TCP_SYN_SENT) && @@ -405,10 +452,9 @@ found: } } - /* Next, check if the incoming segment acknowledges any outstanding - * data. If so, we update the sequence number, reset the length of - * the outstanding data, calculate RTT estimations, and reset the - * retransmission timer. + /* Check if the incoming segment acknowledges any outstanding data. If so, + * we update the sequence number, reset the length of the outstanding + * data, calculate RTT estimations, and reset the retransmission timer. */ if ((tcp->flags & TCP_ACK) != 0 && conn->unacked > 0) @@ -764,7 +810,7 @@ found: #endif /* CONFIG_NET_TCPURGDATA */ } -#ifdef NET_TCP_KEEPALIVE +#ifdef CONFIG_NET_TCP_KEEPALIVE /* If the established socket receives an ACK or any kind of data * from the remote peer (whether we accept it or not), then reset * the keep alive timer. diff --git a/net/tcp/tcp_timer.c b/net/tcp/tcp_timer.c index 3c109c7538..e746d9c5b3 100644 --- a/net/tcp/tcp_timer.c +++ b/net/tcp/tcp_timer.c @@ -364,6 +364,7 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, if (conn->keepalive) { socktimeo_t timeo; + uint32_t saveseq; /* If this is the first probe, then the keepstart time is * the time that the last ACK or data was received from the @@ -381,6 +382,7 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, { timeo = (socktimeo_t)conn->keepidle; } + /* Yes... has the idle period elapsed with no data or ACK * received from the remote peer? */ @@ -422,10 +424,38 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, } #endif - /* And send the probe */ + /* And send the probe (along with a garbage byte). + * The packet we sned must have these properties: + * + * - TCP_ACK flag (only) is set. + * - Sequence number is the sequence number of + * previously ACKed data, i.e., the expected + * sequence number minus one. + * - The data payload is one or two bytes. + * + * tcp_send() will send the TCP sequence number as + * conn->sndseq. Rather than creating a new + * interface, we spoof tcp_end() here: + */ - tcp_send(dev, conn, TCP_ACK, tcpiplen); + saveseq = tcp_getsequence(conn->sndseq); + tcp_setsequence(conn->sndseq, saveseq - 1); + tcp_send(dev, conn, TCP_ACK, tcpiplen + 1); + + tcp_setsequence(conn->sndseq, saveseq); + + /* Increment the number of un-ACKed bytes due to the dummy + * byte that we just sent. + */ + + conn->unacked++; + +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS + /* Increment the un-ACKed sequence number */ + + conn->sndseq_max++; +#endif /* Update for the next probe */ conn->keeptime = clock_systimer();