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