Tunnel CASE Session persistence support.

Changes within OpenWeave to support persistence of CASE session
for the Tunnel.
During connection establishment by the Tunnel Agent, if a persisted
session key exists, the TunnelAgent allows for only the WeaveConnection
to be established and then calls back into the application to load the
session key for the created Weave connection object.
When the connection goes down, and before the FabricState removes the
SessionKey, the application is called back to suspend the session into
a serialized form to be persisted by the application.
At time of suspension, resumption message Ids(for both the next message
id to send as well as the last received message id from peer) are
calculated using a fixed offset and included within the session key.
When the session resumes, it would start off communication using the
resumption message ids.

Add Test Client and Server for testing persistence of Tunnel CASE
session.
diff --git a/src/lib/core/WeaveFabricState.cpp b/src/lib/core/WeaveFabricState.cpp
index 8652e67..fa00cef 100644
--- a/src/lib/core/WeaveFabricState.cpp
+++ b/src/lib/core/WeaveFabricState.cpp
@@ -113,6 +113,10 @@
     NodeId = kNodeIdNotSpecified;
     NextMsgId.Init(0);
     MaxRcvdMsgId = 0;
+    InitialSendMsgId = 0;
+    InitialRcvdMsgId = 0;
+    ResumptionSendMsgId = 0;
+    ResumptionRecvMsgId = 0;
     BoundCon = NULL;
     RcvFlags = 0;
     AuthMode = kWeaveAuthMode_NotSpecified;
@@ -130,6 +134,22 @@
     ClearSecretData((uint8_t *)&MsgEncKey.EncKey, sizeof(MsgEncKey.EncKey));
 }
 
+void WeaveSessionKey::ComputeNextResumptionMsgIds(void)
+{
+     // When calculating the resumption message ids, it needs to be ensured that the next resumption message id is always ahead of the current message ids.
+     while (ResumptionSendMsgId < NextMsgId.GetValue())
+     {
+         ResumptionSendMsgId = (ResumptionSendMsgId == 0) ? InitialSendMsgId + WEAVE_CONFIG_TUNNEL_SESSION_RESUMPTION_MSG_ID_OFFSET :
+                                ResumptionSendMsgId + WEAVE_CONFIG_TUNNEL_SESSION_RESUMPTION_MSG_ID_OFFSET;
+     }
+
+     while (ResumptionRecvMsgId < MaxRcvdMsgId)
+     {
+         ResumptionRecvMsgId = (ResumptionRecvMsgId == 0) ? InitialRcvdMsgId + WEAVE_CONFIG_TUNNEL_SESSION_RESUMPTION_MSG_ID_OFFSET - 1:
+                                ResumptionRecvMsgId + WEAVE_CONFIG_TUNNEL_SESSION_RESUMPTION_MSG_ID_OFFSET;
+     }
+}
+
 /**
  * @fn bool WeaveSessionKey::IsAllocated() const
  *
@@ -324,6 +344,11 @@
     sessionKey->Flags = WeaveSessionKey::kFlag_RecentlyActive;
     sessionKey->ReserveCount = 1;
 
+    if (boundCon)
+    {
+        sessionKey->SetUsedOverConnection(true);
+    }
+
     return WEAVE_NO_ERROR;
 }
 
@@ -356,6 +381,8 @@
     sessionKey->MsgEncKey.EncType = encType;
     sessionKey->MsgEncKey.EncKey = *encKey;
     sessionKey->NextMsgId.Init(msgId);
+    sessionKey->InitialSendMsgId = msgId;
+    sessionKey->InitialRcvdMsgId = 0;
     sessionKey->MaxRcvdMsgId = 0;
     sessionKey->RcvFlags = 0;
     sessionKey->AuthMode = authMode;
@@ -633,7 +660,6 @@
     // Assert various requirements about the session.
     VerifyOrExit(sessionKey->IsKeySet(), err = WEAVE_ERROR_KEY_NOT_FOUND);
     VerifyOrExit(!sessionKey->IsSuspended(), err = WEAVE_ERROR_SESSION_KEY_SUSPENDED);
-    VerifyOrExit(sessionKey->BoundCon == NULL, err = WEAVE_ERROR_INVALID_USE_OF_SESSION_KEY);
     VerifyOrExit(IsCertAuthMode(sessionKey->AuthMode), err = WEAVE_ERROR_INVALID_USE_OF_SESSION_KEY);
 
     {
@@ -709,6 +735,28 @@
             ExitNow(err = WEAVE_ERROR_UNSUPPORTED_ENCRYPTION_TYPE);
         }
 
+        if (sessionKey->AreResumptionMsgIdsValid())
+        {
+            // Generate a new set of resumption msg ids
+            sessionKey->ComputeNextResumptionMsgIds();
+
+            err = writer.PutBoolean(ContextTag(kTag_SerializedSession_AreResumptionMsgIdsValid),
+                             sessionKey->AreResumptionMsgIdsValid());
+            SuccessOrExit(err);
+
+            err = writer.Put(ContextTag(kTag_SerializedSession_ResumptionSendMessageId),
+                             sessionKey->ResumptionSendMsgId);
+            SuccessOrExit(err);
+
+            err = writer.Put(ContextTag(kTag_SerializedSession_ResumptionRecvMessageId),
+                             sessionKey->ResumptionRecvMsgId);
+            SuccessOrExit(err);
+        }
+
+        err = writer.PutBoolean(ContextTag(kTag_SerializedSession_IsUsedOverConnection),
+                                sessionKey->IsUsedOverConnection());
+        SuccessOrExit(err);
+
         // End the Security:SerializedSession TLV structure and finalize the encoding.
         err = writer.EndContainer(container);
         SuccessOrExit(err);
@@ -738,7 +786,7 @@
  * Restore a previously suspended Weave Security Session from a serialized state.
  *
  */
-WEAVE_ERROR WeaveFabricState::RestoreSession(uint8_t * serializedSession, uint16_t serializedSessionLen)
+WEAVE_ERROR WeaveFabricState::RestoreSession(uint8_t * serializedSession, uint16_t serializedSessionLen, WeaveConnection *con)
 {
     WEAVE_ERROR err = WEAVE_NO_ERROR;
     TLVReader reader;
@@ -886,6 +934,63 @@
         ExitNow(err = WEAVE_ERROR_UNSUPPORTED_ENCRYPTION_TYPE);
     }
 
+    bool resumptionMsgIdsValid;
+
+    err = reader.Next(kTLVType_Boolean, ContextTag(kTag_SerializedSession_AreResumptionMsgIdsValid));
+    SuccessOrExit(err);
+    err = reader.Get(resumptionMsgIdsValid);
+    SuccessOrExit(err);
+
+    sessionKey->SetResumptionMsgIdsValid(resumptionMsgIdsValid);
+
+    // Get the resumptionMsgIds;
+    if (resumptionMsgIdsValid)
+    {
+        err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_SerializedSession_ResumptionSendMessageId));
+        SuccessOrExit(err);
+
+        err = reader.Get(sessionKey->ResumptionSendMsgId);
+        SuccessOrExit(err);
+
+        err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_SerializedSession_ResumptionRecvMessageId));
+        SuccessOrExit(err);
+
+        err = reader.Get(sessionKey->ResumptionRecvMsgId);
+        SuccessOrExit(err);
+
+        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;
+
+        WeaveLogDetail(MessageLayer, "New Next MsgId:%016" PRIX64 "\n", sessionKey->NextMsgId.GetValue());
+        WeaveLogDetail(MessageLayer, "New MaxRcvMsgId:%016" PRIX64 "\n", sessionKey->MaxRcvdMsgId);
+    }
+
+    bool usedOverConnection;
+
+    err = reader.Next(kTLVType_Boolean, ContextTag(kTag_SerializedSession_IsUsedOverConnection));
+    SuccessOrExit(err);
+
+    err = reader.Get(usedOverConnection);
+    SuccessOrExit(err);
+
+    sessionKey->SetUsedOverConnection(usedOverConnection);
+
+    // If a connection object has been passed to be bound to a restored session,
+    // ensure that the AuthMode is CASE and the resumed session was previously
+    // used over a connection.
+    if (con && IsCASEAuthMode(con->AuthMode) && sessionKey->IsUsedOverConnection())
+    {
+        // Bind the connection to the restored session
+        sessionKey->BoundCon = con;
+        con->DefaultKeyId = sessionKey->MsgEncKey.KeyId;
+        con->DefaultEncryptionType = sessionKey->MsgEncKey.EncType;
+        con->PeerNodeId = sessionKey->NodeId;
+    }
+
     // Verify no other data in the serialized session structure.
     err = reader.VerifyEndOfContainer();
     SuccessOrExit(err);
