Matter SDK Coverage Report
Current view: top level - transport - SecureSessionTable.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 100.0 % 29 29
Test Date: 2025-01-17 19:00:11 Functions: 66.7 % 24 16

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2020-2021 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 <lib/core/CHIPError.h>
      20              : #include <lib/support/CodeUtils.h>
      21              : #include <lib/support/Pool.h>
      22              : #include <lib/support/SortUtils.h>
      23              : #include <system/TimeSource.h>
      24              : #include <transport/SecureSession.h>
      25              : 
      26              : namespace chip {
      27              : namespace Transport {
      28              : 
      29              : inline constexpr uint16_t kMaxSessionID       = UINT16_MAX;
      30              : inline constexpr uint16_t kUnsecuredSessionId = 0;
      31              : 
      32              : /**
      33              :  * Handles a set of sessions.
      34              :  *
      35              :  * Intended for:
      36              :  *   - handle session active time and expiration
      37              :  *   - allocate and free space for sessions.
      38              :  */
      39              : class SecureSessionTable
      40              : {
      41              : public:
      42          419 :     ~SecureSessionTable() { mEntries.ReleaseAll(); }
      43              : 
      44          370 :     void Init() { mNextSessionId = chip::Crypto::GetRandU16(); }
      45              : 
      46              :     /**
      47              :      * Allocate a new secure session out of the internal resource pool.
      48              :      *
      49              :      * @param secureSessionType secure session type
      50              :      * @param localSessionId unique identifier for the local node's secure unicast session context
      51              :      * @param localNodeId represents the local Node ID for this node
      52              :      * @param peerNodeId represents peer Node's ID
      53              :      * @param peerCATs represents peer CASE Authenticated Tags
      54              :      * @param peerSessionId represents the encryption key ID assigned by peer node
      55              :      * @param fabricIndex represents fabric index for the session
      56              :      * @param config represents the reliable message protocol configuration
      57              :      *
      58              :      * @note the newly created state will have an 'active' time set based on the current time source.
      59              :      *
      60              :      * @returns CHIP_NO_ERROR if state could be initialized. May fail if maximum session count
      61              :      *          has been reached (with CHIP_ERROR_NO_MEMORY).
      62              :      */
      63              :     CHECK_RETURN_VALUE
      64              :     Optional<SessionHandle> CreateNewSecureSessionForTest(SecureSession::Type secureSessionType, uint16_t localSessionId,
      65              :                                                           NodeId localNodeId, NodeId peerNodeId, CATValues peerCATs,
      66              :                                                           uint16_t peerSessionId, FabricIndex fabricIndex,
      67              :                                                           const ReliableMessageProtocolConfig & config);
      68              :     /**
      69              :      * Allocate a new secure session out of the internal resource pool with a
      70              :      * non-colliding session ID and increments mNextSessionId to give a clue to
      71              :      * the allocator for the next allocation.  The secure session session will
      72              :      * not become active until the call to SecureSession::Activate.
      73              :      *
      74              :      * @returns allocated session, or NullOptional on failure
      75              :      */
      76              :     CHECK_RETURN_VALUE
      77              :     Optional<SessionHandle> CreateNewSecureSession(SecureSession::Type secureSessionType, ScopedNodeId sessionEvictionHint);
      78              : 
      79       135111 :     void ReleaseSession(SecureSession * session) { mEntries.ReleaseObject(session); }
      80              : 
      81              :     template <typename Function>
      82          896 :     Loop ForEachSession(Function && function)
      83              :     {
      84          896 :         return mEntries.ForEachActiveObject(std::forward<Function>(function));
      85              :     }
      86              : 
      87              :     /**
      88              :      * Get a secure session given its session ID.
      89              :      *
      90              :      * @param localSessionId the identifier of a secure unicast session context within the local node
      91              :      *
      92              :      * @return the session if found, NullOptional if not found
      93              :      */
      94              :     CHECK_RETURN_VALUE
      95              :     Optional<SessionHandle> FindSecureSessionByLocalKey(uint16_t localSessionId);
      96              : 
      97              :     // Select SessionHolders which are pointing to a session with the same peer as the given session. Shift them to the given
      98              :     // session.
      99              :     // This is an internal API, using raw pointer to a session is allowed here.
     100          100 :     void NewerSessionAvailable(SecureSession * session)
     101              :     {
     102          100 :         VerifyOrDie(session->GetSecureSessionType() == SecureSession::Type::kCASE);
     103          100 :         mEntries.ForEachActiveObject([&](SecureSession * oldSession) {
     104          393 :             if (session == oldSession)
     105          100 :                 return Loop::Continue;
     106              : 
     107          293 :             SessionHandle ref(*oldSession);
     108              : 
     109              :             // This will give all SessionHolders pointing to oldSession a chance to switch to the provided session
     110              :             //
     111              :             // See documentation for SessionDelegate::GetNewSessionHandlingPolicy about how session auto-shifting works, and how
     112              :             // to disable it for a specific SessionHolder in a specific scenario.
     113          346 :             if (oldSession->GetSecureSessionType() == SecureSession::Type::kCASE && oldSession->GetPeer() == session->GetPeer() &&
     114           53 :                 oldSession->GetPeerCATs() == session->GetPeerCATs())
     115              :             {
     116           53 :                 oldSession->NewerSessionAvailable(SessionHandle(*session));
     117              :             }
     118              : 
     119          293 :             return Loop::Continue;
     120          293 :         });
     121          100 :     }
     122              : 
     123              : private:
     124              :     friend class TestSecureSessionTable;
     125              : 
     126              :     /**
     127              :      * This provides a sortable wrapper for a SecureSession object. A SecureSession
     128              :      * isn't directly sortable since it is not swappable (i.e meet criteria for ValueSwappable).
     129              :      *
     130              :      * However, this wrapper has a stable pointer to a SecureSession while being swappable with
     131              :      * another instance of it.
     132              :      *
     133              :      */
     134              :     struct SortableSession
     135              :     {
     136              :     public:
     137              :         void swap(SortableSession & other)
     138              :         {
     139              :             SortableSession tmp(other);
     140              :             other.mSession = mSession;
     141              :             mSession       = tmp.mSession;
     142              :         }
     143              : 
     144          198 :         const Transport::SecureSession * operator->() const { return mSession; }
     145              :         auto GetNumMatchingOnFabric() { return mNumMatchingOnFabric; }
     146              :         auto GetNumMatchingOnPeer() { return mNumMatchingOnPeer; }
     147              : 
     148              :     private:
     149              :         SecureSession * mSession;
     150              :         uint16_t mNumMatchingOnFabric;
     151              :         uint16_t mNumMatchingOnPeer;
     152              : 
     153              :         static_assert(CHIP_CONFIG_SECURE_SESSION_POOL_SIZE <= std::numeric_limits<decltype(mNumMatchingOnFabric)>::max(),
     154              :                       "mNumMatchingOnFabric must be able to count up to CHIP_CONFIG_SECURE_SESSION_POOL_SIZE!");
     155              :         static_assert(CHIP_CONFIG_SECURE_SESSION_POOL_SIZE <= std::numeric_limits<decltype(mNumMatchingOnPeer)>::max(),
     156              :                       "mNumMatchingOnPeer must be able to count up to CHIP_CONFIG_SECURE_SESSION_POOL_SIZE!");
     157              : 
     158              :         friend class SecureSessionTable;
     159              :     };
     160              : 
     161              :     /**
     162              :      *
     163              :      * Encapsulates all the necessary context for an eviction policy callback
     164              :      * to implement its specific policy. The context is provided to the callee
     165              :      * with the expectation that it'll call Sort() with a comparator function provided
     166              :      * to get the list of sessions sorted in the desired order.
     167              :      *
     168              :      */
     169              :     class EvictionPolicyContext
     170              :     {
     171              :     public:
     172              :         /*
     173              :          * Called by the policy implementor to sort the list of sessions given a comparator
     174              :          * function. The provided function shall have the following signature:
     175              :          *
     176              :          * bool CompareFunc(const SortableSession &a, const SortableSession &b);
     177              :          *
     178              :          * If a is a better candidate than b, true should be returned. Else, return false.
     179              :          *
     180              :          * NOTE: Sort() can be called multiple times.
     181              :          *
     182              :          */
     183              :         template <typename CompareFunc>
     184           13 :         void Sort(CompareFunc func)
     185              :         {
     186           13 :             Sorting::InsertionSort(mSessionList.begin(), mSessionList.size(), func);
     187           13 :         }
     188              : 
     189          520 :         const ScopedNodeId & GetSessionEvictionHint() const { return mSessionEvictionHint; }
     190              : 
     191              :     private:
     192           13 :         EvictionPolicyContext(Span<SortableSession> sessionList, ScopedNodeId sessionEvictionHint)
     193           13 :         {
     194           13 :             mSessionList         = sessionList;
     195           13 :             mSessionEvictionHint = sessionEvictionHint;
     196           13 :         }
     197              : 
     198              :         friend class SecureSessionTable;
     199              :         Span<SortableSession> mSessionList;
     200              :         ScopedNodeId mSessionEvictionHint;
     201              :     };
     202              : 
     203              :     /**
     204              :      *
     205              :      * This implements an eviction policy by sorting sessions using the following sorting keys and selecting
     206              :      * the session that is most ahead as the best candidate for eviction:
     207              :      *
     208              :      *  - Key1:  Sessions on fabrics that have more sessions in the table are placed ahead of sessions on fabrics
     209              :      *           with fewer sessions. We conclusively know that if a particular fabric has more sessions in the table
     210              :      *           than another, then that fabric is definitely over minimas (assuming a minimally sized session table
     211              :      *           conformant to spec minimas).
     212              :      *
     213              :      *    Key2:  Sessions that match the eviction hint's fabric are placed ahead of those that don't. This ensures that
     214              :      *           if Key1 is even (i.e two fabrics are tied in count), that you attempt to select sessions that match
     215              :      *           the eviction hint's fabric to ensure we evict sessions within the fabric that a new session might be about
     216              :      *           to be created within. This is essential to preventing cross-fabric denial of service possibilities.
     217              :      *
     218              :      *    Key3:  Sessions with a higher mNumMatchingOnPeer are placed ahead of those with a lower one. This ensures
     219              :      *           we pick sessions that have a higher number of duplicated sessions to a peer over those with lower since
     220              :      *           evicting a duplicated session will have less of an impact to that peer.
     221              :      *
     222              :      *    Key4:  Sessions whose target peer's ScopedNodeId matches the eviction hint are placed ahead of those who don't. This
     223              :      * ensures that all things equal, a session that already exists to the peer is refreshed ahead of another to another peer.
     224              :      *
     225              :      *    Key5:  Sessions that are in defunct state are placed ahead of those in the active state, ahead of any other state.
     226              :      *           This ensures that we prioritize evicting defunct sessions (since they have been deemed non-functional anyways)
     227              :      *           over active, healthy ones, over those are currently in the process of establishment.
     228              :      *
     229              :      *    Key6:  Sessions that have a less recent activity time are placed ahead of those with a more recent activity time. This
     230              :      *           is the canonical sorting criteria for basic LRU.
     231              :      *
     232              :      */
     233              :     void DefaultEvictionPolicy(EvictionPolicyContext & evictionContext);
     234              : 
     235              :     /**
     236              :      *
     237              :      * Evicts a session from the session table using the DefaultEvictionPolicy implementation.
     238              :      *
     239              :      */
     240              :     SecureSession * EvictAndAllocate(uint16_t localSessionId, SecureSession::Type secureSessionType,
     241              :                                      const ScopedNodeId & sessionEvictionHint);
     242              : 
     243              :     /**
     244              :      * Find an available session ID that is unused in the secure session table.
     245              :      *
     246              :      * The search algorithm iterates over the session ID space in the outer loop
     247              :      * and the session table in the inner loop to locate an available session ID
     248              :      * from the starting mNextSessionId clue.
     249              :      *
     250              :      * The outer-loop considers 64 session IDs in each iteration to give a
     251              :      * runtime complexity of O(CHIP_CONFIG_PEER_CONNECTION_POOL_SIZE^2/64).  Speed up could be
     252              :      * achieved with a sorted session table or additional storage.
     253              :      *
     254              :      * @return an unused session ID if any is found, else NullOptional
     255              :      */
     256              :     CHECK_RETURN_VALUE
     257              :     Optional<uint16_t> FindUnusedSessionId();
     258              : 
     259              :     bool mRunningEvictionLogic = false;
     260              :     ObjectPool<SecureSession, CHIP_CONFIG_SECURE_SESSION_POOL_SIZE> mEntries;
     261              : 
     262       133763 :     size_t GetMaxSessionTableSize() const
     263              :     {
     264              : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
     265       133763 :         return mMaxSessionTableSize;
     266              : #else
     267              :         return CHIP_CONFIG_SECURE_SESSION_POOL_SIZE;
     268              : #endif
     269              :     }
     270              : 
     271              : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
     272              :     size_t mMaxSessionTableSize = CHIP_CONFIG_SECURE_SESSION_POOL_SIZE;
     273              :     void SetMaxSessionTableSize(size_t size) { mMaxSessionTableSize = size; }
     274              : #endif
     275              : 
     276              :     uint16_t mNextSessionId = 0;
     277              : };
     278              : 
     279              : } // namespace Transport
     280              : } // namespace chip
        

Generated by: LCOV version 2.0-1