net: tcp: Add optional TIME_WAIT support

The RFC requires we honor the 2MSL TIME_WAIT timeout, support for
which was just removed with the FIN cleanup.  Add it back, but make it
optional (proper sequence number and ephemeral port randomization
makes true collisions a birthday problem in a ~80 bit space!).

Change-Id: I176c6250f43bba0c914da1ee7f0136dcb1008046
Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
Andy Ross 2017-01-27 10:05:46 -08:00 committed by Jukka Rissanen
commit 3efe6b7ede
2 changed files with 28 additions and 1 deletions

View file

@ -99,6 +99,18 @@ config NET_DEBUG_TCP
help
Enables TCP handler output debug messages
config NET_TCP_TIME_WAIT
bool "Enable TCP TIME_WAIT timeouts"
default n
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_UDP
bool "Enable UDP"
default y

View file

@ -44,6 +44,9 @@ static struct net_tcp tcp_context[NET_MAX_TCP_CONTEXT];
#define INIT_RETRY_MS 200
/* 2MSL timeout, where "MSL" is arbitrarily 2 minutes in the RFC */
#define TIME_WAIT_MS (2 * 2 * 60 * 1000)
struct tcp_segment {
uint32_t seq;
uint32_t ack;
@ -120,6 +123,10 @@ static void tcp_retry_expired(struct k_timer *timer)
buf = CONTAINER_OF(sys_slist_peek_head(&tcp->sent_list),
struct net_buf, sent_list);
net_tcp_send_buf(net_buf_ref(buf));
} else if (IS_ENABLED(CONFIG_NET_TCP_TIME_WAIT)) {
if (tcp->fin_sent && tcp->fin_rcvd) {
net_context_unref(tcp->context);
}
}
}
@ -637,10 +644,18 @@ int net_tcp_send_buf(struct net_buf *buf)
static void restart_timer(struct net_tcp *tcp)
{
if (sys_slist_is_empty(&tcp->sent_list)) {
if (!sys_slist_is_empty(&tcp->sent_list)) {
tcp->flags |= NET_TCP_RETRYING;
tcp->retry_timeout_shift = 0;
k_timer_start(&tcp->retry_timer, retry_timeout(tcp), 0);
} else if (IS_ENABLED(CONFIG_NET_TCP_TIME_WAIT)) {
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_timer_start(&tcp->retry_timer, TIME_WAIT_MS, 0);
net_context_ref(tcp->context);
}
} else {
k_timer_stop(&tcp->retry_timer);
tcp->flags &= ~NET_TCP_RETRYING;