@@ -921,11 +1026,11 @@
         if (con == NULL)
         {
             FindOrAllocPeerEntry(remoteNodeId, true, peerIndex);
-            outSessionState = WeaveSessionState(NULL, kWeaveAuthMode_Unauthenticated, &NextUnencUDPMsgId,
+            outSessionState = WeaveSessionState(NULL, kWeaveAuthMode_Unauthenticated, &NextUnencUDPMsgId, &PeerStates.MaxUnencUDPMsgIdRcvd[peerIndex],
                                                 &PeerStates.MaxUnencUDPMsgIdRcvd[peerIndex], &PeerStates.UnencRcvFlags[peerIndex]);
         }
         else
-            outSessionState = WeaveSessionState(NULL, kWeaveAuthMode_Unauthenticated, &NextUnencTCPMsgId, NULL, NULL);
+            outSessionState = WeaveSessionState(NULL, kWeaveAuthMode_Unauthenticated, &NextUnencTCPMsgId, NULL, NULL, NULL);
         break;
 
     case WeaveKeyId::kType_Session:
@@ -939,7 +1044,7 @@
             return (sessionKey->MsgEncKey.EncType == kWeaveEncryptionType_None) ? WEAVE_ERROR_KEY_NOT_FOUND : WEAVE_ERROR_WRONG_ENCRYPTION_TYPE;
         if (sessionKey->BoundCon != NULL && sessionKey->BoundCon != con)
             return WEAVE_ERROR_INVALID_USE_OF_SESSION_KEY;
-        outSessionState = WeaveSessionState(&sessionKey->MsgEncKey, sessionKey->AuthMode, &sessionKey->NextMsgId, &sessionKey->MaxRcvdMsgId, &sessionKey->RcvFlags);
+        outSessionState = WeaveSessionState(&sessionKey->MsgEncKey, sessionKey->AuthMode, &sessionKey->NextMsgId, &sessionKey->InitialRcvdMsgId, &sessionKey->MaxRcvdMsgId, &sessionKey->RcvFlags);
         break;
 
 #if WEAVE_CONFIG_USE_APP_GROUP_KEYS_FOR_MSG_ENC
@@ -953,9 +1058,9 @@
         WeaveAuthMode authMode = GroupKeyAuthMode(keyId);
 
         if (FindOrAllocPeerEntry(remoteNodeId, false, peerIndex))
-            outSessionState = WeaveSessionState(applicationKey, authMode, &NextGroupKeyMsgId, &PeerStates.MaxGroupKeyMsgIdRcvd[peerIndex], &PeerStates.GroupKeyRcvFlags[peerIndex]);
+            outSessionState = WeaveSessionState(applicationKey, authMode, &NextGroupKeyMsgId, NULL, &PeerStates.MaxGroupKeyMsgIdRcvd[peerIndex], &PeerStates.GroupKeyRcvFlags[peerIndex]);
         else
-            outSessionState = WeaveSessionState(applicationKey, authMode, &NextGroupKeyMsgId, NULL, NULL);
+            outSessionState = WeaveSessionState(applicationKey, authMode, &NextGroupKeyMsgId, NULL, NULL, NULL);
         break;
     }
 #endif
@@ -1663,6 +1768,13 @@
     {
         if (sessionKey->IsAllocated() && SessionKeys[i].BoundCon == con)
         {
+            // Call application callback to notify about session connection
+            // closure
+            if (BoundConnectionClosedForSession)
+            {
+                BoundConnectionClosedForSession(con);
+            }
+
             RemoveSessionKey(sessionKey);
         }
     }
@@ -1675,16 +1787,19 @@
     MsgEncKey = NULL;
     AuthMode = kWeaveAuthMode_NotSpecified;
     NextMsgId = NULL;
+    InitialMsgIdRcvd = NULL;
     MaxMsgIdRcvd = NULL;
     RcvFlags = NULL;
 }
 
 WeaveSessionState::WeaveSessionState(WeaveMsgEncryptionKey *msgEncKey, WeaveAuthMode authMode,
-                                     MonotonicallyIncreasingCounter *nextMsgId, uint32_t *maxMsgIdRcvd, ReceiveFlagsType *rcvFlags)
+                                     MonotonicallyIncreasingCounter *nextMsgId, uint32_t *initialRcvdMsgId,
+                                     uint32_t *maxMsgIdRcvd, ReceiveFlagsType *rcvFlags)
 {
     MsgEncKey = msgEncKey;
     AuthMode = authMode;
     NextMsgId = nextMsgId;
+    InitialMsgIdRcvd = initialRcvdMsgId;
     MaxMsgIdRcvd = maxMsgIdRcvd;
     RcvFlags = rcvFlags;
 }
@@ -1748,6 +1863,7 @@
         {
             *RcvFlags = kReceiveFlags_MessageIdSynchronized;
             *MaxMsgIdRcvd = msgId;
+            *InitialMsgIdRcvd = msgId;
             ExitNow();
         }
     }
diff --git a/src/lib/core/WeaveFabricState.h b/src/lib/core/WeaveFabricState.h
index 10631df..e463ac6 100644
--- a/src/lib/core/WeaveFabricState.h
+++ b/src/lib/core/WeaveFabricState.h
@@ -330,7 +330,7 @@
 
     WeaveSessionState(void);
     WeaveSessionState(WeaveMsgEncryptionKey *msgEncKey, WeaveAuthMode authMode,
-                      MonotonicallyIncreasingCounter *nextMsgId, uint32_t *maxRcvdMsgId, ReceiveFlagsType *rcvFlags);
+                      MonotonicallyIncreasingCounter *nextMsgId, uint32_t *initialRcvdMsgId, uint32_t *maxRcvdMsgId, ReceiveFlagsType *rcvFlags);
 
     WeaveMsgEncryptionKey *MsgEncKey;
     WeaveAuthMode AuthMode;
@@ -342,6 +342,7 @@
 private:
     MonotonicallyIncreasingCounter *NextMsgId;
     uint32_t *MaxMsgIdRcvd;
+    uint32_t *InitialMsgIdRcvd;
     ReceiveFlagsType *RcvFlags;
 };
 
@@ -362,6 +363,8 @@
                                                              that are not bound to a connection). */
         kFlag_RecentlyActive         = 0x08,            /**< The session was recently active. */
         kFlag_Suspended              = 0x10,            /**< The session has been suspended. */
+        kFlag_ResumptionMsgIdsValid  = 0x20,            /**< The session has valid resumption message Ids. */
+        kFlag_IsUsedOverConnection   = 0x40,            /**< The session has been setup over a connection. */
     };
 
     uint64_t NodeId;                                    /**< The id of the node with which the session key is shared. */
@@ -371,11 +374,16 @@
     WeaveSessionState::ReceiveFlagsType RcvFlags;       /**< Flags tracking messages received under the key. */
     WeaveAuthMode AuthMode;                             /**< The means by which the peer node was authenticated during session establishment. */
     WeaveMsgEncryptionKey MsgEncKey;                    /**< The Weave message encryption key. */
+    uint32_t InitialSendMsgId;                          /**< The initial send message id in the session. Used to calculate resumption message id. */
+    uint32_t InitialRcvdMsgId;                          /**< The initial received message id in the session. Used to calculate resumption message id. */
+    uint32_t ResumptionSendMsgId;                       /**< The next message id to be used when the session resumes after suspension. */
+    uint32_t ResumptionRecvMsgId;                       /**< The next message id expected to be received from the peer on session resumption. */
     uint8_t ReserveCount;                               /**< Number of times the session key has been reserved. */
     uint8_t Flags;                                      /**< Various flags associated with the session. */
 
     void Init(void);
     void Clear(void);
+    void ComputeNextResumptionMsgIds(void);
 
     bool IsAllocated() const            { return MsgEncKey.KeyId != WeaveKeyId::kNone; }
     bool IsKeySet() const               { return MsgEncKey.EncType != 0; }
@@ -391,6 +399,10 @@
     bool IsSuspended() const            { return GetFlag(Flags, kFlag_Suspended); }
     void MarkSuspended()                { SetFlag(Flags, kFlag_Suspended); }
     void ClearSuspended()               { ClearFlag(Flags, kFlag_Suspended); }
+    bool AreResumptionMsgIdsValid() const { return GetFlag(Flags, kFlag_ResumptionMsgIdsValid); }
+    void SetResumptionMsgIdsValid(bool val) { SetFlag(Flags, kFlag_ResumptionMsgIdsValid, val); }
+    bool IsUsedOverConnection() const  { return GetFlag(Flags, kFlag_IsUsedOverConnection); }
+    void SetUsedOverConnection(bool val) { SetFlag(Flags, kFlag_IsUsedOverConnection, val); }
 };
 
 /**
@@ -467,6 +479,7 @@
      *
      */
     virtual void DidLeaveFabric(WeaveFabricState *fabricState, uint64_t oldFabricId) = 0;
+
 };
 
 class NL_DLL_EXPORT WeaveFabricState
@@ -528,7 +541,7 @@
     void RemoveSharedSessionEndNodes(const WeaveSessionKey *sessionKey);
 
     WEAVE_ERROR SuspendSession(uint16_t keyId, uint64_t peerNodeId, uint8_t * buf, uint16_t bufSize, uint16_t & serializedSessionLen);
-    WEAVE_ERROR RestoreSession(uint8_t * serializedSession, uint16_t serializedSessionLen);
+    WEAVE_ERROR RestoreSession(uint8_t * serializedSession, uint16_t serializedSessionLen, WeaveConnection *con = NULL);
 
     WEAVE_ERROR GetSessionState(uint64_t remoteNodeId, uint16_t keyId, uint8_t encType, WeaveConnection *con, WeaveSessionState& outSessionState);
 
@@ -579,6 +592,14 @@
     };
 
     WEAVE_ERROR RegisterSessionEndCallback(SessionEndCbCtxt *sessionEndCb);
