diff --git a/include/openthread/history_tracker.h b/include/openthread/history_tracker.h index 4c9b4088e..02d72f2e5 100644 --- a/include/openthread/history_tracker.h +++ b/include/openthread/history_tracker.h @@ -192,6 +192,35 @@ typedef struct otHistoryTrackerNeighborInfo bool mIsChild : 1; ///< Indicates whether or not the neighbor is a child. } otHistoryTrackerNeighborInfo; +/** + * This enumeration defines the events in a router info (i.e. whether router is added, removed, or changed). + * + */ +typedef enum +{ + OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED = 0, ///< Router is added (router ID allocated). + OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED = 1, ///< Router entry is removed (router ID released). + OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED = 2, ///< Router entry next hop and cost changed. + OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED = 3, ///< Router entry path cost changed (next hop as before). +} otHistoryTrackerRouterEvent; + +#define OT_HISTORY_TRACKER_NO_NEXT_HOP 63 ///< No next hop - For `mNextHop` in `otHistoryTrackerRouterInfo`. + +#define OT_HISTORY_TRACKER_INFINITE_PATH_COST 0 ///< Infinite path cost - used in `otHistoryTrackerRouterInfo`. + +/** + * This structure represents a router table entry event. + * + */ +typedef struct otHistoryTrackerRouterInfo +{ + uint8_t mEvent : 2; ///< Router entry event (`OT_HISTORY_TRACKER_ROUTER_EVENT_*` enumeration). + uint8_t mRouterId : 6; ///< Router ID. + uint8_t mNextHop; ///< Next Hop Router ID - `OT_HISTORY_TRACKER_NO_NEXT_HOP` if no next hop. + uint8_t mOldPathCost : 4; ///< Old path cost - `OT_HISTORY_TRACKER_INFINITE_PATH_COST` if infinite or unknown. + uint8_t mPathCost : 4; ///< New path cost - `OT_HISTORY_TRACKER_INFINITE_PATH_COST` if infinite or unknown. +} otHistoryTrackerRouterInfo; + /** * This enumeration defines the events for a Network Data entry (i.e., whether an entry is added or removed). * @@ -342,6 +371,23 @@ const otHistoryTrackerNeighborInfo *otHistoryTrackerIterateNeighborHistory(otIns otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge); +/** + * This function iterates over the entries in the router history list. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in,out] aIterator A pointer to an iterator. MUST be initialized or the behavior is undefined. + * @param[out] aEntryAge A pointer to a variable to output the entry's age. MUST NOT be NULL. + * Age is provided as the duration (in milliseconds) from when entry was recorded to + * @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries + * older than max age. + * + * @returns The `otHistoryTrackerRouterInfo` entry or `NULL` if no more entries in the list. + * + */ +const otHistoryTrackerRouterInfo *otHistoryTrackerIterateRouterHistory(otInstance *aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t *aEntryAge); + /** * This function iterates over the entries in the Network Data on mesh prefix entry history list. * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 30443d8c0..d7c88e390 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (284) +#define OPENTHREAD_API_VERSION (285) /** * @addtogroup api-instance diff --git a/src/cli/README_HISTORY.md b/src/cli/README_HISTORY.md index e3ea55dc8..268cf7d24 100644 --- a/src/cli/README_HISTORY.md +++ b/src/cli/README_HISTORY.md @@ -17,6 +17,7 @@ Usage : `history [command] ...` - [netinfo](#netinfo) - [prefix](#prefix) - [route](#route) +- [router](#router) - [rx](#rx) - [rxtx](#rxtx) - [tx](#tx) @@ -56,6 +57,7 @@ neighbor netinfo prefix route +router rx rxtx tx @@ -298,7 +300,7 @@ Print the history as a list. 00:06:01.711 -> event:Added prefix:fd00:dead:beef:1::/64 flags:paros pref:med rloc16:0x8800 ``` -### prefix +### route Usage `history route [list] []` @@ -337,6 +339,82 @@ Print the history as a list (last two entries). Done ``` +### router + +Usage `history router [list] []` + +Print the route table history. Each item provides: + +- Event (`Added`, `Removed`, `NextHopChnaged`, `CostChanged`) +- Router ID and RLOC16 of router +- Next Hop (Router ID and RLOC16) - `none` if no next hop. +- Path cost (old `->` new) - `inf` to indicate infinite path cost. + +Print the history as a table. + +```bash +> history router +| Age | Event | ID (RLOC16) | Next Hop | Path Cost | ++----------------------+----------------+-------------+-------------+------------+ +| 00:00:05.258 | NextHopChanged | 7 (0x1c00) | 34 (0x8800) | inf -> 3 | +| 00:00:08.604 | NextHopChanged | 34 (0x8800) | 34 (0x8800) | inf -> 2 | +| 00:00:08.604 | Added | 7 (0x1c00) | none | inf -> inf | +| 00:00:11.931 | Added | 34 (0x8800) | none | inf -> inf | +| 00:00:14.948 | Removed | 59 (0xec00) | none | inf -> inf | +| 00:00:14.948 | Removed | 54 (0xd800) | none | inf -> inf | +| 00:00:14.948 | Removed | 34 (0x8800) | none | inf -> inf | +| 00:00:14.948 | Removed | 7 (0x1c00) | none | inf -> inf | +| 00:00:54.795 | NextHopChanged | 59 (0xec00) | 34 (0x8800) | 1 -> 5 | +| 00:02:33.735 | NextHopChanged | 54 (0xd800) | none | 15 -> inf | +| 00:03:10.915 | CostChanged | 54 (0xd800) | 34 (0x8800) | 13 -> 15 | +| 00:03:45.716 | NextHopChanged | 54 (0xd800) | 34 (0x8800) | 15 -> 13 | +| 00:03:46.188 | CostChanged | 54 (0xd800) | 59 (0xec00) | 13 -> 15 | +| 00:04:19.124 | CostChanged | 54 (0xd800) | 59 (0xec00) | 11 -> 13 | +| 00:04:52.008 | CostChanged | 54 (0xd800) | 59 (0xec00) | 9 -> 11 | +| 00:05:23.176 | CostChanged | 54 (0xd800) | 59 (0xec00) | 7 -> 9 | +| 00:05:51.081 | CostChanged | 54 (0xd800) | 59 (0xec00) | 5 -> 7 | +| 00:06:48.721 | CostChanged | 54 (0xd800) | 59 (0xec00) | 3 -> 5 | +| 00:07:13.792 | NextHopChanged | 54 (0xd800) | 59 (0xec00) | 1 -> 3 | +| 00:09:28.681 | NextHopChanged | 7 (0x1c00) | 34 (0x8800) | inf -> 3 | +| 00:09:31.882 | Added | 7 (0x1c00) | none | inf -> inf | +| 00:09:51.240 | NextHopChanged | 54 (0xd800) | 54 (0xd800) | inf -> 1 | +| 00:09:54.204 | Added | 54 (0xd800) | none | inf -> inf | +| 00:10:20.645 | NextHopChanged | 34 (0x8800) | 34 (0x8800) | inf -> 2 | +| 00:10:24.242 | NextHopChanged | 59 (0xec00) | 59 (0xec00) | inf -> 1 | +| 00:10:24.242 | Added | 34 (0x8800) | none | inf -> inf | +| 00:10:41.900 | NextHopChanged | 59 (0xec00) | none | 1 -> inf | +| 00:10:42.480 | Added | 3 (0x0c00) | 3 (0x0c00) | inf -> inf | +| 00:10:43.614 | Added | 59 (0xec00) | 59 (0xec00) | inf -> 1 | +Done +``` + +Print the history as a list (last 20 entries). + +```bash +> history router list 20 +00:00:06.959 -> event:NextHopChanged router:7(0x1c00) nexthop:34(0x8800) old-cost:inf new-cost:3 +00:00:10.305 -> event:NextHopChanged router:34(0x8800) nexthop:34(0x8800) old-cost:inf new-cost:2 +00:00:10.305 -> event:Added router:7(0x1c00) nexthop:none old-cost:inf new-cost:inf +00:00:13.632 -> event:Added router:34(0x8800) nexthop:none old-cost:inf new-cost:inf +00:00:16.649 -> event:Removed router:59(0xec00) nexthop:none old-cost:inf new-cost:inf +00:00:16.649 -> event:Removed router:54(0xd800) nexthop:none old-cost:inf new-cost:inf +00:00:16.649 -> event:Removed router:34(0x8800) nexthop:none old-cost:inf new-cost:inf +00:00:16.649 -> event:Removed router:7(0x1c00) nexthop:none old-cost:inf new-cost:inf +00:00:56.496 -> event:NextHopChanged router:59(0xec00) nexthop:34(0x8800) old-cost:1 new-cost:5 +00:02:35.436 -> event:NextHopChanged router:54(0xd800) nexthop:none old-cost:15 new-cost:inf +00:03:12.616 -> event:CostChanged router:54(0xd800) nexthop:34(0x8800) old-cost:13 new-cost:15 +00:03:47.417 -> event:NextHopChanged router:54(0xd800) nexthop:34(0x8800) old-cost:15 new-cost:13 +00:03:47.889 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:13 new-cost:15 +00:04:20.825 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:11 new-cost:13 +00:04:53.709 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:9 new-cost:11 +00:05:24.877 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:7 new-cost:9 +00:05:52.782 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:5 new-cost:7 +00:06:50.422 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:3 new-cost:5 +00:07:15.493 -> event:NextHopChanged router:54(0xd800) nexthop:59(0xec00) old-cost:1 new-cost:3 +00:09:30.382 -> event:NextHopChanged router:7(0x1c00) nexthop:34(0x8800) old-cost:inf new-cost:3 +Done +``` + ### rx Usage `history rx [list] []` diff --git a/src/cli/cli_history.cpp b/src/cli/cli_history.cpp index b712b26ab..e5285658c 100644 --- a/src/cli/cli_history.cpp +++ b/src/cli/cli_history.cpp @@ -256,6 +256,92 @@ exit: return error; } +template <> otError History::Process(Arg aArgs[]) +{ + static const char *const kEventString[] = { + /* (0) OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED -> */ "Added", + /* (1) OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED -> */ "Removed", + /* (2) OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED -> */ "NextHopChanged", + /* (3) OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED -> */ "CostChanged", + }; + + constexpr uint8_t kRouterIdOffset = 10; // Bit offset of Router ID in RLOC16 + + otError error; + bool isList; + uint16_t numEntries; + otHistoryTrackerIterator iterator; + const otHistoryTrackerRouterInfo *info; + uint32_t entryAge; + char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; + + static_assert(0 == OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED, "EVENT_ADDED is incorrect"); + static_assert(1 == OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED, "EVENT_REMOVED is incorrect"); + static_assert(2 == OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED, "EVENT_NEXT_HOP_CHANGED is incorrect"); + static_assert(3 == OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED, "EVENT_COST_CHANGED is incorrect"); + + SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); + + if (!isList) + { + // | Age | Event | ID (RlOC16) | Next Hop | Path Cost | + // +----------------------+----------------+-------------+------------+-------------+ + + static const char *const kRouterInfoTitles[] = { + "Age", "Event", "ID (RLOC16)", "Next Hop", "Path Cost", + }; + + static const uint8_t kRouterInfoColumnWidths[] = {22, 16, 13, 13, 12}; + + OutputTableHeader(kRouterInfoTitles, kRouterInfoColumnWidths); + } + + otHistoryTrackerInitIterator(&iterator); + + for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) + { + info = otHistoryTrackerIterateRouterHistory(GetInstancePtr(), &iterator, &entryAge); + VerifyOrExit(info != nullptr); + + otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); + + OutputFormat(isList ? "%s -> event:%s router:%u(0x%04x) nexthop:" : "| %20s | %-14s | %2u (0x%04x) | ", + ageString, kEventString[info->mEvent], info->mRouterId, + static_cast(info->mRouterId) << kRouterIdOffset); + + if (info->mNextHop != OT_HISTORY_TRACKER_NO_NEXT_HOP) + { + OutputFormat(isList ? "%u(0x%04x)" : "%2u (0x%04x)", info->mNextHop, + static_cast(info->mNextHop) << kRouterIdOffset); + } + else + { + OutputFormat(isList ? "%s" : "%11s", "none"); + } + + if (info->mOldPathCost != OT_HISTORY_TRACKER_INFINITE_PATH_COST) + { + OutputFormat(isList ? " old-cost:%u" : " | %3u ->", info->mOldPathCost); + } + else + { + OutputFormat(isList ? " old-cost:inf" : " | inf ->"); + } + + if (info->mPathCost != OT_HISTORY_TRACKER_INFINITE_PATH_COST) + { + OutputLine(isList ? " new-cost:%u" : " %3u |", info->mPathCost); + } + else + { + OutputLine(isList ? " new-cost:inf" : " inf |"); + } + } + +exit: + return error; +} + template <> otError History::Process(Arg aArgs[]) { otError error; @@ -659,7 +745,7 @@ otError History::Process(Arg aArgs[]) static constexpr Command kCommands[] = { CmdEntry("ipaddr"), CmdEntry("ipmaddr"), CmdEntry("neighbor"), CmdEntry("netinfo"), CmdEntry("prefix"), - CmdEntry("route"), CmdEntry("rx"), CmdEntry("rxtx"), CmdEntry("tx"), + CmdEntry("route"), CmdEntry("router"), CmdEntry("rx"), CmdEntry("rxtx"), CmdEntry("tx"), }; #undef CmdEntry diff --git a/src/core/api/history_tracker_api.cpp b/src/core/api/history_tracker_api.cpp index d1a6b05c2..f3d0357c3 100644 --- a/src/core/api/history_tracker_api.cpp +++ b/src/core/api/history_tracker_api.cpp @@ -103,6 +103,15 @@ const otHistoryTrackerNeighborInfo *otHistoryTrackerIterateNeighborHistory(otIns return AsCoreType(aInstance).Get().IterateNeighborHistory(AsCoreType(aIterator), *aEntryAge); } +const otHistoryTrackerRouterInfo *otHistoryTrackerIterateRouterHistory(otInstance *aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t *aEntryAge) +{ + AssertPointerIsNotNull(aEntryAge); + + return AsCoreType(aInstance).Get().IterateRouterHistory(AsCoreType(aIterator), *aEntryAge); +} + const otHistoryTrackerOnMeshPrefixInfo *otHistoryTrackerIterateOnMeshPrefixHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, uint32_t *aEntryAge) diff --git a/src/core/config/history_tracker.h b/src/core/config/history_tracker.h index 32da285aa..591474a3f 100644 --- a/src/core/config/history_tracker.h +++ b/src/core/config/history_tracker.h @@ -127,6 +127,18 @@ #define OPENTHREAD_CONFIG_HISTORY_TRACKER_NEIGHBOR_LIST_SIZE 64 #endif +/** + * @def OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE + * + * Specifies the maximum number of entries in router table history list. + * + * Can be set to zero to configure History Tracker module not to collect any router table history. + * + */ +#ifndef OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE +#define OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE 256 +#endif + /** * @def OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE * diff --git a/src/core/thread/router_table.cpp b/src/core/thread/router_table.cpp index 71d78c8f2..fb6feb5b5 100644 --- a/src/core/thread/router_table.cpp +++ b/src/core/thread/router_table.cpp @@ -871,10 +871,14 @@ void RouterTable::HandleTableChanged(void) #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) LogRouteTable(); #endif + +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordRouterTableChange(); +#endif } #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) -void RouterTable::LogRouteTable(void) +void RouterTable::LogRouteTable(void) const { static constexpr uint16_t kStringSize = 128; diff --git a/src/core/thread/router_table.hpp b/src/core/thread/router_table.hpp index 1e4e5da4b..02b6f2fc4 100644 --- a/src/core/thread/router_table.hpp +++ b/src/core/thread/router_table.hpp @@ -440,7 +440,7 @@ private: void SignalTableChanged(void); void HandleTableChanged(void); - void LogRouteTable(void); + void LogRouteTable(void) const; class RouterIdMap { diff --git a/src/core/utils/history_tracker.cpp b/src/core/utils/history_tracker.cpp index 9fa465d35..fece22fad 100644 --- a/src/core/utils/history_tracker.cpp +++ b/src/core/utils/history_tracker.cpp @@ -59,6 +59,10 @@ HistoryTracker::HistoryTracker(Instance &aInstance) #endif { mTimer.Start(kAgeCheckPeriod); + +#if OPENTHREAD_FTD && (OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0) + memset(mRouterEntries, 0, sizeof(mRouterEntries)); +#endif } void HistoryTracker::RecordNetworkInfo(void) @@ -279,6 +283,76 @@ exit: return; } +#if OPENTHREAD_FTD +void HistoryTracker::RecordRouterTableChange(void) +{ +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0 + + for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + { + RouterInfo entry; + RouterEntry &oldEntry = mRouterEntries[routerId]; + + entry.mRouterId = routerId; + + if (Get().IsAllocated(routerId)) + { + uint16_t nextHopRloc; + uint8_t pathCost; + + Get().GetNextHopAndPathCost(Mle::Rloc16FromRouterId(routerId), nextHopRloc, pathCost); + + entry.mNextHop = (nextHopRloc == Mle::kInvalidRloc16) ? kNoNextHop : Mle::RouterIdFromRloc16(nextHopRloc); + entry.mPathCost = (pathCost < Mle::kMaxRouteCost) ? pathCost : 0; + + if (!oldEntry.mIsAllocated) + { + entry.mEvent = kRouterAdded; + entry.mOldPathCost = 0; + } + else if (oldEntry.mNextHop != entry.mNextHop) + { + entry.mEvent = kRouterNextHopChanged; + entry.mOldPathCost = oldEntry.mPathCost; + } + else if ((entry.mNextHop != kNoNextHop) && (oldEntry.mPathCost != entry.mPathCost)) + { + entry.mEvent = kRouterCostChanged; + entry.mOldPathCost = oldEntry.mPathCost; + } + else + { + continue; + } + + mRouterHistory.AddNewEntry(entry); + + oldEntry.mIsAllocated = true; + oldEntry.mNextHop = entry.mNextHop; + oldEntry.mPathCost = entry.mPathCost; + } + else + { + // `routerId` is not allocated. + + if (oldEntry.mIsAllocated) + { + entry.mEvent = kRouterRemoved; + entry.mNextHop = Mle::kInvalidRouterId; + entry.mOldPathCost = 0; + entry.mPathCost = 0; + + mRouterHistory.AddNewEntry(entry); + + oldEntry.mIsAllocated = false; + } + } + } + +#endif // (OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0) +} +#endif // OPENTHREAD_FTD + #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA void HistoryTracker::RecordNetworkDataChange(void) { diff --git a/src/core/utils/history_tracker.hpp b/src/core/utils/history_tracker.hpp index f92d8c49d..a7bcee2eb 100644 --- a/src/core/utils/history_tracker.hpp +++ b/src/core/utils/history_tracker.hpp @@ -54,6 +54,7 @@ #include "thread/mle_types.hpp" #include "thread/neighbor_table.hpp" #include "thread/network_data.hpp" +#include "thread/router_table.hpp" namespace ot { namespace Utils { @@ -78,6 +79,9 @@ class HistoryTracker : public InstanceLocator, private NonCopyable friend class ot::Mle::Mle; friend class ot::NeighborTable; friend class ot::Ip6::Netif; +#if OPENTHREAD_FTD + friend class ot::RouterTable; +#endif public: /** @@ -94,6 +98,14 @@ public: */ static constexpr uint16_t kEntryAgeStringSize = OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE; + /** + * This constants specifid no next hop. + * + * Used for `mNextHop` in `RouteInfo` struture. + * + */ + static constexpr uint8_t kNoNextHop = OT_HISTORY_TRACKER_NO_NEXT_HOP; + /** * This type represents an iterator to iterate through a history list. * @@ -125,6 +137,7 @@ public: typedef otHistoryTrackerMulticastAddressInfo MulticastAddressInfo; ///< Multicast IPv6 address info. typedef otHistoryTrackerMessageInfo MessageInfo; ///< RX/TX IPv6 message info. typedef otHistoryTrackerNeighborInfo NeighborInfo; ///< Neighbor info. + typedef otHistoryTrackerRouterInfo RouterInfo; ///< Router info. typedef otHistoryTrackerOnMeshPrefixInfo OnMeshPrefixInfo; ///< Network Data on mesh prefix info. typedef otHistoryTrackerExternalRouteInfo ExternalRouteInfo; ///< Network Data external route info @@ -226,6 +239,11 @@ public: return mNeighborHistory.Iterate(aIterator, aEntryAge); } + const RouterInfo *IterateRouterHistory(Iterator &aIterator, uint32_t &aEntryAge) const + { + return mRouterHistory.Iterate(aIterator, aEntryAge); + } + const OnMeshPrefixInfo *IterateOnMeshPrefixHistory(Iterator &aIterator, uint32_t &aEntryAge) const { return mOnMeshPrefixHistory.Iterate(aIterator, aEntryAge); @@ -265,6 +283,7 @@ private: static constexpr uint16_t kRxListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_RX_LIST_SIZE; static constexpr uint16_t kTxListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_TX_LIST_SIZE; static constexpr uint16_t kNeighborListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_NEIGHBOR_LIST_SIZE; + static constexpr uint16_t kRouterListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE; static constexpr uint16_t kOnMeshPrefixListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE; static constexpr uint16_t kExternalRouteListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE; @@ -282,6 +301,13 @@ private: static constexpr NeighborEvent kNeighborChanged = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_CHANGED; static constexpr NeighborEvent kNeighborRestoring = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_RESTORING; + typedef otHistoryTrackerRouterEvent RouterEvent; + + static constexpr RouterEvent kRouterAdded = OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED; + static constexpr RouterEvent kRouterRemoved = OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED; + static constexpr RouterEvent kRouterNextHopChanged = OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED; + static constexpr RouterEvent kRouterCostChanged = OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED; + typedef otHistoryTrackerNetDataEvent NetDataEvent; static constexpr NetDataEvent kNetDataEntryAdded = OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED; @@ -387,6 +413,9 @@ private: Ip6::Netif::AddressOrigin aAddressOrigin); void HandleNotifierEvents(Events aEvents); void HandleTimer(void); +#if OPENTHREAD_FTD + void RecordRouterTableChange(void); +#endif #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA void RecordNetworkDataChange(void); void RecordOnMeshPrefixEvent(NetDataEvent aEvent, const NetworkData::OnMeshPrefixConfig &aPrefix); @@ -401,11 +430,23 @@ private: EntryList mRxHistory; EntryList mTxHistory; EntryList mNeighborHistory; + EntryList mRouterHistory; EntryList mOnMeshPrefixHistory; EntryList mExternalRouteHistory; TrackerTimer mTimer; +#if OPENTHREAD_FTD && (OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0) + struct RouterEntry + { + bool mIsAllocated : 1; + uint8_t mNextHop : 6; + uint8_t mPathCost : 4; + }; + + RouterEntry mRouterEntries[Mle::kMaxRouterId + 1]; +#endif + #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA NetworkData::MutableNetworkData mPreviousNetworkData; @@ -419,6 +460,7 @@ DefineCoreType(otHistoryTrackerIterator, Utils::HistoryTracker::Iterator); DefineCoreType(otHistoryTrackerNetworkInfo, Utils::HistoryTracker::NetworkInfo); DefineCoreType(otHistoryTrackerMessageInfo, Utils::HistoryTracker::MessageInfo); DefineCoreType(otHistoryTrackerNeighborInfo, Utils::HistoryTracker::NeighborInfo); +DefineCoreType(otHistoryTrackerRouterInfo, Utils::HistoryTracker::RouterInfo); DefineCoreType(otHistoryTrackerOnMeshPrefixInfo, Utils::HistoryTracker::OnMeshPrefixInfo); DefineCoreType(otHistoryTrackerExternalRouteInfo, Utils::HistoryTracker::ExternalRouteInfo);