Tunnel TCP connection repair.

Try to repair the TCP connection by fetching TCP repair info metadata from
the application. If repairing fails, fallback to existing flow of
setting up the connection and the tunnel.
If repair was successful, suppress sending of TunnelOpen message and
mark the Tunnel as established.
Ensure, TCPEndPoint and WeaveConnection objects have correctly populated
state upon successful repair.
diff --git a/src/inet/InetConfig.h b/src/inet/InetConfig.h
index cf0d43a..00f62b6 100644
--- a/src/inet/InetConfig.h
+++ b/src/inet/InetConfig.h
@@ -726,6 +726,24 @@
 #ifndef INET_CONFIG_IP_MULTICAST_HOP_LIMIT
 #define INET_CONFIG_IP_MULTICAST_HOP_LIMIT                 (64)
 #endif // INET_CONFIG_IP_MULTICAST_HOP_LIMIT
+
+/**
+ * @def INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+ *
+ * @brief
+ *   When this flag is enabled, the TCPEndPoint can attempt to
+ *   restore the TCP connection using supplied information from
+ *   a previously active connection.
+ *
+ * @note
+ *   The TCP connection restoration can only be done
+ *   from the client side. Upon failure to restore, the client
+ *   would fall back to establishing a negotiated TCP connection.
+ *
+ */
+#ifndef INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+#define INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED              (0)
+#endif // INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED
 // clang-format on
 
 #endif /* INETCONFIG_H */
diff --git a/src/inet/TCPEndPoint.cpp b/src/inet/TCPEndPoint.cpp
index 21f498d..24eea8d 100644
--- a/src/inet/TCPEndPoint.cpp
+++ b/src/inet/TCPEndPoint.cpp
@@ -296,6 +296,253 @@
     return res;
 }
 
