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 55 : UnauthenticatedSession(SessionRole sessionRole, NodeId ephemeralInitiatorNodeID, const Transport::PeerAddress & peerAddress,
49 55 : const ReliableMessageProtocolConfig & config) :
50 55 : mEphemeralInitiatorNodeId(ephemeralInitiatorNodeID),
51 55 : mSessionRole(sessionRole), mPeerAddress(peerAddress), mLastActivityTime(System::SystemClock().GetMonotonicTimestamp()),
52 55 : mLastPeerActivityTime(System::Clock::kZero), // Start at zero to default to IDLE state
53 110 : mRemoteSessionParams(config)
54 55 : {}
55 55 : ~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 239 : System::Clock::Timestamp GetLastPeerActivityTime() const { return mLastPeerActivityTime; }
65 258 : void MarkActive() { mLastActivityTime = System::SystemClock().GetMonotonicTimestamp(); }
66 132 : void MarkActiveRx()
67 : {
68 132 : mLastPeerActivityTime = System::SystemClock().GetMonotonicTimestamp();
69 132 : MarkActive();
70 132 : }
71 :
72 3015 : Session::SessionType GetSessionType() const override { return Session::SessionType::kUnauthenticated; }
73 :
74 1708 : void Retain() override { ReferenceCounted<UnauthenticatedSession, UnauthenticatedSession, 0>::Retain(); }
75 1708 : void Release() override { ReferenceCounted<UnauthenticatedSession, UnauthenticatedSession, 0>::Release(); }
76 :
77 108 : bool IsActiveSession() const override { return true; }
78 :
79 242 : 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 518 : bool AllowsMRP() const override { return (GetPeerAddress().GetTransportType() == Transport::Type::kUdp); }
88 :
89 123 : bool AllowsLargePayload() const override { return GetPeerAddress().GetTransportType() == Transport::Type::kTcp; }
90 :
91 113 : System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const override
92 : {
93 113 : switch (mPeerAddress.GetTransportType())
94 : {
95 113 : case Transport::Type::kUdp: {
96 113 : const ReliableMessageProtocolConfig & remoteMRPConfig = mRemoteSessionParams.GetMRPConfig();
97 113 : return GetRetransmissionTimeout(remoteMRPConfig.mActiveRetransTimeout, remoteMRPConfig.mIdleRetransTimeout,
98 226 : GetLastPeerActivityTime(), remoteMRPConfig.mActiveThresholdTime,
99 113 : 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 113 : System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
112 : bool isFirstMessageOnExchange) const override
113 : {
114 113 : switch (mPeerAddress.GetTransportType())
115 : {
116 113 : case Transport::Type::kUdp: {
117 113 : const auto & maybeLocalMRPConfig = GetLocalMRPConfig();
118 113 : const auto & defaultMRRPConfig = GetDefaultMRPConfig();
119 113 : const auto & localMRPConfig = maybeLocalMRPConfig.ValueOr(defaultMRRPConfig);
120 226 : return GetRetransmissionTimeout(localMRPConfig.mActiveRetransTimeout, localMRPConfig.mIdleRetransTimeout,
121 226 : 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 242 : NodeId GetPeerNodeId() const
134 : {
135 242 : if (mSessionRole == SessionRole::kInitiator)
136 : {
137 102 : return kUndefinedNodeId;
138 : }
139 :
140 140 : return mEphemeralInitiatorNodeId;
141 : }
142 :
143 471 : SessionRole GetSessionRole() const { return mSessionRole; }
144 344 : NodeId GetEphemeralInitiatorNodeID() const { return mEphemeralInitiatorNodeId; }
145 1029 : const PeerAddress & GetPeerAddress() const { return mPeerAddress; }
146 150 : void SetPeerAddress(const PeerAddress & peerAddress) { mPeerAddress = peerAddress; }
147 :
148 126 : bool IsPeerActive() const
149 : {
150 252 : return ((System::SystemClock().GetMonotonicTimestamp() - GetLastPeerActivityTime()) <
151 252 : GetRemoteMRPConfig().mActiveThresholdTime);
152 : }
153 :
154 28 : System::Clock::Timestamp GetMRPBaseTimeout() const override
155 : {
156 28 : return IsPeerActive() ? GetRemoteMRPConfig().mActiveRetransTimeout : GetRemoteMRPConfig().mIdleRetransTimeout;
157 : }
158 :
159 40 : void SetRemoteSessionParameters(const SessionParameters & sessionParams) { mRemoteSessionParams = sessionParams; }
160 :
161 322 : const SessionParameters & GetRemoteSessionParameters() const override { return mRemoteSessionParams; }
162 :
163 244 : PeerMessageCounter & GetPeerMessageCounter() { return mPeerMessageCounter; }
164 :
165 55 : 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 55 : obj->ReleaseSelfToPool();
173 : #else
174 : // Just do nothing.
175 : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
176 55 : }
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 55 : 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 55 : mSessionTable(sessionTable)
208 : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
209 55 : {}
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 590 : ~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 590 : }
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 71 : Optional<SessionHandle> FindOrAllocateResponder(NodeId ephemeralInitiatorNodeID, const ReliableMessageProtocolConfig & config,
252 : const Transport::PeerAddress & peerAddress)
253 : {
254 71 : UnauthenticatedSession * result =
255 71 : FindEntry(UnauthenticatedSession::SessionRole::kResponder, ephemeralInitiatorNodeID, peerAddress);
256 71 : if (result != nullptr)
257 44 : return MakeOptional<SessionHandle>(*result);
258 :
259 : CHIP_ERROR err =
260 27 : AllocEntry(UnauthenticatedSession::SessionRole::kResponder, ephemeralInitiatorNodeID, peerAddress, config, result);
261 54 : if (err == CHIP_NO_ERROR)
262 : {
263 27 : return MakeOptional<SessionHandle>(*result);
264 : }
265 :
266 0 : return Optional<SessionHandle>::Missing();
267 : }
268 :
269 51 : CHECK_RETURN_VALUE Optional<SessionHandle> FindInitiator(NodeId ephemeralInitiatorNodeID,
270 : const Transport::PeerAddress & peerAddress)
271 : {
272 : UnauthenticatedSession * result =
273 51 : FindEntry(UnauthenticatedSession::SessionRole::kInitiator, ephemeralInitiatorNodeID, peerAddress);
274 51 : if (result != nullptr)
275 : {
276 51 : return MakeOptional<SessionHandle>(*result);
277 : }
278 :
279 0 : return Optional<SessionHandle>::Missing();
280 : }
281 :
282 28 : CHECK_RETURN_VALUE Optional<SessionHandle> AllocInitiator(NodeId ephemeralInitiatorNodeID, const PeerAddress & peerAddress,
283 : const ReliableMessageProtocolConfig & config)
284 : {
285 28 : UnauthenticatedSession * result = nullptr;
286 : CHIP_ERROR err =
287 28 : AllocEntry(UnauthenticatedSession::SessionRole::kInitiator, ephemeralInitiatorNodeID, peerAddress, config, result);
288 56 : if (err == CHIP_NO_ERROR)
289 : {
290 28 : result->SetPeerAddress(peerAddress);
291 28 : return MakeOptional<SessionHandle>(*result);
292 : }
293 :
294 0 : return Optional<SessionHandle>::Missing();
295 : }
296 :
297 : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
298 2 : void MarkSessionOverTCPForEviction(Transport::ActiveTCPConnectionState & conn)
299 : {
300 2 : mEntries.ForEachActiveObject([&](UnauthenticatedSession * session) {
301 0 : if (session->GetTCPConnection() == conn)
302 : {
303 0 : session->ReleaseTCPConnection();
304 :
305 : // If the session has no other references, we can release it.
306 0 : if (session->GetReferenceCount() == 0)
307 : {
308 0 : mEntries.ReleaseObject(static_cast<EntryType *>(session));
309 : }
310 : }
311 0 : return Loop::Continue;
312 : });
313 2 : }
314 : #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
315 :
316 : private:
317 : using EntryType = detail::UnauthenticatedSessionPoolEntry<kMaxSessionCount>;
318 : friend EntryType;
319 :
320 : /**
321 : * Allocates a new session out of the internal resource pool.
322 : *
323 : * @returns CHIP_NO_ERROR if new session created. May fail if maximum session count has been reached (with
324 : * CHIP_ERROR_NO_MEMORY).
325 : */
326 55 : CHIP_ERROR AllocEntry(UnauthenticatedSession::SessionRole sessionRole, NodeId ephemeralInitiatorNodeID,
327 : const PeerAddress & peerAddress, const ReliableMessageProtocolConfig & config,
328 : UnauthenticatedSession *& entry)
329 : {
330 55 : auto entryToUse = mEntries.CreateObject(sessionRole, ephemeralInitiatorNodeID, peerAddress, config, *this);
331 55 : if (entryToUse != nullptr)
332 : {
333 55 : entry = entryToUse;
334 55 : return CHIP_NO_ERROR;
335 : }
336 :
337 : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
338 : // permanent failure if heap was insufficient
339 0 : return CHIP_ERROR_NO_MEMORY;
340 : #else
341 : entryToUse = FindLeastRecentUsedEntry();
342 : VerifyOrReturnError(entryToUse != nullptr, CHIP_ERROR_NO_MEMORY);
343 :
344 : // Drop the least recent entry to allow for a new alloc.
345 : mEntries.ReleaseObject(entryToUse);
346 : entryToUse = mEntries.CreateObject(sessionRole, ephemeralInitiatorNodeID, peerAddress, config, *this);
347 :
348 : if (entryToUse == nullptr)
349 : {
350 : // this is NOT expected: we freed an object to have space
351 : return CHIP_ERROR_INTERNAL;
352 : }
353 :
354 : entry = entryToUse;
355 : return CHIP_NO_ERROR;
356 : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
357 : }
358 :
359 122 : CHECK_RETURN_VALUE UnauthenticatedSession * FindEntry(UnauthenticatedSession::SessionRole sessionRole,
360 : NodeId ephemeralInitiatorNodeID,
361 : const Transport::PeerAddress & peerAddress)
362 : {
363 122 : UnauthenticatedSession * result = nullptr;
364 122 : mEntries.ForEachActiveObject([&](UnauthenticatedSession * entry) {
365 248 : if (entry->GetSessionRole() == sessionRole && entry->GetEphemeralInitiatorNodeID() == ephemeralInitiatorNodeID &&
366 95 : entry->GetPeerAddress().GetTransportType() == peerAddress.GetTransportType())
367 : {
368 95 : result = entry;
369 95 : return Loop::Break;
370 : }
371 58 : return Loop::Continue;
372 : });
373 122 : return result;
374 : }
375 :
376 : EntryType * FindLeastRecentUsedEntry()
377 : {
378 : EntryType * result = nullptr;
379 : System::Clock::Timestamp oldestTime = System::Clock::Timestamp(std::numeric_limits<System::Clock::Timestamp::rep>::max());
380 :
381 : mEntries.ForEachActiveObject([&](EntryType * entry) {
382 : if (entry->GetReferenceCount() == 0 && entry->GetLastActivityTime() < oldestTime)
383 : {
384 : result = entry;
385 : oldestTime = entry->GetLastActivityTime();
386 : }
387 : return Loop::Continue;
388 : });
389 :
390 : return result;
391 : }
392 :
393 55 : void ReleaseEntry(EntryType * entry) { mEntries.ReleaseObject(entry); }
394 :
395 : ObjectPool<EntryType, kMaxSessionCount> mEntries;
396 : };
397 :
398 : #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
399 : template <size_t kMaxSessionCount>
400 55 : void detail::UnauthenticatedSessionPoolEntry<kMaxSessionCount>::ReleaseSelfToPool()
401 : {
402 55 : mSessionTable.ReleaseEntry(this);
403 55 : }
404 : #endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
405 :
406 : } // namespace Transport
407 : } // namespace chip
|