diff --git a/src/messaging/ExchangeContext.cpp b/src/messaging/ExchangeContext.cpp index a2aa221a7cc78e..08d916a15246e2 100644 --- a/src/messaging/ExchangeContext.cpp +++ b/src/messaging/ExchangeContext.cpp @@ -325,6 +325,9 @@ ExchangeContext::~ExchangeContext() VerifyOrDie(mExchangeMgr != nullptr && GetReferenceCount() == 0); VerifyOrDie(!IsAckPending()); + if (IsAutoReleaseSession() && mSession) + mSession->AsSecureSession()->MarkForRemoval(); + #if CONFIG_DEVICE_LAYER && CHIP_DEVICE_CONFIG_ENABLE_SED // Make sure that the exchange withdraws the request for Sleepy End Device active mode. UpdateSEDIntervalMode(false); diff --git a/src/messaging/ExchangeMgr.cpp b/src/messaging/ExchangeMgr.cpp index 2c1242aff55c1c..b9e133e75cdd53 100644 --- a/src/messaging/ExchangeMgr.cpp +++ b/src/messaging/ExchangeMgr.cpp @@ -376,5 +376,19 @@ void ExchangeManager::CloseAllContextsForDelegate(const ExchangeDelegate * deleg }); } +void ExchangeManager::AbortExchangesForFabricExceptOne(FabricIndex fabricIndex, ExchangeContext * deferred) +{ + mContextPool.ForEachActiveObject([&](auto * ec) { + if (ec->HasSessionHandle() && ec->GetSessionHandle()->GetPeer().GetFabricIndex() == fabricIndex) + { + if (ec == deferred) + ec->SetAutoReleaseSession(); + else + ec->Abort(); + } + return Loop::Continue; + }); +} + } // namespace Messaging } // namespace chip diff --git a/src/messaging/ExchangeMgr.h b/src/messaging/ExchangeMgr.h index d6b775436799f4..69dbd80a79ff40 100644 --- a/src/messaging/ExchangeMgr.h +++ b/src/messaging/ExchangeMgr.h @@ -183,6 +183,10 @@ class DLL_EXPORT ExchangeManager : public SessionMessageDelegate */ void CloseAllContextsForDelegate(const ExchangeDelegate * delegate); + // This API is used by UpdateNOC command, to abort all exchanges except the given one, whose abort is deferred until UpdateNOC + // command finishing its work. + void AbortExchangesForFabricExceptOne(FabricIndex fabricIndex, ExchangeContext * deferred); + SessionManager * GetSessionManager() const { return mSessionManager; } ReliableMessageMgr * GetReliableMessageMgr() { return &mReliableMessageMgr; }; diff --git a/src/messaging/ReliableMessageContext.h b/src/messaging/ReliableMessageContext.h index e67b2d596e963e..d4a89c73237e86 100644 --- a/src/messaging/ReliableMessageContext.h +++ b/src/messaging/ReliableMessageContext.h @@ -124,6 +124,10 @@ class ReliableMessageContext /// Determine whether this exchange is a EphemeralExchange for replying a StandaloneAck bool IsEphemeralExchange() const; + // For UpdateNOC command, I'm the last exchange, I must release the session when finishing my work. + void SetAutoReleaseSession(); + bool IsAutoReleaseSession(); + /** * Get the reliable message manager that corresponds to this reliable * message context. @@ -164,6 +168,9 @@ class ReliableMessageContext /// When set, signifies that the exchange created sorely for replying a StandaloneAck kFlagEphemeralExchange = (1u << 9), + + /// When set, automatically release the session when finishing its work. Used by UpdateNOC command + kFlagAutoReleaseSession = (1u << 10), }; BitFlags mFlags; // Internal state flags @@ -245,5 +252,15 @@ inline bool ReliableMessageContext::IsEphemeralExchange() const return mFlags.Has(Flags::kFlagEphemeralExchange); } +inline void ReliableMessageContext::SetAutoReleaseSession() +{ + mFlags.Set(Flags::kFlagAutoReleaseSession, true); +} + +inline bool ReliableMessageContext::IsAutoReleaseSession() +{ + return mFlags.Has(Flags::kFlagAutoReleaseSession); +} + } // namespace Messaging } // namespace chip diff --git a/src/transport/SecureSession.cpp b/src/transport/SecureSession.cpp index f2ec416c0ba9d6..6aa5cc044a868f 100644 --- a/src/transport/SecureSession.cpp +++ b/src/transport/SecureSession.cpp @@ -39,6 +39,7 @@ void SecureSession::MarkForRemoval() NotifySessionReleased(); return; case State::kActive: + case State::kInactive: Release(); // Decrease the ref which is retained at Activate mState = State::kPendingRemoval; NotifySessionReleased(); @@ -49,6 +50,27 @@ void SecureSession::MarkForRemoval() } } +void SecureSession::MarkForInactive() +{ + ChipLogDetail(Inet, "SecureSession[%p]: MarkForInactive Type:%d LSID:%d", this, to_underlying(mSecureSessionType), + mLocalSessionId); + ReferenceCountedHandle ref(*this); + switch (mState) + { + case State::kPairing: + VerifyOrDie(false); + return; + case State::kActive: + // By setting this state, IsActiveSession() will return false, which prevent creating new exchanges. + mState = State::kInactive; + return; + case State::kInactive: + case State::kPendingRemoval: + // Do nothing + return; + } +} + Access::SubjectDescriptor SecureSession::GetSubjectDescriptor() const { Access::SubjectDescriptor subjectDescriptor; diff --git a/src/transport/SecureSession.h b/src/transport/SecureSession.h index a505a3237f00eb..9978918929a1d2 100644 --- a/src/transport/SecureSession.h +++ b/src/transport/SecureSession.h @@ -144,6 +144,9 @@ class SecureSession : public Session, public ReferenceCountedGetPeer().GetFabricIndex() == fabricIndex) + { + if (session == exception->AsSecureSession()) + session->MarkForInactive(); + else + session->MarkForRemoval(); + } + return Loop::Continue; + }); +} + +void SessionManager::ReleaseSessionForNodeExceptOne(const ScopedNodeId & node, const SessionHandle & exception) +{ + mSecureSessions.ForEachSession([&](auto session) { + if (session->GetPeer() == node) + { + if (session == exception->AsSecureSession()) + session->MarkForInactive(); + else + session->MarkForRemoval(); + } + return Loop::Continue; + }); +} + Optional SessionManager::AllocateSession(SecureSession::Type secureSessionType, const ScopedNodeId & sessionEvictionHint) { diff --git a/src/transport/SessionManager.h b/src/transport/SessionManager.h index 1b01f1d5bcb5e7..c57f7089f04725 100644 --- a/src/transport/SessionManager.h +++ b/src/transport/SessionManager.h @@ -176,6 +176,10 @@ class DLL_EXPORT SessionManager : public TransportMgrDelegate void ExpireAllPairingsForFabric(FabricIndex fabric); void ExpireAllPASEPairings(); + // This API is used by UpdateNOC command, to invalidate all sessions except the given one, whose release is deferred until + // UpdateNOC command finishing its work. + void ReleaseSessionsForFabricExceptOne(FabricIndex fabricIndex, const SessionHandle & deferred); + /** * @brief * Return the System Layer pointer used by current SessionManager.