net: tcp: Define single config option for TIME_WAIT delay

Previously, there was a boolean CONFIG_NET_TCP_TIME_WAIT setting
("master switch") and numeric CONFIG_NET_TCP_2MSL_TIME setting,
both named not ideally (there were both NET_TCP_TIME_WAIT and
CONFIG_NET_TCP_TIME_WAIT symbols in the source, with very different
meaning; "2MSL_TIME" was also a roundabout way to refer to
TIME_WAIT state time). In addition to that, some code was defining
adhoc, hardcoded duplicates for these settings.

CONFIG_NET_TCP_2MSL_TIME was also measured in seconds, giving
poor precision control for this resource-tying setting.

Instead, replace them all with the single
CONFIG_NET_TCP_TIME_WAIT_DELAY setting, measured in milliseconds.
The value of 0 means that TIME_WAIT state is skipped.

Fixes: #7459

Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
This commit is contained in:
Paul Sokolovsky 2018-05-11 14:17:48 +03:00 committed by Jukka Rissanen
parent d07391d386
commit 89f57c225a
2 changed files with 23 additions and 32 deletions

View File

@ -170,25 +170,25 @@ config NET_TCP_BACKLOG_SIZE
The number of simultaneous TCP connection attempts, i.e. outstanding
TCP connections waiting for initial ACK.
config NET_TCP_TIME_WAIT
bool "Enable TCP TIME_WAIT timeouts"
config NET_TCP_TIME_WAIT_DELAY
int "How long to wait in TIME_WAIT state (in milliseconds)"
depends on NET_TCP
default n
default 250
help
Officially, the TCP standard requires a 4 minute timeout on
connection close before that particular port pair can be used
again. This requires that the net_context and net_tcp structs
persist for the full duration, so has non-trivial memory costs
and is optional. Modern systems with well-randomized sequence
numbers don't need this, but it is present for specification
compliance where needed.
config NET_TCP_2MSL_TIME
int "How long to wait in TIME_WAIT (in seconds)"
depends on NET_TCP_TIME_WAIT
default 240
help
The value is in seconds.
To avoid a (low-probability) issue when delayed packets from
previous connection get delivered to next connection reusing
the same local/remote ports, RFC 793 (TCP) suggests to keep
an old, closed connection in a special "TIME_WAIT" state for
the duration of 2*MSL (Maximum Segment Lifetime). The RFC
suggests to use MSL of 2 minutes, but notes "This is an
engineering choice, and may be changed if experience indicates
it is desirable to do so." For low-resource systems, having
large MSL may lead to quick resource exhaustion (and related
DoS attacks). At the same time, the issue of packet mis-delivery
is largely alleviated in the modern TCP stacks by using random,
non-repeating port numbers and initial sequence numbers. Due
to this, Zephyr uses much lower value of 250ms by default.
Value of 0 disables TIME_WAIT state completely.
config NET_TCP_ACK_TIMEOUT
int "How long to wait for ACK (in milliseconds)"

View File

@ -55,22 +55,12 @@ static struct tcp_backlog_entry {
struct k_delayed_work ack_timer;
} tcp_backlog[CONFIG_NET_TCP_BACKLOG_SIZE];
/* 2MSL timeout, where "MSL" is arbitrarily 2 minutes in the RFC */
#if defined(CONFIG_NET_TCP_2MSL_TIME)
#define TIME_WAIT_MS K_SECONDS(CONFIG_NET_TCP_2MSL_TIME)
#else
#define TIME_WAIT_MS K_SECONDS(2 * 2 * 60)
#endif
#if defined(CONFIG_NET_TCP_ACK_TIMEOUT)
#define ACK_TIMEOUT CONFIG_NET_TCP_ACK_TIMEOUT
#else
#define ACK_TIMEOUT K_SECONDS(1)
#endif
/* TODO: It should be 2 * MSL (Maximum segment lifetime) */
#define TIMEWAIT_TIMEOUT MSEC(250)
#define FIN_TIMEOUT K_SECONDS(1)
/* Declares a wrapper function for a net_conn callback that refs the
@ -251,7 +241,7 @@ static void tcp_retry_expired(struct k_work *work)
net_pkt_iface(pkt));
}
}
} else if (IS_ENABLED(CONFIG_NET_TCP_TIME_WAIT)) {
} else if (CONFIG_NET_TCP_TIME_WAIT_DELAY != 0) {
if (tcp->fin_sent && tcp->fin_rcvd) {
NET_DBG("[%p] Closing connection (context %p)",
tcp, tcp->context);
@ -948,12 +938,13 @@ static void restart_timer(struct net_tcp *tcp)
tcp->flags |= NET_TCP_RETRYING;
tcp->retry_timeout_shift = 0;
k_delayed_work_submit(&tcp->retry_timer, retry_timeout(tcp));
} else if (IS_ENABLED(CONFIG_NET_TCP_TIME_WAIT)) {
} else if (CONFIG_NET_TCP_TIME_WAIT_DELAY != 0) {
if (tcp->fin_sent && tcp->fin_rcvd) {
/* We know sent_list is empty, which means if
* fin_sent is true it must have been ACKd
*/
k_delayed_work_submit(&tcp->retry_timer, TIME_WAIT_MS);
k_delayed_work_submit(&tcp->retry_timer,
CONFIG_NET_TCP_TIME_WAIT_DELAY);
net_context_ref(tcp->context);
}
} else {
@ -1731,7 +1722,7 @@ static void handle_timewait_timeout(struct k_work *work)
struct net_tcp *tcp = CONTAINER_OF(work, struct net_tcp,
timewait_timer);
NET_DBG("Timewait expired in %dms", TIMEWAIT_TIMEOUT);
NET_DBG("Timewait expired in %dms", CONFIG_NET_TCP_TIME_WAIT_DELAY);
if (net_tcp_get_state(tcp) == NET_TCP_TIME_WAIT) {
net_tcp_change_state(tcp, NET_TCP_CLOSED);
@ -2080,7 +2071,7 @@ NET_CONN_CB(tcp_established)
clean_up:
if (net_tcp_get_state(context->tcp) == NET_TCP_TIME_WAIT) {
k_delayed_work_submit(&context->tcp->timewait_timer,
TIMEWAIT_TIMEOUT);
CONFIG_NET_TCP_TIME_WAIT_DELAY);
}
if (net_tcp_get_state(context->tcp) == NET_TCP_CLOSED) {