+
+    /**
+     * Notify the application about the closure of a connection that was bound
+     * to a session specified by the KeyId and PeerNodeId
+     */
+    typedef void (*BoundConnectionClosedForSessionFunct)(WeaveConnection *con);
+    BoundConnectionClosedForSessionFunct BoundConnectionClosedForSession;
+
 private:
     PeerIndexType PeerCount;
     MonotonicallyIncreasingCounter NextUnencUDPMsgId;
diff --git a/src/lib/core/WeaveSecurityMgr.cpp b/src/lib/core/WeaveSecurityMgr.cpp
index 88122ab..6e0857d 100644
--- a/src/lib/core/WeaveSecurityMgr.cpp
+++ b/src/lib/core/WeaveSecurityMgr.cpp
@@ -2514,6 +2514,7 @@
     }
     else
     {
+        con->PeerNodeId = rcvdMsgInfo->SourceNodeId;
         ec = ExchangeManager->NewContext(con, this);
     }
     VerifyOrExit(ec != NULL, err = WEAVE_ERROR_NO_MEMORY);
diff --git a/src/lib/core/WeaveTunnelConfig.h b/src/lib/core/WeaveTunnelConfig.h
index 4a4e6fd..466568b 100644
--- a/src/lib/core/WeaveTunnelConfig.h
+++ b/src/lib/core/WeaveTunnelConfig.h
@@ -469,6 +469,30 @@
 #define WEAVE_CONFIG_TUNNEL_INTERFACE_MTU                           (1536)
 #endif // WEAVE_CONFIG_TUNNEL_INTERFACE_MTU
 
+/**
+ * @def WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+ *
+ * @brief
+ *    Set to true if the Tunnel CASE session needs to be persisted,
+ *    false otherwise.
+ */
+#ifndef WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+#define WEAVE_CONFIG_PERSIST_CONNECTED_SESSION                      (0)
+#endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+
+/**
+ * @def WEAVE_CONFIG_TUNNEL_SESSION_RESUMPTION_MSG_ID_OFFSET
+ *
+ * @brief
+ *   The offset to use to move the message id forward when
+ *   computing the resumption message ids when suspending
+ *   the current secure tunnel session.
+ *
+ */
+#ifndef WEAVE_CONFIG_TUNNEL_SESSION_RESUMPTION_MSG_ID_OFFSET
+#define WEAVE_CONFIG_TUNNEL_SESSION_RESUMPTION_MSG_ID_OFFSET      (10000)
+#endif // WEAVE_CONFIG_TUNNEL_SESSION_RESUMPTION_MSG_ID_OFFSET
+
 // clang-format on
 
 #endif /* WEAVE_TUNNEL_CONFIG_H_ */
diff --git a/src/lib/profiles/security/WeaveSecurity.h b/src/lib/profiles/security/WeaveSecurity.h
index 2473e51..e414994 100644
--- a/src/lib/profiles/security/WeaveSecurity.h
+++ b/src/lib/profiles/security/WeaveSecurity.h
@@ -342,7 +342,12 @@
     kTag_SerializedSession_AES128CTRSHA1_DataKey        = 11, // [ BYTE STRING, len 16 ] For sessions supporting AES128CTRSHA1
                                                               //    message encryption, the data encryption key.
     kTag_SerializedSession_AES128CTRSHA1_IntegrityKey   = 12, // [ BYTE STRING, len 20 ] For sessions supporting AES128CTRSHA1
-                                                              //    message encryption, the data integrity key.
+    kTag_SerializedSession_AreResumptionMsgIdsValid     = 13, // [ BOOLEAN ] Are session resumption message Ids valid
+    kTag_SerializedSession_ResumptionSendMessageId      = 14, // [ UNSIGNED INT, range 32bits ] Send message id for a persisted
+                                                              //    session to resume from.
+    kTag_SerializedSession_ResumptionRecvMessageId      = 15, // [ UNSIGNED INT, range 32bits ] Next expected receive message id
+                                                              //    for a session resumed after persistence.
+    kTag_SerializedSession_IsUsedOverConnection         = 16, // [ BOOLEAN ] Is session used over a connection
 };
 
 // Weave-defined elliptic curve ids
diff --git a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp
index e077022..3f1f554 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp
@@ -365,6 +365,19 @@
     return mTunAgentState;
 }
 
+#if WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+void WeaveTunnelAgent::SetCallbacksForPersistedTunnelConnection(WeaveTunnelConnectionMgr::PersistedSecureSessionExistsFunct aIsPersistedTunnelSessionPresent,
+                                                                WeaveTunnelConnectionMgr::LoadPersistedSessionFunct aLoadPersistedTunnelSession)
+{
+    mPrimaryTunConnMgr.SetCallbacksForPersistedTunnelConnection(aIsPersistedTunnelSessionPresent, aLoadPersistedTunnelSession);
+
+#if WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
+    mBackupTunConnMgr.SetCallbacksForPersistedTunnelConnection(aIsPersistedTunnelSessionPresent, aLoadPersistedTunnelSession);
+#endif // WEAVE_CONFIG_TUNNEL_FAILOVER_SUPPORTED
+
+}
+#endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+
 /**
  * Shutdown the Tunnel Agent. This tears down connection to the Service and closes the TunEndPoint
  * interface after removing addresses and routes associated with the tunnel interface.
diff --git a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h
index 7ad0cd3..a252005 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h
@@ -265,6 +265,14 @@
  */
     WEAVE_ERROR ResetPrimaryReconnectBackoff(bool reconnectImmediately);
 
+#if WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+/**
+ * Set Callbacks for persisted tunnel connection
+ */
+    void SetCallbacksForPersistedTunnelConnection(WeaveTunnelConnectionMgr::PersistedSecureSessionExistsFunct aIsPersistedTunnelSessionPresent,
+                                                  WeaveTunnelConnectionMgr::LoadPersistedSessionFunct aLoadPersistedTunnelSession);
+#endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+
 #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 99df63e..7e4c5a5 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.cpp
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.cpp
@@ -46,9 +46,13 @@
 
 WeaveTunnelConnectionMgr::WeaveTunnelConnectionMgr(void)
 {
-    mConnectionState            = kState_NotConnected;
-    mServiceCon                 = NULL;
-    mTunFailedConnAttemptsInRow = 0;
+    mConnectionState                = kState_NotConnected;
+    mServiceCon                     = NULL;
+    mTunFailedConnAttemptsInRow     = 0;
+#if WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+    IsPersistedTunnelSessionPresent = NULL;
+    LoadPersistedTunnelSession      = NULL;
+#endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
 }
 
 /*
@@ -79,6 +83,7 @@
     mMaxFailedConAttemptsBeforeNotify = WEAVE_CONFIG_TUNNELING_MAX_NUM_CONNECT_BEFORE_NOTIFY;
     mServiceConnDelayPolicyCallback   = DefaultReconnectPolicyCallback;
     mResetReconnectArmed              = false;
+
     if (connIntfName)
     {
         strncpy(mServiceConIntf, connIntfName, sizeof(mServiceConIntf) - 1);
@@ -263,6 +268,15 @@
 }
 #endif // WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED
 
+#if WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+void WeaveTunnelConnectionMgr::SetCallbacksForPersistedTunnelConnection(PersistedSecureSessionExistsFunct aIsPersistedTunnelSessionPresent,
+                                                                        LoadPersistedSessionFunct aLoadPersistedTunnelSession)
+{
+    IsPersistedTunnelSessionPresent = aIsPersistedTunnelSessionPresent;
+    LoadPersistedTunnelSession      = aLoadPersistedTunnelSession;
+}
+#endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+
 /**
  * Try to establish a connecttion to the Service either using
  * ServiceManager or directly
@@ -272,6 +286,8 @@
 {
     WEAVE_ERROR err = WEAVE_NO_ERROR;
     InterfaceId connIntfId = INET_NULL_INTERFACEID;
+    WeaveAuthMode currentAuthMode = mTunAgent->mAuthMode;
+
 #if WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS
     WeaveTunnelCommonStatistics *tunStats = NULL;
 #endif // WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS
@@ -288,13 +304,25 @@
         SuccessOrExit(err);
     }
 
+#if WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+    // If Tunnel secure session is persisted and available then setup an unsecured WeaveConnection before using
+    // the persisted secure session to open the tunnel.
+    if (IsPersistedTunnelSessionPresent)
+    {
+        if (IsPersistedTunnelSessionPresent(kServiceEndpoint_WeaveTunneling))
+        {
+            currentAuthMode = kWeaveAuthMode_Unauthenticated;
+        }
+    }
+#endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+
 #if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
     // Initiate TCP connection with Service.
 
     if (mTunAgent->mServiceMgr)
     {
         err = mTunAgent->mServiceMgr->connect(mTunAgent->mPeerNodeId,
-                                              mTunAgent->mAuthMode, this,
+                                              currentAuthMode, this,
                                               ServiceMgrStatusHandler,
                                               HandleServiceConnectionComplete,
                                               WEAVE_CONFIG_TUNNEL_CONNECT_TIMEOUT_SECS * nl::Weave::System::kTimerFactor_milli_per_unit,
@@ -306,7 +334,7 @@
         err = StartServiceTunnelConn(mTunAgent->mPeerNodeId,
                                      mTunAgent->mServiceAddress,
                                      mTunAgent->mServicePort,
-                                     mTunAgent->mAuthMode,
+                                     currentAuthMode,
                                      connIntfId);
     }
 
@@ -732,6 +760,21 @@
     tConnMgr->mServiceCon->GetTCPEndPoint()->OnTCPSendIdleChanged = HandleTCPSendIdleChanged;
 #endif // WEAVE_CONFIG_TUNNEL_ENABLE_TCP_IDLE_CALLBACK
 
+#if WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+    if (tConnMgr->LoadPersistedTunnelSession)
+    {
+        // Set the configured AuthMode on the created WeaveConnection object
+        // before attempting to load persisted session. The callback will use
+        // this configuration information to make the decision of binding the
+        // persisted session to the connection.
+        con->AuthMode = tConnMgr->mTunAgent->mAuthMode;
+
+        // Pass the WeaveConnection object to have the persisted secure session
+        // loaded onto it.
+        tConnMgr->LoadPersistedTunnelSession(con);
+    }
+#endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+
     // Set the appropriate route priority based on the tunnel type
 
     if (tConnMgr->mTunType == kType_TunnelBackup)
diff --git a/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h b/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h
index 663ecd1..aac22c0 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h
@@ -112,6 +112,24 @@
 
     WeaveTunnelConnectionMgr(void);
 
+#if WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+/**
+ * Check if a persisted secure session for the tunnel exists
+ */
+    typedef bool (*PersistedSecureSessionExistsFunct)(uint64_t peerNodeId);
+    PersistedSecureSessionExistsFunct IsPersistedTunnelSessionPresent;
+
+/**
+ * Populate the persisted secure session within the passed WeaveConnection object
+ */
+    typedef void (*LoadPersistedSessionFunct)(WeaveConnection *con);
+    LoadPersistedSessionFunct LoadPersistedTunnelSession;
+
+    void SetCallbacksForPersistedTunnelConnection(PersistedSecureSessionExistsFunct aIsPersistedTunnelSessionPresent,
+                                                  LoadPersistedSessionFunct aLoadPersistedTunnelSession);
+
+#endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+
 /**
  * Initialize the WeaveTunnelConnectionMgr.
  */