+#if INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
+bool TCPConnRepairInfo::IsValid (void) const
+{
+    if (!srcPort || !dstPort || !txSeq || !rxSeq || !sndWl1 || !sndWnd ||
+        !maxWindow || !rcvWnd || !rcvWup)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+void TCPConnRepairInfo::Dump (void) const
+{
+    char srcIPStr[64] = { 0 }, dstIPStr[64] = { 0 };
+
+    srcIP.ToString(srcIPStr, sizeof(srcIPStr));
+    dstIP.ToString(dstIPStr, sizeof(dstIPStr));
+
+    WeaveLogDetail(Inet, "TCP repair src: %s:%d seq %u\n", srcIPStr, srcPort, txSeq);
+    WeaveLogDetail(Inet, "TCP repair dst: %s:%d seq %u\n", dstIPStr, dstPort, rxSeq);
+
+    WeaveLogDetail(Inet, "TCP repair opt: snd_wl1 %u snd_wnd %u max_window %u rcv_wnd %u rcv_wup %u\n",
+                   sndWl1, sndWnd, maxWindow, rcvWnd, rcvWup);
+
+    WeaveLogDetail(Inet, "TCP repair opt: mss %u snd_wscale %u rcv_wscale %u tcpi_opt %u\n",
+                   mss, sndWscale, rcvWscale, tcpOptions);
+
+    WeaveLogDetail(Inet, "local_port(%u), server_port(%u), tx_seq(%u), rx_seq(%u), snd_wl1(%u), snd_wnd(%u), max_window(%u), rcv_wnd(%u), rcv_wup(%u)\n",
+                   srcPort, dstPort, txSeq, rxSeq, sndWl1, sndWnd,
+                   maxWindow, rcvWnd, rcvWup);
+}
+
+INET_ERROR TCPEndPoint::RepairConnection(const TCPConnRepairInfo &connRepairInfo, InterfaceId intf)
+{
+    INET_ERROR res = INET_NO_ERROR;
+
+#if WEAVE_SYSTEM_CONFIG_USE_LWIP
+    res = INET_ERROR_NOT_SUPPORTED;
+    ExitNow();
+#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
+
+#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
+
+    struct tcp_repair_opt opts[4];
+    int opCtr = 0;
+    uint32_t val;
+
+    if (!connRepairInfo.IsValid())
+    {
+        WeaveLogError(Inet, "Not enough info to repair TCP connection\n");
+        ExitNow(res = INET_ERROR_BAD_ARGS);
+    }
+
+    // Dump the contents of the TCP Connection Repair Info
+    connRepairInfo.Dump();
+
+    res = GetSocket(connRepairInfo.addrType);
+    if (res != INET_NO_ERROR)
+        ExitNow();
+
+    val = 1;
+    if (setsockopt(mSocket, SOL_TCP, TCP_REPAIR, &val, sizeof(val)) != 0)
+    {
+        WeaveLogError(Inet, "TCP_REPAIR failed: %d", errno);
+        ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+    }
+
+    val = 1;
+    if (setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0)
+    {
+        WeaveLogError(Inet, "SO_REUSEADDR failed: %d", errno);
+        ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+    }
+
+    /* ============= Restore TCP properties ==================*/
+
+    val = TCP_SEND_QUEUE;
+    if (setsockopt(mSocket, SOL_TCP, TCP_REPAIR_QUEUE, &val, sizeof(val)) != 0)
+    {
+        WeaveLogError(Inet, "Repairing TCP_SEND_QUEUE failed: %d", errno);
+        ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+    }
+
+    val = connRepairInfo.txSeq;
+    WeaveLogDetail(Inet, "Restoring Tx seq: %u", val);
+    if (setsockopt(mSocket, SOL_TCP, TCP_QUEUE_SEQ, &val, sizeof(val)) != 0)
+    {
+        WeaveLogError(Inet, "Tx Seq failed: %d", errno);
+        ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+    }
+
+    val = TCP_RECV_QUEUE;
+    if (setsockopt(mSocket, SOL_TCP, TCP_REPAIR_QUEUE, &val, sizeof(val)) != 0) {
+        WeaveLogError(Inet, "Repairing TCP_RECV_QUEUE failed: %d", errno);
+        ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+    }
+
+    val = connRepairInfo.rxSeq;
+    WeaveLogDetail(Inet, "Restoring Rx seq: %u", val);
+    if (setsockopt(mSocket, SOL_TCP, TCP_QUEUE_SEQ, &val, sizeof(val)) != 0) {
+        WeaveLogError(Inet, "Rx Seq failed: %d", errno);
+        ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+    }
+
+    if (connRepairInfo.addrType == kIPAddressType_IPv6)
+    {
+        struct sockaddr_in6 serverAddress, localAddress;
+        memset(&localAddress, 0, sizeof(localAddress));
+        localAddress.sin6_family = AF_INET6;
+        localAddress.sin6_addr = connRepairInfo.srcIP.ToIPv6();
+        localAddress.sin6_port = htons(connRepairInfo.srcPort);
+
+        memset(&serverAddress, 0, sizeof(serverAddress));
+        serverAddress.sin6_family = AF_INET6;
+        serverAddress.sin6_addr = connRepairInfo.dstIP.ToIPv6();
+        serverAddress.sin6_port = htons(connRepairInfo.dstPort);
+
+        if (bind(mSocket, (struct sockaddr *) &localAddress, sizeof(localAddress)) != 0)
+        {
+            WeaveLogError(Inet, "Bind src address failed: %d", errno);
+            ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+        }
+
+        if (connect(mSocket, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) != 0)
+        {
+            WeaveLogError(Inet, "Connect to dst address failed: %d", errno);
+            ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+        }
+    }
+#if INET_CONFIG_ENABLE_IPV4
+    else if (connRepairInfo.addrType == kIPAddressType_IPv4)
+    {
+        struct sockaddr_in serverAddress, localAddress;
+        memset(&localAddress, 0, sizeof(localAddress));
+        localAddress.sin_family = AF_INET;
+        localAddress.sin_addr = connRepairInfo.srcIP.ToIPv4();
+        localAddress.sin_port = htons(connRepairInfo.srcPort);
+
+        memset(&serverAddress, 0, sizeof(serverAddress));
+        serverAddress.sin_family = AF_INET;
+        serverAddress.sin_addr = connRepairInfo.dstIP.ToIPv4();
+        serverAddress.sin_port = htons(connRepairInfo.dstPort);
+
+        if (bind(mSocket, (struct sockaddr *) &localAddress, sizeof(localAddress)) != 0)
+        {
+            WeaveLogError(Inet, "Bind src address failed: %d", errno);
+            ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+        }
+
+        if (connect(mSocket, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) != 0)
+        {
+            WeaveLogError(Inet, "Connect to dst address failed: %d", errno);
+            ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+        }
+    }
+#endif // INET_CONFIG_ENABLE_IPV4
+    else
+    {
+        ExitNow(res = INET_ERROR_WRONG_ADDRESS_TYPE);
+    }
+
+    /* Repair tcp options */
+
+    if (connRepairInfo.tcpOptions & TCPI_OPT_SACK)
+    {
+        WeaveLogDetail(Inet, "Turning TCPI_OPT_SACK on\n");
+        opts[opCtr].opt_code = TCPOPT_SACK_PERMITTED;
+        opts[opCtr].opt_val = 0;
+        opCtr++;
+    }
+
+    if (connRepairInfo.tcpOptions & TCPI_OPT_WSCALE)
+    {
+        WeaveLogDetail(Inet, "Set Send Window Scale to %u\n", connRepairInfo.sndWscale);
+        WeaveLogDetail(Inet, "Set Receive Window Scale to %u\n", connRepairInfo.rcvWscale);
+        opts[opCtr].opt_code = TCPOPT_WINDOW;
+        opts[opCtr].opt_val = connRepairInfo.sndWscale + (connRepairInfo.rcvWscale << 16);
+        opCtr++;
+    }
+
+    if (connRepairInfo.tcpOptions & TCPI_OPT_TIMESTAMPS)
+    {
+        WeaveLogDetail(Inet, "Turning TCPI_OPT_TIMESTAMPS on\n");
+        opts[opCtr].opt_code = TCPOPT_TIMESTAMP;
+        opts[opCtr].opt_val = 0;
+        opCtr++;
+    }
+
+    WeaveLogDetail(Inet, "Set MSS clamp to %u\n", connRepairInfo.mss);
+    opts[opCtr].opt_code = TCPOPT_MAXSEG;
+    opts[opCtr].opt_val = connRepairInfo.mss;
+    opCtr++;
+
+    if (setsockopt(mSocket, SOL_TCP, TCP_REPAIR_OPTIONS, opts, opCtr * sizeof(struct tcp_repair_opt)) < 0)
+    {
+        WeaveLogError(Inet, "%s: %d: Can't repair tcp options", __func__, __LINE__);
+        ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+    }
+
+    if (connRepairInfo.tcpOptions & TCPI_OPT_TIMESTAMPS)
+    {
+        if (setsockopt(mSocket, SOL_TCP, TCP_TIMESTAMP, &connRepairInfo.tsVal, sizeof(connRepairInfo.tsVal)) < 0)
+        {
+            WeaveLogError(Inet, "%s: %d: Can't set timestamp", __func__, __LINE__);
+            ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+        }
+    }
+
+    if (connRepairInfo.maxWindow)
+    {
+        /* restore window */
+        struct tcp_repair_window windowOpt = {
+            .snd_wl1 = connRepairInfo.sndWl1,
+            .snd_wnd = connRepairInfo.sndWnd,
+            .max_window = connRepairInfo.maxWindow,
+            .rcv_wnd = connRepairInfo.rcvWnd,
+            .rcv_wup = connRepairInfo.rcvWup,
+        };
+
+        if (setsockopt(mSocket, SOL_TCP, TCP_REPAIR_WINDOW, &windowOpt, sizeof(windowOpt)) != 0)
+        {
+            WeaveLogError(Inet, "%s: %d: Unable to set window parameters", __func__, __LINE__);
+            ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+        }
+    }
+
+    val = 0;
+    if (setsockopt(mSocket, SOL_TCP, TCP_REPAIR, &val, sizeof(val)) != 0)
+    {
+        WeaveLogError(Inet, "%s: %d: TCP_REPAIR failed", __func__, __LINE__);
+        ExitNow(res = Weave::System::MapErrorPOSIX(errno));
+    }
+
+    mAddrType = connRepairInfo.addrType;
+
+    // Mark state as Connected
+    State = kState_Connected;
+#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
+
+exit:
+
+    return res;
+}
+#endif // INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
 INET_ERROR TCPEndPoint::Connect(IPAddress addr, uint16_t port, InterfaceId intf)
 {
     INET_ERROR res = INET_NO_ERROR;
diff --git a/src/inet/TCPEndPoint.h b/src/inet/TCPEndPoint.h
index bd570fe..a8d07ce 100644
--- a/src/inet/TCPEndPoint.h
+++ b/src/inet/TCPEndPoint.h
@@ -33,11 +33,51 @@
 
 #include <SystemLayer/SystemPacketBuffer.h>
 
+#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
+#include <netinet/tcp.h>
+#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
+
 namespace nl {
 namespace Inet {
 
 class InetLayer;
 
+#if INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
+#if (!defined(TCP_REPAIR) || !defined(TCP_REPAIR_QUEUE) || !defined(TCP_REPAIR_OPTIONS) || \
+     !defined(TCPI_OPT_SACK) || !defined(TCPI_OPT_WSCALE) || !defined(TCPI_OPT_TIMESTAMPS) || \
+     !defined(TCPOPT_MAXSEG) || !defined(TCP_REPAIR_WINDOW))
+#error "INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED set but platform does not support TCP REPAIR"
+#endif
+
+typedef struct TCPConnRepairInfo
+{
+    IPAddress srcIP;                     // Source IP address
+    IPAddress dstIP;                     // Destination IP address
+    IPAddressType addrType;              // Address family type
+    uint16_t srcPort;                    // Source port
+    uint16_t dstPort;                    // Destination port
+    uint32_t txSeq;                      // Transmit sequence
+    uint32_t rxSeq;                      // Receive sequence
+    uint32_t sndWl1;                     // Segment seq number for last window update
+    uint32_t sndWnd;                     // Send window
+    uint32_t maxWindow;                  // Max window
+    uint32_t rcvWnd;                     // Receive window
+    uint32_t rcvWup;                     // Last ack number that was sent/
+    uint32_t tsVal;                      // TCP Timestamp
+    uint32_t tsecr;
+    uint16_t mss;                        // Max segment size
+    uint8_t  sndWscale;                  // Send window scale
+    uint8_t  rcvWscale;                  // Receive window scale
+    uint8_t  tcpOptions;                 // TCP options
+
+    bool IsValid (void) const;
+
+    void Dump (void) const;
+
+} TCPConnRepairInfo;
+#endif // INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
 /**
  * @brief   Objects of this class represent TCP transport endpoints.
  *
@@ -397,6 +437,11 @@
      */
     uint16_t LogId(void);
 
+#if INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+    INET_ERROR RepairConnection(const TCPConnRepairInfo &connRepairInfo, InterfaceId intf);
+#endif // INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
+
     /**
      * @brief   Type of connection establishment event handling function.
      *
diff --git a/src/lib/core/WeaveConfig.h b/src/lib/core/WeaveConfig.h
index b3990a2..acf5067 100644
--- a/src/lib/core/WeaveConfig.h
+++ b/src/lib/core/WeaveConfig.h
@@ -2391,6 +2391,18 @@
 #endif // WEAVE_CONFIG_ENABLE_OFFLOAD_EVENTS_FIRST
 
 /**
+ * @def WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+ *
+ * @brief
+ *   When this option is enabled, Weave will try to restore the TCP connection
+ *   first before attempting to establish a new one.
+ *
+ */
+#ifndef WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+#define WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED            (INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED)
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
+/**
  * @def WEAVE_NON_PRODUCTION_MARKER
  *
  * @brief Defines the name of a mark symbol whose presence signals that the Weave code
diff --git a/src/lib/core/WeaveConnection.cpp b/src/lib/core/WeaveConnection.cpp
index 6a4cff4..8a7c962 100644
--- a/src/lib/core/WeaveConnection.cpp
+++ b/src/lib/core/WeaveConnection.cpp
@@ -38,8 +38,12 @@
 #include <Weave/Support/CodeUtils.h>
 
 #include <SystemLayer/SystemStats.h>
+#include <SystemLayer/SystemTimer.h>
 #include <InetLayer/InetLayer.h>
 
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+#define CONN_REPAIR_APP_CALLBACK_DELAY_MSECS                    (1)
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
 
 namespace nl {
 namespace Weave {
@@ -1243,6 +1247,84 @@
     }
 }
 
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+void WeaveConnection::AppCallbackAfterConnectionRepair(System::Layer* aSystemLayer, void* aAppState, System::Error aError)
+{
+    WeaveConnection* con = static_cast<WeaveConnection*>(aAppState);
+
+    con->mTcpEndPoint->OnConnectComplete(con->mTcpEndPoint, INET_NO_ERROR);
+}
+
+/**
+ *  Attempt to repair the TCP connection using a given TCP RepairInfo.
+ *
+ *  @param[in]    peerNodeId      NodeId of the peer with which to repair TCP
+ *                                connection.
+ *
+ *  @param[in]    repairInfo      A reference to the TCP Connection repair info.
+ *
+ *  @param[in]    intf            InterfaceId over which the connection is
+ *                                expected to be made.
+ *
+ *  @note
+ *   The TCP connection restoration can only be attempted from the client side.
+ *   Upon failure to restore, the client would fall back to establishing a
+ *   negotiated TCP connection.
+ *
+ */
+WEAVE_ERROR WeaveConnection::TryConnectionRepair(uint64_t peerNodeId, const TCPConnRepairInfo &repairInfo, InterfaceId intf)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    // Allocate a new TCP end point.
+    err = MessageLayer->Inet->NewTCPEndPoint(&mTcpEndPoint);
+    SuccessOrExit(err);
+
+    mTcpEndPoint->AppState = this;
+    mTcpEndPoint->OnConnectComplete = HandleConnectComplete;
+
+    mRefCount++;
+
+    State = kState_Connecting;
+
+    WeaveLogProgress(MessageLayer, "Trying to repair connection to node %" PRIx64 "", peerNodeId);
+
+    // Call into TCPEndPoint to repair the TCP connection with the fetched
+    // repair info.
+    err = mTcpEndPoint->RepairConnection(repairInfo, intf);
+    SuccessOrExit(err);
+
+    WeaveLogProgress(MessageLayer, "Connection successfully repaired");
+
+    // Set the connection variables
+    PeerAddr = repairInfo.dstIP;
+    PeerNodeId = peerNodeId;
+    PeerPort = repairInfo.dstPort;
+    IsRepaired = true;
+
+    // After repairing connection, set an immediate timer to call the OnConnectComplete callback.
+    // This is necessary to allow the call stack to unwind and also prevent the
+    // caller of this function to inadvertently change state variables after the
+    // application has been called back.
+    MessageLayer->SystemLayer->StartTimer(CONN_REPAIR_APP_CALLBACK_DELAY_MSECS, AppCallbackAfterConnectionRepair, this);
+
+exit:
+
+    if (err != WEAVE_NO_ERROR)
+    {
+        WeaveLogProgress(MessageLayer, "Failed to repair connection to node %" PRIx64 ", err = %ld" , peerNodeId, (long)err);
+        if (mTcpEndPoint != NULL)
+        {
+            mTcpEndPoint->Abort();
+            mTcpEndPoint->Free();
+            mTcpEndPoint = NULL;
+        }
+    }
+
+    return err;
+}
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
 WEAVE_ERROR WeaveConnection::StartConnect()
 {
     WEAVE_ERROR err;
@@ -1356,7 +1438,6 @@
             return;
         }
 
-
         // Negotiate secure session (or not) based on AuthMode.
         con->StartSession();
     }
@@ -1675,6 +1756,10 @@
 #if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
     mDNSOptions = 0;
 #endif
+
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+    IsRepaired = false;
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
     mFlags = 0;
 }
 
