1 /*
2 * Copyright (c) 2021, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements the History Tracker module.
32 */
33
34 #include "history_tracker.hpp"
35
36 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
37
38 #include "common/as_core_type.hpp"
39 #include "common/code_utils.hpp"
40 #include "common/debug.hpp"
41 #include "common/locator_getters.hpp"
42 #include "common/num_utils.hpp"
43 #include "common/string.hpp"
44 #include "common/timer.hpp"
45 #include "instance/instance.hpp"
46 #include "net/ip6_headers.hpp"
47
48 namespace ot {
49 namespace Utils {
50
51 //---------------------------------------------------------------------------------------------------------------------
52 // HistoryTracker
53
HistoryTracker(Instance & aInstance)54 HistoryTracker::HistoryTracker(Instance &aInstance)
55 : InstanceLocator(aInstance)
56 , mTimer(aInstance)
57 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA
58 , mPreviousNetworkData(aInstance, mNetworkDataTlvBuffer, 0, sizeof(mNetworkDataTlvBuffer))
59 #endif
60 {
61 mTimer.Start(kAgeCheckPeriod);
62
63 #if OPENTHREAD_FTD && (OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0)
64 ClearAllBytes(mRouterEntries);
65 #endif
66 }
67
RecordNetworkInfo(void)68 void HistoryTracker::RecordNetworkInfo(void)
69 {
70 NetworkInfo *entry = mNetInfoHistory.AddNewEntry();
71 Mle::DeviceMode mode;
72
73 VerifyOrExit(entry != nullptr);
74
75 entry->mRole = MapEnum(Get<Mle::Mle>().GetRole());
76 entry->mRloc16 = Get<Mle::Mle>().GetRloc16();
77 entry->mPartitionId = Get<Mle::Mle>().GetLeaderData().GetPartitionId();
78 mode = Get<Mle::Mle>().GetDeviceMode();
79 mode.Get(entry->mMode);
80
81 exit:
82 return;
83 }
84
RecordMessage(const Message & aMessage,const Mac::Address & aMacAddress,MessageType aType)85 void HistoryTracker::RecordMessage(const Message &aMessage, const Mac::Address &aMacAddress, MessageType aType)
86 {
87 MessageInfo *entry = nullptr;
88 Ip6::Headers headers;
89
90 VerifyOrExit(aMessage.GetType() == Message::kTypeIp6);
91
92 SuccessOrExit(headers.ParseFrom(aMessage));
93
94 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_EXCLUDE_THREAD_CONTROL_MESSAGES
95 if (headers.IsUdp())
96 {
97 uint16_t port = 0;
98
99 switch (aType)
100 {
101 case kRxMessage:
102 port = headers.GetDestinationPort();
103 break;
104
105 case kTxMessage:
106 port = headers.GetSourcePort();
107 break;
108 }
109
110 VerifyOrExit((port != Mle::kUdpPort) && (port != Tmf::kUdpPort));
111 }
112 #endif
113
114 switch (aType)
115 {
116 case kRxMessage:
117 entry = mRxHistory.AddNewEntry();
118 break;
119
120 case kTxMessage:
121 entry = mTxHistory.AddNewEntry();
122 break;
123 }
124
125 VerifyOrExit(entry != nullptr);
126
127 entry->mPayloadLength = headers.GetIp6Header().GetPayloadLength();
128 entry->mNeighborRloc16 = aMacAddress.IsShort() ? aMacAddress.GetShort() : kInvalidRloc16;
129 entry->mSource.mAddress = headers.GetSourceAddress();
130 entry->mSource.mPort = headers.GetSourcePort();
131 entry->mDestination.mAddress = headers.GetDestinationAddress();
132 entry->mDestination.mPort = headers.GetDestinationPort();
133 entry->mChecksum = headers.GetChecksum();
134 entry->mIpProto = headers.GetIpProto();
135 entry->mIcmp6Type = headers.IsIcmp6() ? headers.GetIcmpHeader().GetType() : 0;
136 entry->mAveRxRss = (aType == kRxMessage) ? aMessage.GetRssAverager().GetAverage() : Radio::kInvalidRssi;
137 entry->mLinkSecurity = aMessage.IsLinkSecurityEnabled();
138 entry->mTxSuccess = (aType == kTxMessage) ? aMessage.GetTxSuccess() : true;
139 entry->mPriority = aMessage.GetPriority();
140
141 if (aMacAddress.IsExtended())
142 {
143 Neighbor *neighbor = Get<NeighborTable>().FindNeighbor(aMacAddress, Neighbor::kInStateAnyExceptInvalid);
144
145 if (neighbor != nullptr)
146 {
147 entry->mNeighborRloc16 = neighbor->GetRloc16();
148 }
149 }
150
151 #if OPENTHREAD_CONFIG_MULTI_RADIO
152 if (aMessage.IsRadioTypeSet())
153 {
154 switch (aMessage.GetRadioType())
155 {
156 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
157 case Mac::kRadioTypeIeee802154:
158 entry->mRadioIeee802154 = true;
159 break;
160 #endif
161 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
162 case Mac::kRadioTypeTrel:
163 entry->mRadioTrelUdp6 = true;
164 break;
165 #endif
166 }
167
168 // Radio type may not be set on a tx message indicating that it
169 // was sent over all radio types (e.g., for broadcast frame).
170 // In such a case, we set all supported radios from `else`
171 // block below.
172 }
173 else
174 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
175 {
176 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
177 entry->mRadioIeee802154 = true;
178 #endif
179
180 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
181 entry->mRadioTrelUdp6 = true;
182 #endif
183 }
184
185 exit:
186 return;
187 }
188
RecordNeighborEvent(NeighborTable::Event aEvent,const NeighborTable::EntryInfo & aInfo)189 void HistoryTracker::RecordNeighborEvent(NeighborTable::Event aEvent, const NeighborTable::EntryInfo &aInfo)
190 {
191 NeighborInfo *entry = mNeighborHistory.AddNewEntry();
192
193 VerifyOrExit(entry != nullptr);
194
195 switch (aEvent)
196 {
197 case NeighborTable::kChildAdded:
198 case NeighborTable::kChildRemoved:
199 case NeighborTable::kChildModeChanged:
200 entry->mExtAddress = aInfo.mInfo.mChild.mExtAddress;
201 entry->mRloc16 = aInfo.mInfo.mChild.mRloc16;
202 entry->mAverageRssi = aInfo.mInfo.mChild.mAverageRssi;
203 entry->mRxOnWhenIdle = aInfo.mInfo.mChild.mRxOnWhenIdle;
204 entry->mFullThreadDevice = aInfo.mInfo.mChild.mFullThreadDevice;
205 entry->mFullNetworkData = aInfo.mInfo.mChild.mFullNetworkData;
206 entry->mIsChild = true;
207 break;
208
209 case NeighborTable::kRouterAdded:
210 case NeighborTable::kRouterRemoved:
211 entry->mExtAddress = aInfo.mInfo.mRouter.mExtAddress;
212 entry->mRloc16 = aInfo.mInfo.mRouter.mRloc16;
213 entry->mAverageRssi = aInfo.mInfo.mRouter.mAverageRssi;
214 entry->mRxOnWhenIdle = aInfo.mInfo.mRouter.mRxOnWhenIdle;
215 entry->mFullThreadDevice = aInfo.mInfo.mRouter.mFullThreadDevice;
216 entry->mFullNetworkData = aInfo.mInfo.mRouter.mFullNetworkData;
217 entry->mIsChild = false;
218 break;
219 }
220
221 switch (aEvent)
222 {
223 case NeighborTable::kChildAdded:
224 if (aInfo.mInfo.mChild.mIsStateRestoring)
225 {
226 entry->mEvent = kNeighborRestoring;
227 break;
228 }
229
230 OT_FALL_THROUGH;
231
232 case NeighborTable::kRouterAdded:
233 entry->mEvent = kNeighborAdded;
234 break;
235
236 case NeighborTable::kChildRemoved:
237 case NeighborTable::kRouterRemoved:
238 entry->mEvent = kNeighborRemoved;
239 break;
240
241 case NeighborTable::kChildModeChanged:
242 entry->mEvent = kNeighborChanged;
243 break;
244 }
245
246 exit:
247 return;
248 }
249
RecordAddressEvent(Ip6::Netif::AddressEvent aEvent,const Ip6::Netif::UnicastAddress & aUnicastAddress)250 void HistoryTracker::RecordAddressEvent(Ip6::Netif::AddressEvent aEvent,
251 const Ip6::Netif::UnicastAddress &aUnicastAddress)
252 {
253 UnicastAddressInfo *entry = mUnicastAddressHistory.AddNewEntry();
254
255 VerifyOrExit(entry != nullptr);
256
257 entry->mAddress = aUnicastAddress.GetAddress();
258 entry->mPrefixLength = aUnicastAddress.GetPrefixLength();
259 entry->mAddressOrigin = aUnicastAddress.GetOrigin();
260 entry->mEvent = (aEvent == Ip6::Netif::kAddressAdded) ? kAddressAdded : kAddressRemoved;
261 entry->mScope = (aUnicastAddress.GetScope() & 0xf);
262 entry->mPreferred = aUnicastAddress.mPreferred;
263 entry->mValid = aUnicastAddress.mValid;
264 entry->mRloc = aUnicastAddress.mRloc;
265
266 exit:
267 return;
268 }
269
RecordAddressEvent(Ip6::Netif::AddressEvent aEvent,const Ip6::Netif::MulticastAddress & aMulticastAddress,Ip6::Netif::AddressOrigin aAddressOrigin)270 void HistoryTracker::RecordAddressEvent(Ip6::Netif::AddressEvent aEvent,
271 const Ip6::Netif::MulticastAddress &aMulticastAddress,
272 Ip6::Netif::AddressOrigin aAddressOrigin)
273 {
274 MulticastAddressInfo *entry = mMulticastAddressHistory.AddNewEntry();
275
276 VerifyOrExit(entry != nullptr);
277
278 entry->mAddress = aMulticastAddress.GetAddress();
279 entry->mAddressOrigin = aAddressOrigin;
280 entry->mEvent = (aEvent == Ip6::Netif::kAddressAdded) ? kAddressAdded : kAddressRemoved;
281
282 exit:
283 return;
284 }
285
286 #if OPENTHREAD_FTD
RecordRouterTableChange(void)287 void HistoryTracker::RecordRouterTableChange(void)
288 {
289 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0
290
291 for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
292 {
293 RouterInfo entry;
294 RouterEntry &oldEntry = mRouterEntries[routerId];
295
296 entry.mRouterId = routerId;
297
298 if (Get<RouterTable>().IsAllocated(routerId))
299 {
300 uint16_t nextHopRloc;
301 uint8_t pathCost;
302
303 Get<RouterTable>().GetNextHopAndPathCost(Mle::Rloc16FromRouterId(routerId), nextHopRloc, pathCost);
304
305 entry.mNextHop = (nextHopRloc == Mle::kInvalidRloc16) ? kNoNextHop : Mle::RouterIdFromRloc16(nextHopRloc);
306 entry.mPathCost = (pathCost < Mle::kMaxRouteCost) ? pathCost : 0;
307
308 if (!oldEntry.mIsAllocated)
309 {
310 entry.mEvent = kRouterAdded;
311 entry.mOldPathCost = 0;
312 }
313 else if (oldEntry.mNextHop != entry.mNextHop)
314 {
315 entry.mEvent = kRouterNextHopChanged;
316 entry.mOldPathCost = oldEntry.mPathCost;
317 }
318 else if ((entry.mNextHop != kNoNextHop) && (oldEntry.mPathCost != entry.mPathCost))
319 {
320 entry.mEvent = kRouterCostChanged;
321 entry.mOldPathCost = oldEntry.mPathCost;
322 }
323 else
324 {
325 continue;
326 }
327
328 mRouterHistory.AddNewEntry(entry);
329
330 oldEntry.mIsAllocated = true;
331 oldEntry.mNextHop = entry.mNextHop;
332 oldEntry.mPathCost = entry.mPathCost;
333 }
334 else
335 {
336 // `routerId` is not allocated.
337
338 if (oldEntry.mIsAllocated)
339 {
340 entry.mEvent = kRouterRemoved;
341 entry.mNextHop = Mle::kInvalidRouterId;
342 entry.mOldPathCost = 0;
343 entry.mPathCost = 0;
344
345 mRouterHistory.AddNewEntry(entry);
346
347 oldEntry.mIsAllocated = false;
348 }
349 }
350 }
351
352 #endif // (OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0)
353 }
354 #endif // OPENTHREAD_FTD
355
356 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA
RecordNetworkDataChange(void)357 void HistoryTracker::RecordNetworkDataChange(void)
358 {
359 NetworkData::Iterator iterator;
360 NetworkData::OnMeshPrefixConfig prefix;
361 NetworkData::ExternalRouteConfig route;
362
363 // On mesh prefix entries
364
365 iterator = NetworkData::kIteratorInit;
366
367 while (mPreviousNetworkData.GetNextOnMeshPrefix(iterator, prefix) == kErrorNone)
368 {
369 if (!Get<NetworkData::Leader>().ContainsOnMeshPrefix(prefix))
370 {
371 RecordOnMeshPrefixEvent(kNetDataEntryRemoved, prefix);
372 }
373 }
374
375 iterator = NetworkData::kIteratorInit;
376
377 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefix) == kErrorNone)
378 {
379 if (!mPreviousNetworkData.ContainsOnMeshPrefix(prefix))
380 {
381 RecordOnMeshPrefixEvent(kNetDataEntryAdded, prefix);
382 }
383 }
384
385 // External route entries
386
387 iterator = NetworkData::kIteratorInit;
388
389 while (mPreviousNetworkData.GetNextExternalRoute(iterator, route) == kErrorNone)
390 {
391 if (!Get<NetworkData::Leader>().ContainsExternalRoute(route))
392 {
393 RecordExternalRouteEvent(kNetDataEntryRemoved, route);
394 }
395 }
396
397 iterator = NetworkData::kIteratorInit;
398
399 while (Get<NetworkData::Leader>().GetNextExternalRoute(iterator, route) == kErrorNone)
400 {
401 if (!mPreviousNetworkData.ContainsExternalRoute(route))
402 {
403 RecordExternalRouteEvent(kNetDataEntryAdded, route);
404 }
405 }
406
407 SuccessOrAssert(Get<NetworkData::Leader>().CopyNetworkData(NetworkData::kFullSet, mPreviousNetworkData));
408 }
409
RecordOnMeshPrefixEvent(NetDataEvent aEvent,const NetworkData::OnMeshPrefixConfig & aPrefix)410 void HistoryTracker::RecordOnMeshPrefixEvent(NetDataEvent aEvent, const NetworkData::OnMeshPrefixConfig &aPrefix)
411 {
412 OnMeshPrefixInfo *entry = mOnMeshPrefixHistory.AddNewEntry();
413
414 VerifyOrExit(entry != nullptr);
415 entry->mPrefix = aPrefix;
416 entry->mEvent = aEvent;
417
418 exit:
419 return;
420 }
421
RecordExternalRouteEvent(NetDataEvent aEvent,const NetworkData::ExternalRouteConfig & aRoute)422 void HistoryTracker::RecordExternalRouteEvent(NetDataEvent aEvent, const NetworkData::ExternalRouteConfig &aRoute)
423 {
424 ExternalRouteInfo *entry = mExternalRouteHistory.AddNewEntry();
425
426 VerifyOrExit(entry != nullptr);
427 entry->mRoute = aRoute;
428 entry->mEvent = aEvent;
429
430 exit:
431 return;
432 }
433
434 #endif // OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA
435
HandleNotifierEvents(Events aEvents)436 void HistoryTracker::HandleNotifierEvents(Events aEvents)
437 {
438 if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadRlocAdded | kEventThreadRlocRemoved |
439 kEventThreadPartitionIdChanged))
440 {
441 RecordNetworkInfo();
442 }
443
444 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA
445 if (aEvents.Contains(kEventThreadNetdataChanged))
446 {
447 RecordNetworkDataChange();
448 }
449 #endif
450 }
451
HandleTimer(void)452 void HistoryTracker::HandleTimer(void)
453 {
454 mNetInfoHistory.UpdateAgedEntries();
455 mUnicastAddressHistory.UpdateAgedEntries();
456 mMulticastAddressHistory.UpdateAgedEntries();
457 mRxHistory.UpdateAgedEntries();
458 mTxHistory.UpdateAgedEntries();
459 mNeighborHistory.UpdateAgedEntries();
460 mOnMeshPrefixHistory.UpdateAgedEntries();
461 mExternalRouteHistory.UpdateAgedEntries();
462
463 mTimer.Start(kAgeCheckPeriod);
464 }
465
EntryAgeToString(uint32_t aEntryAge,char * aBuffer,uint16_t aSize)466 void HistoryTracker::EntryAgeToString(uint32_t aEntryAge, char *aBuffer, uint16_t aSize)
467 {
468 StringWriter writer(aBuffer, aSize);
469
470 if (aEntryAge >= kMaxAge)
471 {
472 writer.Append("more than %u days", static_cast<uint16_t>(kMaxAge / Time::kOneDayInMsec));
473 }
474 else
475 {
476 uint32_t days = aEntryAge / Time::kOneDayInMsec;
477
478 if (days > 0)
479 {
480 writer.Append("%lu day%s ", ToUlong(days), (days == 1) ? "" : "s");
481 aEntryAge -= days * Time::kOneDayInMsec;
482 }
483
484 writer.Append("%02u:%02u:%02u.%03u", static_cast<uint16_t>(aEntryAge / Time::kOneHourInMsec),
485 static_cast<uint16_t>((aEntryAge % Time::kOneHourInMsec) / Time::kOneMinuteInMsec),
486 static_cast<uint16_t>((aEntryAge % Time::kOneMinuteInMsec) / Time::kOneSecondInMsec),
487 static_cast<uint16_t>(aEntryAge % Time::kOneSecondInMsec));
488 }
489 }
490
491 //---------------------------------------------------------------------------------------------------------------------
492 // HistoryTracker::Timestamp
493
SetToNow(void)494 void HistoryTracker::Timestamp::SetToNow(void)
495 {
496 mTime = TimerMilli::GetNow();
497
498 // If the current time happens to be the special value which we
499 // use to indicate "distant past", decrement the time by one.
500
501 if (mTime.GetValue() == kDistantPast)
502 {
503 mTime.SetValue(mTime.GetValue() - 1);
504 }
505 }
506
GetDurationTill(TimeMilli aTime) const507 uint32_t HistoryTracker::Timestamp::GetDurationTill(TimeMilli aTime) const
508 {
509 return IsDistantPast() ? kMaxAge : Min(aTime - mTime, kMaxAge);
510 }
511
512 //---------------------------------------------------------------------------------------------------------------------
513 // HistoryTracker::List
514
List(void)515 HistoryTracker::List::List(void)
516 : mStartIndex(0)
517 , mSize(0)
518 {
519 }
520
Clear(void)521 void HistoryTracker::List::Clear(void)
522 {
523 mStartIndex = 0;
524 mSize = 0;
525 }
526
Add(uint16_t aMaxSize,Timestamp aTimestamps[])527 uint16_t HistoryTracker::List::Add(uint16_t aMaxSize, Timestamp aTimestamps[])
528 {
529 // Add a new entry and return its list index. Overwrites the
530 // oldest entry if list is full.
531 //
532 // Entries are saved in the order they are added such that
533 // `mStartIndex` is the newest entry and the entries after up
534 // to `mSize` are the previously added entries.
535
536 mStartIndex = (mStartIndex == 0) ? aMaxSize - 1 : mStartIndex - 1;
537 mSize += (mSize == aMaxSize) ? 0 : 1;
538
539 aTimestamps[mStartIndex].SetToNow();
540
541 return mStartIndex;
542 }
543
Iterate(uint16_t aMaxSize,const Timestamp aTimestamps[],Iterator & aIterator,uint16_t & aListIndex,uint32_t & aEntryAge) const544 Error HistoryTracker::List::Iterate(uint16_t aMaxSize,
545 const Timestamp aTimestamps[],
546 Iterator &aIterator,
547 uint16_t &aListIndex,
548 uint32_t &aEntryAge) const
549 {
550 Error error = kErrorNone;
551
552 VerifyOrExit(aIterator.GetEntryNumber() < mSize, error = kErrorNotFound);
553
554 aListIndex = MapEntryNumberToListIndex(aIterator.GetEntryNumber(), aMaxSize);
555 aEntryAge = aTimestamps[aListIndex].GetDurationTill(aIterator.GetInitTime());
556
557 aIterator.IncrementEntryNumber();
558
559 exit:
560 return error;
561 }
562
MapEntryNumberToListIndex(uint16_t aEntryNumber,uint16_t aMaxSize) const563 uint16_t HistoryTracker::List::MapEntryNumberToListIndex(uint16_t aEntryNumber, uint16_t aMaxSize) const
564 {
565 // Map the `aEntryNumber` to the list index. `aEntryNumber` value
566 // of zero corresponds to the newest (the most recently added)
567 // entry and value one to next one and so on. List index
568 // warps at the end of array to start of array. Caller MUST
569 // ensure `aEntryNumber` is smaller than `mSize`.
570
571 uint32_t index;
572
573 OT_ASSERT(aEntryNumber < mSize);
574
575 index = static_cast<uint32_t>(aEntryNumber) + mStartIndex;
576 index -= (index >= aMaxSize) ? aMaxSize : 0;
577
578 return static_cast<uint16_t>(index);
579 }
580
UpdateAgedEntries(uint16_t aMaxSize,Timestamp aTimestamps[])581 void HistoryTracker::List::UpdateAgedEntries(uint16_t aMaxSize, Timestamp aTimestamps[])
582 {
583 TimeMilli now = TimerMilli::GetNow();
584
585 // We go through the entries in reverse (starting with the oldest
586 // entry) and check if the entry's age is larger than `kMaxAge`
587 // and if so mark it as "distant past". We can stop as soon as we
588 // get to an entry with age smaller than max.
589 //
590 // The `for()` loop condition is `(entryNumber < mSize)` which
591 // ensures that we go through the loop body for `entryNumber`
592 // value of zero and then in the next iteration (when the
593 // `entryNumber` rolls over) we stop.
594
595 for (uint16_t entryNumber = mSize - 1; entryNumber < mSize; entryNumber--)
596 {
597 uint16_t index = MapEntryNumberToListIndex(entryNumber, aMaxSize);
598
599 if (aTimestamps[index].GetDurationTill(now) < kMaxAge)
600 {
601 break;
602 }
603
604 aTimestamps[index].MarkAsDistantPast();
605 }
606 }
607
608 } // namespace Utils
609 } // namespace ot
610
611 #endif // #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
612