Line data Source code
1 : /*
2 : * Copyright (c) 2020-2021 Project CHIP Authors
3 : * All rights reserved.
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 :
18 : /**
19 : * @file
20 : * This file implements the CHIP reliable message protocol.
21 : *
22 : */
23 :
24 : #include <errno.h>
25 : #include <inttypes.h>
26 :
27 : #include <app/icd/server/ICDServerConfig.h>
28 : #include <lib/support/BitFlags.h>
29 : #include <lib/support/CHIPFaultInjection.h>
30 : #include <lib/support/CodeUtils.h>
31 : #include <lib/support/logging/CHIPLogging.h>
32 : #include <messaging/ErrorCategory.h>
33 : #include <messaging/ExchangeMessageDispatch.h>
34 : #include <messaging/ExchangeMgr.h>
35 : #include <messaging/Flags.h>
36 : #include <messaging/ReliableMessageContext.h>
37 : #include <messaging/ReliableMessageMgr.h>
38 : #include <platform/ConnectivityManager.h>
39 : #include <tracing/metric_event.h>
40 :
41 : #if CHIP_CONFIG_ENABLE_ICD_SERVER
42 : #include <app/icd/server/ICDConfigurationData.h> // nogncheck
43 : #include <app/icd/server/ICDNotifier.h> // nogncheck
44 : #endif
45 :
46 : using namespace chip::System::Clock::Literals;
47 :
48 : namespace chip {
49 : namespace Messaging {
50 :
51 : System::Clock::Timeout ReliableMessageMgr::sAdditionalMRPBackoffTime = CHIP_CONFIG_MRP_RETRY_INTERVAL_SENDER_BOOST;
52 :
53 12659 : ReliableMessageMgr::RetransTableEntry::RetransTableEntry(ReliableMessageContext * rc) :
54 12659 : ec(*rc->GetExchangeContext()), nextRetransTime(0), sendCount(0)
55 : {
56 12659 : ec->SetWaitingForAck(true);
57 12659 : }
58 :
59 12659 : ReliableMessageMgr::RetransTableEntry::~RetransTableEntry()
60 : {
61 12659 : ec->SetWaitingForAck(false);
62 12659 : }
63 :
64 439 : ReliableMessageMgr::ReliableMessageMgr(ObjectPool<ExchangeContext, CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS> & contextPool) :
65 439 : mContextPool(contextPool), mSystemLayer(nullptr)
66 439 : {}
67 :
68 439 : ReliableMessageMgr::~ReliableMessageMgr() {}
69 :
70 376 : void ReliableMessageMgr::Init(chip::System::Layer * systemLayer)
71 : {
72 376 : mSystemLayer = systemLayer;
73 376 : }
74 :
75 376 : void ReliableMessageMgr::Shutdown()
76 : {
77 376 : StopTimer();
78 :
79 : // Clear the retransmit table
80 376 : mRetransTable.ForEachActiveObject([&](auto * entry) {
81 8 : mRetransTable.ReleaseObject(entry);
82 8 : return Loop::Continue;
83 : });
84 :
85 376 : mSystemLayer = nullptr;
86 376 : }
87 :
88 38091 : void ReliableMessageMgr::TicklessDebugDumpRetransTable(const char * log)
89 : {
90 : #if defined(RMP_TICKLESS_DEBUG)
91 : ChipLogDetail(ExchangeManager, "%s", log);
92 :
93 : mRetransTable.ForEachActiveObject([&](auto * entry) {
94 : ChipLogDetail(ExchangeManager,
95 : "EC:" ChipLogFormatExchange " MessageCounter:" ChipLogFormatMessageCounter
96 : " NextRetransTimeCtr: 0x" ChipLogFormatX64,
97 : ChipLogValueExchange(&entry->ec.Get()), entry->retainedBuf.GetMessageCounter(),
98 : ChipLogValueX64(entry->nextRetransTime.count()));
99 : return Loop::Continue;
100 : });
101 : #endif
102 38091 : }
103 :
104 : #if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
105 25259 : void ReliableMessageMgr::NotifyMessageSendAnalytics(const RetransTableEntry & entry, const SessionHandle & sessionHandle,
106 : const ReliableMessageAnalyticsDelegate::EventType & eventType)
107 : {
108 : // For now we only support sending analytics for messages being sent over an established CASE session.
109 25259 : if (!mAnalyticsDelegate || !sessionHandle->IsSecureSession())
110 : {
111 25247 : return;
112 : }
113 :
114 14 : auto secureSession = sessionHandle->AsSecureSession();
115 14 : if (!secureSession->IsCASESession())
116 : {
117 2 : return;
118 : }
119 :
120 12 : uint32_t messageCounter = entry.retainedBuf.GetMessageCounter();
121 12 : auto fabricIndex = sessionHandle->GetFabricIndex();
122 12 : auto destination = secureSession->GetPeerNodeId();
123 12 : ReliableMessageAnalyticsDelegate::TransmitEvent event = { .nodeId = destination,
124 : .fabricIndex = fabricIndex,
125 : .sessionType =
126 : ReliableMessageAnalyticsDelegate::SessionType::kEstablishedCase,
127 12 : .eventType = eventType,
128 12 : .messageCounter = messageCounter };
129 :
130 12 : if (eventType == ReliableMessageAnalyticsDelegate::EventType::kRetransmission)
131 : {
132 8 : event.retransmissionCount = entry.sendCount;
133 : }
134 12 : if (eventType == ReliableMessageAnalyticsDelegate::EventType::kAcknowledged)
135 : {
136 1 : auto now = System::SystemClock().GetMonotonicTimestamp();
137 1 : event.ackLatencyMs = now - entry.initialSentTime;
138 : }
139 :
140 12 : mAnalyticsDelegate->OnTransmitEvent(event);
141 : }
142 : #endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
143 :
144 100 : void ReliableMessageMgr::ExecuteActions()
145 : {
146 100 : System::Clock::Timestamp now = System::SystemClock().GetMonotonicTimestamp();
147 :
148 : #if defined(RMP_TICKLESS_DEBUG)
149 : ChipLogDetail(ExchangeManager, "ReliableMessageMgr::ExecuteActions at 0x" ChipLogFormatX64 "ms", ChipLogValueX64(now.count()));
150 : #endif
151 :
152 100 : ExecuteForAllContext([&](ReliableMessageContext * rc) {
153 155 : if (rc->IsAckPending())
154 : {
155 0 : if (rc->mNextAckTime <= now)
156 : {
157 : #if defined(RMP_TICKLESS_DEBUG)
158 : ChipLogDetail(ExchangeManager, "ReliableMessageMgr::ExecuteActions sending ACK %p", rc);
159 : #endif
160 0 : rc->SendStandaloneAckMessage();
161 : }
162 : }
163 155 : });
164 :
165 : // Retransmit / cancel anything in the retrans table whose retrans timeout has expired
166 100 : mRetransTable.ForEachActiveObject([&](auto * entry) {
167 132 : if (entry->nextRetransTime > now)
168 31 : return Loop::Continue;
169 :
170 101 : VerifyOrDie(!entry->retainedBuf.IsNull());
171 :
172 : // Don't check whether the session in the exchange is valid, because when the session is released, the retrans entry is
173 : // cleared inside ExchangeContext::OnSessionReleased, so the session must be valid if the entry exists.
174 101 : auto session = entry->ec->GetSessionHandle();
175 101 : uint8_t sendCount = entry->sendCount;
176 : #if CHIP_ERROR_LOGGING || CHIP_PROGRESS_LOGGING
177 101 : uint32_t messageCounter = entry->retainedBuf.GetMessageCounter();
178 101 : auto fabricIndex = session->GetFabricIndex();
179 101 : auto destination = kUndefinedNodeId;
180 101 : if (session->IsSecureSession())
181 : {
182 98 : destination = session->AsSecureSession()->GetPeerNodeId();
183 : }
184 : #endif // CHIP_ERROR_LOGGING || CHIP_DETAIL_LOGGING
185 :
186 101 : if (sendCount == CHIP_CONFIG_RMP_DEFAULT_MAX_RETRANS)
187 : {
188 : // Make sure our exchange stays alive until we are done working with it.
189 15 : ExchangeHandle ec(entry->ec);
190 :
191 15 : ChipLogError(ExchangeManager,
192 : "<<%d [E:" ChipLogFormatExchange " S:%u M:" ChipLogFormatMessageCounter
193 : "] (%s) Msg Retransmission to %u:" ChipLogFormatX64 " failure (max retries:%d)",
194 : sendCount + 1, ChipLogValueExchange(&entry->ec.Get()), session->SessionIdForLogging(), messageCounter,
195 : Transport::GetSessionTypeString(session), fabricIndex, ChipLogValueX64(destination),
196 : CHIP_CONFIG_RMP_DEFAULT_MAX_RETRANS);
197 :
198 : #if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
199 15 : NotifyMessageSendAnalytics(*entry, session, ReliableMessageAnalyticsDelegate::EventType::kFailed);
200 : #endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
201 :
202 : // If the exchange is expecting a response, it will handle sending
203 : // this notification once it detects that it has not gotten a
204 : // response. Otherwise, we need to do it.
205 15 : if (!ec->IsResponseExpected())
206 : {
207 4 : if (session->IsSecureSession() && session->AsSecureSession()->IsCASESession())
208 : {
209 3 : session->AsSecureSession()->MarkAsDefunct();
210 : }
211 4 : session->NotifySessionHang();
212 : }
213 :
214 : // Do not StartTimer, we will schedule the timer at the end of the timer handler.
215 15 : mRetransTable.ReleaseObject(entry);
216 :
217 15 : return Loop::Continue;
218 15 : }
219 :
220 86 : entry->sendCount++;
221 :
222 86 : ChipLogProgress(ExchangeManager,
223 : "<<%d [E:" ChipLogFormatExchange " S:%u M:" ChipLogFormatMessageCounter
224 : "] (%s) Msg Retransmission to %u:" ChipLogFormatX64,
225 : entry->sendCount, ChipLogValueExchange(&entry->ec.Get()), session->SessionIdForLogging(), messageCounter,
226 : Transport::GetSessionTypeString(session), fabricIndex, ChipLogValueX64(destination));
227 : MATTER_LOG_METRIC(Tracing::kMetricDeviceRMPRetryCount, entry->sendCount);
228 :
229 86 : SendFromRetransTable(entry);
230 :
231 86 : return Loop::Continue;
232 101 : });
233 :
234 100 : TicklessDebugDumpRetransTable("ReliableMessageMgr::ExecuteActions Dumping mRetransTable entries after processing");
235 100 : }
236 :
237 100 : void ReliableMessageMgr::Timeout(System::Layer * aSystemLayer, void * aAppState)
238 : {
239 100 : ReliableMessageMgr * manager = reinterpret_cast<ReliableMessageMgr *>(aAppState);
240 :
241 100 : VerifyOrDie((aSystemLayer != nullptr) && (manager != nullptr));
242 :
243 : #if defined(RMP_TICKLESS_DEBUG)
244 : ChipLogDetail(ExchangeManager, "ReliableMessageMgr::Timeout");
245 : #endif
246 :
247 : // Execute any actions that are due this tick
248 100 : manager->ExecuteActions();
249 :
250 : // Calculate next physical wakeup
251 100 : manager->StartTimer();
252 100 : }
253 :
254 12659 : CHIP_ERROR ReliableMessageMgr::AddToRetransTable(ReliableMessageContext * rc, RetransTableEntry ** rEntry)
255 : {
256 12659 : VerifyOrReturnError(!rc->IsWaitingForAck(), CHIP_ERROR_INCORRECT_STATE);
257 :
258 12659 : *rEntry = mRetransTable.CreateObject(rc);
259 12659 : if (*rEntry == nullptr)
260 : {
261 0 : ChipLogError(ExchangeManager, "mRetransTable Already Full");
262 0 : return CHIP_ERROR_RETRANS_TABLE_FULL;
263 : }
264 :
265 12659 : return CHIP_NO_ERROR;
266 : }
267 :
268 110920 : System::Clock::Timeout ReliableMessageMgr::GetBackoff(System::Clock::Timeout baseInterval, uint8_t sendCount,
269 : bool computeMaxPossible)
270 : {
271 : // See section "4.11.8. Parameters and Constants" for the parameters below:
272 : // MRP_BACKOFF_JITTER = 0.25
273 110920 : constexpr uint32_t MRP_BACKOFF_JITTER_BASE = 1024;
274 : // MRP_BACKOFF_MARGIN = 1.1
275 110920 : constexpr uint32_t MRP_BACKOFF_MARGIN_NUMERATOR = 1127;
276 110920 : constexpr uint32_t MRP_BACKOFF_MARGIN_DENOMINATOR = 1024;
277 : // MRP_BACKOFF_BASE = 1.6
278 110920 : constexpr uint32_t MRP_BACKOFF_BASE_NUMERATOR = 16;
279 110920 : constexpr uint32_t MRP_BACKOFF_BASE_DENOMINATOR = 10;
280 110920 : constexpr int MRP_BACKOFF_THRESHOLD = 1;
281 :
282 : // Implement `i = MRP_BACKOFF_MARGIN * i` from section "4.12.2.1. Retransmissions", where:
283 : // i == interval
284 110920 : System::Clock::Milliseconds64 interval = baseInterval;
285 110920 : interval *= MRP_BACKOFF_MARGIN_NUMERATOR;
286 110920 : interval /= MRP_BACKOFF_MARGIN_DENOMINATOR;
287 :
288 : // Implement:
289 : // mrpBackoffTime = i * MRP_BACKOFF_BASE^(max(0,n-MRP_BACKOFF_THRESHOLD)) * (1.0 + random(0,1) * MRP_BACKOFF_JITTER)
290 : // from section "4.12.2.1. Retransmissions", where:
291 : // i == interval
292 : // n == sendCount
293 :
294 : // 1. Calculate exponent `max(0,n−MRP_BACKOFF_THRESHOLD)`
295 110920 : int exponent = sendCount - MRP_BACKOFF_THRESHOLD;
296 110920 : if (exponent < 0)
297 32284 : exponent = 0; // Enforce floor
298 110920 : if (exponent > 4)
299 12 : exponent = 4; // Enforce reasonable maximum after 5 tries
300 :
301 : // 2. Calculate `mrpBackoffTime = i * MRP_BACKOFF_BASE^(max(0,n-MRP_BACKOFF_THRESHOLD))`
302 110920 : uint32_t backoffNum = 1;
303 110920 : uint32_t backoffDenom = 1;
304 :
305 228924 : for (int i = 0; i < exponent; i++)
306 : {
307 118004 : backoffNum *= MRP_BACKOFF_BASE_NUMERATOR;
308 118004 : backoffDenom *= MRP_BACKOFF_BASE_DENOMINATOR;
309 : }
310 :
311 110920 : System::Clock::Milliseconds64 mrpBackoffTime = interval * backoffNum / backoffDenom;
312 :
313 : // 3. Calculate `mrpBackoffTime *= (1.0 + random(0,1) * MRP_BACKOFF_JITTER)`
314 110920 : uint32_t jitter = MRP_BACKOFF_JITTER_BASE + (computeMaxPossible ? UINT8_MAX : Crypto::GetRandU8());
315 110920 : mrpBackoffTime = mrpBackoffTime * jitter / MRP_BACKOFF_JITTER_BASE;
316 :
317 : #if CHIP_CONFIG_ENABLE_ICD_SERVER
318 : // Implement:
319 : // "An ICD sender SHOULD increase t to also account for its own sleepy interval
320 : // required to receive the acknowledgment"
321 : mrpBackoffTime += ICDConfigurationData::GetInstance().GetFastPollingInterval();
322 : #endif
323 :
324 110920 : mrpBackoffTime += sAdditionalMRPBackoffTime;
325 :
326 110920 : return std::chrono::duration_cast<System::Clock::Timeout>(mrpBackoffTime);
327 : }
328 :
329 12654 : void ReliableMessageMgr::StartRetransmision(RetransTableEntry * entry)
330 : {
331 12654 : CalculateNextRetransTime(*entry);
332 : #if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
333 12654 : entry->initialSentTime = System::SystemClock().GetMonotonicTimestamp();
334 12654 : NotifyMessageSendAnalytics(*entry, entry->ec->GetSessionHandle(), ReliableMessageAnalyticsDelegate::EventType::kInitialSend);
335 : #endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
336 12654 : StartTimer();
337 12654 : }
338 :
339 12522 : bool ReliableMessageMgr::CheckAndRemRetransTable(ReliableMessageContext * rc, uint32_t ackMessageCounter)
340 : {
341 12522 : bool removed = false;
342 12522 : mRetransTable.ForEachActiveObject([&](auto * entry) {
343 30618 : if (entry->ec->GetReliableMessageContext() == rc && entry->retainedBuf.GetMessageCounter() == ackMessageCounter)
344 : {
345 : #if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
346 12504 : auto session = entry->ec->GetSessionHandle();
347 12504 : NotifyMessageSendAnalytics(*entry, session, ReliableMessageAnalyticsDelegate::EventType::kAcknowledged);
348 : #endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
349 :
350 : // Clear the entry from the retransmision table.
351 12504 : ClearRetransTable(*entry);
352 :
353 12504 : ChipLogDetail(ExchangeManager,
354 : "Rxd Ack; Removing MessageCounter:" ChipLogFormatMessageCounter
355 : " from Retrans Table on exchange " ChipLogFormatExchange,
356 : ackMessageCounter, ChipLogValueExchange(rc->GetExchangeContext()));
357 12504 : removed = true;
358 12504 : return Loop::Break;
359 12504 : }
360 18114 : return Loop::Continue;
361 : });
362 :
363 12522 : return removed;
364 : }
365 :
366 86 : CHIP_ERROR ReliableMessageMgr::SendFromRetransTable(RetransTableEntry * entry)
367 : {
368 86 : if (!entry->ec->HasSessionHandle())
369 : {
370 : // Using same error message for all errors to reduce code size.
371 0 : ChipLogError(ExchangeManager,
372 : "Crit-err %" CHIP_ERROR_FORMAT " when sending CHIP MessageCounter:" ChipLogFormatMessageCounter
373 : " on exchange " ChipLogFormatExchange ", send tries: %d",
374 : CHIP_ERROR_INCORRECT_STATE.Format(), entry->retainedBuf.GetMessageCounter(),
375 : ChipLogValueExchange(&entry->ec.Get()), entry->sendCount);
376 0 : ClearRetransTable(*entry);
377 0 : return CHIP_ERROR_INCORRECT_STATE;
378 : }
379 :
380 86 : auto * sessionManager = entry->ec->GetExchangeMgr()->GetSessionManager();
381 86 : CHIP_ERROR err = sessionManager->SendPreparedMessage(entry->ec->GetSessionHandle(), entry->retainedBuf);
382 86 : err = MapSendError(err, entry->ec->GetExchangeId(), entry->ec->IsInitiator());
383 :
384 86 : if (err == CHIP_NO_ERROR)
385 : {
386 86 : CalculateNextRetransTime(*entry);
387 :
388 : #if CHIP_CONFIG_ENABLE_ICD_SERVER
389 : app::ICDNotifier::GetInstance().NotifyNetworkActivityNotification();
390 : #endif // CHIP_CONFIG_ENABLE_ICD_SERVER
391 : #if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
392 86 : NotifyMessageSendAnalytics(*entry, entry->ec->GetSessionHandle(),
393 172 : ReliableMessageAnalyticsDelegate::EventType::kRetransmission);
394 : #endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
395 : #if CHIP_CONFIG_RESOLVE_PEER_ON_FIRST_TRANSMIT_FAILURE
396 : const ExchangeManager * exchangeMgr = entry->ec->GetExchangeMgr();
397 : // TODO: investigate why in ReliableMessageMgr::CheckResendApplicationMessageWithPeerExchange unit test released exchange
398 : // context with mExchangeMgr==nullptr is used.
399 : if (exchangeMgr)
400 : {
401 : // After the first failure notify session manager to refresh device data
402 : if (entry->sendCount == 1 && mSessionUpdateDelegate != nullptr && entry->ec->GetSessionHandle()->IsSecureSession() &&
403 : entry->ec->GetSessionHandle()->AsSecureSession()->IsCASESession())
404 : {
405 : ChipLogDetail(ExchangeManager, "Notify session manager to update peer address");
406 : mSessionUpdateDelegate->UpdatePeerAddress(entry->ec->GetSessionHandle()->GetPeer());
407 : }
408 : }
409 : #endif // CHIP_CONFIG_RESOLVE_PEER_ON_FIRST_TRANSMIT_FAILURE
410 : }
411 : else
412 : {
413 : // Remove from table
414 : // Using same error message for all errors to reduce code size.
415 0 : ChipLogError(ExchangeManager,
416 : "Crit-err %" CHIP_ERROR_FORMAT " when sending CHIP MessageCounter:" ChipLogFormatMessageCounter
417 : " on exchange " ChipLogFormatExchange ", send tries: %d",
418 : err.Format(), entry->retainedBuf.GetMessageCounter(), ChipLogValueExchange(&entry->ec.Get()),
419 : entry->sendCount);
420 :
421 0 : ClearRetransTable(*entry);
422 : }
423 86 : return err;
424 : }
425 :
426 253 : void ReliableMessageMgr::ClearRetransTable(ReliableMessageContext * rc)
427 : {
428 253 : mRetransTable.ForEachActiveObject([&](auto * entry) {
429 225 : if (entry->ec->GetReliableMessageContext() == rc)
430 : {
431 127 : ClearRetransTable(*entry);
432 127 : return Loop::Break;
433 : }
434 98 : return Loop::Continue;
435 : });
436 253 : }
437 :
438 12636 : void ReliableMessageMgr::ClearRetransTable(RetransTableEntry & entry)
439 : {
440 12636 : mRetransTable.ReleaseObject(&entry);
441 : // Expire any virtual ticks that have expired so all wakeup sources reflect the current time
442 12636 : StartTimer();
443 12636 : }
444 :
445 37991 : void ReliableMessageMgr::StartTimer()
446 : {
447 : // When do we need to next wake up to send an ACK?
448 37991 : System::Clock::Timestamp nextWakeTime = System::Clock::Timestamp::max();
449 :
450 37991 : ExecuteForAllContext([&](ReliableMessageContext * rc) {
451 231407 : if (rc->IsAckPending() && rc->mNextAckTime < nextWakeTime)
452 : {
453 15007 : nextWakeTime = rc->mNextAckTime;
454 : }
455 231407 : });
456 :
457 : // When do we need to next wake up for ReliableMessageProtocol retransmit?
458 37991 : mRetransTable.ForEachActiveObject([&](auto * entry) {
459 138247 : if (entry->nextRetransTime < nextWakeTime)
460 : {
461 27190 : nextWakeTime = entry->nextRetransTime;
462 : }
463 138247 : return Loop::Continue;
464 : });
465 :
466 37991 : StopTimer();
467 :
468 37991 : if (nextWakeTime != System::Clock::Timestamp::max())
469 : {
470 36040 : const System::Clock::Timestamp now = System::SystemClock().GetMonotonicTimestamp();
471 36040 : const auto nextWakeDelay = (nextWakeTime > now) ? nextWakeTime - now : 0_ms;
472 :
473 : #if defined(RMP_TICKLESS_DEBUG)
474 : ChipLogDetail(ExchangeManager,
475 : "ReliableMessageMgr::StartTimer at 0x" ChipLogFormatX64 "ms wake at 0x" ChipLogFormatX64
476 : "ms (in 0x" ChipLogFormatX64 "ms)",
477 : ChipLogValueX64(now.count()), ChipLogValueX64(nextWakeTime.count()), ChipLogValueX64(nextWakeDelay.count()));
478 : #endif
479 36040 : VerifyOrDie(mSystemLayer->StartTimer(nextWakeDelay, Timeout, this) == CHIP_NO_ERROR);
480 : }
481 : else
482 : {
483 : #if defined(RMP_TICKLESS_DEBUG)
484 : ChipLogDetail(ExchangeManager, "ReliableMessageMgr skipped timer");
485 : #endif
486 : }
487 :
488 37991 : TicklessDebugDumpRetransTable("ReliableMessageMgr::StartTimer Dumping mRetransTable entries after setting wakeup times");
489 37991 : }
490 :
491 38373 : void ReliableMessageMgr::StopTimer()
492 : {
493 38373 : mSystemLayer->CancelTimer(Timeout, this);
494 38373 : }
495 :
496 2 : void ReliableMessageMgr::RegisterSessionUpdateDelegate(SessionUpdateDelegate * sessionUpdateDelegate)
497 : {
498 2 : mSessionUpdateDelegate = sessionUpdateDelegate;
499 2 : }
500 :
501 : #if CHIP_CONFIG_MRP_ANALYTICS_ENABLED
502 4 : void ReliableMessageMgr::RegisterAnalyticsDelegate(ReliableMessageAnalyticsDelegate * analyticsDelegate)
503 : {
504 4 : mAnalyticsDelegate = analyticsDelegate;
505 4 : }
506 : #endif // CHIP_CONFIG_MRP_ANALYTICS_ENABLED
507 :
508 12744 : CHIP_ERROR ReliableMessageMgr::MapSendError(CHIP_ERROR error, uint16_t exchangeId, bool isInitiator)
509 : {
510 12744 : if (
511 : #if CHIP_SYSTEM_CONFIG_USE_LWIP
512 : error == System::MapErrorLwIP(ERR_MEM)
513 : #else
514 12744 : error == CHIP_ERROR_POSIX(ENOBUFS)
515 : #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
516 : )
517 : {
518 : // sendmsg on BSD-based systems never blocks, no matter how the
519 : // socket is configured, and will return ENOBUFS in situation in
520 : // which Linux, for example, blocks.
521 : //
522 : // This is typically a transient situation, so we pretend like this
523 : // packet drop happened somewhere on the network instead of inside
524 : // sendmsg and will just resend it in the normal MRP way later.
525 : //
526 : // Similarly, on LwIP an ERR_MEM on send indicates a likely
527 : // temporary lack of TX buffers.
528 0 : ChipLogError(ExchangeManager, "Ignoring transient send error: %" CHIP_ERROR_FORMAT " on exchange " ChipLogFormatExchangeId,
529 : error.Format(), ChipLogValueExchangeId(exchangeId, isInitiator));
530 0 : error = CHIP_NO_ERROR;
531 : }
532 :
533 12744 : return error;
534 : }
535 :
536 754 : void ReliableMessageMgr::SetAdditionalMRPBackoffTime(const Optional<System::Clock::Timeout> & additionalTime)
537 : {
538 754 : sAdditionalMRPBackoffTime = additionalTime.ValueOr(CHIP_CONFIG_MRP_RETRY_INTERVAL_SENDER_BOOST);
539 754 : }
540 :
541 12740 : void ReliableMessageMgr::CalculateNextRetransTime(RetransTableEntry & entry)
542 : {
543 12740 : System::Clock::Timeout baseTimeout = System::Clock::Timeout(0);
544 12740 : const auto sessionHandle = entry.ec->GetSessionHandle();
545 :
546 : // Check if we have received at least one application-level message
547 12740 : if (entry.ec->HasReceivedAtLeastOneMessage())
548 : {
549 : // If we have received at least one message, assume peer is active and use ActiveRetransTimeout
550 10398 : baseTimeout = sessionHandle->GetRemoteMRPConfig().mActiveRetransTimeout;
551 : }
552 : else
553 : {
554 : // If we haven't received at least one message
555 : // Choose active/idle timeout from PeerActiveMode of session per 4.11.2.1. Retransmissions.
556 2342 : baseTimeout = sessionHandle->GetMRPBaseTimeout();
557 : }
558 :
559 12740 : System::Clock::Timeout backoff = ReliableMessageMgr::GetBackoff(baseTimeout, entry.sendCount);
560 12740 : entry.nextRetransTime = System::SystemClock().GetMonotonicTimestamp() + backoff;
561 :
562 : #if CHIP_PROGRESS_LOGGING
563 12740 : const auto config = sessionHandle->GetRemoteMRPConfig();
564 12740 : uint32_t messageCounter = entry.retainedBuf.GetMessageCounter();
565 12740 : auto fabricIndex = sessionHandle->GetFabricIndex();
566 12740 : auto destination = kUndefinedNodeId;
567 12740 : bool peerIsActive = false;
568 :
569 12740 : if (sessionHandle->IsSecureSession())
570 : {
571 12654 : peerIsActive = sessionHandle->AsSecureSession()->IsPeerActive();
572 12654 : destination = sessionHandle->AsSecureSession()->GetPeerNodeId();
573 : }
574 86 : else if (sessionHandle->IsUnauthenticatedSession())
575 : {
576 86 : peerIsActive = sessionHandle->AsUnauthenticatedSession()->IsPeerActive();
577 : }
578 :
579 12740 : ChipLogProgress(ExchangeManager,
580 : "??%d [E:" ChipLogFormatExchange " S:%u M:" ChipLogFormatMessageCounter
581 : "] (%s) Msg Retransmission to %u:" ChipLogFormatX64 " scheduled for %" PRIu32
582 : "ms from now [State:%s II:%" PRIu32 " AI:%" PRIu32 " AT:%u]",
583 : entry.sendCount + 1, ChipLogValueExchange(&entry.ec.Get()), sessionHandle->SessionIdForLogging(),
584 : messageCounter, Transport::GetSessionTypeString(sessionHandle), fabricIndex, ChipLogValueX64(destination),
585 : backoff.count(), peerIsActive ? "Active" : "Idle", config.mIdleRetransTimeout.count(),
586 : config.mActiveRetransTimeout.count(), config.mActiveThresholdTime.count());
587 : #endif // CHIP_PROGRESS_LOGGING
588 12740 : }
589 :
590 : #if CHIP_CONFIG_TEST
591 212247 : int ReliableMessageMgr::TestGetCountRetransTable()
592 : {
593 212247 : int count = 0;
594 212247 : mRetransTable.ForEachActiveObject([&](auto * entry) {
595 234976 : count++;
596 234976 : return Loop::Continue;
597 : });
598 212247 : return count;
599 : }
600 : #endif // CHIP_CONFIG_TEST
601 :
602 : } // namespace Messaging
603 : } // namespace chip
|