Matter SDK Coverage Report
Current view: top level - transport - UnauthenticatedSessionTable.h (source / functions) Coverage Total Hit
Test: SHA:2a48c1efeab1c0f76f3adb3a0940b0f7de706453 Lines: 79.2 % 125 99
Test Date: 2026-01-31 08:14:20 Functions: 90.0 % 40 36

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2020 Project CHIP Authors
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : #pragma once
      18              : 
      19              : #include <ble/Ble.h>
      20              : #include <lib/core/CHIPError.h>
      21              : #include <lib/core/ReferenceCounted.h>
      22              : #include <lib/support/CodeUtils.h>
      23              : #include <lib/support/Pool.h>
      24              : #include <messaging/ReliableMessageProtocolConfig.h>
      25              : #include <system/SystemConfig.h>
      26              : #include <system/TimeSource.h>
      27              : #include <transport/PeerMessageCounter.h>
      28              : #include <transport/Session.h>
      29              : #include <transport/raw/PeerAddress.h>
      30              : 
      31              : namespace chip {
      32              : namespace Transport {
      33              : 
      34              : /**
      35              :  * @brief
      36              :  *   An UnauthenticatedSession stores the binding of TransportAddress, and message counters.
      37              :  */
      38              : class UnauthenticatedSession : public Session, public ReferenceCounted<UnauthenticatedSession, UnauthenticatedSession, 0>
      39              : {
      40              : public:
      41              :     enum class SessionRole
      42              :     {
      43              :         kInitiator,
      44              :         kResponder,
      45              :     };
      46              : 
      47              : protected:
      48           55 :     UnauthenticatedSession(SessionRole sessionRole, NodeId ephemeralInitiatorNodeID, const Transport::PeerAddress & peerAddress,
      49           55 :                            const ReliableMessageProtocolConfig & config) :
      50           55 :         mEphemeralInitiatorNodeId(ephemeralInitiatorNodeID),
      51           55 :         mSessionRole(sessionRole), mPeerAddress(peerAddress), mLastActivityTime(System::SystemClock().GetMonotonicTimestamp()),
      52           55 :         mLastPeerActivityTime(System::Clock::kZero), // Start at zero to default to IDLE state
      53          110 :         mRemoteSessionParams(config)
      54           55 :     {}
      55           55 :     ~UnauthenticatedSession() override { VerifyOrDie(GetReferenceCount() == 0); }
      56              : 
      57              : public:
      58              :     UnauthenticatedSession(const UnauthenticatedSession &)             = delete;
      59              :     UnauthenticatedSession & operator=(const UnauthenticatedSession &) = delete;
      60              :     UnauthenticatedSession(UnauthenticatedSession &&)                  = delete;
      61              :     UnauthenticatedSession & operator=(UnauthenticatedSession &&)      = delete;
      62              : 
      63              :     System::Clock::Timestamp GetLastActivityTime() const { return mLastActivityTime; }
      64          239 :     System::Clock::Timestamp GetLastPeerActivityTime() const { return mLastPeerActivityTime; }
      65          258 :     void MarkActive() { mLastActivityTime = System::SystemClock().GetMonotonicTimestamp(); }
      66          132 :     void MarkActiveRx()
      67              :     {
      68          132 :         mLastPeerActivityTime = System::SystemClock().GetMonotonicTimestamp();
      69          132 :         MarkActive();
      70          132 :     }
      71              : 
      72         3015 :     Session::SessionType GetSessionType() const override { return Session::SessionType::kUnauthenticated; }
      73              : 
      74         1708 :     void Retain() override { ReferenceCounted<UnauthenticatedSession, UnauthenticatedSession, 0>::Retain(); }
      75         1708 :     void Release() override { ReferenceCounted<UnauthenticatedSession, UnauthenticatedSession, 0>::Release(); }
      76              : 
      77          108 :     bool IsActiveSession() const override { return true; }
      78              : 
      79          242 :     ScopedNodeId GetPeer() const override { return ScopedNodeId(GetPeerNodeId(), kUndefinedFabricIndex); }
      80            0 :     ScopedNodeId GetLocalScopedNodeId() const override { return ScopedNodeId(kUndefinedNodeId, kUndefinedFabricIndex); }
      81              : 
      82            0 :     Access::SubjectDescriptor GetSubjectDescriptor() const override
      83              :     {
      84            0 :         return Access::SubjectDescriptor(); // return an empty ISD for unauthenticated session.
      85              :     }
      86              : 
      87          518 :     bool AllowsMRP() const override { return (GetPeerAddress().GetTransportType() == Transport::Type::kUdp); }
      88              : 
      89          123 :     bool AllowsLargePayload() const override { return GetPeerAddress().GetTransportType() == Transport::Type::kTcp; }
      90              : 
      91          113 :     System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const override
      92              :     {
      93          113 :         switch (mPeerAddress.GetTransportType())
      94              :         {
      95          113 :         case Transport::Type::kUdp: {
      96          113 :             const ReliableMessageProtocolConfig & remoteMRPConfig = mRemoteSessionParams.GetMRPConfig();
      97          113 :             return GetRetransmissionTimeout(remoteMRPConfig.mActiveRetransTimeout, remoteMRPConfig.mIdleRetransTimeout,
      98          226 :                                             GetLastPeerActivityTime(), remoteMRPConfig.mActiveThresholdTime,
      99          113 :                                             isFirstMessageOnExchange);
     100              :         }
     101            0 :         case Transport::Type::kTcp:
     102            0 :             return System::Clock::Seconds16(30);
     103            0 :         case Transport::Type::kBle:
     104            0 :             return System::Clock::Milliseconds32(BTP_ACK_TIMEOUT_MS);
     105            0 :         default:
     106            0 :             break;
     107              :         }
     108            0 :         return System::Clock::Timeout();
     109              :     }
     110              : 
     111          113 :     System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
     112              :                                                            bool isFirstMessageOnExchange) const override
     113              :     {
     114          113 :         switch (mPeerAddress.GetTransportType())
     115              :         {
     116          113 :         case Transport::Type::kUdp: {
     117          113 :             const auto & maybeLocalMRPConfig = GetLocalMRPConfig();
     118          113 :             const auto & defaultMRRPConfig   = GetDefaultMRPConfig();
     119          113 :             const auto & localMRPConfig      = maybeLocalMRPConfig.ValueOr(defaultMRRPConfig);
     120          226 :             return GetRetransmissionTimeout(localMRPConfig.mActiveRetransTimeout, localMRPConfig.mIdleRetransTimeout,
     121          226 :                                             ourLastActivity, localMRPConfig.mActiveThresholdTime, isFirstMessageOnExchange);
     122              :         }
     123            0 :         case Transport::Type::kTcp:
     124            0 :             return System::Clock::Seconds16(30);
     125            0 :         case Transport::Type::kBle:
     126            0 :             return System::Clock::Milliseconds32(BTP_ACK_TIMEOUT_MS);
     127            0 :         default:
     128            0 :             break;
     129              :         }
     130            0 :         return System::Clock::Timeout();
     131              :     }
     132              : 
     133          242 :     NodeId GetPeerNodeId() const
     134              :     {
     135          242 :         if (mSessionRole == SessionRole::kInitiator)
     136              :         {
     137          102 :             return kUndefinedNodeId;
     138              :         }
     139              : 
     140          140 :         return mEphemeralInitiatorNodeId;
     141              :     }
     142              : 
     143          471 :     SessionRole GetSessionRole() const { return mSessionRole; }
     144          344 :     NodeId GetEphemeralInitiatorNodeID() const { return mEphemeralInitiatorNodeId; }
     145         1029 :     const PeerAddress & GetPeerAddress() const { return mPeerAddress; }
     146          150 :     void SetPeerAddress(const PeerAddress & peerAddress) { mPeerAddress = peerAddress; }
     147              : 
     148          126 :     bool IsPeerActive() const
     149              :     {
     150          252 :         return ((System::SystemClock().GetMonotonicTimestamp() - GetLastPeerActivityTime()) <
     151          252 :                 GetRemoteMRPConfig().mActiveThresholdTime);
     152              :     }
     153              : 
     154           28 :     System::Clock::Timestamp GetMRPBaseTimeout() const override
     155              :     {
     156           28 :         return IsPeerActive() ? GetRemoteMRPConfig().mActiveRetransTimeout : GetRemoteMRPConfig().mIdleRetransTimeout;
     157              :     }
     158              : 
     159           40 :     void SetRemoteSessionParameters(const SessionParameters & sessionParams) { mRemoteSessionParams = sessionParams; }
     160              : 
     161          322 :     const SessionParameters & GetRemoteSessionParameters() const override { return mRemoteSessionParams; }
     162              : 
     163          244 :     PeerMessageCounter & GetPeerMessageCounter() { return mPeerMessageCounter; }
     164              : 
     165           55 :     static void Release(UnauthenticatedSession * obj)
     166              :     {
     167              :         // When using heap pools, we need to make sure to release ourselves back to
     168              :         // the pool.  When not using heap pools, we don't want the extra cost of the
     169              :         // table pointer here, and the table itself handles entry reuse and cleanup
     170              :         // as needed.
     171              : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     172           55 :         obj->ReleaseSelfToPool();
     173              : #else
     174              :         // Just do nothing.
     175              : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     176           55 :     }
     177              : 
     178              : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     179              :     virtual void ReleaseSelfToPool() = 0;
     180              : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     181              : 
     182              : private:
     183              :     const NodeId mEphemeralInitiatorNodeId;
     184              :     const SessionRole mSessionRole;
     185              :     PeerAddress mPeerAddress;
     186              :     System::Clock::Timestamp mLastActivityTime;     ///< Timestamp of last tx or rx
     187              :     System::Clock::Timestamp mLastPeerActivityTime; ///< Timestamp of last rx
     188              :     SessionParameters mRemoteSessionParams;
     189              :     PeerMessageCounter mPeerMessageCounter;
     190              : };
     191              : 
     192              : template <size_t kMaxSessionCount>
     193              : class UnauthenticatedSessionTable;
     194              : 
     195              : namespace detail {
     196              : 
     197              : template <size_t kMaxSessionCount>
     198              : class UnauthenticatedSessionPoolEntry : public UnauthenticatedSession
     199              : {
     200              : public:
     201           55 :     UnauthenticatedSessionPoolEntry(SessionRole sessionRole, NodeId ephemeralInitiatorNodeID,
     202              :                                     const Transport::PeerAddress & peerAddress, const ReliableMessageProtocolConfig & config,
     203              :                                     UnauthenticatedSessionTable<kMaxSessionCount> & sessionTable) :
     204              :         UnauthenticatedSession(sessionRole, ephemeralInitiatorNodeID, peerAddress, config)
     205              : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     206              :         ,
     207           55 :         mSessionTable(sessionTable)
     208              : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     209           55 :     {}
     210              : 
     211              : private:
     212              : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     213              :     virtual void ReleaseSelfToPool();
     214              : 
     215              :     UnauthenticatedSessionTable<kMaxSessionCount> & mSessionTable;
     216              : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     217              : };
     218              : 
     219              : } // namespace detail
     220              : 
     221              : /*
     222              :  * @brief
     223              :  *   An table which manages UnauthenticatedSessions
     224              :  *
     225              :  *   The UnauthenticatedSession entries are rotated using LRU, but entry can be hold by using SessionHandle or
     226              :  *   SessionHolder, which increase the reference count by 1. If the reference count is not 0, the entry won't be pruned.
     227              :  */
     228              : template <size_t kMaxSessionCount>
     229              : class UnauthenticatedSessionTable
     230              : {
     231              : public:
     232          590 :     ~UnauthenticatedSessionTable()
     233              :     {
     234              : #if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     235              :         // When not using heap pools, our entries never actually get released
     236              :         // back to the pool (which lets us make the entries 4 bytes smaller by
     237              :         // not storing a reference to the table in them) and we LRU reuse ones
     238              :         // that have 0 refcount.  But we should release them all here, to ensure
     239              :         // that we don't hit fatal asserts in our pool destructor.
     240              :         mEntries.ReleaseAll();
     241              : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     242          590 :     }
     243              : 
     244              :     /**
     245              :      * Get a responder session with the given ephemeralInitiatorNodeID. If the session doesn't exist in the cache, allocate a new
     246              :      * entry for it.
     247              :      *
     248              :      * @return the session found or allocated, or Optional::Missing if not found and allocation failed.
     249              :      */
     250              :     CHECK_RETURN_VALUE
     251           71 :     Optional<SessionHandle> FindOrAllocateResponder(NodeId ephemeralInitiatorNodeID, const ReliableMessageProtocolConfig & config,
     252              :                                                     const Transport::PeerAddress & peerAddress)
     253              :     {
     254           71 :         UnauthenticatedSession * result =
     255           71 :             FindEntry(UnauthenticatedSession::SessionRole::kResponder, ephemeralInitiatorNodeID, peerAddress);
     256           71 :         if (result != nullptr)
     257           44 :             return MakeOptional<SessionHandle>(*result);
     258              : 
     259              :         CHIP_ERROR err =
     260           27 :             AllocEntry(UnauthenticatedSession::SessionRole::kResponder, ephemeralInitiatorNodeID, peerAddress, config, result);
     261           54 :         if (err == CHIP_NO_ERROR)
     262              :         {
     263           27 :             return MakeOptional<SessionHandle>(*result);
     264              :         }
     265              : 
     266            0 :         return Optional<SessionHandle>::Missing();
     267              :     }
     268              : 
     269           51 :     CHECK_RETURN_VALUE Optional<SessionHandle> FindInitiator(NodeId ephemeralInitiatorNodeID,
     270              :                                                              const Transport::PeerAddress & peerAddress)
     271              :     {
     272              :         UnauthenticatedSession * result =
     273           51 :             FindEntry(UnauthenticatedSession::SessionRole::kInitiator, ephemeralInitiatorNodeID, peerAddress);
     274           51 :         if (result != nullptr)
     275              :         {
     276           51 :             return MakeOptional<SessionHandle>(*result);
     277              :         }
     278              : 
     279            0 :         return Optional<SessionHandle>::Missing();
     280              :     }
     281              : 
     282           28 :     CHECK_RETURN_VALUE Optional<SessionHandle> AllocInitiator(NodeId ephemeralInitiatorNodeID, const PeerAddress & peerAddress,
     283              :                                                               const ReliableMessageProtocolConfig & config)
     284              :     {
     285           28 :         UnauthenticatedSession * result = nullptr;
     286              :         CHIP_ERROR err =
     287           28 :             AllocEntry(UnauthenticatedSession::SessionRole::kInitiator, ephemeralInitiatorNodeID, peerAddress, config, result);
     288           56 :         if (err == CHIP_NO_ERROR)
     289              :         {
     290           28 :             result->SetPeerAddress(peerAddress);
     291           28 :             return MakeOptional<SessionHandle>(*result);
     292              :         }
     293              : 
     294            0 :         return Optional<SessionHandle>::Missing();
     295              :     }
     296              : 
     297              : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
     298            2 :     void MarkSessionOverTCPForEviction(Transport::ActiveTCPConnectionState & conn)
     299              :     {
     300            2 :         mEntries.ForEachActiveObject([&](UnauthenticatedSession * session) {
     301            0 :             if (session->GetTCPConnection() == conn)
     302              :             {
     303            0 :                 session->ReleaseTCPConnection();
     304              : 
     305              :                 // If the session has no other references, we can release it.
     306            0 :                 if (session->GetReferenceCount() == 0)
     307              :                 {
     308            0 :                     mEntries.ReleaseObject(static_cast<EntryType *>(session));
     309              :                 }
     310              :             }
     311            0 :             return Loop::Continue;
     312              :         });
     313            2 :     }
     314              : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
     315              : 
     316              : private:
     317              :     using EntryType = detail::UnauthenticatedSessionPoolEntry<kMaxSessionCount>;
     318              :     friend EntryType;
     319              : 
     320              :     /**
     321              :      * Allocates a new session out of the internal resource pool.
     322              :      *
     323              :      * @returns CHIP_NO_ERROR if new session created. May fail if maximum session count has been reached (with
     324              :      * CHIP_ERROR_NO_MEMORY).
     325              :      */
     326           55 :     CHIP_ERROR AllocEntry(UnauthenticatedSession::SessionRole sessionRole, NodeId ephemeralInitiatorNodeID,
     327              :                           const PeerAddress & peerAddress, const ReliableMessageProtocolConfig & config,
     328              :                           UnauthenticatedSession *& entry)
     329              :     {
     330           55 :         auto entryToUse = mEntries.CreateObject(sessionRole, ephemeralInitiatorNodeID, peerAddress, config, *this);
     331           55 :         if (entryToUse != nullptr)
     332              :         {
     333           55 :             entry = entryToUse;
     334           55 :             return CHIP_NO_ERROR;
     335              :         }
     336              : 
     337              : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     338              :         // permanent failure if heap was insufficient
     339            0 :         return CHIP_ERROR_NO_MEMORY;
     340              : #else
     341              :         entryToUse = FindLeastRecentUsedEntry();
     342              :         VerifyOrReturnError(entryToUse != nullptr, CHIP_ERROR_NO_MEMORY);
     343              : 
     344              :         // Drop the least recent entry to allow for a new alloc.
     345              :         mEntries.ReleaseObject(entryToUse);
     346              :         entryToUse = mEntries.CreateObject(sessionRole, ephemeralInitiatorNodeID, peerAddress, config, *this);
     347              : 
     348              :         if (entryToUse == nullptr)
     349              :         {
     350              :             // this is NOT expected: we freed an object to have space
     351              :             return CHIP_ERROR_INTERNAL;
     352              :         }
     353              : 
     354              :         entry = entryToUse;
     355              :         return CHIP_NO_ERROR;
     356              : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     357              :     }
     358              : 
     359          122 :     CHECK_RETURN_VALUE UnauthenticatedSession * FindEntry(UnauthenticatedSession::SessionRole sessionRole,
     360              :                                                           NodeId ephemeralInitiatorNodeID,
     361              :                                                           const Transport::PeerAddress & peerAddress)
     362              :     {
     363          122 :         UnauthenticatedSession * result = nullptr;
     364          122 :         mEntries.ForEachActiveObject([&](UnauthenticatedSession * entry) {
     365          248 :             if (entry->GetSessionRole() == sessionRole && entry->GetEphemeralInitiatorNodeID() == ephemeralInitiatorNodeID &&
     366           95 :                 entry->GetPeerAddress().GetTransportType() == peerAddress.GetTransportType())
     367              :             {
     368           95 :                 result = entry;
     369           95 :                 return Loop::Break;
     370              :             }
     371           58 :             return Loop::Continue;
     372              :         });
     373          122 :         return result;
     374              :     }
     375              : 
     376              :     EntryType * FindLeastRecentUsedEntry()
     377              :     {
     378              :         EntryType * result                  = nullptr;
     379              :         System::Clock::Timestamp oldestTime = System::Clock::Timestamp(std::numeric_limits<System::Clock::Timestamp::rep>::max());
     380              : 
     381              :         mEntries.ForEachActiveObject([&](EntryType * entry) {
     382              :             if (entry->GetReferenceCount() == 0 && entry->GetLastActivityTime() < oldestTime)
     383              :             {
     384              :                 result     = entry;
     385              :                 oldestTime = entry->GetLastActivityTime();
     386              :             }
     387              :             return Loop::Continue;
     388              :         });
     389              : 
     390              :         return result;
     391              :     }
     392              : 
     393           55 :     void ReleaseEntry(EntryType * entry) { mEntries.ReleaseObject(entry); }
     394              : 
     395              :     ObjectPool<EntryType, kMaxSessionCount> mEntries;
     396              : };
     397              : 
     398              : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     399              : template <size_t kMaxSessionCount>
     400           55 : void detail::UnauthenticatedSessionPoolEntry<kMaxSessionCount>::ReleaseSelfToPool()
     401              : {
     402           55 :     mSessionTable.ReleaseEntry(this);
     403           55 : }
     404              : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
     405              : 
     406              : } // namespace Transport
     407              : } // namespace chip
        

Generated by: LCOV version 2.0-1