• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/instance.hpp"
42 #include "common/locator_getters.hpp"
43 #include "common/string.hpp"
44 #include "common/timer.hpp"
45 #include "net/ip6_headers.hpp"
46 
47 namespace ot {
48 namespace Utils {
49 
50 //---------------------------------------------------------------------------------------------------------------------
51 // HistoryTracker
52 
HistoryTracker(Instance & aInstance)53 HistoryTracker::HistoryTracker(Instance &aInstance)
54     : InstanceLocator(aInstance)
55     , mTimer(aInstance, HandleTimer)
56 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA
57     , mPreviousNetworkData(aInstance, mNetworkDataTlvBuffer, 0, sizeof(mNetworkDataTlvBuffer))
58 #endif
59 {
60     mTimer.Start(kAgeCheckPeriod);
61 }
62 
RecordNetworkInfo(void)63 void HistoryTracker::RecordNetworkInfo(void)
64 {
65     NetworkInfo *   entry = mNetInfoHistory.AddNewEntry();
66     Mle::DeviceMode mode;
67 
68     VerifyOrExit(entry != nullptr);
69 
70     entry->mRole        = MapEnum(Get<Mle::Mle>().GetRole());
71     entry->mRloc16      = Get<Mle::Mle>().GetRloc16();
72     entry->mPartitionId = Get<Mle::Mle>().GetLeaderData().GetPartitionId();
73     mode                = Get<Mle::Mle>().GetDeviceMode();
74     mode.Get(entry->mMode);
75 
76 exit:
77     return;
78 }
79 
RecordMessage(const Message & aMessage,const Mac::Address & aMacAddresss,MessageType aType)80 void HistoryTracker::RecordMessage(const Message &aMessage, const Mac::Address &aMacAddresss, MessageType aType)
81 {
82     MessageInfo *entry = nullptr;
83     Ip6::Headers headers;
84 
85     VerifyOrExit(aMessage.GetType() == Message::kTypeIp6);
86 
87     SuccessOrExit(headers.ParseFrom(aMessage));
88 
89 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_EXCLUDE_THREAD_CONTROL_MESSAGES
90     if (headers.IsUdp())
91     {
92         uint16_t port = 0;
93 
94         switch (aType)
95         {
96         case kRxMessage:
97             port = headers.GetDestinationPort();
98             break;
99 
100         case kTxMessage:
101             port = headers.GetSourcePort();
102             break;
103         }
104 
105         VerifyOrExit((port != Mle::kUdpPort) && (port != Tmf::kUdpPort));
106     }
107 #endif
108 
109     switch (aType)
110     {
111     case kRxMessage:
112         entry = mRxHistory.AddNewEntry();
113         break;
114 
115     case kTxMessage:
116         entry = mTxHistory.AddNewEntry();
117         break;
118     }
119 
120     VerifyOrExit(entry != nullptr);
121 
122     entry->mPayloadLength        = headers.GetIp6Header().GetPayloadLength();
123     entry->mNeighborRloc16       = aMacAddresss.IsShort() ? aMacAddresss.GetShort() : kInvalidRloc16;
124     entry->mSource.mAddress      = headers.GetSourceAddress();
125     entry->mSource.mPort         = headers.GetSourcePort();
126     entry->mDestination.mAddress = headers.GetDestinationAddress();
127     entry->mDestination.mPort    = headers.GetDestinationPort();
128     entry->mChecksum             = headers.GetChecksum();
129     entry->mIpProto              = headers.GetIpProto();
130     entry->mIcmp6Type            = headers.IsIcmp6() ? headers.GetIcmpHeader().GetType() : 0;
131     entry->mAveRxRss             = (aType == kRxMessage) ? aMessage.GetRssAverager().GetAverage() : kInvalidRss;
132     entry->mLinkSecurity         = aMessage.IsLinkSecurityEnabled();
133     entry->mTxSuccess            = (aType == kTxMessage) ? aMessage.GetTxSuccess() : true;
134     entry->mPriority             = aMessage.GetPriority();
135 
136     if (aMacAddresss.IsExtended())
137     {
138         Neighbor *neighbor = Get<NeighborTable>().FindNeighbor(aMacAddresss, Neighbor::kInStateAnyExceptInvalid);
139 
140         if (neighbor != nullptr)
141         {
142             entry->mNeighborRloc16 = neighbor->GetRloc16();
143         }
144     }
145 
146 #if OPENTHREAD_CONFIG_MULTI_RADIO
147     if (aMessage.IsRadioTypeSet())
148     {
149         switch (aMessage.GetRadioType())
150         {
151 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
152         case Mac::kRadioTypeIeee802154:
153             entry->mRadioIeee802154 = true;
154             break;
155 #endif
156 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
157         case Mac::kRadioTypeTrel:
158             entry->mRadioTrelUdp6 = true;
159             break;
160 #endif
161         }
162 
163         // Radio type may not be set on a tx message indicating that it
164         // was sent over all radio types (e.g., for broadcast frame).
165         // In such a case, we set all supported radios from `else`
166         // block below.
167     }
168     else
169 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
170     {
171 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
172         entry->mRadioIeee802154 = true;
173 #endif
174 
175 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
176         entry->mRadioTrelUdp6 = true;
177 #endif
178     }
179 
180 exit:
181     return;
182 }
183 
RecordNeighborEvent(NeighborTable::Event aEvent,const NeighborTable::EntryInfo & aInfo)184 void HistoryTracker::RecordNeighborEvent(NeighborTable::Event aEvent, const NeighborTable::EntryInfo &aInfo)
185 {
186     NeighborInfo *entry = mNeighborHistory.AddNewEntry();
187 
188     VerifyOrExit(entry != nullptr);
189 
190     switch (aEvent)
191     {
192     case NeighborTable::kChildAdded:
193     case NeighborTable::kChildRemoved:
194     case NeighborTable::kChildModeChanged:
195         entry->mExtAddress       = aInfo.mInfo.mChild.mExtAddress;
196         entry->mRloc16           = aInfo.mInfo.mChild.mRloc16;
197         entry->mAverageRssi      = aInfo.mInfo.mChild.mAverageRssi;
198         entry->mRxOnWhenIdle     = aInfo.mInfo.mChild.mRxOnWhenIdle;
199         entry->mFullThreadDevice = aInfo.mInfo.mChild.mFullThreadDevice;
200         entry->mFullNetworkData  = aInfo.mInfo.mChild.mFullNetworkData;
201         entry->mIsChild          = true;
202         break;
203 
204     case NeighborTable::kRouterAdded:
205     case NeighborTable::kRouterRemoved:
206         entry->mExtAddress       = aInfo.mInfo.mRouter.mExtAddress;
207         entry->mRloc16           = aInfo.mInfo.mRouter.mRloc16;
208         entry->mAverageRssi      = aInfo.mInfo.mRouter.mAverageRssi;
209         entry->mRxOnWhenIdle     = aInfo.mInfo.mRouter.mRxOnWhenIdle;
210         entry->mFullThreadDevice = aInfo.mInfo.mRouter.mFullThreadDevice;
211         entry->mFullNetworkData  = aInfo.mInfo.mRouter.mFullNetworkData;
212         entry->mIsChild          = false;
213         break;
214     }
215 
216     switch (aEvent)
217     {
218     case NeighborTable::kChildAdded:
219         if (aInfo.mInfo.mChild.mIsStateRestoring)
220         {
221             entry->mEvent = kNeighborRestoring;
222             break;
223         }
224 
225         OT_FALL_THROUGH;
226 
227     case NeighborTable::kRouterAdded:
228         entry->mEvent = kNeighborAdded;
229         break;
230 
231     case NeighborTable::kChildRemoved:
232     case NeighborTable::kRouterRemoved:
233         entry->mEvent = kNeighborRemoved;
234         break;
235 
236     case NeighborTable::kChildModeChanged:
237         entry->mEvent = kNeighborChanged;
238         break;
239     }
240 
241 exit:
242     return;
243 }
244 
RecordAddressEvent(Ip6::Netif::AddressEvent aEvent,const Ip6::Netif::UnicastAddress & aUnicastAddress)245 void HistoryTracker::RecordAddressEvent(Ip6::Netif::AddressEvent          aEvent,
246                                         const Ip6::Netif::UnicastAddress &aUnicastAddress)
247 {
248     UnicastAddressInfo *entry = mUnicastAddressHistory.AddNewEntry();
249 
250     VerifyOrExit(entry != nullptr);
251 
252     entry->mAddress       = aUnicastAddress.GetAddress();
253     entry->mPrefixLength  = aUnicastAddress.GetPrefixLength();
254     entry->mAddressOrigin = aUnicastAddress.GetOrigin();
255     entry->mEvent         = (aEvent == Ip6::Netif::kAddressAdded) ? kAddressAdded : kAddressRemoved;
256     entry->mScope         = (aUnicastAddress.GetScope() & 0xf);
257     entry->mPreferred     = aUnicastAddress.mPreferred;
258     entry->mValid         = aUnicastAddress.mValid;
259     entry->mRloc          = aUnicastAddress.mRloc;
260 
261 exit:
262     return;
263 }
264 
RecordAddressEvent(Ip6::Netif::AddressEvent aEvent,const Ip6::Netif::MulticastAddress & aMulticastAddress,Ip6::Netif::AddressOrigin aAddressOrigin)265 void HistoryTracker::RecordAddressEvent(Ip6::Netif::AddressEvent            aEvent,
266                                         const Ip6::Netif::MulticastAddress &aMulticastAddress,
267                                         Ip6::Netif::AddressOrigin           aAddressOrigin)
268 {
269     MulticastAddressInfo *entry = mMulticastAddressHistory.AddNewEntry();
270 
271     VerifyOrExit(entry != nullptr);
272 
273     entry->mAddress       = aMulticastAddress.GetAddress();
274     entry->mAddressOrigin = aAddressOrigin;
275     entry->mEvent         = (aEvent == Ip6::Netif::kAddressAdded) ? kAddressAdded : kAddressRemoved;
276 
277 exit:
278     return;
279 }
280 
281 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA
RecordNetworkDataChange(void)282 void HistoryTracker::RecordNetworkDataChange(void)
283 {
284     NetworkData::Iterator            iterator;
285     NetworkData::OnMeshPrefixConfig  prefix;
286     NetworkData::ExternalRouteConfig route;
287 
288     // On mesh prefix entries
289 
290     iterator = NetworkData::kIteratorInit;
291 
292     while (mPreviousNetworkData.GetNextOnMeshPrefix(iterator, prefix) == kErrorNone)
293     {
294         if (!Get<NetworkData::Leader>().ContainsOnMeshPrefix(prefix))
295         {
296             RecordOnMeshPrefixEvent(kNetDataEntryRemoved, prefix);
297         }
298     }
299 
300     iterator = NetworkData::kIteratorInit;
301 
302     while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefix) == kErrorNone)
303     {
304         if (!mPreviousNetworkData.ContainsOnMeshPrefix(prefix))
305         {
306             RecordOnMeshPrefixEvent(kNetDataEntryAdded, prefix);
307         }
308     }
309 
310     // External route entries
311 
312     iterator = NetworkData::kIteratorInit;
313 
314     while (mPreviousNetworkData.GetNextExternalRoute(iterator, route) == kErrorNone)
315     {
316         if (!Get<NetworkData::Leader>().ContainsExternalRoute(route))
317         {
318             RecordExternalRouteEvent(kNetDataEntryRemoved, route);
319         }
320     }
321 
322     iterator = NetworkData::kIteratorInit;
323 
324     while (Get<NetworkData::Leader>().GetNextExternalRoute(iterator, route) == kErrorNone)
325     {
326         if (!mPreviousNetworkData.ContainsExternalRoute(route))
327         {
328             RecordExternalRouteEvent(kNetDataEntryAdded, route);
329         }
330     }
331 
332     SuccessOrAssert(Get<NetworkData::Leader>().CopyNetworkData(NetworkData::kFullSet, mPreviousNetworkData));
333 }
334 
RecordOnMeshPrefixEvent(NetDataEvent aEvent,const NetworkData::OnMeshPrefixConfig & aPrefix)335 void HistoryTracker::RecordOnMeshPrefixEvent(NetDataEvent aEvent, const NetworkData::OnMeshPrefixConfig &aPrefix)
336 {
337     OnMeshPrefixInfo *entry = mOnMeshPrefixHistory.AddNewEntry();
338 
339     VerifyOrExit(entry != nullptr);
340     entry->mPrefix = aPrefix;
341     entry->mEvent  = aEvent;
342 
343 exit:
344     return;
345 }
346 
RecordExternalRouteEvent(NetDataEvent aEvent,const NetworkData::ExternalRouteConfig & aRoute)347 void HistoryTracker::RecordExternalRouteEvent(NetDataEvent aEvent, const NetworkData::ExternalRouteConfig &aRoute)
348 {
349     ExternalRouteInfo *entry = mExternalRouteHistory.AddNewEntry();
350 
351     VerifyOrExit(entry != nullptr);
352     entry->mRoute = aRoute;
353     entry->mEvent = aEvent;
354 
355 exit:
356     return;
357 }
358 
359 #endif // OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA
360 
HandleNotifierEvents(Events aEvents)361 void HistoryTracker::HandleNotifierEvents(Events aEvents)
362 {
363     if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadRlocAdded | kEventThreadRlocRemoved |
364                             kEventThreadPartitionIdChanged))
365     {
366         RecordNetworkInfo();
367     }
368 
369 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA
370     if (aEvents.Contains(kEventThreadNetdataChanged))
371     {
372         RecordNetworkDataChange();
373     }
374 #endif
375 }
376 
HandleTimer(Timer & aTimer)377 void HistoryTracker::HandleTimer(Timer &aTimer)
378 {
379     aTimer.Get<HistoryTracker>().HandleTimer();
380 }
381 
HandleTimer(void)382 void HistoryTracker::HandleTimer(void)
383 {
384     mNetInfoHistory.UpdateAgedEntries();
385     mUnicastAddressHistory.UpdateAgedEntries();
386     mMulticastAddressHistory.UpdateAgedEntries();
387     mRxHistory.UpdateAgedEntries();
388     mTxHistory.UpdateAgedEntries();
389     mNeighborHistory.UpdateAgedEntries();
390     mOnMeshPrefixHistory.UpdateAgedEntries();
391     mExternalRouteHistory.UpdateAgedEntries();
392 
393     mTimer.Start(kAgeCheckPeriod);
394 }
395 
EntryAgeToString(uint32_t aEntryAge,char * aBuffer,uint16_t aSize)396 void HistoryTracker::EntryAgeToString(uint32_t aEntryAge, char *aBuffer, uint16_t aSize)
397 {
398     StringWriter writer(aBuffer, aSize);
399 
400     if (aEntryAge >= kMaxAge)
401     {
402         writer.Append("more than %u days", kMaxAge / Time::kOneDayInMsec);
403     }
404     else
405     {
406         uint32_t days = aEntryAge / Time::kOneDayInMsec;
407 
408         if (days > 0)
409         {
410             writer.Append("%u day%s ", days, (days == 1) ? "" : "s");
411             aEntryAge -= days * Time::kOneDayInMsec;
412         }
413 
414         writer.Append("%02u:%02u:%02u.%03u", (aEntryAge / Time::kOneHourInMsec),
415                       (aEntryAge % Time::kOneHourInMsec) / Time::kOneMinuteInMsec,
416                       (aEntryAge % Time::kOneMinuteInMsec) / Time::kOneSecondInMsec,
417                       (aEntryAge % Time::kOneSecondInMsec));
418     }
419 }
420 
421 //---------------------------------------------------------------------------------------------------------------------
422 // HistoryTracker::Timestamp
423 
SetToNow(void)424 void HistoryTracker::Timestamp::SetToNow(void)
425 {
426     mTime = TimerMilli::GetNow();
427 
428     // If the current time happens to be the special value which we
429     // use to indicate "distant past", decrement the time by one.
430 
431     if (mTime.GetValue() == kDistantPast)
432     {
433         mTime.SetValue(mTime.GetValue() - 1);
434     }
435 }
436 
GetDurationTill(TimeMilli aTime) const437 uint32_t HistoryTracker::Timestamp::GetDurationTill(TimeMilli aTime) const
438 {
439     return IsDistantPast() ? kMaxAge : OT_MIN(aTime - mTime, kMaxAge);
440 }
441 
442 //---------------------------------------------------------------------------------------------------------------------
443 // HistoryTracker::List
444 
List(void)445 HistoryTracker::List::List(void)
446     : mStartIndex(0)
447     , mSize(0)
448 {
449 }
450 
Clear(void)451 void HistoryTracker::List::Clear(void)
452 {
453     mStartIndex = 0;
454     mSize       = 0;
455 }
456 
Add(uint16_t aMaxSize,Timestamp aTimestamps[])457 uint16_t HistoryTracker::List::Add(uint16_t aMaxSize, Timestamp aTimestamps[])
458 {
459     // Add a new entry and return its list index. Overwrites the
460     // oldest entry if list is full.
461     //
462     // Entries are saved in the order they are added such that
463     // `mStartIndex` is the newest entry and the entries after up
464     // to `mSize` are the previously added entries.
465 
466     mStartIndex = (mStartIndex == 0) ? aMaxSize - 1 : mStartIndex - 1;
467     mSize += (mSize == aMaxSize) ? 0 : 1;
468 
469     aTimestamps[mStartIndex].SetToNow();
470 
471     return mStartIndex;
472 }
473 
Iterate(uint16_t aMaxSize,const Timestamp aTimestamps[],Iterator & aIterator,uint16_t & aListIndex,uint32_t & aEntryAge) const474 Error HistoryTracker::List::Iterate(uint16_t        aMaxSize,
475                                     const Timestamp aTimestamps[],
476                                     Iterator &      aIterator,
477                                     uint16_t &      aListIndex,
478                                     uint32_t &      aEntryAge) const
479 {
480     Error error = kErrorNone;
481 
482     VerifyOrExit(aIterator.GetEntryNumber() < mSize, error = kErrorNotFound);
483 
484     aListIndex = MapEntryNumberToListIndex(aIterator.GetEntryNumber(), aMaxSize);
485     aEntryAge  = aTimestamps[aListIndex].GetDurationTill(aIterator.GetInitTime());
486 
487     aIterator.IncrementEntryNumber();
488 
489 exit:
490     return error;
491 }
492 
MapEntryNumberToListIndex(uint16_t aEntryNumber,uint16_t aMaxSize) const493 uint16_t HistoryTracker::List::MapEntryNumberToListIndex(uint16_t aEntryNumber, uint16_t aMaxSize) const
494 {
495     // Map the `aEntryNumber` to the list index. `aEntryNumber` value
496     // of zero corresponds to the newest (the most recently added)
497     // entry and value one to next one and so on. List index
498     // warps at the end of array to start of array. Caller MUST
499     // ensure `aEntryNumber` is smaller than `mSize`.
500 
501     uint32_t index;
502 
503     OT_ASSERT(aEntryNumber < mSize);
504 
505     index = static_cast<uint32_t>(aEntryNumber) + mStartIndex;
506     index -= (index >= aMaxSize) ? aMaxSize : 0;
507 
508     return static_cast<uint16_t>(index);
509 }
510 
UpdateAgedEntries(uint16_t aMaxSize,Timestamp aTimestamps[])511 void HistoryTracker::List::UpdateAgedEntries(uint16_t aMaxSize, Timestamp aTimestamps[])
512 {
513     TimeMilli now = TimerMilli::GetNow();
514 
515     // We go through the entries in reverse (starting with the oldest
516     // entry) and check if the entry's age is larger than `kMaxAge`
517     // and if so mark it as "distant past". We can stop as soon as we
518     // get to an entry with age smaller than max.
519     //
520     // The `for()` loop condition is `(entryNumber < mSize)` which
521     // ensures that we go through the loop body for `entryNumber`
522     // value of zero and then in the next iteration (when the
523     // `entryNumber` rolls over) we stop.
524 
525     for (uint16_t entryNumber = mSize - 1; entryNumber < mSize; entryNumber--)
526     {
527         uint16_t index = MapEntryNumberToListIndex(entryNumber, aMaxSize);
528 
529         if (aTimestamps[index].GetDurationTill(now) < kMaxAge)
530         {
531             break;
532         }
533 
534         aTimestamps[index].MarkAsDistantPast();
535     }
536 }
537 
538 } // namespace Utils
539 } // namespace ot
540 
541 #endif // #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
542