diff --git a/src/test-apps/Makefile.am b/src/test-apps/Makefile.am
index 801e077..b613399 100644
--- a/src/test-apps/Makefile.am
+++ b/src/test-apps/Makefile.am
@@ -499,6 +499,8 @@
     TestWRMP                                     \
     TestWeaveMessageLayer                        \
     TestWeaveTunnelBR                            \
+    TestWeaveTunnelCASEPersistClient             \
+    TestWeaveTunnelCASEPersistServer             \
     TestWeaveTunnelServer                        \
     TestWdmNext                                  \
     TestWdmOneWayCommandSender                   \
@@ -1400,6 +1402,14 @@
 TestWeaveTunnelBR_LDFLAGS                = $(AM_CPPFLAGS)
 TestWeaveTunnelBR_LDADD                  = libWeaveTestCommon.a $(COMMON_LDADD)
 
+TestWeaveTunnelCASEPersistClient_SOURCES = TestWeaveTunnelCASEPersistClient.cpp
+TestWeaveTunnelCASEPersistClient_LDFLAGS = $(AM_CPPFLAGS)
+TestWeaveTunnelCASEPersistClient_LDADD   = libWeaveTestCommon.a $(COMMON_LDADD)
+
+TestWeaveTunnelCASEPersistServer_SOURCES = TestWeaveTunnelCASEPersistServer.cpp
+TestWeaveTunnelCASEPersistServer_LDFLAGS = $(AM_CPPFLAGS)
+TestWeaveTunnelCASEPersistServer_LDADD   = libWeaveTestCommon.a $(COMMON_LDADD)
+
 TestWeaveTunnelServer_SOURCES            = TestWeaveTunnelServer.cpp
 TestWeaveTunnelServer_LDFLAGS            = $(AM_CPPFLAGS)
 TestWeaveTunnelServer_LDADD              = libWeaveTestCommon.a $(COMMON_LDADD)
diff --git a/src/test-apps/TestWeaveTunnel.h b/src/test-apps/TestWeaveTunnel.h
index aa3bf33..23b2fb6 100644
--- a/src/test-apps/TestWeaveTunnel.h
+++ b/src/test-apps/TestWeaveTunnel.h
@@ -82,6 +82,7 @@
     kTestNum_TestTunnelNoStatusReportResetReconnectBackoff      = 26,
     kTestNum_TestTunnelRestrictedRoutingOnStandaloneTunnelOpen  = 27,
     kTestNum_TestTunnelTCPIdle                                  = 28,
+    kTestNum_TestTunnelPersistCASESession                       = 29,
 };
 
 #endif // WEAVE_CONFIG_ENABLE_TUNNELING
