summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Ross <andrew.j.ross@intel.com>2017-01-27 10:05:46 -0800
committerJukka Rissanen <jukka.rissanen@linux.intel.com>2017-01-31 13:45:52 +0200
commitde0f6256fae9be1bf7e4286a0a7de9bedaa57348 (patch)
treee703f545a1a40cb84663d76066c9d0a86a01a985
parent3c3e5b1544caa03cf7027559214d272848169d72 (diff)
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>
-rw-r--r--subsys/net/ip/Kconfig12
-rw-r--r--subsys/net/ip/tcp.c17
2 files changed, 28 insertions, 1 deletions
diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig
index 8a5dfa24b..7fba56354 100644
--- a/subsys/net/ip/Kconfig
+++ b/subsys/net/ip/Kconfig
@@ -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
diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c
index cec7f702f..2909e89f5 100644
--- a/subsys/net/ip/tcp.c
+++ b/subsys/net/ip/tcp.c
@@ -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;