diff --git a/src/lib/core/WeaveError.cpp b/src/lib/core/WeaveError.cpp
index 875f723..0af4e14 100644
--- a/src/lib/core/WeaveError.cpp
+++ b/src/lib/core/WeaveError.cpp
@@ -233,6 +233,7 @@
     case WEAVE_ERROR_UNSUPPORTED_WIRELESS_REGULATORY_DOMAIN     : desc = "Unsupported wireless regulatory domain"; break;
     case WEAVE_ERROR_UNSUPPORTED_WIRELESS_OPERATING_LOCATION    : desc = "Unsupported wireless operating location"; break;
     case WEAVE_ERROR_WDM_EVENT_TOO_BIG                          : desc = "The WDM Event is too big to be successfully transmitted to a peer node"; break;
+    case WEAVE_ERROR_CONNECTION_REPAIR_FAILED                   : desc = "Failed to repair the TCP connection using application supplied repair info"; break;
     }
 #endif // !WEAVE_CONFIG_SHORT_ERROR_STR
 
diff --git a/src/lib/core/WeaveError.h b/src/lib/core/WeaveError.h
index 2cadb51..76084a9 100644
--- a/src/lib/core/WeaveError.h
+++ b/src/lib/core/WeaveError.h
@@ -1768,6 +1768,14 @@
  */
 #define WEAVE_ERROR_WDM_EVENT_TOO_BIG                            _WEAVE_ERROR(186)
 
