net/can,icmp,icmpv6,tcp,tcp_timer,udp: device should poll only those connections that are bound to the device.

tcp_timer: eliminated false decrements of conn->timer in case of multiple network adapters.
The false timer decrements sometimes provoked TCP spurious retransmissions due to premature timeouts.
This commit is contained in:
Alexander Lunev
2021-10-02 03:47:08 +03:00
committed by Xiang Xiao
parent 1e607a70bc
commit 1e25602678
7 changed files with 254 additions and 229 deletions
+19 -18
View File
@@ -26,6 +26,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_CAN)
#include <assert.h>
#include <debug.h>
#include <nuttx/net/netconfig.h>
@@ -53,31 +54,31 @@
*
* Assumptions:
* The network is locked.
* dev is not NULL.
* conn is not NULL.
* The connection (conn) is bound to the polling device (dev).
*
****************************************************************************/
void can_poll(FAR struct net_driver_s *dev, FAR struct can_conn_s *conn)
{
/* Verify that the packet connection is valid */
DEBUGASSERT(dev != NULL && conn != NULL && dev == conn->dev);
if (conn != NULL)
/* Setup for the application callback */
dev->d_appdata = &dev->d_buf[NET_LL_HDRLEN(dev)];
dev->d_len = 0;
dev->d_sndlen = 0;
/* Perform the application callback */
can_callback(dev, conn, CAN_POLL);
/* Check if the application has data to send */
if (dev->d_sndlen > 0)
{
/* Setup for the application callback */
dev->d_appdata = &dev->d_buf[NET_LL_HDRLEN(dev)];
dev->d_len = 0;
dev->d_sndlen = 0;
/* Perform the application callback */
can_callback(dev, conn, CAN_POLL);
/* Check if the application has data to send */
if (dev->d_sndlen > 0)
{
return;
}
return;
}
/* Make sure that d_len is zero meaning that there is nothing to be sent */
+65 -36
View File
@@ -248,10 +248,12 @@ static int devif_poll_can_connections(FAR struct net_driver_s *dev,
while (!bstop && (can_conn = can_nextconn(can_conn)))
{
/* Perform the packet TX poll */
/* Skip connections that are bound to other polling devices */
if (dev == can_conn->dev)
{
/* Perform the packet TX poll */
can_poll(dev, can_conn);
/* Call back into the driver */
@@ -361,17 +363,22 @@ static inline int devif_poll_icmp(FAR struct net_driver_s *dev,
while (!bstop && (conn = icmp_nextconn(conn)) != NULL)
{
/* Perform the ICMP poll */
/* Skip ICMP connections that are bound to other polling devices */
icmp_poll(dev, conn);
if (dev == conn->dev)
{
/* Perform the ICMP poll */
/* Perform any necessary conversions on outgoing packets */
icmp_poll(dev, conn);
devif_packet_conversion(dev, DEVIF_ICMP);
/* Perform any necessary conversions on outgoing packets */
/* Call back into the driver */
devif_packet_conversion(dev, DEVIF_ICMP);
bstop = callback(dev);
/* Call back into the driver */
bstop = callback(dev);
}
}
return bstop;
@@ -393,27 +400,32 @@ static inline int devif_poll_icmpv6(FAR struct net_driver_s *dev,
FAR struct icmpv6_conn_s *conn = NULL;
int bstop = 0;
/* Traverse all of the allocated ICMPV6 connections and perform the poll
/* Traverse all of the allocated ICMPv6 connections and perform the poll
* action.
*/
do
{
/* Perform the ICMPV6 poll
* Note: conn equal NULL in the first iteration means poll dev's
* callback list since icmpv6_autoconfig and icmpv6_neighbor still
* append it's callback into this list.
*/
/* Skip ICMPv6 connections that are bound to other polling devices */
icmpv6_poll(dev, conn);
if (conn == NULL || dev == conn->dev)
{
/* Perform the ICMPV6 poll
* Note: conn equal NULL in the first iteration means poll dev's
* callback list since icmpv6_autoconfig and icmpv6_neighbor still
* append it's callback into this list.
*/
/* Perform any necessary conversions on outgoing packets */
icmpv6_poll(dev, conn);
devif_packet_conversion(dev, DEVIF_ICMP6);
/* Perform any necessary conversions on outgoing packets */
/* Call back into the driver */
devif_packet_conversion(dev, DEVIF_ICMP6);
bstop = callback(dev);
/* Call back into the driver */
bstop = callback(dev);
}
}
while (!bstop && (conn = icmpv6_nextconn(conn)) != NULL);
@@ -533,17 +545,24 @@ static int devif_poll_udp_connections(FAR struct net_driver_s *dev,
while (!bstop && (conn = udp_nextconn(conn)))
{
/* Perform the UDP TX poll */
#ifdef CONFIG_NET_UDP_WRITE_BUFFERS
/* Skip UDP connections that are bound to other polling devices */
udp_poll(dev, conn);
if (dev == conn->dev)
#endif
{
/* Perform the UDP TX poll */
/* Perform any necessary conversions on outgoing packets */
udp_poll(dev, conn);
devif_packet_conversion(dev, DEVIF_UDP);
/* Perform any necessary conversions on outgoing packets */
/* Call back into the driver */
devif_packet_conversion(dev, DEVIF_UDP);
bstop = callback(dev);
/* Call back into the driver */
bstop = callback(dev);
}
}
return bstop;
@@ -573,17 +592,22 @@ static inline int devif_poll_tcp_connections(FAR struct net_driver_s *dev,
while (!bstop && (conn = tcp_nextconn(conn)))
{
/* Perform the TCP TX poll */
/* Skip TCP connections that are bound to other polling devices */
tcp_poll(dev, conn);
if (dev == conn->dev)
{
/* Perform the TCP TX poll */
/* Perform any necessary conversions on outgoing packets */
tcp_poll(dev, conn);
devif_packet_conversion(dev, DEVIF_TCP);
/* Perform any necessary conversions on outgoing packets */
/* Call back into the driver */
devif_packet_conversion(dev, DEVIF_TCP);
bstop = callback(dev);
/* Call back into the driver */
bstop = callback(dev);
}
}
return bstop;
@@ -619,17 +643,22 @@ static inline int devif_poll_tcp_timer(FAR struct net_driver_s *dev,
while (!bstop && (conn = tcp_nextconn(conn)))
{
/* Perform the TCP timer poll */
/* Skip TCP connections that are bound to other polling devices */
tcp_timer(dev, conn, hsec);
if (dev == conn->dev)
{
/* Perform the TCP timer poll */
/* Perform any necessary conversions on outgoing packets */
tcp_timer(dev, conn, hsec);
devif_packet_conversion(dev, DEVIF_TCP);
/* Perform any necessary conversions on outgoing packets */
/* Call back into the driver */
devif_packet_conversion(dev, DEVIF_TCP);
bstop = callback(dev);
/* Call back into the driver */
bstop = callback(dev);
}
}
return bstop;
+5
View File
@@ -54,11 +54,16 @@
*
* Assumptions:
* The network is locked.
* dev is not NULL.
* conn is not NULL.
* The connection (conn) is bound to the polling device (dev).
*
****************************************************************************/
void icmp_poll(FAR struct net_driver_s *dev, FAR struct icmp_conn_s *conn)
{
DEBUGASSERT(dev != NULL && conn != NULL && dev == conn->dev);
/* Setup for the application callback */
dev->d_appdata = &dev->d_buf[NET_LL_HDRLEN(dev) + IPICMP_HDRLEN];
+6
View File
@@ -53,12 +53,18 @@
*
* Assumptions:
* The network is locked.
* dev is not NULL.
* conn may be NULL.
* The connection (conn), if not NULL, is bound to
* the polling device (dev).
*
****************************************************************************/
void icmpv6_poll(FAR struct net_driver_s *dev,
FAR struct icmpv6_conn_s *conn)
{
DEBUGASSERT(dev != NULL && (conn == NULL || dev == conn->dev));
/* Setup for the application callback */
dev->d_appdata = &dev->d_buf[NET_LL_HDRLEN(dev) + IPICMPv6_HDRLEN];
+23 -27
View File
@@ -74,7 +74,10 @@
* None
*
* Assumptions:
* Called with the network locked.
* It is called with the network locked.
* dev is not NULL.
* conn is not NULL.
* The connection (conn) is bound to the polling device (dev).
*
****************************************************************************/
@@ -82,6 +85,8 @@ void tcp_poll(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn)
{
uint16_t result;
DEBUGASSERT(dev != NULL && conn != NULL && dev == conn->dev);
/* Discard any currently buffered data */
dev->d_len = 0;
@@ -91,43 +96,34 @@ void tcp_poll(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn)
if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED)
{
/* The TCP connection is established and, hence, should be bound
* to a device. Make sure that the polling device is the one that
* we are bound to.
/* Set up for the callback. We can't know in advance if the
* application is going to send a IPv4 or an IPv6 packet, so this
* setup may not actually be used.
*/
DEBUGASSERT(conn->dev != NULL);
if (dev == conn->dev)
{
/* Set up for the callback. We can't know in advance if the
* application is going to send a IPv4 or an IPv6 packet, so this
* setup may not actually be used.
*/
#if defined(CONFIG_NET_IPv6) && defined(CONFIG_NET_IPv4)
if (conn->domain == PF_INET)
{
tcp_ipv4_select(dev);
}
else
{
tcp_ipv6_select(dev);
}
if (conn->domain == PF_INET)
{
tcp_ipv4_select(dev);
}
else
{
tcp_ipv6_select(dev);
}
#elif defined(CONFIG_NET_IPv4)
tcp_ipv4_select(dev);
tcp_ipv4_select(dev);
#else /* if defined(CONFIG_NET_IPv6) */
tcp_ipv6_select(dev);
tcp_ipv6_select(dev);
#endif
/* Perform the callback */
/* Perform the callback */
result = tcp_callback(dev, conn, TCP_POLL);
result = tcp_callback(dev, conn, TCP_POLL);
/* Handle the callback response */
/* Handle the callback response */
tcp_appsend(dev, conn, result);
}
tcp_appsend(dev, conn, result);
}
}
+125 -148
View File
@@ -94,6 +94,9 @@
*
* Assumptions:
* The network is locked.
* dev is not NULL.
* conn is not NULL.
* The connection (conn) is bound to the polling device (dev).
*
****************************************************************************/
@@ -103,6 +106,14 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
uint16_t result;
uint8_t hdrlen;
/* NOTE: It is important to decrease conn->timer at "hsec" pace,
* not faster. Excessive (false) decrements of conn->timer are not allowed
* here. Otherwise, it breaks TCP timings and leads to TCP spurious
* retransmissions and other issues due to premature timeouts.
*/
DEBUGASSERT(dev != NULL && conn != NULL && dev == conn->dev);
/* Set up for the callback. We can't know in advance if the application
* is going to send a IPv4 or an IPv6 packet, so this setup may not
* actually be used. Furthermore, the TCP logic is required to call
@@ -140,6 +151,13 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
dev->d_len = 0;
dev->d_sndlen = 0;
if (conn->tcpstateflags == TCP_CLOSED)
{
/* Nothing to be done */
return;
}
/* Check if the connection is in a state in which we simply wait
* for the connection to time out. If so, we increase the
* connection's timer and remove the connection if it times
@@ -162,30 +180,13 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
/* Set the timer to the maximum value */
conn->timer = TCP_TIME_WAIT_TIMEOUT * HSEC_PER_SEC;
conn->tcpstateflags = TCP_CLOSED;
/* The TCP connection was established and, hence, should be bound
* to a device. Make sure that the polling device is the one that
* we are bound to.
*
* If not, then we will catch the timeout on the next poll from
* the correct device.
*/
/* Notify upper layers about the timeout */
DEBUGASSERT(conn->dev != NULL);
if (dev != conn->dev)
{
ninfo("TCP: TCP_CLOSED pending\n");
}
else
{
conn->tcpstateflags = TCP_CLOSED;
tcp_callback(dev, conn, TCP_TIMEDOUT);
/* Notify upper layers about the timeout */
tcp_callback(dev, conn, TCP_TIMEDOUT);
ninfo("TCP state: TCP_CLOSED\n");
}
ninfo("TCP state: TCP_CLOSED\n");
}
else
{
@@ -217,21 +218,6 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
conn->timer = 0;
/* The TCP is connected and, hence, should be bound to a
* device. Make sure that the polling device is the one that
* we are bound to.
*
* If not, then we will catch the timeout on the next poll
* from the correct device.
*/
DEBUGASSERT(conn->dev != NULL);
if (dev != conn->dev)
{
ninfo("TCP: TCP_CLOSED pending\n");
goto done;
}
/* Check for a timeout on connection in the TCP_SYN_RCVD state.
* On such timeouts, we would normally resend the SYNACK until
* the ACK is received, completing the 3-way handshake. But if
@@ -370,157 +356,148 @@ void tcp_timer(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
else if ((conn->tcpstateflags & TCP_STATE_MASK) == TCP_ESTABLISHED)
{
/* The TCP connection is established and, hence, should be bound
* to a device. Make sure that the polling device is the one that
* we are bound to.
*/
DEBUGASSERT(conn->dev != NULL);
if (dev == conn->dev)
{
#ifdef CONFIG_NET_TCP_KEEPALIVE
/* Is this an established connected with KeepAlive enabled? */
/* Is this an established connected with KeepAlive enabled? */
if (conn->keepalive)
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
* remote.
*
* On subsequent retries, keepstart is the time that the
* last probe was sent.
*/
if (conn->keepretries > 0)
{
socktimeo_t timeo;
uint32_t saveseq;
timeo = (socktimeo_t)conn->keepintvl;
}
else
{
timeo = (socktimeo_t)conn->keepidle;
}
/* If this is the first probe, then the keepstart time is
* the time that the last ACK or data was received from the
* remote.
*
* On subsequent retries, keepstart is the time that the
* last probe was sent.
*/
/* Yes... has the idle period elapsed with no data or ACK
* received from the remote peer?
*/
if (conn->keepretries > 0)
if (net_timeo(conn->keeptime, timeo))
{
/* Yes.. Has the retry count expired? */
if (conn->keepretries >= conn->keepcnt)
{
timeo = (socktimeo_t)conn->keepintvl;
/* Yes... stop the network monitor, closing the
* connection and all sockets associated with the
* connection.
*/
tcp_stop_monitor(conn, TCP_ABORT);
}
else
{
timeo = (socktimeo_t)conn->keepidle;
}
unsigned int tcpiplen;
/* Yes... has the idle period elapsed with no data or ACK
* received from the remote peer?
*/
/* No.. we need to send another probe.
* Get the size of the IP and TCP header.
*/
if (net_timeo(conn->keeptime, timeo))
{
/* Yes.. Has the retry count expired? */
if (conn->keepretries >= conn->keepcnt)
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET)
#endif
{
/* Yes... stop the network monitor, closing the
* connection and all sockets associated with the
* connection.
*/
tcp_stop_monitor(conn, TCP_ABORT);
tcpiplen = IPv4_HDRLEN + TCP_HDRLEN;
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
unsigned int tcpiplen;
/* No.. we need to send another probe.
* Get the size of the IP and TCP header.
*/
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET)
#endif
{
tcpiplen = IPv4_HDRLEN + TCP_HDRLEN;
}
#endif
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
tcpiplen = IPv6_HDRLEN + TCP_HDRLEN;
}
tcpiplen = IPv6_HDRLEN + TCP_HDRLEN;
}
#endif
/* 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:
*/
/* 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);
saveseq = tcp_getsequence(conn->sndseq);
tcp_setsequence(conn->sndseq, saveseq - 1);
tcp_send(dev, conn, TCP_ACK, tcpiplen);
tcp_send(dev, conn, TCP_ACK, tcpiplen);
tcp_setsequence(conn->sndseq, saveseq);
tcp_setsequence(conn->sndseq, saveseq);
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
/* Increment the un-ACKed sequence number */
/* Increment the un-ACKed sequence number */
conn->sndseq_max++;
conn->sndseq_max++;
#endif
/* Update for the next probe */
/* Update for the next probe */
conn->keeptime = clock_systime_ticks();
conn->keepretries++;
}
goto done;
conn->keeptime = clock_systime_ticks();
conn->keepretries++;
}
goto done;
}
}
#endif
#ifdef CONFIG_NET_TCP_DELAYED_ACK
/* Handle delayed acknowledgments. Is there a segment with a
* delayed acknowledgment?
/* Handle delayed acknowledgments. Is there a segment with a
* delayed acknowledgment?
*/
if (conn->rx_unackseg > 0)
{
/* Increment the ACK delay. */
conn->rx_acktimer += hsec;
/* Per RFC 1122: "...an ACK should not be excessively
* delayed; in particular, the delay must be less than
* 0.5 seconds..."
*/
if (conn->rx_unackseg > 0)
if (conn->rx_acktimer >= ACK_DELAY)
{
/* Increment the ACK delay. */
conn->rx_acktimer += hsec;
/* Per RFC 1122: "...an ACK should not be excessively
* delayed; in particular, the delay must be less than
* 0.5 seconds..."
/* Reset the delayed ACK state and send the ACK
* packet.
*/
if (conn->rx_acktimer >= ACK_DELAY)
{
/* Reset the delayed ACK state and send the ACK
* packet.
*/
conn->rx_unackseg = 0;
conn->rx_acktimer = 0;
tcp_synack(dev, conn, TCP_ACK);
goto done;
}
conn->rx_unackseg = 0;
conn->rx_acktimer = 0;
tcp_synack(dev, conn, TCP_ACK);
goto done;
}
}
#endif
/* There was no need for a retransmission and there was no
* need to probe the remote peer and there was no need to
* send a delayed ACK. We poll the application for new
* outgoing data.
*/
/* There was no need for a retransmission and there was no
* need to probe the remote peer and there was no need to
* send a delayed ACK. We poll the application for new
* outgoing data.
*/
result = tcp_callback(dev, conn, TCP_POLL);
tcp_appsend(dev, conn, result);
goto done;
}
result = tcp_callback(dev, conn, TCP_POLL);
tcp_appsend(dev, conn, result);
goto done;
}
}
+11
View File
@@ -45,6 +45,7 @@
#include <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_UDP)
#include <assert.h>
#include <debug.h>
#include <nuttx/net/netconfig.h>
@@ -73,11 +74,21 @@
*
* Assumptions:
* The network is locked.
* dev is not NULL.
* conn is not NULL.
* The connection (conn) is bound to the polling device (dev) in case of
* enabled CONFIG_NET_UDP_WRITE_BUFFERS option.
*
****************************************************************************/
void udp_poll(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn)
{
DEBUGASSERT(dev != NULL && conn != NULL);
#ifdef CONFIG_NET_UDP_WRITE_BUFFERS
DEBUGASSERT(dev == conn->dev);
#endif
/* Verify that the UDP connection is valid */
if (conn->lport != 0)