net/tcp:Added tcp zero window probe timer support

https://www.rfc-editor.org/rfc/rfc1122#page-92

Signed-off-by: wangyingdong <wangyingdong@xiaomi.com>
This commit is contained in:
wangyingdong 2023-06-20 19:43:37 +08:00 committed by Alan Carvalho de Assis
parent 1a832eb554
commit 2ce31c442f
4 changed files with 192 additions and 22 deletions

View File

@ -126,6 +126,9 @@
#define TCP_FAST_RETRANSMISSION_THRESH 3
#define TCP_RTO_MAX 240 /* 120s,The unit is half a second */
#define TCP_RTO_MIN 1 /* 0.5s */
/****************************************************************************
* Public Type Definitions
****************************************************************************/
@ -355,6 +358,7 @@ struct tcp_conn_s
#if defined(CONFIG_NET_SENDFILE) && defined(CONFIG_NET_TCP_WRITE_BUFFERS)
bool sendfile; /* True if sendfile operation is in progress */
#endif
bool zero_probe; /* TCP zero window probe timer */
/* connevents is a list of callbacks for each socket the uses this
* connection (there can be more that one in the event that the the socket
@ -760,7 +764,7 @@ void tcp_stop_monitor(FAR struct tcp_conn_s *conn, uint16_t flags);
* Input Parameters:
* conn - The TCP connection of interest
* cb - devif callback structure
* flags - Set of connection events events
* flags - Set of connection events
*
* Returned Value:
* None
@ -2304,6 +2308,27 @@ void tcp_cc_recv_ack(FAR struct tcp_conn_s *conn, FAR struct tcp_hdr_s *tcp);
}
#endif
/****************************************************************************
* Name: tcp_set_zero_probe
*
* Description:
* Update the TCP probe timer for the provided TCP connection,
* The timeout is accurate
*
* Input Parameters:
* conn - The TCP "connection" to poll for TX data
* flags - Set of connection events
*
* Returned Value:
* None
*
* Assumptions:
* conn is not NULL.
*
****************************************************************************/
void tcp_set_zero_probe(FAR struct tcp_conn_s *conn, uint16_t flags);
#endif /* !CONFIG_NET_TCP_NO_STACK */
#endif /* CONFIG_NET_TCP */
#endif /* __NET_TCP_TCP_H */

View File

@ -633,6 +633,40 @@ static void tcp_parse_option(FAR struct net_driver_s *dev,
}
}
/****************************************************************************
* Name: tcp_clear_zero_probe
*
* Description:
* clear the TCP zero window probe
*
* Input Parameters:
* conn - The TCP connection of interest
* tcp - Header of TCP structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static void tcp_clear_zero_probe(FAR struct tcp_conn_s *conn,
FAR struct tcp_hdr_s *tcp)
{
/* If the receive window is not 0,
* the zero window probe timer needs to be cleared
*/
if ((tcp->wnd[0] || tcp->wnd[1]) && conn->zero_probe &&
(tcp->flags & TCP_ACK) != 0)
{
conn->zero_probe = false;
conn->nrtx = 0;
conn->timer = 0;
}
}
/****************************************************************************
* Name: tcp_input
*
@ -1155,6 +1189,8 @@ found:
tcp_update_retrantimer(conn, conn->rto);
}
tcp_clear_zero_probe(conn, tcp);
/* Update the connection's window size */
if ((tcp->flags & TCP_ACK) != 0 &&

View File

@ -1119,6 +1119,10 @@ static uint16_t psock_send_eventhandler(FAR struct net_driver_s *dev,
flags &= ~TCP_POLL;
}
}
else
{
tcp_set_zero_probe(conn, flags);
}
/* Continue waiting */

View File

