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:
parent
d07391d386
commit
89f57c225a
@ -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)"
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user