From 314e945bb6f827ccf7ff8876afaca160a13065da Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 19 Jan 2022 16:35:40 -0800 Subject: [PATCH] [history-tracker] support tracking netdata prefix and route entries (#7324) This commit adds a mechanism in `HistoryTracker` to track changes in Thread Network Data maintaining a history of on-mesh prefix and external route entries as they are added or removed by different nodes in Network Data. This commit also adds new `cli history` commands to retrieve the Network Data prefix/route history list and updates the documentation in `cli/README_HISTORY.md`. --- include/openthread/history_tracker.h | 66 ++++++++++++++++ include/openthread/instance.h | 2 +- src/cli/README_HISTORY.md | 94 ++++++++++++++++++++++ src/cli/cli_history.cpp | 114 +++++++++++++++++++++++++-- src/cli/cli_history.hpp | 4 + src/cli/cli_network_data.cpp | 72 ++++++++--------- src/cli/cli_network_data.hpp | 41 +++++++++- src/core/api/history_tracker_api.cpp | 17 ++++ src/core/config/history_tracker.h | 24 ++++++ src/core/utils/history_tracker.cpp | 92 +++++++++++++++++++++ src/core/utils/history_tracker.hpp | 78 +++++++++++------- 11 files changed, 528 insertions(+), 76 deletions(-) diff --git a/include/openthread/history_tracker.h b/include/openthread/history_tracker.h index 4f496d161..dfb098f27 100644 --- a/include/openthread/history_tracker.h +++ b/include/openthread/history_tracker.h @@ -29,6 +29,7 @@ #include #include +#include #include #ifdef __cplusplus @@ -191,6 +192,36 @@ typedef struct otHistoryTrackerNeighborInfo bool mIsChild : 1; ///< Indicates whether or not the neighbor is a child. } otHistoryTrackerNeighborInfo; +/** + * This enumeration defines the events for a Network Data entry (i.e., whether an entry is added or removed). + * + */ +typedef enum +{ + OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED = 0, ///< Network data entry is added. + OT_HISTORY_TRACKER_NET_DATA_ENTRY_REMOVED = 1, ///< Network data entry is removed. +} otHistoryTrackerNetDataEvent; + +/** + * This structure represent a Network Data on mesh prefix info. + * + */ +typedef struct otHistoryTrackerOnMeshPrefixInfo +{ + otBorderRouterConfig mPrefix; ///< The on mesh prefix entry. + otHistoryTrackerNetDataEvent mEvent; ///< Indicates the event (added/removed). +} otHistoryTrackerOnMeshPrefixInfo; + +/** + * This structure represent a Network Data extern route info. + * + */ +typedef struct otHistoryTrackerExternalRouteInfo +{ + otExternalRouteConfig mRoute; ///< The external route entry. + otHistoryTrackerNetDataEvent mEvent; ///< Indicates the event (added/removed). +} otHistoryTrackerExternalRouteInfo; + /** * This function initializes an `otHistoryTrackerIterator`. * @@ -311,6 +342,41 @@ const otHistoryTrackerNeighborInfo *otHistoryTrackerIterateNeighborHistory(otIns otHistoryTrackerIterator *aIterator, uint32_t * aEntryAge); +/** + * This function iterates over the entries in the Network Data on mesh prefix entry history list. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[inout] 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 `otHistoryTrackerOnMeshPrefixInfo` entry or `NULL` if no more entries in the list. + * + */ +const otHistoryTrackerOnMeshPrefixInfo *otHistoryTrackerIterateOnMeshPrefixHistory(otInstance * aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t * aEntryAge); + +/** + * This function iterates over the entries in the Network Data external route entry history list. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[inout] 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 `otHistoryTrackerExternalRouteInfo` entry or `NULL` if no more entries in the list. + * + */ +const otHistoryTrackerExternalRouteInfo *otHistoryTrackerIterateExternalRouteHistory( + otInstance * aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t * aEntryAge); + /** * This function converts a given entry age to a human-readable string. * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 7739ff7db..e06b7754e 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 (185) +#define OPENTHREAD_API_VERSION (186) /** * @addtogroup api-instance diff --git a/src/cli/README_HISTORY.md b/src/cli/README_HISTORY.md index 53caf164b..e3ea55dc8 100644 --- a/src/cli/README_HISTORY.md +++ b/src/cli/README_HISTORY.md @@ -15,6 +15,8 @@ Usage : `history [command] ...` - [ipmaddr](#ipmaddr) - [neighbor](#neighbor) - [netinfo](#netinfo) +- [prefix](#prefix) +- [route](#route) - [rx](#rx) - [rxtx](#rxtx) - [tx](#tx) @@ -52,6 +54,8 @@ ipaddr ipmaddr neighbor netinfo +prefix +route rx rxtx tx @@ -243,6 +247,96 @@ Print only the latest 2 entries. Done ``` +### prefix + +Usage `history prefix [list] []` + +Print the Network Data on mesh prefix history. Each item provides: + +- Event (`Added` or `Removed`) +- Prefix +- Flags +- Preference (`high`, `med`, `low`) +- RLOC16 + +The flags are as follows: + +- `p`: Preferred flag +- `a`: Stateless IPv6 Address Autoconfiguration flag +- `d`: DHCPv6 IPv6 Address Configuration flag +- `c`: DHCPv6 Other Configuration flag +- `r`: Default Route flag +- `o`: On Mesh flag +- `s`: Stable flag +- `n`: Nd Dns flag +- `D`: Domain Prefix flag + +Print the history as a table. + +```bash +> history prefix +| Age | Event | Prefix | Flags | Pref | RLOC16 | ++----------------------+---------+---------------------------------------------+-----------+------+--------+ +| 00:00:10.663 | Added | fd00:1111:2222:3333::/64 | paro | med | 0x5400 | +| 00:01:02.054 | Removed | fd00:dead:beef:1::/64 | paros | high | 0x5400 | +| 00:01:21.136 | Added | fd00:abba:cddd:0::/64 | paos | med | 0x5400 | +| 00:01:45.144 | Added | fd00:dead:beef:1::/64 | paros | high | 0x3c00 | +| 00:01:50.944 | Added | fd00:dead:beef:1::/64 | paros | high | 0x5400 | +| 00:01:59.887 | Added | fd00:dead:beef:1::/64 | paros | med | 0x8800 | +Done +``` + +Print the history as a list. + +```bash +> history prefix list +00:04:12.487 -> event:Added prefix:fd00:1111:2222:3333::/64 flags:paro pref:med rloc16:0x5400 +00:05:03.878 -> event:Removed prefix:fd00:dead:beef:1::/64 flags:paros pref:high rloc16:0x5400 +00:05:22.960 -> event:Added prefix:fd00:abba:cddd:0::/64 flags:paos pref:med rloc16:0x5400 +00:05:46.968 -> event:Added prefix:fd00:dead:beef:1::/64 flags:paros pref:high rloc16:0x3c00 +00:05:52.768 -> event:Added prefix:fd00:dead:beef:1::/64 flags:paros pref:high rloc16:0x5400 +00:06:01.711 -> event:Added prefix:fd00:dead:beef:1::/64 flags:paros pref:med rloc16:0x8800 +``` + +### prefix + +Usage `history route [list] []` + +Print the Network Data external route history. Each item provides: + +- Event (`Added` or `Removed`) +- Route +- Flags +- Preference (`high`, `med`, `low`) +- RLOC16 + +The flags are as follows: + +- `s`: Stable flag +- `n`: NAT64 flag + +Print the history as a table. + +```bash +history route +| Age | Event | Route | Flags | Pref | RLOC16 | ++----------------------+---------+---------------------------------------------+-----------+------+--------+ +| 00:00:05.456 | Removed | fd00:1111:0::/48 | s | med | 0x3c00 | +| 00:00:29.310 | Added | fd00:1111:0::/48 | s | med | 0x3c00 | +| 00:00:42.822 | Added | fd00:1111:0::/48 | s | med | 0x5400 | +| 00:01:27.688 | Added | fd00:aaaa:bbbb:cccc::/64 | s | med | 0x8800 | +Done +``` + +Print the history as a list (last two entries). + +```bash +> history route list 2 +00:00:48.704 -> event:Removed route:fd00:1111:0::/48 flags:s pref:med rloc16:0x3c00 +00:01:12.558 -> event:Added route:fd00:1111:0::/48 flags:s pref:med rloc16:0x3c00 +Done +``` + ### rx Usage `history rx [list] []` diff --git a/src/cli/cli_history.cpp b/src/cli/cli_history.cpp index e5e12c9e9..422ee6167 100644 --- a/src/cli/cli_history.cpp +++ b/src/cli/cli_history.cpp @@ -44,6 +44,11 @@ namespace Cli { constexpr History::Command History::sCommands[]; +static const char *const kSimpleEventStrings[] = { + "Added", // (0) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESSS_EVENT}_ADDED + "Removed" // (1) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESS_EVENT}_REMOVED +}; + otError History::ProcessHelp(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -82,11 +87,6 @@ otError History::ParseArgs(Arg aArgs[], bool &aIsList, uint16_t &aNumEntries) co otError History::ProcessIpAddr(Arg aArgs[]) { - static const char *const kEventStrings[] = { - "Added", // (0) OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED - "Removed" // (1) OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED - }; - otError error; bool isList; uint16_t numEntries; @@ -129,14 +129,14 @@ otError History::ProcessIpAddr(Arg aArgs[]) sprintf(&addressString[strlen(addressString)], "/%d", info->mPrefixLength); OutputLine("| %20s | %-7s | %-43s | %-6s | %3d | %c | %c | %c |", ageString, - Stringify(info->mEvent, kEventStrings), addressString, + Stringify(info->mEvent, kSimpleEventStrings), addressString, Interpreter::AddressOriginToString(info->mAddressOrigin), info->mScope, info->mPreferred ? 'Y' : 'N', info->mValid ? 'Y' : 'N', info->mRloc ? 'Y' : 'N'); } else { OutputLine("%s -> event:%s address:%s prefixlen:%d origin:%s scope:%d preferred:%s valid:%s rloc:%s", - ageString, Stringify(info->mEvent, kEventStrings), addressString, info->mPrefixLength, + ageString, Stringify(info->mEvent, kSimpleEventStrings), addressString, info->mPrefixLength, Interpreter::AddressOriginToString(info->mAddressOrigin), info->mScope, info->mPreferred ? "yes" : "no", info->mValid ? "yes" : "no", info->mRloc ? "yes" : "no"); } @@ -571,6 +571,106 @@ void History::OutputRxTxEntryTableFormat(const otHistoryTrackerMessageInfo &aInf OutputLine("| %20s | dst: %-70s |", "", addrString); } +otError History::ProcessPrefix(Arg aArgs[]) +{ + otError error; + bool isList; + uint16_t numEntries; + otHistoryTrackerIterator iterator; + const otHistoryTrackerOnMeshPrefixInfo *info; + uint32_t entryAge; + char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; + char prefixString[OT_IP6_PREFIX_STRING_SIZE]; + NetworkData::FlagsString flagsString; + + static_assert(0 == OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED, "NET_DATA_ENTRY_ADDED value is incorrect"); + static_assert(1 == OT_HISTORY_TRACKER_NET_DATA_ENTRY_REMOVED, "NET_DATA_ENTRY_REMOVED value is incorrect"); + + SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); + + if (!isList) + { + // | Age | Event | Prefix | Flags | Pref | RLOC16 | + // +----------------------+---------+---------------------------------------------+-----------+------+--------+ + + static const char *const kPrefixTitles[] = {"Age", "Event", "Prefix", "Flags", "Pref", "RLOC16"}; + static const uint8_t kPrefixColumnWidths[] = {22, 9, 45, 11, 6, 8}; + + OutputTableHeader(kPrefixTitles, kPrefixColumnWidths); + } + + otHistoryTrackerInitIterator(&iterator); + + for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) + { + info = otHistoryTrackerIterateOnMeshPrefixHistory(GetInstancePtr(), &iterator, &entryAge); + VerifyOrExit(info != nullptr); + + otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); + + otIp6PrefixToString(&info->mPrefix.mPrefix, prefixString, sizeof(prefixString)); + NetworkData::PrefixFlagsToString(info->mPrefix, flagsString); + + OutputLine(isList ? "%s -> event:%s prefix:%s flags:%s pref:%s rloc16:0x%04x" + : "| %20s | %-7s | %-43s | %-9s | %-4s | 0x%04x |", + ageString, Stringify(info->mEvent, kSimpleEventStrings), prefixString, flagsString, + NetworkData::PreferenceToString(info->mPrefix.mPreference), info->mPrefix.mRloc16); + } + +exit: + return error; +} + +otError History::ProcessRoute(Arg aArgs[]) +{ + otError error; + bool isList; + uint16_t numEntries; + otHistoryTrackerIterator iterator; + const otHistoryTrackerExternalRouteInfo *info; + uint32_t entryAge; + char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; + char prefixString[OT_IP6_PREFIX_STRING_SIZE]; + NetworkData::FlagsString flagsString; + + static_assert(0 == OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED, "NET_DATA_ENTRY_ADDED value is incorrect"); + static_assert(1 == OT_HISTORY_TRACKER_NET_DATA_ENTRY_REMOVED, "NET_DATA_ENTRY_REMOVED value is incorrect"); + + SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); + + if (!isList) + { + // | Age | Event | Route | Flags | Pref | RLOC16 | + // +----------------------+---------+---------------------------------------------+-----------+------+--------+ + + static const char *const kRouteTitles[] = {"Age", "Event", "Route", "Flags", "Pref", "RLOC16"}; + static const uint8_t kRouteColumnWidths[] = {22, 9, 45, 11, 6, 8}; + + OutputTableHeader(kRouteTitles, kRouteColumnWidths); + } + + otHistoryTrackerInitIterator(&iterator); + + for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) + { + info = otHistoryTrackerIterateExternalRouteHistory(GetInstancePtr(), &iterator, &entryAge); + VerifyOrExit(info != nullptr); + + otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); + + otIp6PrefixToString(&info->mRoute.mPrefix, prefixString, sizeof(prefixString)); + NetworkData::RouteFlagsToString(info->mRoute, flagsString); + + OutputLine(isList ? "%s -> event:%s route:%s flags:%s pref:%s rloc16:0x%04x" + : "| %20s | %-7s | %-43s | %-9s | %-4s | 0x%04x |", + ageString, Stringify(info->mEvent, kSimpleEventStrings), prefixString, flagsString, + NetworkData::PreferenceToString(info->mRoute.mPreference), info->mRoute.mRloc16); + } + +exit: + return error; +} + otError History::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_COMMAND; diff --git a/src/cli/cli_history.hpp b/src/cli/cli_history.hpp index 95cd1a312..b39a877f6 100644 --- a/src/cli/cli_history.hpp +++ b/src/cli/cli_history.hpp @@ -93,6 +93,8 @@ private: otError ProcessIpMulticastAddr(Arg aArgs[]); otError ProcessNetInfo(Arg aArgs[]); otError ProcessNeighbor(Arg aArgs[]); + otError ProcessPrefix(Arg aArgs[]); + otError ProcessRoute(Arg aArgs[]); otError ProcessRx(Arg aArgs[]); otError ProcessRxTx(Arg aArgs[]); otError ProcessTx(Arg aArgs[]); @@ -112,6 +114,8 @@ private: {"ipmaddr", &History::ProcessIpMulticastAddr}, {"neighbor", &History::ProcessNeighbor}, {"netinfo", &History::ProcessNetInfo}, + {"prefix", &History::ProcessPrefix}, + {"route", &History::ProcessRoute}, {"rx", &History::ProcessRx}, {"rxtx", &History::ProcessRxTx}, {"tx", &History::ProcessTx}, diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp index 627d46425..54a223939 100644 --- a/src/cli/cli_network_data.cpp +++ b/src/cli/cli_network_data.cpp @@ -45,20 +45,9 @@ namespace Cli { constexpr NetworkData::Command NetworkData::sCommands[]; -void NetworkData::OutputPrefix(const otBorderRouterConfig &aConfig) +void NetworkData::PrefixFlagsToString(const otBorderRouterConfig &aConfig, FlagsString &aString) { - enum - { - // BorderRouter flag is `uint16_t` (though some of the bits are - // reserved for future use), so we use 17 chars string (16 flags - // plus null char at end of string). - kMaxFlagStringSize = 17, - }; - - char flagsString[kMaxFlagStringSize]; - char *flagsPtr = &flagsString[0]; - - OutputIp6Prefix(aConfig.mPrefix); + char *flagsPtr = &aString[0]; if (aConfig.mPreferred) { @@ -106,31 +95,27 @@ void NetworkData::OutputPrefix(const otBorderRouterConfig &aConfig) } *flagsPtr = '\0'; +} - if (flagsPtr != &flagsString[0]) +void NetworkData::OutputPrefix(const otBorderRouterConfig &aConfig) +{ + FlagsString flagsString; + + OutputIp6Prefix(aConfig.mPrefix); + + PrefixFlagsToString(aConfig, flagsString); + + if (flagsString[0] != '\0') { OutputFormat(" %s", flagsString); } - OutputPreference(aConfig.mPreference); - - OutputLine(" %04x", aConfig.mRloc16); + OutputLine(" %s %04x", PreferenceToString(aConfig.mPreference), aConfig.mRloc16); } -void NetworkData::OutputRoute(const otExternalRouteConfig &aConfig) +void NetworkData::RouteFlagsToString(const otExternalRouteConfig &aConfig, FlagsString &aString) { - enum - { - // ExternalRoute flag is `uint8_t` (though some of the bits are - // reserved for future use), so we use 9 chars string (8 flags - // plus null char at end of string). - kMaxFlagStringSize = 9, - }; - - char flagsString[kMaxFlagStringSize]; - char *flagsPtr = &flagsString[0]; - - OutputIp6Prefix(aConfig.mPrefix); + char *flagsPtr = &aString[0]; if (aConfig.mStable) { @@ -143,36 +128,47 @@ void NetworkData::OutputRoute(const otExternalRouteConfig &aConfig) } *flagsPtr = '\0'; +} - if (flagsPtr != &flagsString[0]) +void NetworkData::OutputRoute(const otExternalRouteConfig &aConfig) +{ + FlagsString flagsString; + + OutputIp6Prefix(aConfig.mPrefix); + + RouteFlagsToString(aConfig, flagsString); + + if (flagsString[0] != '\0') { OutputFormat(" %s", flagsString); } - OutputPreference(aConfig.mPreference); - - OutputLine(" %04x", aConfig.mRloc16); + OutputLine(" %s %04x", PreferenceToString(aConfig.mPreference), aConfig.mRloc16); } -void NetworkData::OutputPreference(signed int aPreference) +const char *NetworkData::PreferenceToString(signed int aPreference) { + const char *str = ""; + switch (aPreference) { case OT_ROUTE_PREFERENCE_LOW: - OutputFormat(" low"); + str = "low"; break; case OT_ROUTE_PREFERENCE_MED: - OutputFormat(" med"); + str = "med"; break; case OT_ROUTE_PREFERENCE_HIGH: - OutputFormat(" high"); + str = "high"; break; default: break; } + + return str; } void NetworkData::OutputService(const otServiceConfig &aConfig) diff --git a/src/cli/cli_network_data.hpp b/src/cli/cli_network_data.hpp index d52170ce9..2efc3a7b6 100644 --- a/src/cli/cli_network_data.hpp +++ b/src/cli/cli_network_data.hpp @@ -52,6 +52,18 @@ class NetworkData : private OutputWrapper public: typedef Utils::CmdLineParser::Arg Arg; + /** + * This constant specifies the string size for representing Network Data prefix/route entry flags. + * + * BorderRouter (OnMeshPrefix) TLV uses `uint16_t` for its flags and ExternalRoute uses `uint8_t`, though some of + * the bits are not currently used and reserved for future, so 17 chars string (16 flags plus null char at end of + * string) covers current and future flags. + * + */ + static constexpr uint16_t kFlagsStringSize = 17; + + typedef char FlagsString[kFlagsStringSize]; ///< Flags String type (char array of `kFlagsStringSize`). + /** * Constructor * @@ -95,6 +107,34 @@ public: */ void OutputService(const otServiceConfig &aConfig); + /** + * This method converts the flags from a given prefix config to string. + * + * @param[in] aConfig The prefix config. + * @param[out] aString The string to populate from @a Config flags. + * + */ + static void PrefixFlagsToString(const otBorderRouterConfig &aConfig, FlagsString &aString); + + /** + * This method converts the flags from a given route config to string. + * + * @param[in] aConfig The route config. + * @param[out] aString The string to populate from @a Config flags. + * + */ + static void RouteFlagsToString(const otExternalRouteConfig &aConfig, FlagsString &aString); + + /** + * This static method converts a route preference value to human-readable string. + * + * @param[in] aPreference The preference value to convert (`OT_ROUTE_PREFERENCE_*` values). + * + * @returns A string representation @p aPreference. + * + */ + static const char *PreferenceToString(signed int aPreference); + private: using Command = CommandEntry; @@ -117,7 +157,6 @@ private: void OutputPrefixes(bool aLocal); void OutputRoutes(bool aLocal); void OutputServices(bool aLocal); - void OutputPreference(signed int aPreference); static constexpr Command sCommands[] = { {"help", &NetworkData::ProcessHelp}, diff --git a/src/core/api/history_tracker_api.cpp b/src/core/api/history_tracker_api.cpp index a9714efcf..5e52b36a7 100644 --- a/src/core/api/history_tracker_api.cpp +++ b/src/core/api/history_tracker_api.cpp @@ -94,6 +94,23 @@ const otHistoryTrackerNeighborInfo *otHistoryTrackerIterateNeighborHistory(otIns return AsCoreType(aInstance).Get().IterateNeighborHistory(AsCoreType(aIterator), *aEntryAge); } +const otHistoryTrackerOnMeshPrefixInfo *otHistoryTrackerIterateOnMeshPrefixHistory(otInstance * aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t * aEntryAge) +{ + return AsCoreType(aInstance).Get().IterateOnMeshPrefixHistory(AsCoreType(aIterator), + *aEntryAge); +} + +const otHistoryTrackerExternalRouteInfo *otHistoryTrackerIterateExternalRouteHistory( + otInstance * aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t * aEntryAge) +{ + return AsCoreType(aInstance).Get().IterateExternalRouteHistory(AsCoreType(aIterator), + *aEntryAge); +} + void otHistoryTrackerEntryAgeToString(uint32_t aEntryAge, char *aBuffer, uint16_t aSize) { Utils::HistoryTracker::EntryAgeToString(aEntryAge, aBuffer, aSize); diff --git a/src/core/config/history_tracker.h b/src/core/config/history_tracker.h index 7cf65744f..32da285aa 100644 --- a/src/core/config/history_tracker.h +++ b/src/core/config/history_tracker.h @@ -127,4 +127,28 @@ #define OPENTHREAD_CONFIG_HISTORY_TRACKER_NEIGHBOR_LIST_SIZE 64 #endif +/** + * @def OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE + * + * Specifies the maximum number of entries in Network Data On Mesh Prefix history list. + * + * Can be set to zero to configure History Tracker module not to collect prefix info. + * + */ +#ifndef OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE +#define OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE 32 +#endif + +/** + * @def OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE + * + * Specifies the maximum number of entries in Network Data External Route history list. + * + * Can be set to zero to configure History Tracker module not to collect route info. + * + */ +#ifndef OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE +#define OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE 32 +#endif + #endif // CONFIG_HISTORY_TRACKER_H_ diff --git a/src/core/utils/history_tracker.cpp b/src/core/utils/history_tracker.cpp index e43d5bf87..f590217fd 100644 --- a/src/core/utils/history_tracker.cpp +++ b/src/core/utils/history_tracker.cpp @@ -52,6 +52,9 @@ namespace Utils { HistoryTracker::HistoryTracker(Instance &aInstance) : InstanceLocator(aInstance) , mTimer(aInstance, HandleTimer) +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA + , mPreviousNetworkData(aInstance, mNetworkDataTlvBuffer, 0, sizeof(mNetworkDataTlvBuffer)) +#endif { mTimer.Start(kAgeCheckPeriod); } @@ -291,6 +294,86 @@ exit: return; } +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA +void HistoryTracker::RecordNetworkDataChange(void) +{ + NetworkData::Iterator iterator; + NetworkData::OnMeshPrefixConfig prefix; + NetworkData::ExternalRouteConfig route; + + // On mesh prefix entries + + iterator = NetworkData::kIteratorInit; + + while (mPreviousNetworkData.GetNextOnMeshPrefix(iterator, prefix) == kErrorNone) + { + if (!Get().ContainsOnMeshPrefix(prefix)) + { + RecordOnMeshPrefixEvent(kNetDataEntryRemoved, prefix); + } + } + + iterator = NetworkData::kIteratorInit; + + while (Get().GetNextOnMeshPrefix(iterator, prefix) == kErrorNone) + { + if (!mPreviousNetworkData.ContainsOnMeshPrefix(prefix)) + { + RecordOnMeshPrefixEvent(kNetDataEntryAdded, prefix); + } + } + + // External route entries + + iterator = NetworkData::kIteratorInit; + + while (mPreviousNetworkData.GetNextExternalRoute(iterator, route) == kErrorNone) + { + if (!Get().ContainsExternalRoute(route)) + { + RecordExternalRouteEvent(kNetDataEntryRemoved, route); + } + } + + iterator = NetworkData::kIteratorInit; + + while (Get().GetNextExternalRoute(iterator, route) == kErrorNone) + { + if (!mPreviousNetworkData.ContainsExternalRoute(route)) + { + RecordExternalRouteEvent(kNetDataEntryAdded, route); + } + } + + SuccessOrAssert(Get().CopyNetworkData(/* aSatble */ false, mPreviousNetworkData)); +} + +void HistoryTracker::RecordOnMeshPrefixEvent(NetDataEvent aEvent, const NetworkData::OnMeshPrefixConfig &aPrefix) +{ + OnMeshPrefixInfo *entry = mOnMeshPrefixHistory.AddNewEntry(); + + VerifyOrExit(entry != nullptr); + entry->mPrefix = aPrefix; + entry->mEvent = aEvent; + +exit: + return; +} + +void HistoryTracker::RecordExternalRouteEvent(NetDataEvent aEvent, const NetworkData::ExternalRouteConfig &aRoute) +{ + ExternalRouteInfo *entry = mExternalRouteHistory.AddNewEntry(); + + VerifyOrExit(entry != nullptr); + entry->mRoute = aRoute; + entry->mEvent = aEvent; + +exit: + return; +} + +#endif // OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA + void HistoryTracker::HandleNotifierEvents(Events aEvents) { if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadRlocAdded | kEventThreadRlocRemoved | @@ -298,6 +381,13 @@ void HistoryTracker::HandleNotifierEvents(Events aEvents) { RecordNetworkInfo(); } + +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA + if (aEvents.Contains(kEventThreadNetdataChanged)) + { + RecordNetworkDataChange(); + } +#endif } void HistoryTracker::HandleTimer(Timer &aTimer) @@ -313,6 +403,8 @@ void HistoryTracker::HandleTimer(void) mRxHistory.UpdateAgedEntries(); mTxHistory.UpdateAgedEntries(); mNeighborHistory.UpdateAgedEntries(); + mOnMeshPrefixHistory.UpdateAgedEntries(); + mExternalRouteHistory.UpdateAgedEntries(); mTimer.Start(kAgeCheckPeriod); } diff --git a/src/core/utils/history_tracker.hpp b/src/core/utils/history_tracker.hpp index 82270b6ea..a3670983f 100644 --- a/src/core/utils/history_tracker.hpp +++ b/src/core/utils/history_tracker.hpp @@ -53,10 +53,20 @@ #include "thread/mle.hpp" #include "thread/mle_types.hpp" #include "thread/neighbor_table.hpp" +#include "thread/network_data.hpp" namespace ot { namespace Utils { +#ifdef OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA +#error "OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA should not be defined directly." \ + "It is derived from other configs: on-mesh prefix and external route history list sizes" +#endif + +#define OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA \ + ((OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE > 0) || \ + (OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE > 0)) + /** * This class implements History Tracker. * @@ -110,35 +120,13 @@ public: void SetInitTime(void) { mData32 = TimerMilli::GetNow().GetValue(); } }; - /** - * This type represents Thread network info. - * - */ - typedef otHistoryTrackerNetworkInfo NetworkInfo; - - /** - * This structure represents a unicast IPv6 address info. - * - */ - typedef otHistoryTrackerUnicastAddressInfo UnicastAddressInfo; - - /** - * This structure represent a multicast IPv6 address info. - * - */ - typedef otHistoryTrackerMulticastAddressInfo MulticastAddressInfo; - - /** - * This type represents a RX/TX IPv6 message info. - * - */ - typedef otHistoryTrackerMessageInfo MessageInfo; - - /** - * This type represents a neighbor info. - * - */ - typedef otHistoryTrackerNeighborInfo NeighborInfo; + typedef otHistoryTrackerNetworkInfo NetworkInfo; ///< Thread network info. + typedef otHistoryTrackerUnicastAddressInfo UnicastAddressInfo; ///< Unicast IPv6 address info. + typedef otHistoryTrackerMulticastAddressInfo MulticastAddressInfo; ///< Multicast IPv6 address info. + typedef otHistoryTrackerMessageInfo MessageInfo; ///< RX/TX IPv6 message info. + typedef otHistoryTrackerNeighborInfo NeighborInfo; ///< Neighbor info. + typedef otHistoryTrackerOnMeshPrefixInfo OnMeshPrefixInfo; ///< Network Data on mesh prefix info. + typedef otHistoryTrackerExternalRouteInfo ExternalRouteInfo; ///< Network Data external route info /** * This constructor initializes the `HistoryTracker`. @@ -233,6 +221,16 @@ public: return mNeighborHistory.Iterate(aIterator, aEntryAge); } + const OnMeshPrefixInfo *IterateOnMeshPrefixHistory(Iterator &aIterator, uint32_t &aEntryAge) const + { + return mOnMeshPrefixHistory.Iterate(aIterator, aEntryAge); + } + + const ExternalRouteInfo *IterateExternalRouteHistory(Iterator &aIterator, uint32_t &aEntryAge) const + { + return mExternalRouteHistory.Iterate(aIterator, aEntryAge); + } + /** * This static method converts a given entry age to a human-readable string. * @@ -262,6 +260,8 @@ 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 kOnMeshPrefixListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE; + static constexpr uint16_t kExternalRouteListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE; typedef otHistoryTrackerAddressEvent AddressEvent; @@ -278,6 +278,11 @@ private: static constexpr NeighborEvent kNeighborChanged = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_CHANGED; static constexpr NeighborEvent kNeighborRestoring = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_RESTORING; + typedef otHistoryTrackerNetDataEvent NetDataEvent; + + static constexpr NetDataEvent kNetDataEntryAdded = OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED; + static constexpr NetDataEvent kNetDataEntryRemoved = OT_HISTORY_TRACKER_NET_DATA_ENTRY_REMOVED; + class Timestamp { public: @@ -379,6 +384,11 @@ private: void HandleNotifierEvents(Events aEvents); static void HandleTimer(Timer &aTimer); void HandleTimer(void); +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA + void RecordNetworkDataChange(void); + void RecordOnMeshPrefixEvent(NetDataEvent aEvent, const NetworkData::OnMeshPrefixConfig &aPrefix); + void RecordExternalRouteEvent(NetDataEvent aEvent, const NetworkData::ExternalRouteConfig &aRoute); +#endif EntryList mNetInfoHistory; EntryList mUnicastAddressHistory; @@ -386,8 +396,16 @@ private: EntryList mRxHistory; EntryList mTxHistory; EntryList mNeighborHistory; + EntryList mOnMeshPrefixHistory; + EntryList mExternalRouteHistory; TimerMilli mTimer; + +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA + NetworkData::MutableNetworkData mPreviousNetworkData; + + uint8_t mNetworkDataTlvBuffer[NetworkData::NetworkData::kMaxSize]; +#endif }; } // namespace Utils @@ -396,6 +414,8 @@ DefineCoreType(otHistoryTrackerIterator, Utils::HistoryTracker::Iterator); DefineCoreType(otHistoryTrackerNetworkInfo, Utils::HistoryTracker::NetworkInfo); DefineCoreType(otHistoryTrackerMessageInfo, Utils::HistoryTracker::MessageInfo); DefineCoreType(otHistoryTrackerNeighborInfo, Utils::HistoryTracker::NeighborInfo); +DefineCoreType(otHistoryTrackerOnMeshPrefixInfo, Utils::HistoryTracker::OnMeshPrefixInfo); +DefineCoreType(otHistoryTrackerExternalRouteInfo, Utils::HistoryTracker::ExternalRouteInfo); } // namespace ot