+/**
+ *  @def WEAVE_ERROR_CONNECTION_REPAIR_FAILED
+ *
+ *  @brief
+ *    The attempt to repair the TCP connection failed.
+ *
+ */
+#define WEAVE_ERROR_CONNECTION_REPAIR_FAILED                     _WEAVE_ERROR(187)
 
 /**
  *  @}
diff --git a/src/lib/core/WeaveFabricState.cpp b/src/lib/core/WeaveFabricState.cpp
index 5a5bfaf..435f99c 100644
--- a/src/lib/core/WeaveFabricState.cpp
+++ b/src/lib/core/WeaveFabricState.cpp
@@ -963,9 +963,14 @@
         WeaveLogDetail(MessageLayer, "Prev Next MsgId:%016" PRIX64 "\n", sessionKey->NextMsgId.GetValue());
         WeaveLogDetail(MessageLayer, "Prev MaxRcvMsgId:%016" PRIX64 "\n", sessionKey->MaxRcvdMsgId);
 
-        // Assign resumption message ids to the session key
-        sessionKey->NextMsgId.Init(sessionKey->ResumptionSendMsgId);
-        sessionKey->MaxRcvdMsgId = sessionKey->ResumptionRecvMsgId;
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+        if (con && !con->IsRepaired)
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+        {
+            // Assign resumption message ids to the session key
+            sessionKey->NextMsgId.Init(sessionKey->ResumptionSendMsgId);
+            sessionKey->MaxRcvdMsgId = sessionKey->ResumptionRecvMsgId;
+        }
 
         WeaveLogDetail(MessageLayer, "New Next MsgId:%016" PRIX64 "\n", sessionKey->NextMsgId.GetValue());
         WeaveLogDetail(MessageLayer, "New MaxRcvMsgId:%016" PRIX64 "\n", sessionKey->MaxRcvdMsgId);
diff --git a/src/lib/core/WeaveMessageLayer.h b/src/lib/core/WeaveMessageLayer.h
index 75fc5eb..44cb6c1 100644
--- a/src/lib/core/WeaveMessageLayer.h
+++ b/src/lib/core/WeaveMessageLayer.h
@@ -250,6 +250,9 @@
     bool SendDestNodeId;                                /**< True if all messages sent via this connection must include
                                                              an explicitly encoded destination node identifier, false
                                                              otherwise. */
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+    bool IsRepaired;                                    /**< True if the connection was repaired, false otherwise. */
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
 
     void AddRef(void);
     void Release(void);
