From 2ce31c442f2fa1309235026ead3d8fdbafb72de7 Mon Sep 17 00:00:00 2001 From: wangyingdong Date: Tue, 20 Jun 2023 19:43:37 +0800 Subject: [PATCH] net/tcp:Added tcp zero window probe timer support https://www.rfc-editor.org/rfc/rfc1122#page-92 Signed-off-by: wangyingdong --- net/tcp/tcp.h | 27 ++++++- net/tcp/tcp_input.c | 36 +++++++++ net/tcp/tcp_send_buffered.c | 4 + net/tcp/tcp_timer.c | 147 ++++++++++++++++++++++++++++++------ 4 files changed, 192 insertions(+), 22 deletions(-) diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index 2907f63bd3..7398084f06 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -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 */ diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c index dd34cd369c..dcc16d9b07 100644 --- a/net/tcp/tcp_input.c +++ b/net/tcp/tcp_input.c @@ -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 && diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c index f911831a28..4b6f4ba53b 100644 --- a/net/tcp/tcp_send_buffered.c +++ b/net/tcp/tcp_send_buffered.c @@ -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 */ diff --git a/net/tcp/tcp_timer.c b/net/tcp/tcp_timer.c index 591c884e12..c0b774c043 100644 --- a/net/tcp/tcp_timer.c +++ b/net/tcp/tcp_timer.c @@ -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?