@ -155,6 +155,50 @@ static void tcp_timer_expiry(FAR void *arg)
net_unlock();
}
/****************************************************************************
* Name: tcp_xmit_probe
*
* Description:
* TCP retransmission probe packet
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
* conn - The TCP "connection" to poll for TX data
*
* Returned Value:
* None
*
* Assumptions:
* dev is not NULL.
* conn is not NULL.
*
****************************************************************************/
static void tcp_xmit_probe(FAR struct net_driver_s *dev,
FAR struct tcp_conn_s *conn)
{
/* And send the probe.
* The packet we send 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.
*
* tcp_send() will send the TCP sequence number as
* conn->sndseq. Rather than creating a new
* interface, we spoof tcp_end() here:
*/
uint16_t hdrlen = tcpip_hdrsize(conn);
uint32_t saveseq = tcp_getsequence(conn->sndseq);
tcp_setsequence(conn->sndseq, saveseq - 1);
tcp_send(dev, conn, TCP_ACK, hdrlen);
tcp_setsequence(conn->sndseq, saveseq);
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -292,6 +336,40 @@ void tcp_stop_timer(FAR struct tcp_conn_s *conn)
work_cancel(LPWORK, &conn->work);
}
/****************************************************************************
* Name: tcp_set_zero_probe
*
* Description:
* Update the TCP probe timer for the provided TCP connection,
* The timeout is accurate
*
* Input Parameters:
* conn - The TCP "connection" to poll for TX data
* flags - Set of connection events
*
* Returned Value:
* None
*
* Assumptions:
* conn is not NULL.
*
****************************************************************************/
void tcp_set_zero_probe(FAR struct tcp_conn_s *conn, uint16_t flags)
{
if ((conn->tcpstateflags & TCP_ESTABLISHED) &&
((flags & TCP_NEWDATA) == 0) && conn->tx_unacked <= 0 &&
(flags & (TCP_POLL | TCP_REXMIT | TCP_ACKDATA)) &&
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
!(sq_empty(&conn->write_q)) &&
#endif
!conn->timeout && !conn->zero_probe)
{
tcp_update_retrantimer(conn, TCP_RTO_MIN);
conn->zero_probe = true;
}
}
/****************************************************************************
* Name: tcp_timer
*
@ -616,8 +694,6 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn)
if (conn->keepalive)
{
uint32_t saveseq;
/* Yes... has the idle period elapsed with no data or ACK
* received from the remote peer?
*/
@ -643,25 +719,7 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn)
}
else
{
/* And send the probe.
* The packet we send 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.
*
* tcp_send() will send the TCP sequence number as
* conn->sndseq. Rather than creating a new
* interface, we spoof tcp_end() here:
*/
saveseq = tcp_getsequence(conn->sndseq);
tcp_setsequence(conn->sndseq, saveseq - 1);
tcp_send(dev, conn, TCP_ACK, hdrlen);
tcp_setsequence(conn->sndseq, saveseq);
tcp_xmit_probe(dev, conn);
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* Increment the un-ACKed sequence number */
@ -679,6 +737,53 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn)
}
#endif
/* Is this an established connected with
* Zero window probe enabled?
*/
if (conn->zero_probe)
{
if (conn->timer > hsec)
{
/* Will not yet decrement to zero */
conn->timer -= hsec;
}
else
{
/* Yes.. Has the retry count expired? */
if (conn->nrtx >= TCP_MAXRTX)
{
/* Yes... stop the network monitor, closing the
* connection and all sockets associated with the
* connection.
*/
conn->zero_probe = false;
tcp_stop_monitor(conn, TCP_ABORT);
}
else
{
tcp_xmit_probe(dev, conn);
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* Increment the un-ACKed sequence number */
conn->sndseq_max++;
#endif
/* Update for the next probe */
conn->nrtx++;
conn->timer = MIN((TCP_RTO_MIN << conn->nrtx),
TCP_RTO_MAX);
}
goto done;
}
}
#ifdef CONFIG_NET_TCP_DELAYED_ACK
/* Handle delayed acknowledgments. Is there a segment with a
* delayed acknowledgment?