From 5dc3f8dc3d7503fad9cfe866c6bc97d4a63c2d2d Mon Sep 17 00:00:00 2001
From: Simon Brummer <simon.brummer@posteo.de>
Date: Mon, 4 Feb 2019 20:57:30 +0100
Subject: [PATCH] gnrc_tcp: syn_rcvd pkt loss fix

---
 sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c   | 29 +++++++++++++++----
 .../gnrc/transport_layer/tcp/gnrc_tcp_fsm.c   |  1 +
 2 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c
index 15a2401250..e972744bc5 100644
--- a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c
+++ b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp.c
@@ -203,14 +203,33 @@ static int _gnrc_tcp_open(gnrc_tcp_tcb_t *tcb, char *target_addr, uint16_t targe
            tcb->state != FSM_STATE_CLOSE_WAIT) {
         mbox_get(&(tcb->mbox), &msg);
         switch (msg.type) {
+            case MSG_TYPE_NOTIFY_USER:
+                DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : MSG_TYPE_NOTIFY_USER\n");
+
+                /* Setup a timeout to be able to revert back to LISTEN state, in case the
+                 * send SYN+ACK we received upon entering SYN_RCVD is never acknowledged
+                 * by the peer. */
+                if ((tcb->state == FSM_STATE_SYN_RCVD) && (tcb->status & STATUS_PASSIVE)) {
+                    _setup_timeout(&connection_timeout, GNRC_TCP_CONNECTION_TIMEOUT_DURATION,
+                                   _cb_mbox_put_msg, &connection_timeout_arg);
+                }
+                break;
+
             case MSG_TYPE_CONNECTION_TIMEOUT:
                 DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : CONNECTION_TIMEOUT\n");
-                _fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
-                ret = -ETIMEDOUT;
-                break;
 
-            case MSG_TYPE_NOTIFY_USER:
-                DEBUG("gnrc_tcp.c : _gnrc_tcp_open() : MSG_TYPE_NOTIFY_USER\n");
+                /* The connection establishment attempt timed out:
+                 * 1) Active connections return -ETIMEOUT.
+                 * 2) Passive connections stop the ongoing retransmissions and repeat the
+                 *    open call to wait for the next connection attempt. */
+                if (tcb->status & STATUS_PASSIVE) {
+                    _fsm(tcb, FSM_EVENT_CLEAR_RETRANSMIT, NULL, NULL, 0);
+                    _fsm(tcb, FSM_EVENT_CALL_OPEN, NULL, NULL, 0);
+                }
+                else {
+                    _fsm(tcb, FSM_EVENT_TIMEOUT_CONNECTION, NULL, NULL, 0);
+                    ret = -ETIMEDOUT;
+                }
                 break;
 
             default:
diff --git a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_fsm.c b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_fsm.c
index 3a8ffe3e18..09cbe23d39 100644
--- a/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_fsm.c
+++ b/sys/net/gnrc/transport_layer/tcp/gnrc_tcp_fsm.c
@@ -191,6 +191,7 @@ static int _transition_to(gnrc_tcp_tcb_t *tcb, fsm_state_t state)
             mutex_unlock(&_list_tcb_lock);
             break;
 
+        case FSM_STATE_SYN_RCVD:
         case FSM_STATE_ESTABLISHED:
         case FSM_STATE_CLOSE_WAIT:
             tcb->status |= STATUS_NOTIFY_USER;
-- 
GitLab