Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 1 | /* |
| 2 | * |
Evgeny Margolis | ea87cc6 | 2021-11-29 22:08:00 -0800 | [diff] [blame] | 3 | * Copyright (c) 2020-2021 Project CHIP Authors |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 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 | */ |
Andrei Litvin | 1873e8c | 2020-10-12 10:52:26 -0400 | [diff] [blame] | 17 | #pragma once |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 18 | |
Zang MingJie | 53dd583 | 2021-09-03 03:05:16 +0800 | [diff] [blame] | 19 | #include <lib/core/CHIPError.h> |
| 20 | #include <lib/support/CodeUtils.h> |
Zang MingJie | c9642fc | 2021-10-30 04:19:49 +0800 | [diff] [blame] | 21 | #include <lib/support/Pool.h> |
Jerry Johns | c6724a0 | 2022-06-21 06:47:02 -0700 | [diff] [blame] | 22 | #include <lib/support/SortUtils.h> |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 23 | #include <system/TimeSource.h> |
Zang MingJie | eca9bff | 2021-09-23 03:19:51 +0800 | [diff] [blame] | 24 | #include <transport/SecureSession.h> |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 25 | |
| 26 | namespace chip { |
| 27 | namespace Transport { |
| 28 | |
Karsten Sperling | b15a6e6 | 2023-09-16 03:58:46 +1200 | [diff] [blame] | 29 | inline constexpr uint16_t kMaxSessionID = UINT16_MAX; |
| 30 | inline constexpr uint16_t kUnsecuredSessionId = 0; |
Zang MingJie | c07d818 | 2021-01-13 01:12:10 +0800 | [diff] [blame] | 31 | |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 32 | /** |
Zang MingJie | d786c58 | 2021-10-27 23:58:10 +0800 | [diff] [blame] | 33 | * Handles a set of sessions. |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 34 | * |
| 35 | * Intended for: |
Zang MingJie | d786c58 | 2021-10-27 23:58:10 +0800 | [diff] [blame] | 36 | * - handle session active time and expiration |
| 37 | * - allocate and free space for sessions. |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 38 | */ |
Zang MingJie | eca9bff | 2021-09-23 03:19:51 +0800 | [diff] [blame] | 39 | class SecureSessionTable |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 40 | { |
| 41 | public: |
Kevin Schoedel | 00434dd | 2021-12-11 02:56:10 -0500 | [diff] [blame] | 42 | ~SecureSessionTable() { mEntries.ReleaseAll(); } |
| 43 | |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 44 | void Init() { mNextSessionId = chip::Crypto::GetRandU16(); } |
| 45 | |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 46 | /** |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 47 | * Allocate a new secure session out of the internal resource pool. |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 48 | * |
Kevin Schoedel | fcd9785 | 2021-12-07 17:16:40 -0500 | [diff] [blame] | 49 | * @param secureSessionType secure session type |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 50 | * @param localSessionId unique identifier for the local node's secure unicast session context |
Tennessee Carmel-Veilleux | 7965de7 | 2022-04-27 20:33:58 -0400 | [diff] [blame] | 51 | * @param localNodeId represents the local Node ID for this node |
Zang MingJie | c9642fc | 2021-10-30 04:19:49 +0800 | [diff] [blame] | 52 | * @param peerNodeId represents peer Node's ID |
Evgeny Margolis | ea87cc6 | 2021-11-29 22:08:00 -0800 | [diff] [blame] | 53 | * @param peerCATs represents peer CASE Authenticated Tags |
Zang MingJie | c9642fc | 2021-10-30 04:19:49 +0800 | [diff] [blame] | 54 | * @param peerSessionId represents the encryption key ID assigned by peer node |
Tennessee Carmel-Veilleux | 7965de7 | 2022-04-27 20:33:58 -0400 | [diff] [blame] | 55 | * @param fabricIndex represents fabric index for the session |
Victor Morales | f7f1cc3 | 2021-11-30 08:31:47 -0800 | [diff] [blame] | 56 | * @param config represents the reliable message protocol configuration |
Pankaj Garg | ed3995b | 2020-08-17 13:04:52 -0700 | [diff] [blame] | 57 | * |
| 58 | * @note the newly created state will have an 'active' time set based on the current time source. |
| 59 | * |
Zang MingJie | d786c58 | 2021-10-27 23:58:10 +0800 | [diff] [blame] | 60 | * @returns CHIP_NO_ERROR if state could be initialized. May fail if maximum session count |
Pankaj Garg | ed3995b | 2020-08-17 13:04:52 -0700 | [diff] [blame] | 61 | * has been reached (with CHIP_ERROR_NO_MEMORY). |
| 62 | */ |
| 63 | CHECK_RETURN_VALUE |
Zang MingJie | a3f636d | 2022-04-19 09:18:40 +0800 | [diff] [blame] | 64 | Optional<SessionHandle> CreateNewSecureSessionForTest(SecureSession::Type secureSessionType, uint16_t localSessionId, |
Tennessee Carmel-Veilleux | 7965de7 | 2022-04-27 20:33:58 -0400 | [diff] [blame] | 65 | NodeId localNodeId, NodeId peerNodeId, CATValues peerCATs, |
| 66 | uint16_t peerSessionId, FabricIndex fabricIndex, |
Jerry Johns | c6724a0 | 2022-06-21 06:47:02 -0700 | [diff] [blame] | 67 | const ReliableMessageProtocolConfig & config); |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 68 | /** |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 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 |
Jerry Johns | c6724a0 | 2022-06-21 06:47:02 -0700 | [diff] [blame] | 77 | Optional<SessionHandle> CreateNewSecureSession(SecureSession::Type secureSessionType, ScopedNodeId sessionEvictionHint); |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 78 | |
Zang MingJie | c9642fc | 2021-10-30 04:19:49 +0800 | [diff] [blame] | 79 | void ReleaseSession(SecureSession * session) { mEntries.ReleaseObject(session); } |
| 80 | |
| 81 | template <typename Function> |
Zang MingJie | e2e4c17 | 2021-12-03 01:15:02 +0800 | [diff] [blame] | 82 | Loop ForEachSession(Function && function) |
Andrei Litvin | c567d83 | 2020-06-25 17:42:06 -0400 | [diff] [blame] | 83 | { |
Zang MingJie | c9642fc | 2021-10-30 04:19:49 +0800 | [diff] [blame] | 84 | return mEntries.ForEachActiveObject(std::forward<Function>(function)); |
Andrei Litvin | c567d83 | 2020-06-25 17:42:06 -0400 | [diff] [blame] | 85 | } |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 86 | |
Pankaj Garg | ed3995b | 2020-08-17 13:04:52 -0700 | [diff] [blame] | 87 | /** |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 88 | * Get a secure session given its session ID. |
Pankaj Garg | ed3995b | 2020-08-17 13:04:52 -0700 | [diff] [blame] | 89 | * |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 90 | * @param localSessionId the identifier of a secure unicast session context within the local node |
Pankaj Garg | ed3995b | 2020-08-17 13:04:52 -0700 | [diff] [blame] | 91 | * |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 92 | * @return the session if found, NullOptional if not found |
Pankaj Garg | ed3995b | 2020-08-17 13:04:52 -0700 | [diff] [blame] | 93 | */ |
| 94 | CHECK_RETURN_VALUE |
Jerry Johns | c6724a0 | 2022-06-21 06:47:02 -0700 | [diff] [blame] | 95 | Optional<SessionHandle> FindSecureSessionByLocalKey(uint16_t localSessionId); |
Pankaj Garg | 692b86c | 2021-09-13 15:34:05 -0700 | [diff] [blame] | 96 | |
Zang MingJie | c8149b1 | 2022-06-22 08:36:59 +0800 | [diff] [blame] | 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 | void NewerSessionAvailable(SecureSession * session) |
| 101 | { |
| 102 | VerifyOrDie(session->GetSecureSessionType() == SecureSession::Type::kCASE); |
| 103 | mEntries.ForEachActiveObject([&](SecureSession * oldSession) { |
| 104 | if (session == oldSession) |
| 105 | return Loop::Continue; |
| 106 | |
| 107 | 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 | if (oldSession->GetSecureSessionType() == SecureSession::Type::kCASE && oldSession->GetPeer() == session->GetPeer() && |
| 114 | oldSession->GetPeerCATs() == session->GetPeerCATs()) |
| 115 | { |
| 116 | oldSession->NewerSessionAvailable(SessionHandle(*session)); |
| 117 | } |
| 118 | |
| 119 | return Loop::Continue; |
| 120 | }); |
| 121 | } |
| 122 | |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 123 | private: |
Jerry Johns | c6724a0 | 2022-06-21 06:47:02 -0700 | [diff] [blame] | 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 | const Transport::SecureSession * operator->() const { return mSession; } |
Jerry Johns | 161e76a | 2022-06-24 23:48:11 -0700 | [diff] [blame] | 145 | auto GetNumMatchingOnFabric() { return mNumMatchingOnFabric; } |
| 146 | auto GetNumMatchingOnPeer() { return mNumMatchingOnPeer; } |
Jerry Johns | c6724a0 | 2022-06-21 06:47:02 -0700 | [diff] [blame] | 147 | |
| 148 | private: |
| 149 | SecureSession * mSession; |
Jerry Johns | 161e76a | 2022-06-24 23:48:11 -0700 | [diff] [blame] | 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 | |
Jerry Johns | c6724a0 | 2022-06-21 06:47:02 -0700 | [diff] [blame] | 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 | void Sort(CompareFunc func) |
| 185 | { |
| 186 | Sorting::BubbleSort(mSessionList.begin(), mSessionList.size(), func); |
| 187 | } |
| 188 | |
| 189 | const ScopedNodeId & GetSessionEvictionHint() const { return mSessionEvictionHint; } |
| 190 | |
| 191 | private: |
| 192 | EvictionPolicyContext(Span<SortableSession> sessionList, ScopedNodeId sessionEvictionHint) |
| 193 | { |
| 194 | mSessionList = sessionList; |
| 195 | mSessionEvictionHint = sessionEvictionHint; |
| 196 | } |
| 197 | |
| 198 | friend class SecureSessionTable; |
| 199 | Span<SortableSession> mSessionList; |
| 200 | ScopedNodeId mSessionEvictionHint; |
| 201 | }; |
| 202 | |
| 203 | /** |
| 204 | * |
Jerry Johns | 161e76a | 2022-06-24 23:48:11 -0700 | [diff] [blame] | 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: |
Jerry Johns | c6724a0 | 2022-06-21 06:47:02 -0700 | [diff] [blame] | 207 | * |
Jerry Johns | 161e76a | 2022-06-24 23:48:11 -0700 | [diff] [blame] | 208 | * - Key1: Sessions on fabrics that have more sessions in the table are placed ahead of sessions on fabrics |
Boris Zbarsky | 16f8d7a | 2023-12-04 19:21:41 -0500 | [diff] [blame] | 209 | * with fewer sessions. We conclusively know that if a particular fabric has more sessions in the table |
Jerry Johns | 161e76a | 2022-06-24 23:48:11 -0700 | [diff] [blame] | 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. |
Jerry Johns | c6724a0 | 2022-06-21 06:47:02 -0700 | [diff] [blame] | 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 | |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 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 |
Zang MingJie | cf7d85c | 2022-05-20 23:35:15 +0800 | [diff] [blame] | 251 | * runtime complexity of O(CHIP_CONFIG_PEER_CONNECTION_POOL_SIZE^2/64). Speed up could be |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 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 |
Jerry Johns | c6724a0 | 2022-06-21 06:47:02 -0700 | [diff] [blame] | 257 | Optional<uint16_t> FindUnusedSessionId(); |
Andrei Litvin | 1866f46 | 2022-04-08 05:21:04 -1000 | [diff] [blame] | 258 | |
Jerry Johns | c6724a0 | 2022-06-21 06:47:02 -0700 | [diff] [blame] | 259 | bool mRunningEvictionLogic = false; |
| 260 | ObjectPool<SecureSession, CHIP_CONFIG_SECURE_SESSION_POOL_SIZE> mEntries; |
Jerry Johns | 161e76a | 2022-06-24 23:48:11 -0700 | [diff] [blame] | 261 | |
| 262 | size_t GetMaxSessionTableSize() const |
| 263 | { |
| 264 | #if CONFIG_BUILD_FOR_HOST_UNIT_TEST |
| 265 | 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 | |
Michael Sandstedt | 41a431d | 2022-04-03 22:54:51 -0500 | [diff] [blame] | 276 | uint16_t mNextSessionId = 0; |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 277 | }; |
| 278 | |
Andrei Litvin | 8ca8ff6 | 2020-06-18 14:41:56 -0400 | [diff] [blame] | 279 | } // namespace Transport |
| 280 | } // namespace chip |