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/BleConfig.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 0 : UnauthenticatedSession(SessionRole sessionRole, NodeId ephemeralInitiatorNodeID, const ReliableMessageProtocolConfig & config) :
49 0 : mEphemeralInitiatorNodeId(ephemeralInitiatorNodeID), mSessionRole(sessionRole),
50 0 : mLastActivityTime(System::SystemClock().GetMonotonicTimestamp()),
51 0 : mLastPeerActivityTime(System::Clock::kZero), // Start at zero to default to IDLE state
52 0 : mRemoteSessionParams(config)
53 0 : {}
54 0 : ~UnauthenticatedSession() override { VerifyOrDie(GetReferenceCount() == 0); }
55 :
56 : public:
57 : UnauthenticatedSession(const UnauthenticatedSession &) = delete;
58 : UnauthenticatedSession & operator=(const UnauthenticatedSession &) = delete;
59 : UnauthenticatedSession(UnauthenticatedSession &&) = delete;
60 : UnauthenticatedSession & operator=(UnauthenticatedSession &&) = delete;
61 :
62 : System::Clock::Timestamp GetLastActivityTime() const { return mLastActivityTime; }
63 0 : System::Clock::Timestamp GetLastPeerActivityTime() const { return mLastPeerActivityTime; }
64 205 : void MarkActive() { mLastActivityTime = System::SystemClock().GetMonotonicTimestamp(); }
65 104 : void MarkActiveRx()
66 : {
67 104 : mLastPeerActivityTime = System::SystemClock().GetMonotonicTimestamp();
68 104 : MarkActive();
69 104 : }
70 :
71 0 : Session::SessionType GetSessionType() const override { return Session::SessionType::kUnauthenticated; }
72 :
73 0 : void Retain() override { ReferenceCounted<UnauthenticatedSession, UnauthenticatedSession, 0>::Retain(); }
74 0 : void Release() override { ReferenceCounted<UnauthenticatedSession, UnauthenticatedSession, 0>::Release(); }
75 :
76 0 : bool IsActiveSession() const override { return true; }
77 :
78 0 : ScopedNodeId GetPeer() const override { return ScopedNodeId(GetPeerNodeId(), kUndefinedFabricIndex); }
79 0 : ScopedNodeId GetLocalScopedNodeId() const override { return ScopedNodeId(kUndefinedNodeId, kUndefinedFabricIndex); }
80 :
81 0 : Access::SubjectDescriptor GetSubjectDescriptor() const override
82 : {
83 0 : return Access::SubjectDescriptor(); // return an empty ISD for unauthenticated session.
84 : }
85 :
86 0 : bool AllowsMRP() const override { return GetPeerAddress().GetTransportType() == Transport::Type::kUdp; }
87 :
88 0 : bool AllowsLargePayload() const override { return GetPeerAddress().GetTransportType() == Transport::Type::kTcp; }
89 :
90 0 : System::Clock::Milliseconds32 GetAckTimeout() const override
91 : {
92 0 : switch (mPeerAddress.GetTransportType())
93 : {
94 0 : case Transport::Type::kUdp: {
95 0 : const ReliableMessageProtocolConfig & remoteMRPConfig = mRemoteSessionParams.GetMRPConfig();
96 0 : return GetRetransmissionTimeout(remoteMRPConfig.mActiveRetransTimeout, remoteMRPConfig.mIdleRetransTimeout,
97 0 : GetLastPeerActivityTime(), remoteMRPConfig.mActiveThresholdTime);
98 : }
99 0 : case Transport::Type::kTcp:
100 0 : return System::Clock::Seconds16(30);
101 0 : case Transport::Type::kBle:
102 0 : return System::Clock::Milliseconds32(BTP_ACK_TIMEOUT_MS);
103 0 : default:
104 0 : break;
105 : }
106 0 : return System::Clock::Timeout();
107 : }
108 :
109 0 : NodeId GetPeerNodeId() const
110 : {
111 0 : if (mSessionRole == SessionRole::kInitiator)
112 : {
113 0 : return kUndefinedNodeId;
114 : }
115 :
116 0 : return mEphemeralInitiatorNodeId;
117 : }
118 :
119 220 : SessionRole GetSessionRole() const { return mSessionRole; }
120 176 : NodeId GetEphemeralInitiatorNodeID() const { return mEphemeralInitiatorNodeId; }
121 0 : const PeerAddress & GetPeerAddress() const { return mPeerAddress; }
122 0 : void SetPeerAddress(const PeerAddress & peerAddress) { mPeerAddress = peerAddress; }
123 :
124 0 : bool IsPeerActive() const
125 : {
126 0 : return ((System::SystemClock().GetMonotonicTimestamp() - GetLastPeerActivityTime()) <
127 0 : GetRemoteMRPConfig().mActiveThresholdTime);
128 : }
129 :
130 0 : System::Clock::Timestamp GetMRPBaseTimeout() const override
131 : {
132 0 : return IsPeerActive() ? GetRemoteMRPConfig().mActiveRetransTimeout : GetRemoteMRPConfig().mIdleRetransTimeout;
133 : }
134 :
135 17 : void SetRemoteSessionParameters(const SessionParameters & sessionParams) { mRemoteSessionParams = sessionParams; }
136 :
137 0 : const SessionParameters & GetRemoteSessionParameters() const override { return mRemoteSessionParams; }
138 :
139 192 : PeerMessageCounter & GetPeerMessageCounter() { return mPeerMessageCounter; }
140 :
141 0 : static void Release(UnauthenticatedSession * obj)
142 : {
143 : // When using heap pools, we need to make sure to release ourselves back to
144 : // the pool. When not using heap pools, we don't want the extra cost of the
145 : // table pointer here, and the table itself handles entry reuse and cleanup
146 : // as needed.
147 : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
148 0 : obj->ReleaseSelfToPool();
149 : #else
150 : // Just do nothing.
151 : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
152 0 : }
153 :
154 : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
155 : virtual void ReleaseSelfToPool() = 0;
156 : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
157 :
158 : private:
159 : const NodeId mEphemeralInitiatorNodeId;
160 : const SessionRole mSessionRole;
161 : PeerAddress mPeerAddress;
162 : System::Clock::Timestamp mLastActivityTime; ///< Timestamp of last tx or rx
163 : System::Clock::Timestamp mLastPeerActivityTime; ///< Timestamp of last rx
164 : SessionParameters mRemoteSessionParams;
165 : PeerMessageCounter mPeerMessageCounter;
166 : };
167 :
168 : template <size_t kMaxSessionCount>
169 : class UnauthenticatedSessionTable;
170 :
171 : namespace detail {
172 :
173 : template <size_t kMaxSessionCount>
174 : class UnauthenticatedSessionPoolEntry : public UnauthenticatedSession
175 : {
176 : public:
177 0 : UnauthenticatedSessionPoolEntry(SessionRole sessionRole, NodeId ephemeralInitiatorNodeID,
178 : const ReliableMessageProtocolConfig & config,
179 : UnauthenticatedSessionTable<kMaxSessionCount> & sessionTable) :
180 : UnauthenticatedSession(sessionRole, ephemeralInitiatorNodeID, config)
181 : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
182 : ,
183 0 : mSessionTable(sessionTable)
184 : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
185 0 : {}
186 :
187 : private:
188 : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
189 : virtual void ReleaseSelfToPool();
190 :
191 : UnauthenticatedSessionTable<kMaxSessionCount> & mSessionTable;
192 : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
193 : };
194 :
195 : } // namespace detail
196 :
197 : /*
198 : * @brief
199 : * An table which manages UnauthenticatedSessions
200 : *
201 : * The UnauthenticatedSession entries are rotated using LRU, but entry can be hold by using SessionHandle or
202 : * SessionHolder, which increase the reference count by 1. If the reference count is not 0, the entry won't be pruned.
203 : */
204 : template <size_t kMaxSessionCount>
205 : class UnauthenticatedSessionTable
206 : {
207 : public:
208 101 : ~UnauthenticatedSessionTable()
209 : {
210 : #if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
211 : // When not using heap pools, our entries never actually get released
212 : // back to the pool (which lets us make the entries 4 bytes smaller by
213 : // not storing a reference to the table in them) and we LRU reuse ones
214 : // that have 0 refcount. But we should release them all here, to ensure
215 : // that we don't hit fatal asserts in our pool destructor.
216 : mEntries.ReleaseAll();
217 : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
218 101 : }
219 :
220 : /**
221 : * Get a responder session with the given ephemeralInitiatorNodeID. If the session doesn't exist in the cache, allocate a new
222 : * entry for it.
223 : *
224 : * @return the session found or allocated, or Optional::Missing if not found and allocation failed.
225 : */
226 : CHECK_RETURN_VALUE
227 56 : Optional<SessionHandle> FindOrAllocateResponder(NodeId ephemeralInitiatorNodeID, const ReliableMessageProtocolConfig & config)
228 : {
229 56 : UnauthenticatedSession * result = FindEntry(UnauthenticatedSession::SessionRole::kResponder, ephemeralInitiatorNodeID);
230 56 : if (result != nullptr)
231 35 : return MakeOptional<SessionHandle>(*result);
232 :
233 21 : CHIP_ERROR err = AllocEntry(UnauthenticatedSession::SessionRole::kResponder, ephemeralInitiatorNodeID, config, result);
234 21 : if (err == CHIP_NO_ERROR)
235 : {
236 21 : return MakeOptional<SessionHandle>(*result);
237 : }
238 :
239 0 : return Optional<SessionHandle>::Missing();
240 : }
241 :
242 40 : CHECK_RETURN_VALUE Optional<SessionHandle> FindInitiator(NodeId ephemeralInitiatorNodeID)
243 : {
244 40 : UnauthenticatedSession * result = FindEntry(UnauthenticatedSession::SessionRole::kInitiator, ephemeralInitiatorNodeID);
245 40 : if (result != nullptr)
246 : {
247 40 : return MakeOptional<SessionHandle>(*result);
248 : }
249 :
250 0 : return Optional<SessionHandle>::Missing();
251 : }
252 :
253 0 : CHECK_RETURN_VALUE Optional<SessionHandle> AllocInitiator(NodeId ephemeralInitiatorNodeID, const PeerAddress & peerAddress,
254 : const ReliableMessageProtocolConfig & config)
255 : {
256 0 : UnauthenticatedSession * result = nullptr;
257 0 : CHIP_ERROR err = AllocEntry(UnauthenticatedSession::SessionRole::kInitiator, ephemeralInitiatorNodeID, config, result);
258 0 : if (err == CHIP_NO_ERROR)
259 : {
260 0 : result->SetPeerAddress(peerAddress);
261 0 : return MakeOptional<SessionHandle>(*result);
262 : }
263 :
264 0 : return Optional<SessionHandle>::Missing();
265 : }
266 :
267 : private:
268 : using EntryType = detail::UnauthenticatedSessionPoolEntry<kMaxSessionCount>;
269 : friend EntryType;
270 :
271 : /**
272 : * Allocates a new session out of the internal resource pool.
273 : *
274 : * @returns CHIP_NO_ERROR if new session created. May fail if maximum session count has been reached (with
275 : * CHIP_ERROR_NO_MEMORY).
276 : */
277 : CHECK_RETURN_VALUE
278 0 : CHIP_ERROR AllocEntry(UnauthenticatedSession::SessionRole sessionRole, NodeId ephemeralInitiatorNodeID,
279 : const ReliableMessageProtocolConfig & config, UnauthenticatedSession *& entry)
280 : {
281 0 : auto entryToUse = mEntries.CreateObject(sessionRole, ephemeralInitiatorNodeID, config, *this);
282 0 : if (entryToUse != nullptr)
283 : {
284 0 : entry = entryToUse;
285 0 : return CHIP_NO_ERROR;
286 : }
287 :
288 : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
289 : // permanent failure if heap was insufficient
290 0 : return CHIP_ERROR_NO_MEMORY;
291 : #else
292 : entryToUse = FindLeastRecentUsedEntry();
293 : VerifyOrReturnError(entryToUse != nullptr, CHIP_ERROR_NO_MEMORY);
294 :
295 : // Drop the least recent entry to allow for a new alloc.
296 : mEntries.ReleaseObject(entryToUse);
297 : entryToUse = mEntries.CreateObject(sessionRole, ephemeralInitiatorNodeID, config, *this);
298 :
299 : if (entryToUse == nullptr)
300 : {
301 : // this is NOT expected: we freed an object to have space
302 : return CHIP_ERROR_INTERNAL;
303 : }
304 :
305 : entry = entryToUse;
306 : return CHIP_NO_ERROR;
307 : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
308 : }
309 :
310 96 : CHECK_RETURN_VALUE UnauthenticatedSession * FindEntry(UnauthenticatedSession::SessionRole sessionRole,
311 : NodeId ephemeralInitiatorNodeID)
312 : {
313 96 : UnauthenticatedSession * result = nullptr;
314 96 : mEntries.ForEachActiveObject([&](UnauthenticatedSession * entry) {
315 122 : if (entry->GetSessionRole() == sessionRole && entry->GetEphemeralInitiatorNodeID() == ephemeralInitiatorNodeID)
316 : {
317 75 : result = entry;
318 75 : return Loop::Break;
319 : }
320 47 : return Loop::Continue;
321 : });
322 96 : return result;
323 : }
324 :
325 : EntryType * FindLeastRecentUsedEntry()
326 : {
327 : EntryType * result = nullptr;
328 : System::Clock::Timestamp oldestTime = System::Clock::Timestamp(std::numeric_limits<System::Clock::Timestamp::rep>::max());
329 :
330 : mEntries.ForEachActiveObject([&](EntryType * entry) {
331 : if (entry->GetReferenceCount() == 0 && entry->GetLastActivityTime() < oldestTime)
332 : {
333 : result = entry;
334 : oldestTime = entry->GetLastActivityTime();
335 : }
336 : return Loop::Continue;
337 : });
338 :
339 : return result;
340 : }
341 :
342 0 : void ReleaseEntry(EntryType * entry) { mEntries.ReleaseObject(entry); }
343 :
344 : ObjectPool<EntryType, kMaxSessionCount> mEntries;
345 : };
346 :
347 : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
348 : template <size_t kMaxSessionCount>
349 0 : void detail::UnauthenticatedSessionPoolEntry<kMaxSessionCount>::ReleaseSelfToPool()
350 : {
351 0 : mSessionTable.ReleaseEntry(this);
352 0 : }
353 : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
354 :
355 : } // namespace Transport
356 : } // namespace chip
|