@@ -307,6 +310,34 @@
 
     TCPEndPoint * GetTCPEndPoint(void) const { return mTcpEndPoint; }
 
+
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+    /**
+     *  This function is the application callback that is invoked to fetch the TCP connection repair
+     *  parameters.
+     *
+     *
+     *  @param[in]    peerNodeId      NodeId of the peer with which to repair TCP
+     *                                connection.
+     *  @param[in]    destAddr        IP address of the peer with
+     *                                which to repair TCP connection. Defaults to
+     *                                IPAddress::Any.
+     *  @param[in]    destPort        Destination port of the peer connection.
+     *                                Defaults to WEAVE_PORT.
+     *  @param[in]    connRepairInfo  Pointer to the TCP connection repair info
+     *                                object that would be filled in by the
+     *                                callee.
+     *  @param[in]    repairCtxt      pointer to an application-specific context
+     *                                object required for TCP repair info
+     *                                retrieval.
+     *
+     */
+    typedef WEAVE_ERROR (*ConnectionRepairInfoGetterFunct)(uint64_t peerNodeId, IPAddress destAddr,
+                                                           uint16_t destPort, TCPConnRepairInfo *connRepairInfo, void *repairCtxt);
+
+    WEAVE_ERROR TryConnectionRepair(uint64_t peerNodeId, const TCPConnRepairInfo &repairInfo, InterfaceId intf = INET_NULL_INTERFACEID);
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
     /**
      *  This function is the application callback that is invoked when a connection setup is complete.
      *
@@ -409,6 +440,9 @@
     void DisconnectOnError(WEAVE_ERROR err);
     WEAVE_ERROR StartConnectToAddressLiteral(const char *peerAddr, size_t peerAddrLen);
 
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+    static void AppCallbackAfterConnectionRepair(System::Layer* aSystemLayer, void* aAppState, System::Error aError);
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
     static void HandleResolveComplete(void *appState, INET_ERROR err, uint8_t addrCount, IPAddress *addrArray);
     static void HandleConnectComplete(TCPEndPoint *endPoint, INET_ERROR conRes);
     static void HandleDataReceived(TCPEndPoint *endPoint, PacketBuffer *data);
diff --git a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp
index e9375d3..3528497 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp
@@ -260,6 +260,28 @@
     return err;
 }
 
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+void WeaveTunnelAgent::SetTunnelConnectionRepairInfoCallback(TunnelType tunType,
+                                                             nl::Weave::WeaveConnection::ConnectionRepairInfoGetterFunct aGetConnectionRepairInfo,
+                                                             void *repairCtxt)
+{
+    switch (tunType)
+    {
+      case kType_TunnelPrimary:
+        mPrimaryTunConnMgr.SetConnectionRepairInfoCallback(aGetConnectionRepairInfo, repairCtxt);
+        break;
+
+#if WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
+      case kType_TunnelBackup:
+        mBackupTunConnMgr.SetConnectionRepairInfoCallback(aGetConnectionRepairInfo, repairCtxt);
+        break;
+#endif // WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
+      default:
+        break;
+    }
+
+}
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
 /**
  * Set the WeaveAuthMode for the Tunnel.
  *
diff --git a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h
index 2188729..1367d4e 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h
@@ -279,6 +279,15 @@
                                                   WeaveTunnelConnectionMgr::LoadPersistedSessionFunct aLoadPersistedTunnelSession);
 #endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
 
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+/**
+ * Set the callback to get TCP connection repair info
+ */
+    void SetTunnelConnectionRepairInfoCallback(TunnelType tunType,
+                                               nl::Weave::WeaveConnection::ConnectionRepairInfoGetterFunct aGetConnectionRepairInfo,
+                                               void *repairCtxt);
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
 #if WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
 
 /**
diff --git a/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.cpp b/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.cpp
index ab137c2..fa92dd6 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.cpp
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.cpp
@@ -31,6 +31,10 @@
 #include <Weave/Profiles/weave-tunneling/WeaveTunnelAgent.h>
 #include <Weave/Profiles/weave-tunneling/WeaveTunnelCommon.h>
 
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+#include <Weave/Core/WeaveMessageLayer.h>
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
 #include <Weave/Support/CodeUtils.h>
 #include <Weave/Support/RandUtils.h>
 #include <Weave/Support/FibonacciUtils.h>
@@ -132,6 +136,11 @@
     mMaxNumProbes                     = WEAVE_CONFIG_TUNNEL_MAX_KEEPALIVE_PROBES;
 #endif // WEAVE_CONFIG_TUNNEL_TCP_KEEPALIVE_SUPPORTED
 
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+    mConnRepairInfoGet = NULL;
+    mConnRepairCtxt = NULL;
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
     // Initialize WeaveTunnelControl
 
     err = mTunControl.Init(mTunAgent);
@@ -277,6 +286,99 @@
 }
 #endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
 
+WEAVE_ERROR WeaveTunnelConnectionMgr::AllocateAndPrepTunnelConnection(WeaveConnection **outCon)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    if (mServiceCon != NULL && mConnectionState == kState_NotConnected)
+    {
+        // Remove previous connection(currently closed)
+
+        mServiceCon->Close();
+        mServiceCon = NULL;
+    }
+
+    // Do nothing if a connect attempt is already in progress.
+
+    VerifyOrExit(mServiceCon == NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+    // Create a new WeaveConnection object
+
+    *outCon = mTunAgent->mExchangeMgr->MessageLayer->NewConnection();
+
+    VerifyOrExit(*outCon != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+    // Setup connection handlers
+
+    (*outCon)->OnConnectionComplete = HandleServiceConnectionComplete;
+
+    // Set app state to WeaveTunnelConnectionMgr
+
+    (*outCon)->AppState = this;
+
+    // Set the connection timeout
+
+    (*outCon)->SetConnectTimeout(WEAVE_CONFIG_TUNNEL_CONNECT_TIMEOUT_SECS * nl::Weave::System::kTimerFactor_milli_per_unit);
+
+exit:
+
+    return err;
+}
+
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+void WeaveTunnelConnectionMgr::SetConnectionRepairInfoCallback(nl::Weave::WeaveConnection::ConnectionRepairInfoGetterFunct aGetConnectionRepairInfo,
+                                                               void *repairCtxt)
+{
+    WeaveLogDetail(WeaveTunnel, "%s Setting Connection RepairInfo callback", __func__);
+
+    mConnRepairInfoGet = aGetConnectionRepairInfo;
+    mConnRepairCtxt = repairCtxt;
+}
+
+WEAVE_ERROR WeaveTunnelConnectionMgr::AttemptRepairTunnelConnection(uint64_t aPeerNodeId,
+                                                                    WeaveAuthMode aAuthMode,
+                                                                    nl::Inet::InterfaceId aConnIntfId)
+{
+
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    TCPConnRepairInfo repairInfo;
+    WeaveConnection *newCon = NULL;
+    // The AuthMode has to be UnAuthenticated because after the TCP repair, the Tunnel CASE session
+    // would also have to be restored without negotiating it with the peer.
+    VerifyOrExit(aAuthMode == kWeaveAuthMode_Unauthenticated, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+    // Get Repair Info for restoring the TCP connection.
+    VerifyOrExit(mConnRepairInfoGet != NULL, err = WEAVE_ERROR_CONNECTION_REPAIR_FAILED);
+
+    err = mConnRepairInfoGet(aPeerNodeId, IPAddress::Any, WEAVE_PORT, &repairInfo, mConnRepairCtxt);
+    SuccessOrExit(err);
+
+    WeaveLogProgress(WeaveTunnel, "TCP connection repair info fetched");
+
+    err = AllocateAndPrepTunnelConnection(&newCon);
+    SuccessOrExit(err);
+
+    newCon->AuthMode = aAuthMode;
+
+    err = newCon->TryConnectionRepair(aPeerNodeId, repairInfo, aConnIntfId);
+    SuccessOrExit(err);
+
+    mServiceCon = newCon;
+
+exit:
+
+    if (err != WEAVE_NO_ERROR)
+    {
+        if (newCon != NULL)
+        {
+            newCon->Abort();
+            newCon = NULL;
+        }
+    }
+
+    return err;
+}
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
 /**
  * Try to establish a connecttion to the Service either using
  * ServiceManager or directly
@@ -311,48 +413,60 @@
     {
         if (IsPersistedTunnelSessionPresent(kServiceEndpoint_WeaveTunneling))
         {
+            WeaveLogDetail(WeaveTunnel, "Persisted Tunnel CASE session exists. Setting AuthMode to UnAuthenticated for initial connection");
             currentAuthMode = kWeaveAuthMode_Unauthenticated;
         }
     }
 #endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
 
-#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
-    // Initiate TCP connection with Service.
-
-    if (mTunAgent->mServiceMgr)
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+    err = AttemptRepairTunnelConnection(mTunAgent->mPeerNodeId, currentAuthMode, connIntfId);
+    if (err == WEAVE_NO_ERROR)
     {
-        err = mTunAgent->mServiceMgr->connect(mTunAgent->mPeerNodeId,
-                                              currentAuthMode, this,
-                                              ServiceMgrStatusHandler,
-                                              HandleServiceConnectionComplete,
-                                              WEAVE_CONFIG_TUNNEL_CONNECT_TIMEOUT_SECS * nl::Weave::System::kTimerFactor_milli_per_unit,
-                                              connIntfId);
+        ExitNow();
     }
     else
-#endif
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
     {
-        err = StartServiceTunnelConn(mTunAgent->mPeerNodeId,
-                                     mTunAgent->mServiceAddress,
-                                     mTunAgent->mServicePort,
-                                     currentAuthMode,
-                                     connIntfId);
-    }
+#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+         WeaveLogDetail(WeaveTunnel, "Attempting regular connection method via ServiceDirectory");
+        // Initiate TCP connection with Service.
 
-    SuccessOrExit(err);
+        if (mTunAgent->mServiceMgr)
+        {
+            err = mTunAgent->mServiceMgr->connect(mTunAgent->mPeerNodeId,
+                                                  currentAuthMode, this,
+                                                  ServiceMgrStatusHandler,
+                                                  HandleServiceConnectionComplete,
+                                                  WEAVE_CONFIG_TUNNEL_CONNECT_TIMEOUT_SECS * nl::Weave::System::kTimerFactor_milli_per_unit,
+                                                  connIntfId);
+        }
+        else
+#endif
+        {
+            err = StartServiceTunnelConn(mTunAgent->mPeerNodeId,
+                                         mTunAgent->mServiceAddress,
+                                         mTunAgent->mServicePort,
+                                         currentAuthMode,
+                                         connIntfId);
+        }
 
-    // Change the connection state to connecting.
+        SuccessOrExit(err);
 
-    mConnectionState = kState_Connecting;
+        // Change the connection state to connecting.
+
+        mConnectionState = kState_Connecting;
 
 #if WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS
-    // Update tunnel statistics
-    tunStats = mTunAgent->GetCommonTunnelStatistics(mTunType);
+        // Update tunnel statistics
+        tunStats = mTunAgent->GetCommonTunnelStatistics(mTunType);
 
-    if (tunStats != NULL)
-    {
-        tunStats->mTunnelConnAttemptCount++;
-    }
+        if (tunStats != NULL)
+        {
+            tunStats->mTunnelConnAttemptCount++;
+        }
 #endif // WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS
+    }
 exit:
     return err;
 }
@@ -514,36 +628,8 @@
 {
     WEAVE_ERROR err = WEAVE_NO_ERROR;
 
-    if (mServiceCon != NULL && mConnectionState == kState_NotConnected)
-    {
-        // Remove previous connection(currently closed)
-
-        mServiceCon->Close();
-        mServiceCon = NULL;
-    }
-
-    // Do nothing if a connect attempt is already in progress.
-
-    VerifyOrExit(mServiceCon == NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
-
-    // Create a new WeaveConnection object
-
-    mServiceCon = mTunAgent->mExchangeMgr->MessageLayer->NewConnection();
-
-    VerifyOrExit(mServiceCon != NULL, err = WEAVE_ERROR_NO_MEMORY);
-
-    // Setup connection handlers
-
-    mServiceCon->OnConnectionComplete = HandleServiceConnectionComplete;
-
-
-    // Set app state to WeaveTunnelConnectionMgr
-
-    mServiceCon->AppState = this;
-
-    // Set the connection timeout
-
-    mServiceCon->SetConnectTimeout(WEAVE_CONFIG_TUNNEL_CONNECT_TIMEOUT_SECS * nl::Weave::System::kTimerFactor_milli_per_unit);
+    err = AllocateAndPrepTunnelConnection(&mServiceCon);
+    SuccessOrExit(err);
 
     err = mServiceCon->Connect(destNodeId, authMode, destIPAddr, destPort, connIntfId);
 
@@ -769,6 +855,10 @@
 
     SuccessOrExit(conErr);
 
+    // Set state variables to indicate successful connection.
+
+    tConnMgr->mConnectionState = kState_ConnectionEstablished;
+
     WeaveLogDetail(WeaveTunnel, "Connection established to node %" PRIx64 " (%s) on %s tunnel\n",
                    con->PeerNodeId, ipAddrStr, tConnMgr->mTunType == kType_TunnelPrimary?"primary":"backup");
 
@@ -803,52 +893,79 @@
         routePriority = WeaveTunnelRoute::kRoutePriority_Low;
     }
 
-    // Create tunnel route for Service and send Tunnel control message.
-
-    tunRoute = WeaveTunnelRoute();
-    globalId = WeaveFabricIdToIPv6GlobalId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->FabricId);
-    if (tConnMgr->mTunAgent->mRole == kClientRole_BorderGateway)
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+    // If underlying TCP connection was repaired, then no need to send TunnelOpen
+    // but, instead, signal up that Tunnel is established.
+    if (tConnMgr->mServiceCon->IsRepaired)
     {
-        tunRoute.tunnelRoutePrefix[0].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_ThreadMesh, 0);
-        tunRoute.tunnelRoutePrefix[0].Length = NL_INET_IPV6_DEFAULT_PREFIX_LEN;
-        tunRoute.tunnelRoutePrefix[1].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_PrimaryWiFi, 0);
-        tunRoute.tunnelRoutePrefix[1].Length = NL_INET_IPV6_DEFAULT_PREFIX_LEN;
-        tunRoute.tunnelRoutePrefix[2].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_PrimaryWiFi,
-                                                           WeaveNodeIdToIPv6InterfaceId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->LocalNodeId));
-        tunRoute.tunnelRoutePrefix[2].Length = NL_INET_IPV6_MAX_PREFIX_LEN;
-        tunRoute.tunnelRoutePrefix[3].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_ThreadMesh,
-                                                           WeaveNodeIdToIPv6InterfaceId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->LocalNodeId));
-        tunRoute.tunnelRoutePrefix[3].Length = NL_INET_IPV6_MAX_PREFIX_LEN;
-        tunRoute.priority[0] = tunRoute.priority[1] = tunRoute.priority[2] = tunRoute.priority[3] = routePriority;
-        tunRoute.numOfPrefixes = 4;
+        WeaveLogDetail(WeaveTunnel, "Connection repaired: Setting state to kState_TunnelOpen");
+
+        tConnMgr->mConnectionState = kState_TunnelOpen;
+
+        tConnMgr->mTunFailedConnAttemptsInRow = 0;
+        tConnMgr->mTunReconnectFibonacciIndex = 0;
+        tConnMgr->mIsNetworkOnline = true;
+
+        // Stop the online check if it is running since tunnel is established.
+
+        tConnMgr->StopOnlineCheck();
+
+#if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
+        // Start the Tunnel Liveness timer
+
+        tConnMgr->StartLivenessTimer();
+#endif // WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
+
+       // Call the TunnelOpen post processing function.
+
+       tConnMgr->mTunAgent->WeaveTunnelConnectionUp(NULL, tConnMgr, tConnMgr->mTunAgent->mRole != kClientRole_BorderGateway);
     }
-    else if (tConnMgr->mTunAgent->mRole == kClientRole_MobileDevice)
+    else
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
     {
-        tunRoute.tunnelRoutePrefix[0].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_MobileDevice,
-                                                           WeaveNodeIdToIPv6InterfaceId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->LocalNodeId));
-        tunRoute.tunnelRoutePrefix[0].Length = NL_INET_IPV6_MAX_PREFIX_LEN;
-        tunRoute.priority[0] = routePriority;
-        tunRoute.numOfPrefixes = 1;
+        // Create tunnel route for Service and send Tunnel control message.
 
+        tunRoute = WeaveTunnelRoute();
+        globalId = WeaveFabricIdToIPv6GlobalId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->FabricId);
+        if (tConnMgr->mTunAgent->mRole == kClientRole_BorderGateway)
+        {
+            tunRoute.tunnelRoutePrefix[0].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_ThreadMesh, 0);
+            tunRoute.tunnelRoutePrefix[0].Length = NL_INET_IPV6_DEFAULT_PREFIX_LEN;
+            tunRoute.tunnelRoutePrefix[1].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_PrimaryWiFi, 0);
+            tunRoute.tunnelRoutePrefix[1].Length = NL_INET_IPV6_DEFAULT_PREFIX_LEN;
+            tunRoute.tunnelRoutePrefix[2].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_PrimaryWiFi,
+                                                               WeaveNodeIdToIPv6InterfaceId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->LocalNodeId));
+            tunRoute.tunnelRoutePrefix[2].Length = NL_INET_IPV6_MAX_PREFIX_LEN;
+            tunRoute.tunnelRoutePrefix[3].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_ThreadMesh,
+                                                               WeaveNodeIdToIPv6InterfaceId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->LocalNodeId));
+            tunRoute.tunnelRoutePrefix[3].Length = NL_INET_IPV6_MAX_PREFIX_LEN;
+            tunRoute.priority[0] = tunRoute.priority[1] = tunRoute.priority[2] = tunRoute.priority[3] = routePriority;
+            tunRoute.numOfPrefixes = 4;
+        }
+        else if (tConnMgr->mTunAgent->mRole == kClientRole_MobileDevice)
+        {
+            tunRoute.tunnelRoutePrefix[0].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_MobileDevice,
+                                                               WeaveNodeIdToIPv6InterfaceId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->LocalNodeId));
+            tunRoute.tunnelRoutePrefix[0].Length = NL_INET_IPV6_MAX_PREFIX_LEN;
+            tunRoute.priority[0] = routePriority;
+            tunRoute.numOfPrefixes = 1;
+
+        }
+        else if (tConnMgr->mTunAgent->mRole == kClientRole_StandaloneDevice)
+        {
+            tunRoute.tunnelRoutePrefix[0].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_PrimaryWiFi,
+                                                               WeaveNodeIdToIPv6InterfaceId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->LocalNodeId));
+            tunRoute.tunnelRoutePrefix[0].Length = NL_INET_IPV6_MAX_PREFIX_LEN;
+            tunRoute.tunnelRoutePrefix[1].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_ThreadMesh,
+                                                               WeaveNodeIdToIPv6InterfaceId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->LocalNodeId));
+            tunRoute.tunnelRoutePrefix[1].Length = NL_INET_IPV6_MAX_PREFIX_LEN;
+            tunRoute.priority[0] = tunRoute.priority[1] = routePriority;
+            tunRoute.numOfPrefixes = 2;
+        }
+
+        conErr = tConnMgr->mTunControl.SendTunnelOpen(tConnMgr, &tunRoute);
+        SuccessOrExit(conErr);
     }
-    else if (tConnMgr->mTunAgent->mRole == kClientRole_StandaloneDevice)
-    {
-        tunRoute.tunnelRoutePrefix[0].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_PrimaryWiFi,
-                                                           WeaveNodeIdToIPv6InterfaceId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->LocalNodeId));
-        tunRoute.tunnelRoutePrefix[0].Length = NL_INET_IPV6_MAX_PREFIX_LEN;
-        tunRoute.tunnelRoutePrefix[1].IPAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_ThreadMesh,
-                                                           WeaveNodeIdToIPv6InterfaceId(tConnMgr->mTunAgent->mExchangeMgr->FabricState->LocalNodeId));
-        tunRoute.tunnelRoutePrefix[1].Length = NL_INET_IPV6_MAX_PREFIX_LEN;
-        tunRoute.priority[0] = tunRoute.priority[1] = routePriority;
-        tunRoute.numOfPrefixes = 2;
-    }
-
-    conErr = tConnMgr->mTunControl.SendTunnelOpen(tConnMgr, &tunRoute);
-    SuccessOrExit(conErr);
-
-    // Set state variables to indicate successful connection.
-
-    tConnMgr->mConnectionState = kState_ConnectionEstablished;
 
 #if WEAVE_CONFIG_TUNNEL_TCP_USER_TIMEOUT_SUPPORTED
     // With the connection established, configure the User timeout on the connection
diff --git a/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h b/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h
index aee2a96..9a37e71 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h
@@ -241,6 +241,11 @@
  */
     void StopAndReconnectTunnelConn(ReconnectParam & reconnParam);
 