diff --git a/src/test-apps/TestWeaveTunnelCASEPersistClient.cpp b/src/test-apps/TestWeaveTunnelCASEPersistClient.cpp
new file mode 100644
index 0000000..a38735b
--- /dev/null
+++ b/src/test-apps/TestWeaveTunnelCASEPersistClient.cpp
@@ -0,0 +1,573 @@
+/*
+ *
+ *    Copyright (c) 2020 Google LLC.
+ *    Copyright (c) 2014-2017 Nest Labs, Inc.
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file implements the Weave Mock Weave Border Gateway.
+ *
+ *      This is used to instantiate a Tunnel Agent which opens a
+ *      tunnel endpoint and forwards IPv6 packets between the
+ *      Service connection and the tunnel endpoint.
+ *
+ */
+
+#define __STDC_FORMAT_MACROS
+
+
+#include <inttypes.h>
+#include <unistd.h>
+#include <string.h>
+#include <fstream>
+
+#include "ToolCommon.h"
+#include "CASEOptions.h"
+#include <Weave/Support/logging/DecodedIPPacket.h>
+#include <Weave/Profiles/ProfileCommon.h>
+#include <Weave/Profiles/weave-tunneling/WeaveTunnelAgent.h>
+#include <Weave/Profiles/weave-tunneling/WeaveTunnelCommon.h>
+#include <InetLayer/InetInterface.h>
+#include <Weave/Support/WeaveFaultInjection.h>
+#include <Weave/Profiles/device-description/DeviceDescription.h>
+#include <Weave/Profiles/vendor/nestlabs/device-description/NestProductIdentifiers.hpp>
+
+#if WEAVE_CONFIG_ENABLE_TUNNELING
+using namespace ::nl::Weave::Profiles::WeaveTunnel;
+
+#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+using namespace ::nl::Weave::Profiles::DeviceDescription;
+#endif
+
+#define TOOL_NAME "TestTunnelCASEPersistClient"
+
+#define DEFAULT_BG_NODE_ID  (0x18b4300000000001)
+#define DEFAULT_TFE_NODE_ID (0x18b4300000000002)
+#define BUFF_AVAILABLE_SIZE (1024)
+#define PERSISTENT_TUNNEL_SESSION_PATH  "./persistentTunnelCASE-BR"
+
+static bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg);
+static bool HandleNonOptionArgs(const char *progName, int argc, char *argv[]);
+static void HandleLoadPersistedTunnelCASESession(nl::Weave::WeaveConnection *con);
+static void HandleSessionPersistOnTunnelClosure(nl::Weave::WeaveConnection *con);
+static WEAVE_ERROR RestorePersistedTunnelCASESession(nl::Weave::WeaveConnection *con);
+static WEAVE_ERROR SuspendAndPersistTunnelCASESession(nl::Weave::WeaveConnection *con);
+static bool IsPersistentTunnelSessionPresent(uint64_t peerNodeId);
+
+static void
+WeaveTunnelOnStatusNotifyHandlerCB(WeaveTunnelConnectionMgr::TunnelConnNotifyReasons reason,
+                                   WEAVE_ERROR aErr, void *appCtxt);
+static WeaveTunnelAgent gTunAgent;
+
+uint8_t gTunUpCount = 0;
+bool gTestSucceeded = false;
+static bool gTunnelLogging = false;
+static IPAddress gDestAddr = IPAddress::Any;
+static uint16_t gDestPort = 0;
+static uint64_t gDestNodeId = DEFAULT_TFE_NODE_ID;
+
+#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+static bool gUseServiceDirForTunnel = false;
+static ServiceDirectory::WeaveServiceManager gServiceMgr;
+static uint8_t gServiceDirCache[500];
+#endif
+
+static uint8_t gRole = kClientRole_BorderGateway; //Default Value
+
+enum
+{
+    kToolOpt_ConnectTo          = 1000,
+    kToolOpt_UseServiceDir,
+};
+
+static OptionDef gToolOptionDefs[] =
+{
+    { "connect-to",          kArgumentRequired, kToolOpt_ConnectTo },
+#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+    { "service-dir",         kNoArgument,       kToolOpt_UseServiceDir },
+#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+#if WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
+    { "tunnel-log",          kNoArgument,       'l' },
+#endif // WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
+    { }
+};
+
+static const char *const gToolOptionHelp =
+    "  --connect-to <addr>[:<port>][%<interface>]\n"
+    "       Connect to the tunnel service at the supplied address.\n"
+    "\n"
+#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+    "  --service-dir\n"
+    "       Use service directory to lookup the address of the tunnel server.\n"
+    "\n"
+#endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+#if WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
+    "  -l, --tunnel-log\n"
+    "       Use detailed logging of Tunneled IP packet\n"
+    "\n"
+#endif // WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
+    "";
+
+static OptionSet gToolOptions =
+{
+    HandleOption,
+    gToolOptionDefs,
+    "GENERAL OPTIONS",
+    gToolOptionHelp
+};
+
+static HelpOptions gHelpOptions(
+    TOOL_NAME,
+    "Usage: " TOOL_NAME " <options>\n",
+    WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT
+);
+
+static OptionSet *gToolOptionSets[] =
+{
+    &gToolOptions,
+    &gNetworkOptions,
+    &gWeaveNodeOptions,
+    &gWRMPOptions,
+    &gCASEOptions,
+    &gDeviceDescOptions,
+    &gServiceDirClientOptions,
+    &gFaultInjectionOptions,
+    &gHelpOptions,
+    NULL
+};
+
+bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg)
+{
+    switch (id)
+    {
+    case kToolOpt_ConnectTo:
+    {
+        const char *host;
+        uint16_t hostLen;
+        if (ParseHostAndPort(arg, strlen(arg), host, hostLen, gDestPort) != WEAVE_NO_ERROR)
+        {
+            PrintArgError("%s: Invalid value specified for --connect-to: %s\n", progName, arg);
+            return false;
+        }
+        char *hostCopy = strndup(host, hostLen);
+        bool isValidAddr = IPAddress::FromString(hostCopy, gDestAddr);
+        free(hostCopy);
+        if (!isValidAddr)
+        {
+            PrintArgError("%s: Invalid value specified for --connect-to (expected IP address): %s\n", progName, arg);
+            return false;
+        }
+        break;
+    }
+#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+    case kToolOpt_UseServiceDir:
+        gUseServiceDirForTunnel = true;
+        break;
+#endif
+    case 'l':
+        gTunnelLogging = true;
+        break;
+    default:
+        PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name);
+        return false;
+    }
+
+    return true;
+}
+
+bool HandleNonOptionArgs(const char *progName, int argc, char *argv[])
+{
+    if (argc > 0)
+    {
+        if (argc > 1)
+        {
+            PrintArgError("%s: Unexpected argument: %s\n", progName, argv[1]);
+            return false;
+        }
+
+        if (!ParseNodeId(argv[0], gDestNodeId))
+        {
+            PrintArgError("%s: Invalid value specified for destination node-id: %s\n", progName, argv[0]);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+#if WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
+void TunneledPacketTransitHandler(const PacketBuffer &pkt, TunnelPktDirection pktDir, TunnelType tunnelType, bool &toDrop)
+{
+    DecodedIPPacket decodedPkt;
+    char InOrOut[9];
+    char tunTypeStr[9];
+
+    // Decode the packet; skip the tunnel header and pass the IP packet
+
+    decodedPkt.PacketHeaderDecode(pkt.Start() + TUN_HDR_SIZE_IN_BYTES, pkt.DataLength() - TUN_HDR_SIZE_IN_BYTES);
+
+    strncpy(InOrOut, (pktDir == kDir_Outbound) ? "Outbound" : "Inbound", sizeof(InOrOut));
+    strncpy(tunTypeStr, (tunnelType == kType_TunnelPrimary) ? "primary" : (tunnelType == kType_TunnelBackup) ? "backup" : "shortcut", sizeof(tunTypeStr));
+
+    WeaveLogDetail(WeaveTunnel, "Tun: %s over %s", InOrOut, tunTypeStr);
+
+    // Log the header fields
+
+    LogPacket(decodedPkt, true);
+
+    // Inject a packet drop by the application.
+    WEAVE_FAULT_INJECT(FaultInjection::kFault_TunnelPacketDropByPolicy,
+                       toDrop = true);
+
+}
+#endif // WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
+
+void
+WeaveTunnelOnStatusNotifyHandlerCB(WeaveTunnelConnectionMgr::TunnelConnNotifyReasons reason,
+                                   WEAVE_ERROR aErr, void *appCtxt)
+{
+    WeaveLogDetail(WeaveTunnel, "WeaveTunnelAgent notification reason code is %d", reason);
+
+    if (reason == WeaveTunnelConnectionMgr::kStatus_TunPrimaryUp)
+    {
+        if (gTunUpCount < 1)
+        {
+            gTunUpCount++;
+
+            gTunAgent.StopServiceTunnel(WEAVE_ERROR_TUNNEL_FORCE_ABORT);
+
+            gTunAgent.StartServiceTunnel();
+        }
+        else
+        {
+            gTestSucceeded = true;
+        }
+    }
+    else
+    {
+        gTestSucceeded = false;
+    }
+}
+
+bool PersistedSessionKeyExists(const char *name)
+{
+    return (access(name, F_OK) != -1);
+}
+
+WEAVE_ERROR
+SuspendAndPersistTunnelCASESession(nl::Weave::WeaveConnection *con)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    std::ofstream persistentTunOfStream;
+    nl::Weave::WeaveSessionKey * persistedTunnelSessionKey = NULL;
+    const char * persistentTunnelSessionPath = PERSISTENT_TUNNEL_SESSION_PATH;
+
+    VerifyOrExit(con, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+    // If exist, this functions has already been called
+    VerifyOrExit(!PersistedSessionKeyExists(persistentTunnelSessionPath),
+                 err = WEAVE_ERROR_SESSION_KEY_SUSPENDED);
+
+    uint8_t buf[BUFF_AVAILABLE_SIZE];
+    uint16_t dataLen;
+
+    err = FabricState.FindSessionKey(con->DefaultKeyId, con->PeerNodeId, false,
+                                      persistedTunnelSessionKey);
+    SuccessOrExit(err);
+
+    // Set the resumptionMsgIdValid flag
+    persistedTunnelSessionKey->SetResumptionMsgIdsValid(true);
+
+    // This call suspends the CASE session and returns a serialized byte stream
+    err = FabricState.SuspendSession(persistedTunnelSessionKey->MsgEncKey.KeyId,
+                                      persistedTunnelSessionKey->NodeId,
+                                      buf,
+                                      BUFF_AVAILABLE_SIZE,
+                                      dataLen);
+    SuccessOrExit(err);
+
+    // If success, set goodbit in the internal state flag
+    // In case of failure, set failbit.
+    persistentTunOfStream.open(persistentTunnelSessionPath, std::ofstream::binary | std::ios::trunc);
+    // If not open and associated with this stream object, directly fail
+    VerifyOrExit(persistentTunOfStream.is_open(),
+                 err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
+    // If fail, sets badbit or failbit in the internal state flags
+    persistentTunOfStream.write((char*)buf, dataLen);
+    // In case of failure, set failbit.
+    persistentTunOfStream.close();
+    // Check the stream's state flags
+    VerifyOrExit(persistentTunOfStream.good(),
+                 err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
+
+    printf("Suspending and persisting of tunnel CASE session successful\n");
+
+exit:
+    if (err != WEAVE_NO_ERROR)
+    {
+        printf("Suspending and persisting of tunnel CASE Session failed with Weave error: %d\n", err);
+    }
+
+    return err;
+}
+
+WEAVE_ERROR
+RestorePersistedTunnelCASESession(nl::Weave::WeaveConnection *con)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    std::ifstream persistentCASE;
+    const char * persistentTunnelSessionPath = PERSISTENT_TUNNEL_SESSION_PATH;
+
+    if (PersistedSessionKeyExists(persistentTunnelSessionPath))
+    {
+        printf("persistent tunnel CASE session exists\n");
+        uint8_t buf[BUFF_AVAILABLE_SIZE];
+
+        // In case of failure, set failbit.
+        persistentCASE.open(persistentTunnelSessionPath, std::ifstream::binary);
+        VerifyOrExit(persistentCASE.is_open(),
+                     err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
+
+        persistentCASE.seekg(0, persistentCASE.end);
+        uint16_t dataLen = persistentCASE.tellg();
+        persistentCASE.seekg(0, persistentCASE.beg);
+
+        VerifyOrExit(dataLen <= BUFF_AVAILABLE_SIZE,
+                     err = WEAVE_ERROR_BUFFER_TOO_SMALL);
+
+        persistentCASE.read((char*) buf, dataLen);
+        // In case of failure, set failbit.
+        persistentCASE.close();
+
+        VerifyOrExit(!persistentCASE.fail(),
+                     err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
+
+        // delete persist storage before restore session
+        VerifyOrExit(std::remove(persistentTunnelSessionPath) == 0,
+                     err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
+
+        err = FabricState.RestoreSession(buf, dataLen, con);
+        SuccessOrExit(err);
+
+        printf("Restored persistent tunnel CASE session successfully\n");
+    }
+    else
+    {
+        printf("Persistent tunnel CASE Session doesn't exist\n");
+    }
+exit:
+    if (persistentCASE.is_open())
+    {
+        persistentCASE.close();
+    }
+
+    if (err != WEAVE_NO_ERROR)
+    {
+        printf("Restore Persistent CASE Session Failed with weave err: %d\n", err);
+    }
+
+    return err;
+}
+
+void HandleLoadPersistedTunnelCASESession(nl::Weave::WeaveConnection *con)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    err = RestorePersistedTunnelCASESession(con);
+
+    if (err != WEAVE_NO_ERROR)
+    {
+        printf("Restoring Tunnel CASE Session failed with Weave error: %d\n", err);
+    }
+}
+
+void HandleSessionPersistOnTunnelClosure(nl::Weave::WeaveConnection *con)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    err = SuspendAndPersistTunnelCASESession(con);
+
+    if (err != WEAVE_NO_ERROR)
+    {
+        printf("Suspending and persisting Tunnel CASE Session failed with Weave error: %d\n", err);
+    }
+}
+
+bool IsPersistentTunnelSessionPresent(uint64_t peerNodeId)
+{
+    const char * persistentTunnelSessionPath = PERSISTENT_TUNNEL_SESSION_PATH;
+
+    return (access(persistentTunnelSessionPath, F_OK) != -1);
+}
+#endif // WEAVE_CONFIG_ENABLE_TUNNELING
+
+int main(int argc, char *argv[])
+{
+#if WEAVE_CONFIG_ENABLE_TUNNELING
+    WEAVE_ERROR err;
+    gWeaveNodeOptions.LocalNodeId = DEFAULT_BG_NODE_ID;
+    WeaveAuthMode authMode = kWeaveAuthMode_CASE_AnyCert;
+
+    nl::Weave::System::Stats::Snapshot before;
+    nl::Weave::System::Stats::Snapshot after;
+    const bool printStats = true;
+
+    InitToolCommon();
+
+    SetupFaultInjectionContext(argc, argv);
+    UseStdoutLineBuffering();
+    SetSignalHandler(DoneOnHandleSIGUSR1);
+
+    // Configure some alternate defaults for the device descriptor values.
+    gDeviceDescOptions.BaseDeviceDesc.ProductId = nl::Weave::Profiles::Vendor::Nestlabs::DeviceDescription::kNestWeaveProduct_Onyx;
+    strcpy(gDeviceDescOptions.BaseDeviceDesc.SerialNumber, "test-weave-tunnel-persist");
+    strcpy(gDeviceDescOptions.BaseDeviceDesc.SoftwareVersion, "test-weave-tunnel-persist/1.0");
+    gDeviceDescOptions.BaseDeviceDesc.DeviceFeatures = WeaveDeviceDescriptor::kFeature_LinePowered;
+
+    if (argc == 1)
+    {
+        gHelpOptions.PrintBriefUsage(stderr);
+        exit(EXIT_FAILURE);
+    }
+
+    if (!ParseArgsFromEnvVar(TOOL_NAME, TOOL_OPTIONS_ENV_VAR_NAME, gToolOptionSets, NULL, true) ||
+        !ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets, HandleNonOptionArgs) ||
+        !ResolveWeaveNetworkOptions(TOOL_NAME, gWeaveNodeOptions, gNetworkOptions))
+    {
+        exit(EXIT_FAILURE);
+    }
+
+#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+    if (gUseServiceDirForTunnel && gDestAddr != IPAddress::Any)
+    {
+        printf("ERROR: Please specify only one of --connect-to or --service-dir\n");
+        exit(EXIT_FAILURE);
+    }
+    if (!gUseServiceDirForTunnel && gDestAddr == IPAddress::Any)
+    {
+        printf("ERROR: Please specify how to find the tunnel server using either --connect-to or --service-dir\n");
+        exit(EXIT_FAILURE);
+    }
+#else
+    if (gDestAddr == IPAddress::Any)
+    {
+        printf("ERROR: Please specify the address of the tunnel server using --connect-to\n");
+        exit(EXIT_FAILURE);
+    }
+#endif
+
+    InitSystemLayer();
+
+    InitNetwork();
+
+    InitWeaveStack(false, true);
+
+    printf("Weave Node Configuration:\n");
+    printf("  Fabric Id: %" PRIX64 "\n", FabricState.FabricId);
+    printf("  Subnet Number: %X\n", FabricState.DefaultSubnet);
+    printf("  Node Id: %" PRIX64 "\n", FabricState.LocalNodeId);
+
+    nl::Weave::Stats::UpdateSnapshot(before);
+
+#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+    err = gServiceMgr.init(&ExchangeMgr, gServiceDirCache, sizeof(gServiceDirCache),
+            GetRootServiceDirectoryEntry, kWeaveAuthMode_CASE_ServiceEndPoint,
+            NULL, NULL, OverrideServiceConnectArguments);
+    FAIL_ERROR(err, "gServiceMgr.Init failed");
+#endif
+
+#if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+    if (gUseServiceDirForTunnel)
+    {
+        err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId,
+                             authMode, &gServiceMgr,
+                             "weave-tun0", gRole);
+    }
+    else
+#endif
+    {
+        err = gTunAgent.Init(&Inet, &ExchangeMgr, gDestNodeId, gDestAddr,
+                             authMode,
+                             "weave-tun0", gRole);
+    }
+
+    FAIL_ERROR(err, "TunnelAgent.Init failed");
+
+    gTunAgent.OnServiceTunStatusNotify = WeaveTunnelOnStatusNotifyHandlerCB;
+
+    if (gDestAddr != IPAddress::Any)
+    {
+        gTunAgent.SetDestination(gDestNodeId, gDestAddr, gDestPort);
+    }
+
+#if WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
+    if (gTunnelLogging)
+    {
+        gTunAgent.OnTunneledPacketTransit = TunneledPacketTransitHandler;
+    }
+    else
+    {
+        gTunAgent.OnTunneledPacketTransit = NULL;
+    }
+#endif // WEAVE_CONFIG_TUNNEL_ENABLE_TRANSIT_CALLBACK
+
+#if WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+    gTunAgent.SetCallbacksForPersistedTunnelConnection(IsPersistentTunnelSessionPresent, HandleLoadPersistedTunnelCASESession);
+#endif // WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
+    FabricState.BoundConnectionClosedForSession = HandleSessionPersistOnTunnelClosure;
+
+    // Test case of Starting the Tunnel by negotiating the CASE session
+    err = gTunAgent.StartServiceTunnel();
+    FAIL_ERROR(err, "TunnelAgent.StartServiceTunnel failed");
+
+    while (!Done)
+    {
+        struct timeval sleepTime;
+        sleepTime.tv_sec = 0;
+        sleepTime.tv_usec = 100000;
+
+        ServiceNetwork(sleepTime);
+
+        if (gTestSucceeded)
+        {
+            Done = true;
+        }
+        else
+        {
+            continue;
+        }
+    }
+
+    if (gSigusr1Received) {
+        printf("SIGUSR1 received: proceed to exit gracefully\n");
+    }
+
+    gTunAgent.StopServiceTunnel(WEAVE_ERROR_TUNNEL_FORCE_ABORT);
+    gTunAgent.Shutdown();
+
+    ProcessStats(before, after, printStats, NULL);
+    PrintFaultInjectionCounters();
+
+    ShutdownWeaveStack();
+    ShutdownNetwork();
+    ShutdownSystemLayer();
+
+#endif // WEAVE_CONFIG_ENABLE_TUNNELING
+    return EXIT_SUCCESS;
+}
diff --git a/src/test-apps/TestWeaveTunnelCASEPersistServer.cpp b/src/test-apps/TestWeaveTunnelCASEPersistServer.cpp
new file mode 100644
index 0000000..86ed3f2
--- /dev/null
+++ b/src/test-apps/TestWeaveTunnelCASEPersistServer.cpp
@@ -0,0 +1,571 @@
+/*
+ *
+ *    Copyright (c) 2018 Google LLC.
+ *    Copyright (c) 2014-2017 Nest Labs, Inc.
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file implements the Weave Mock Tunnel Service.
+ *
+ *      This instantiates a Server that accepts connections from
+ *      a border gateway and may perform routing functions
+ *      between different border gateways or respond to ping6
+ *      over the tunnel.
+ *      Beyond the Tunneling profile, the server also understands
+ *      private test profiles (@see TestWeaveTunnel.h).
+ *      The tunnel client implemented in @see TestWeaveTunnelBR.cpp
+ *      uses the private profiles to test various scenarios.
+ */
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include <unistd.h>
+#include <fstream>
+
+#include "ToolCommon.h"
+#include <Weave/Core/WeaveEncoding.h>
+#include <Weave/Core/WeaveSecurityMgr.h>
+#include <Weave/Profiles/security/WeaveSecurity.h>
+#include <Weave/Profiles/service-directory/ServiceDirectory.h>
+#include <Weave/Support/CodeUtils.h>
+#include <Weave/Support/logging/WeaveLogging.h>
+#include <Weave/Profiles/ProfileCommon.h>
+#include <Weave/Profiles/echo/WeaveEcho.h>
+#include <Weave/Core/WeaveTLV.h>
+#include "TestWeaveTunnel.h"
+#include "TestWeaveTunnelServer.h"
+
+#if WEAVE_CONFIG_ENABLE_TUNNELING
+
+#define TOOL_NAME "TestWeaveTunnelCASEPersistServer"
+
+#define TUNNEL_SERVICE_INTF "service-tun0"
+#define TUNNEL_SERVICE_LL_ADDR "fe80::2"
+#define DEFAULT_TFE_NODE_ID (0x18b4300000000002)
+#define BUFF_AVAILABLE_SIZE (1024)
+
+#define PERSISTENT_TUNNEL_SESSION_PATH  "./persistentTunnelCASE-Server"
+
+using nl::StatusReportStr;
+using namespace nl::Weave::Encoding;
+using namespace nl::Weave::Profiles::WeaveTunnel;
+using namespace nl::Weave::TLV;
+
+/**
+ * Handler for a Weave Tunnel control message.
+ */
+static void HandleTunnelControlMsg(ExchangeContext *ec, const IPPacketInfo *pktInfo,
+                                   const WeaveMessageInfo *msgInfo, uint32_t profileId,
+                                   uint8_t msgType, PacketBuffer *payload);
+
+static WEAVE_ERROR TunServerInit (WeaveExchangeManager *exchangeMgr);
+static WEAVE_ERROR TunServerShutdown (void);
+
+static void HandleConnectionClosed(WeaveConnection *con, WEAVE_ERROR conErr);
+
+static void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con);
+
+static void HandleSecureSessionEstablished(WeaveSecurityManager *sm, WeaveConnection *con,
+                                           void *reqState, uint16_t sessionKeyId, uint64_t peerNodeId,
+                                           uint8_t encType);
+static void HandleSecureSessionError(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState,
+                                     WEAVE_ERROR localErr, uint64_t peerNodeId, StatusReport *statusReport);
+
+static WEAVE_ERROR SendStatusReportResponse(ExchangeContext *ec, uint32_t profileId, uint32_t tunStatusCode,
+                                            bool isRoutingRestricted = false);
+
+static void HandleSessionPersistOnTunnelClosure(nl::Weave::WeaveConnection *con);
+static WEAVE_ERROR RestorePersistedTunnelCASESession(nl::Weave::WeaveConnection *con);
+static bool IsPersistentTunnelSessionPresent(uint64_t peerNodeId);
+
+static HelpOptions gHelpOptions(
+    TOOL_NAME,
+    "Usage: " TOOL_NAME " [<options...>]\n",
+    WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT
+);
+
+static OptionSet *gToolOptionSets[] =
+{
+    &gNetworkOptions,
+    &gWeaveNodeOptions,
+    &gCASEOptions,
+    &gDeviceDescOptions,
+    &gFaultInjectionOptions,
+    &gHelpOptions,
+    NULL
+};
+
+void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con)
+{
+    char ipAddrStr[64];
+
+    con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
+
+    WeaveLogDetail(WeaveTunnel, "Connection received from node (%s)\n", ipAddrStr);
+
+    con->OnConnectionClosed = HandleConnectionClosed;
+
+    if (IsPersistentTunnelSessionPresent(kServiceEndpoint_WeaveTunneling))
+    {
+        RestorePersistedTunnelCASESession(con);
+    }
+}
+
+WEAVE_ERROR TunServerInit (WeaveExchangeManager *exchangeMgr)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    //ExchMgr = exchangeMgr;
+
+    MessageLayer.OnConnectionReceived = HandleConnectionReceived;
+    MessageLayer.OnReceiveError = HandleMessageReceiveError;
+    MessageLayer.OnAcceptError = HandleAcceptConnectionError;
+
+    ExchangeMgr.RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
+                                                   kMsgType_TunnelOpenV2, HandleTunnelControlMsg,
+                                                   NULL);
+    ExchangeMgr.RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
+                                                   kMsgType_TunnelRouteUpdate, HandleTunnelControlMsg,
+                                                   NULL);
+    ExchangeMgr.RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
+                                                   kMsgType_TunnelClose, HandleTunnelControlMsg,
+                                                   NULL);
+    ExchangeMgr.RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
+                                                   kMsgType_TunnelLiveness, HandleTunnelControlMsg,
+                                                   NULL);
+
+    SecurityMgr.OnSessionEstablished = HandleSecureSessionEstablished;
+    SecurityMgr.OnSessionError = HandleSecureSessionError;
+
+    return err;
+}
+
+WEAVE_ERROR TunServerShutdown (void)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    ExchangeMgr.UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
+                                                   kMsgType_TunnelOpenV2);
+    ExchangeMgr.UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
+                                                   kMsgType_TunnelRouteUpdate);
+    ExchangeMgr.UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
+                                                   kMsgType_TunnelClose);
+    ExchangeMgr.UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling,
+                                                   kMsgType_TunnelLiveness);
+
+    return err;
+}
+
+bool PersistedSessionKeyExists(const char *name)
+{
+    return (access(name, F_OK) != -1);
+}
+
+bool IsPersistentTunnelSessionPresent(uint64_t peerNodeId)
+{
+    const char * persistentTunnelSessionPath = PERSISTENT_TUNNEL_SESSION_PATH;
+
+    return PersistedSessionKeyExists(persistentTunnelSessionPath);
+}
+
+WEAVE_ERROR
+SuspendAndPersistTunnelCASESession(nl::Weave::WeaveConnection *con)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    std::ofstream persistentTunOfStream;
+    nl::Weave::WeaveSessionKey * persistedTunnelSessionKey;
+    const char * persistentTunnelSessionPath = PERSISTENT_TUNNEL_SESSION_PATH;
+
+    VerifyOrExit(con, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+    // If exist, this functions has already been called
+    VerifyOrExit(!PersistedSessionKeyExists(persistentTunnelSessionPath),
+                 err = WEAVE_ERROR_SESSION_KEY_SUSPENDED);
+
+    uint8_t buf[BUFF_AVAILABLE_SIZE];
+    uint16_t dataLen;
+
+    err = FabricState.FindSessionKey(con->DefaultKeyId, con->PeerNodeId, false,
+                                      persistedTunnelSessionKey);
+    SuccessOrExit(err);
+
+    // Set the resumptionMsgIdValid flag
+    persistedTunnelSessionKey->SetResumptionMsgIdsValid(true);
+
+    // This call suspends the CASE session and returns a serialized byte stream
+    err = FabricState.SuspendSession(persistedTunnelSessionKey->MsgEncKey.KeyId,
+                                      persistedTunnelSessionKey->NodeId,
+                                      buf,
+                                      BUFF_AVAILABLE_SIZE,
+                                      dataLen);
+    SuccessOrExit(err);
+
+    // If success, set goodbit in the internal state flag
+    // In case of failure, set failbit.
+    persistentTunOfStream.open(persistentTunnelSessionPath, std::ofstream::binary | std::ios::trunc);
+    // If not open and associated with this stream object, directly fail
+    VerifyOrExit(persistentTunOfStream.is_open(),
+                 err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
+    // If fail, sets badbit or failbit in the internal state flags
+    persistentTunOfStream.write((char*)buf, dataLen);
+    // In case of failure, set failbit.
+    persistentTunOfStream.close();
+    // Check the stream's state flags
+    VerifyOrExit(persistentTunOfStream.good(),
+                 err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
+
+    printf("Suspending and persisting of tunnel CASE session successful\n");
+
+exit:
+    if (err != WEAVE_NO_ERROR)
+    {
+        printf("Suspending and persisting of tunnel CASE Session failed with Weave error: %d\n", err);
+    }
+
+    return err;
+}
+
+WEAVE_ERROR
+RestorePersistedTunnelCASESession(nl::Weave::WeaveConnection *con)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    std::ifstream persistentCASE;
+    const char * persistentTunnelSessionPath = PERSISTENT_TUNNEL_SESSION_PATH;
+
+    if (PersistedSessionKeyExists(persistentTunnelSessionPath))
+    {
+        printf("persistent tunnel CASE session exists\n");
+        uint8_t buf[BUFF_AVAILABLE_SIZE];
+
+        // In case of failure, set failbit.
+        persistentCASE.open(persistentTunnelSessionPath, std::ifstream::binary);
+        VerifyOrExit(persistentCASE.is_open(),
+                     err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
+
+        persistentCASE.seekg(0, persistentCASE.end);
+        uint16_t dataLen = persistentCASE.tellg();
+        persistentCASE.seekg(0, persistentCASE.beg);
+
+        VerifyOrExit(dataLen <= BUFF_AVAILABLE_SIZE,
+                     err = WEAVE_ERROR_BUFFER_TOO_SMALL);
+
+        persistentCASE.read((char*) buf, dataLen);
+        // In case of failure, set failbit.
+        persistentCASE.close();
+
+        VerifyOrExit(!persistentCASE.fail(),
+                     err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
+
+        // delete persist storage before restore session
+        VerifyOrExit(std::remove(persistentTunnelSessionPath) == 0,
+                     err = WEAVE_ERROR_PERSISTED_STORAGE_FAIL);
+
+        con->AuthMode = kWeaveAuthModeCategory_CASE;
+        err = FabricState.RestoreSession(buf, dataLen, con);
+        SuccessOrExit(err);
+
+        printf("Restored persistent tunnel CASE session successfully\n");
+    }
+    else
+    {
+        printf("Persistent tunnel CASE Session doesn't exist\n");
+    }
+exit:
+    if (persistentCASE.is_open())
+    {
+        persistentCASE.close();
+    }
+
+    if (err != WEAVE_NO_ERROR)
+    {
+        printf("Restore Persistent CASE Session Failed with weave err: %d\n", err);
+    }
+
+    return err;
+}
+
+void HandleSessionPersistOnTunnelClosure(nl::Weave::WeaveConnection *con)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    err = SuspendAndPersistTunnelCASESession(con);
+
+    if (err != WEAVE_NO_ERROR)
+    {
+        printf("Suspending and persisting Tunnel CASE Session failed with Weave error: %d\n", err);
+    }
+}
+
+/* Send a tunnel control status report message */
+WEAVE_ERROR SendStatusReportResponse(ExchangeContext *ec, uint32_t profileId, uint32_t tunStatusCode,
+                                     bool isRoutingRestricted)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    StatusReport tunStatusReport;
+    PacketBuffer *msgBuf = NULL;
+    nl::Weave::TLV::TLVWriter tunWriter;
+    nl::Weave::TLV::TLVType containerType;
+    uint8_t *p = NULL;
+
+    msgBuf = PacketBuffer::New();
+    VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+    p = msgBuf->Start();
+
+    // Encode the profile id and status code.
+    LittleEndian::Write32(p, profileId);
+    LittleEndian::Write16(p, tunStatusCode);
+    msgBuf->SetDataLength(4 + 2);
+
+    if (isRoutingRestricted)
+    {
+        // Encode the Tunnel TLVData.
+        tunWriter.Init(msgBuf);
+
+        // Start the anonymous container that wraps the contents.
+        err = tunWriter.StartContainer(AnonymousTag, kTLVType_Structure, containerType);
+        SuccessOrExit(err);
+
+        // Write the boolean tag
+        err = tunWriter.PutBoolean(ProfileTag(kWeaveProfile_Tunneling, kTag_TunnelRoutingRestricted), true);
+        SuccessOrExit(err);
+
+        // End the anonymous container that wraps the contents.
+        err = tunWriter.EndContainer(containerType);
+        SuccessOrExit(err);
+
+        err = tunWriter.Finalize();
+        SuccessOrExit(err);
+    }
+
+    err = ec->SendMessage(kWeaveProfile_Common, Common::kMsgType_StatusReport, msgBuf, 0);
+    msgBuf = NULL;
+
+exit:
+    if (msgBuf != NULL)
+        PacketBuffer::Free(msgBuf);
+
+    return err;
+}
+
+void HandleTunnelControlMsg (ExchangeContext *ec, const IPPacketInfo *pktInfo,
+                             const WeaveMessageInfo *msgInfo, uint32_t profileId,
+                             uint8_t msgType, PacketBuffer *payload)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    WeaveTunnelRoute tunRoute;
+    uint64_t msgFabricId = 0;
+    uint8_t *p = NULL;
+    Role role;
+    TunnelType tunnelType;
+    SrcInterfaceType srcIntfType;
+    LivenessStrategy livenessStrategy;
+    uint16_t livenessTimeout;
+    bool isRoutingRestricted = false;
+
+    if (profileId == kWeaveProfile_Tunneling)
+    {
+        switch (msgType)
+        {
+            case kMsgType_TunnelOpenV2:
+                //Decode the Tunnel Device Role, TunnelType and Source Interface
+                p = payload->Start();
+
+                role = static_cast<Role>(nl::Weave::Encoding::Read8(p));
+
+                tunnelType = static_cast<TunnelType>(nl::Weave::Encoding::Read8(p));
+
+                srcIntfType = static_cast<SrcInterfaceType>(nl::Weave::Encoding::Read8(p));
+
+                livenessStrategy = static_cast<LivenessStrategy>(nl::Weave::Encoding::Read8(p));
+
+                livenessTimeout = nl::Weave::Encoding::LittleEndian::Read16(p);
+
+                WeaveLogDetail(WeaveTunnel, "Received TunOpenV2 message for Tunnel role :%u, type :%u, \
+                                             srcIntf :%u, livenessStrategy :%u, livenessTimeout:%u\n",
+                                            role, tunnelType, srcIntfType, livenessStrategy, livenessTimeout);
+
+                // Set the buffer start pointer for the subsequent parsing of the fabric and routes
+                payload->SetStart(p);
+
+                //Save the routes and connection object
+                memset(&tunRoute, 0, sizeof(tunRoute));
+                err = WeaveTunnelRoute::DecodeFabricTunnelRoutes(&msgFabricId, &tunRoute, payload);
+                SuccessOrExit(err);
+
+                // Send a status report
+
+                err = SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_Success, isRoutingRestricted);
+                SuccessOrExit(err);
+
+                break;
+            case kMsgType_TunnelRouteUpdate:
+
+                // The reason this is not implemented yet is because for all practical purposes of developmental testing
+                // we have not needed to modify the routes that were already sent with the TunnelOpen messages. However,
+                // this message keeps that possibility open to modify the routes that have been sent before.
+
+                // Send a status report
+
+                err = SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_Success);
+                SuccessOrExit(err);
+
+                break;
+            case kMsgType_TunnelClose:
+
+                err = WeaveTunnelRoute::DecodeFabricTunnelRoutes(&msgFabricId, &tunRoute, payload);
+                SuccessOrExit(err);
+
+                err = SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_Success);
+                SuccessOrExit(err);
+
+                break;
+            case kMsgType_TunnelLiveness:
+
+                err = SendStatusReportResponse(ec, kWeaveProfile_Common, Common::kStatus_Success);
+                SuccessOrExit(err);
+
+                break;
+        }
+    }
+
+exit:
+    // Discard the exchange context.
+    ec->Close();
+
+    if (payload != NULL)
+    {
+        PacketBuffer::Free(payload);
+    }
+}
+
+void HandleConnectionClosed (WeaveConnection *con, WEAVE_ERROR conErr)
+{
+    char ipAddrStr[64];
+
+    con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
+
+    if (conErr == WEAVE_NO_ERROR)
+    {
+        WeaveLogDetail(WeaveTunnel, "Connection closed with node %" PRIx64 " (%s)\n",
+                       con->PeerNodeId, ipAddrStr);
+    }
+    else
+    {
+        WeaveLogError(WeaveTunnel, "Connection ABORTED with node %" PRIx64 " (%s): %ld\n",
+                      con->PeerNodeId, ipAddrStr, (long)conErr);
+    }
+
+    con->Close();
+}
+
+void HandleSecureSessionEstablished (WeaveSecurityManager *sm, WeaveConnection *con,
+                                     void *reqState, uint16_t sessionKeyId,
+                                     uint64_t peerNodeId, uint8_t encType)
+{
+    char ipAddrStr[64] = "";
+
+    if (con)
+    {
+        con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
+        con->DefaultKeyId = sessionKeyId;
+        con->PeerNodeId = peerNodeId;
+    }
+    WeaveLogDetail(WeaveTunnel, "Secure session established with node %" PRIX64 " (%s)\n", peerNodeId, ipAddrStr);
+}
+
+void HandleSecureSessionError (WeaveSecurityManager *sm, WeaveConnection *con,
+                               void *reqState, WEAVE_ERROR localErr,
+                               uint64_t peerNodeId, StatusReport *statusReport)
+{
+    char ipAddrStr[64] = "";
+
+    if (con)
+        con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
+
+    if (localErr == WEAVE_ERROR_STATUS_REPORT_RECEIVED && statusReport != NULL)
+        WeaveLogError(WeaveTunnel, "FAILED to establish secure session to node %" PRIX64 " (%s): %s\n", peerNodeId,
+               ipAddrStr, StatusReportStr(statusReport->mProfileId, statusReport->mStatusCode));
+    else
+        WeaveLogDetail(WeaveTunnel, "FAILED to establish secure session to node %" PRIX64 " (%s): %s\n", peerNodeId,
+               ipAddrStr, ErrorStr(localErr));
+}
+
+#endif //WEAVE_CONFIG_ENABLE_TUNNELING
+
+
+int main(int argc, char *argv[])
+{
+
+#if WEAVE_CONFIG_ENABLE_TUNNELING
+    WEAVE_ERROR err;
+    nl::Weave::System::Stats::Snapshot before;
+    nl::Weave::System::Stats::Snapshot after;
+    const bool printStats = true;
+
+    gWeaveNodeOptions.LocalNodeId = DEFAULT_TFE_NODE_ID;
+
+    SetupFaultInjectionContext(argc, argv);
+    SetSignalHandler(DoneOnHandleSIGUSR1);
+
+    if (!ParseArgsFromEnvVar(TOOL_NAME, TOOL_OPTIONS_ENV_VAR_NAME, gToolOptionSets, NULL, true) ||
+        !ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets) ||
+        !ResolveWeaveNetworkOptions(TOOL_NAME, gWeaveNodeOptions, gNetworkOptions))
+    {
+        exit(EXIT_FAILURE);
+    }
+
+    InitSystemLayer();
+    InitNetwork();
+    InitWeaveStack(true, true);
+
+    FabricState.BoundConnectionClosedForSession = HandleSessionPersistOnTunnelClosure;
+
+    WeaveLogDetail(WeaveTunnel, "Weave Node Configuration:\n");
+    WeaveLogDetail(WeaveTunnel, "Fabric Id: %" PRIX64 "\n", FabricState.FabricId);
+    WeaveLogDetail(WeaveTunnel, "Subnet Number: %X\n", FabricState.DefaultSubnet);
+    WeaveLogDetail(WeaveTunnel, "Node Id: %" PRIX64 "\n", FabricState.LocalNodeId);
+
+    nl::Weave::Stats::UpdateSnapshot(before);
+
+    err = TunServerInit(&ExchangeMgr);
+    FAIL_ERROR(err, "TunnelServer.Init failed");
+
+    while (!Done)
+    {
+        struct timeval sleepTime;
+        sleepTime.tv_sec = 0;
+        sleepTime.tv_usec = 100000;
+
+        ServiceNetwork(sleepTime);
+
+    }
+
+    TunServerShutdown();
+
+    ProcessStats(before, after, printStats, NULL);
+    PrintFaultInjectionCounters();
+
+    ShutdownWeaveStack();
+    ShutdownNetwork();
+    ShutdownSystemLayer();
+
+#endif //WEAVE_CONFIG_ENABLE_TUNNELING
+    return 0;
+}