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