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