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