blob: 7f8a072a03a2fe452658a5ee7547bfb7d8abff94 [file] [log] [blame]
Andrei Litvin8ca8ff62020-06-18 14:41:56 -04001/*
2 *
Evgeny Margolisea87cc62021-11-29 22:08:00 -08003 * Copyright (c) 2020-2021 Project CHIP Authors
Andrei Litvin8ca8ff62020-06-18 14:41:56 -04004 *
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 Litvin1873e8c2020-10-12 10:52:26 -040017#pragma once
Andrei Litvin8ca8ff62020-06-18 14:41:56 -040018
Zang MingJie53dd5832021-09-03 03:05:16 +080019#include <lib/core/CHIPError.h>
20#include <lib/support/CodeUtils.h>
Zang MingJiec9642fc2021-10-30 04:19:49 +080021#include <lib/support/Pool.h>
Jerry Johnsc6724a02022-06-21 06:47:02 -070022#include <lib/support/SortUtils.h>
Andrei Litvin8ca8ff62020-06-18 14:41:56 -040023#include <system/TimeSource.h>
Zang MingJieeca9bff2021-09-23 03:19:51 +080024#include <transport/SecureSession.h>
Andrei Litvin8ca8ff62020-06-18 14:41:56 -040025
26namespace chip {
27namespace Transport {
28
Karsten Sperlingb15a6e62023-09-16 03:58:46 +120029inline constexpr uint16_t kMaxSessionID = UINT16_MAX;
30inline constexpr uint16_t kUnsecuredSessionId = 0;
Zang MingJiec07d8182021-01-13 01:12:10 +080031
Andrei Litvin8ca8ff62020-06-18 14:41:56 -040032/**
Zang MingJied786c582021-10-27 23:58:10 +080033 * Handles a set of sessions.
Andrei Litvin8ca8ff62020-06-18 14:41:56 -040034 *
35 * Intended for:
Zang MingJied786c582021-10-27 23:58:10 +080036 * - handle session active time and expiration
37 * - allocate and free space for sessions.
Andrei Litvin8ca8ff62020-06-18 14:41:56 -040038 */
Zang MingJieeca9bff2021-09-23 03:19:51 +080039class SecureSessionTable
Andrei Litvin8ca8ff62020-06-18 14:41:56 -040040{
41public:
Kevin Schoedel00434dd2021-12-11 02:56:10 -050042 ~SecureSessionTable() { mEntries.ReleaseAll(); }
43
Michael Sandstedt41a431d2022-04-03 22:54:51 -050044 void Init() { mNextSessionId = chip::Crypto::GetRandU16(); }
45
Andrei Litvin8ca8ff62020-06-18 14:41:56 -040046 /**
Michael Sandstedt41a431d2022-04-03 22:54:51 -050047 * Allocate a new secure session out of the internal resource pool.
Andrei Litvin8ca8ff62020-06-18 14:41:56 -040048 *
Kevin Schoedelfcd97852021-12-07 17:16:40 -050049 * @param secureSessionType secure session type
Michael Sandstedt41a431d2022-04-03 22:54:51 -050050 * @param localSessionId unique identifier for the local node's secure unicast session context
Tennessee Carmel-Veilleux7965de72022-04-27 20:33:58 -040051 * @param localNodeId represents the local Node ID for this node
Zang MingJiec9642fc2021-10-30 04:19:49 +080052 * @param peerNodeId represents peer Node's ID
Evgeny Margolisea87cc62021-11-29 22:08:00 -080053 * @param peerCATs represents peer CASE Authenticated Tags
Zang MingJiec9642fc2021-10-30 04:19:49 +080054 * @param peerSessionId represents the encryption key ID assigned by peer node
Tennessee Carmel-Veilleux7965de72022-04-27 20:33:58 -040055 * @param fabricIndex represents fabric index for the session
Victor Moralesf7f1cc32021-11-30 08:31:47 -080056 * @param config represents the reliable message protocol configuration
Pankaj Garged3995b2020-08-17 13:04:52 -070057 *
58 * @note the newly created state will have an 'active' time set based on the current time source.
59 *
Zang MingJied786c582021-10-27 23:58:10 +080060 * @returns CHIP_NO_ERROR if state could be initialized. May fail if maximum session count
Pankaj Garged3995b2020-08-17 13:04:52 -070061 * has been reached (with CHIP_ERROR_NO_MEMORY).
62 */
63 CHECK_RETURN_VALUE
Zang MingJiea3f636d2022-04-19 09:18:40 +080064 Optional<SessionHandle> CreateNewSecureSessionForTest(SecureSession::Type secureSessionType, uint16_t localSessionId,
Tennessee Carmel-Veilleux7965de72022-04-27 20:33:58 -040065 NodeId localNodeId, NodeId peerNodeId, CATValues peerCATs,
66 uint16_t peerSessionId, FabricIndex fabricIndex,
Jerry Johnsc6724a02022-06-21 06:47:02 -070067 const ReliableMessageProtocolConfig & config);
Michael Sandstedt41a431d2022-04-03 22:54:51 -050068 /**
Michael Sandstedt41a431d2022-04-03 22:54:51 -050069 * 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 Johnsc6724a02022-06-21 06:47:02 -070077 Optional<SessionHandle> CreateNewSecureSession(SecureSession::Type secureSessionType, ScopedNodeId sessionEvictionHint);
Michael Sandstedt41a431d2022-04-03 22:54:51 -050078
Zang MingJiec9642fc2021-10-30 04:19:49 +080079 void ReleaseSession(SecureSession * session) { mEntries.ReleaseObject(session); }
80
81 template <typename Function>
Zang MingJiee2e4c172021-12-03 01:15:02 +080082 Loop ForEachSession(Function && function)
Andrei Litvinc567d832020-06-25 17:42:06 -040083 {
Zang MingJiec9642fc2021-10-30 04:19:49 +080084 return mEntries.ForEachActiveObject(std::forward<Function>(function));
Andrei Litvinc567d832020-06-25 17:42:06 -040085 }
Andrei Litvin8ca8ff62020-06-18 14:41:56 -040086
Pankaj Garged3995b2020-08-17 13:04:52 -070087 /**
Michael Sandstedt41a431d2022-04-03 22:54:51 -050088 * Get a secure session given its session ID.
Pankaj Garged3995b2020-08-17 13:04:52 -070089 *
Michael Sandstedt41a431d2022-04-03 22:54:51 -050090 * @param localSessionId the identifier of a secure unicast session context within the local node
Pankaj Garged3995b2020-08-17 13:04:52 -070091 *
Michael Sandstedt41a431d2022-04-03 22:54:51 -050092 * @return the session if found, NullOptional if not found
Pankaj Garged3995b2020-08-17 13:04:52 -070093 */
94 CHECK_RETURN_VALUE
Jerry Johnsc6724a02022-06-21 06:47:02 -070095 Optional<SessionHandle> FindSecureSessionByLocalKey(uint16_t localSessionId);
Pankaj Garg692b86c2021-09-13 15:34:05 -070096
Zang MingJiec8149b12022-06-22 08:36:59 +080097 // 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 Litvin8ca8ff62020-06-18 14:41:56 -0400123private:
Jerry Johnsc6724a02022-06-21 06:47:02 -0700124 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 Johns161e76a2022-06-24 23:48:11 -0700145 auto GetNumMatchingOnFabric() { return mNumMatchingOnFabric; }
146 auto GetNumMatchingOnPeer() { return mNumMatchingOnPeer; }
Jerry Johnsc6724a02022-06-21 06:47:02 -0700147
148 private:
149 SecureSession * mSession;
Jerry Johns161e76a2022-06-24 23:48:11 -0700150 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 Johnsc6724a02022-06-21 06:47:02 -0700158 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 Johns161e76a2022-06-24 23:48:11 -0700205 * 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 Johnsc6724a02022-06-21 06:47:02 -0700207 *
Jerry Johns161e76a2022-06-24 23:48:11 -0700208 * - Key1: Sessions on fabrics that have more sessions in the table are placed ahead of sessions on fabrics
Boris Zbarsky16f8d7a2023-12-04 19:21:41 -0500209 * with fewer sessions. We conclusively know that if a particular fabric has more sessions in the table
Jerry Johns161e76a2022-06-24 23:48:11 -0700210 * 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 Johnsc6724a02022-06-21 06:47:02 -0700231 *
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 Sandstedt41a431d2022-04-03 22:54:51 -0500243 /**
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 MingJiecf7d85c2022-05-20 23:35:15 +0800251 * runtime complexity of O(CHIP_CONFIG_PEER_CONNECTION_POOL_SIZE^2/64). Speed up could be
Michael Sandstedt41a431d2022-04-03 22:54:51 -0500252 * 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 Johnsc6724a02022-06-21 06:47:02 -0700257 Optional<uint16_t> FindUnusedSessionId();
Andrei Litvin1866f462022-04-08 05:21:04 -1000258
Jerry Johnsc6724a02022-06-21 06:47:02 -0700259 bool mRunningEvictionLogic = false;
260 ObjectPool<SecureSession, CHIP_CONFIG_SECURE_SESSION_POOL_SIZE> mEntries;
Jerry Johns161e76a2022-06-24 23:48:11 -0700261
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 Sandstedt41a431d2022-04-03 22:54:51 -0500276 uint16_t mNextSessionId = 0;
Andrei Litvin8ca8ff62020-06-18 14:41:56 -0400277};
278
Andrei Litvin8ca8ff62020-06-18 14:41:56 -0400279} // namespace Transport
280} // namespace chip