+
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+    void SetConnectionRepairInfoCallback(nl::Weave::WeaveConnection::ConnectionRepairInfoGetterFunct aGetConnectionRepairInfo, void *repairCtxt);
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
 /**
  * Get a pointer to the Weave Tunnel Connection object for this
  * Tunnel Connection Manager.
@@ -261,6 +266,7 @@
     void ReleaseResourcesAndStopTunnelConn(WEAVE_ERROR err);
     WEAVE_ERROR ResetReconnectBackoff(bool reconnectImmediately);
     static void ServiceConnectTimeout(System::Layer* aSystemLayer, void* aAppState, System::Error aError);
+    WEAVE_ERROR AllocateAndPrepTunnelConnection(WeaveConnection **con);
 #if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
     void StartLivenessTimer(void);
     void StopLivenessTimer(void);
@@ -274,6 +280,11 @@
     void ReStartOnlineCheck(void);
     static void OnlineCheckTimeout(System::Layer* aSystemLayer, void* aAppState, System::Error aError);
     void HandleOnlineCheckResult(bool isOnline);
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+    WEAVE_ERROR AttemptRepairTunnelConnection(uint64_t peerNodeId, WeaveAuthMode aAuthMode,
+                                              nl::Inet::InterfaceId aConnIntfId = INET_NULL_INTERFACEID);
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+
     // Pointer to a Weave Tunnel Agent object.
 
     WeaveTunnelAgent *mTunAgent;
@@ -348,6 +359,10 @@
     // over the network.
 
     uint16_t mOnlineCheckInterval;
+#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
+    nl::Weave::WeaveConnection::ConnectionRepairInfoGetterFunct mConnRepairInfoGet;
+    void *mConnRepairCtxt; // A pointer to an application-specific context object used for repair info retrieval.
+#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
 };
 
 } // namespace WeaveTunnel
diff --git a/src/test-apps/TestErrorStr.cpp b/src/test-apps/TestErrorStr.cpp
index 51655db..9cf6452 100644
--- a/src/test-apps/TestErrorStr.cpp
+++ b/src/test-apps/TestErrorStr.cpp
@@ -295,6 +295,7 @@
       WEAVE_ERROR_UNSUPPORTED_WIRELESS_REGULATORY_DOMAIN,
       WEAVE_ERROR_UNSUPPORTED_WIRELESS_OPERATING_LOCATION,
       WEAVE_ERROR_WDM_EVENT_TOO_BIG,
+      WEAVE_ERROR_CONNECTION_REPAIR_FAILED,
 
       WEAVE_ERROR_TUNNEL_ROUTING_RESTRICTED,