diff --git a/include/openthread/history_tracker.h b/include/openthread/history_tracker.h index 517c6c83a..0cce3e75e 100644 --- a/include/openthread/history_tracker.h +++ b/include/openthread/history_tracker.h @@ -90,6 +90,44 @@ typedef struct otHistoryTrackerNetworkInfo uint32_t mPartitionId; ///< Partition ID (valid when attached). } otHistoryTrackerNetworkInfo; +/** + * This enumeration defines the events for an IPv6 (unicast or multicast) address info (i.e., whether address is added + * or removed). + * + */ +typedef enum +{ + OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED = 0, ///< Address is added. + OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED = 1, ///< Address is removed. +} otHistoryTrackerAddressEvent; + +/** + * This structure represent a unicast IPv6 address info. + * + */ +typedef struct otHistoryTrackerUnicastAddressInfo +{ + otIp6Address mAddress; ///< The unicast IPv6 address. + uint8_t mPrefixLength; ///< The Prefix length (in bits). + uint8_t mAddressOrigin; ///< The address origin (`OT_ADDRESS_ORIGIN_*` constants). + otHistoryTrackerAddressEvent mEvent; ///< Indicates the event (address is added/removed). + uint8_t mScope : 4; ///< The IPv6 scope. + bool mPreferred : 1; ///< If the address is preferred. + bool mValid : 1; ///< If the address is valid. + bool mRloc : 1; ///< If the address is an RLOC. +} otHistoryTrackerUnicastAddressInfo; + +/** + * This structure represent an IPv6 multicast address info. + * + */ +typedef struct otHistoryTrackerMulticastAddressInfo +{ + otIp6Address mAddress; ///< The IPv6 multicast address. + uint8_t mAddressOrigin; ///< The address origin (`OT_ADDRESS_ORIGIN_*` constants). + otHistoryTrackerAddressEvent mEvent; ///< Indicates the event (address is added/removed). +} otHistoryTrackerMulticastAddressInfo; + /** * Constants representing message priority used in `otHistoryTrackerMessageInfo` struct. * @@ -191,6 +229,42 @@ const otHistoryTrackerNetworkInfo *otHistoryTrackerIterateNetInfoHistory(otInsta otHistoryTrackerIterator *aIterator, uint32_t * aEntryAge); +/** + * This function iterates over the entries in the unicast address 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 A pointer to `otHistoryTrackerUnicastAddressInfo` entry or `NULL` if no more entries in the list. + * + */ +const otHistoryTrackerUnicastAddressInfo *otHistoryTrackerIterateUnicastAddressHistory( + otInstance * aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t * aEntryAge); + +/** + * This function iterates over the entries in the multicast address 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 A pointer to `otHistoryTrackerMulticastAddressInfo` entry or `NULL` if no more entries in the list. + * + */ +const otHistoryTrackerMulticastAddressInfo *otHistoryTrackerIterateMulticastAddressHistory( + otInstance * aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t * aEntryAge); + /** * This function iterates over the entries in the RX message history list. * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index ab22b89d2..9605ec7a3 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 (165) +#define OPENTHREAD_API_VERSION (166) /** * @addtogroup api-instance diff --git a/src/cli/README_HISTORY.md b/src/cli/README_HISTORY.md index 8e6c59ded..b11a57eee 100644 --- a/src/cli/README_HISTORY.md +++ b/src/cli/README_HISTORY.md @@ -11,6 +11,8 @@ The number of entries recorded for each history list is configurable through a s Usage : `history [command] ...` - [help](#help) +- [ipaddr](#ipaddr) +- [ipmaddr](#ipmaddr) - [neighbor](#neighbor) - [netinfo](#netinfo) - [rx](#rx) @@ -46,6 +48,8 @@ Print SRP client help menu. ```bash > history help help +ipaddr +ipmaddr neighbor netinfo rx @@ -55,6 +59,92 @@ Done > ``` +### ipaddr + +Usage `history ipaddr [list] []` + +Print the unicast IPv6 address history. Each entry provides: + +- Event: Added or Removed. +- Address: Unicast address along with its prefix length (in bits). +- Origin: Thread, SLAAC, DHCPv6, or Manual. +- Address Scope. +- Flags: Preferred, Valid, and RLOC (whether the address is RLOC). + +Print the unicast IPv6 address history as table. + +```bash +> history ipaddr +| Age | Event | Address / PrefixLength | Origin |Scope| P | V | R | ++----------------------+---------+---------------------------------------------+--------+-----+---+---+---+ +| 00:00:04.991 | Removed | 2001:dead:beef:cafe:c4cb:caba:8d55:e30b/64 | SLAAC | 14 | Y | Y | N | +| 00:00:44.647 | Added | 2001:dead:beef:cafe:c4cb:caba:8d55:e30b/64 | SLAAC | 14 | Y | Y | N | +| 00:01:07.199 | Added | fd00:0:0:0:0:0:0:1/64 | Manual | 14 | Y | Y | N | +| 00:02:17.885 | Added | fdde:ad00:beef:0:0:ff:fe00:fc00/64 | Thread | 3 | N | Y | N | +| 00:02:17.885 | Added | fdde:ad00:beef:0:0:ff:fe00:5400/64 | Thread | 3 | N | Y | Y | +| 00:02:20.107 | Removed | fdde:ad00:beef:0:0:ff:fe00:5400/64 | Thread | 3 | N | Y | Y | +| 00:02:21.575 | Added | fdde:ad00:beef:0:0:ff:fe00:5400/64 | Thread | 3 | N | Y | Y | +| 00:02:21.575 | Added | fdde:ad00:beef:0:ecea:c4fc:ad96:4655/64 | Thread | 3 | N | Y | N | +| 00:02:23.904 | Added | fe80:0:0:0:3c12:a4d2:fbe0:31ad/64 | Thread | 2 | Y | Y | N | +Done +``` + +Print the unicast IPv6 address history as a list (the last 5 entries). + +```bash +> history ipaddr list 5 +00:00:20.327 -> event:Removed address:2001:dead:beef:cafe:c4cb:caba:8d55:e30b prefixlen:64 origin:SLAAC scope:14 preferred:yes valid:yes rloc:no +00:00:59.983 -> event:Added address:2001:dead:beef:cafe:c4cb:caba:8d55:e30b prefixlen:64 origin:SLAAC scope:14 preferred:yes valid:yes rloc:no +00:01:22.535 -> event:Added address:fd00:0:0:0:0:0:0:1 prefixlen:64 origin:Manual scope:14 preferred:yes valid:yes rloc:no +00:02:33.221 -> event:Added address:fdde:ad00:beef:0:0:ff:fe00:fc00 prefixlen:64 origin:Thread scope:3 preferred:no valid:yes rloc:no +00:02:33.221 -> event:Added address:fdde:ad00:beef:0:0:ff:fe00:5400 prefixlen:64 origin:Thread scope:3 preferred:no valid:yes rloc:yes +Done +``` + +### ipmaddr + +Usage `history ipmaddr [list] []` + +Print the multicast IPv6 address history. Each entry provides: + +- Event: Subscribed or Unsubscribed. +- Address: Multicast address. +- Origin: Thread, or Manual. + +Print the multicast IPv6 address history as table. + +```bash +> history ipmaddr +| Age | Event | Multicast Address | Origin | ++----------------------+--------------+-----------------------------------------+--------+ +| 00:00:08.592 | Unsubscribed | ff05:0:0:0:0:0:0:1 | Manual | +| 00:01:25.353 | Subscribed | ff05:0:0:0:0:0:0:1 | Manual | +| 00:01:54.953 | Subscribed | ff03:0:0:0:0:0:0:2 | Thread | +| 00:01:54.953 | Subscribed | ff02:0:0:0:0:0:0:2 | Thread | +| 00:01:59.329 | Subscribed | ff33:40:fdde:ad00:beef:0:0:1 | Thread | +| 00:01:59.329 | Subscribed | ff32:40:fdde:ad00:beef:0:0:1 | Thread | +| 00:02:01.129 | Subscribed | ff03:0:0:0:0:0:0:fc | Thread | +| 00:02:01.129 | Subscribed | ff03:0:0:0:0:0:0:1 | Thread | +| 00:02:01.129 | Subscribed | ff02:0:0:0:0:0:0:1 | Thread | +Done +``` + +Print the multicast IPv6 address history as a list. + +```bash +> history ipmaddr list +00:00:25.447 -> event:Unsubscribed address:ff05:0:0:0:0:0:0:1 origin:Manual +00:01:42.208 -> event:Subscribed address:ff05:0:0:0:0:0:0:1 origin:Manual +00:02:11.808 -> event:Subscribed address:ff03:0:0:0:0:0:0:2 origin:Thread +00:02:11.808 -> event:Subscribed address:ff02:0:0:0:0:0:0:2 origin:Thread +00:02:16.184 -> event:Subscribed address:ff33:40:fdde:ad00:beef:0:0:1 origin:Thread +00:02:16.184 -> event:Subscribed address:ff32:40:fdde:ad00:beef:0:0:1 origin:Thread +00:02:17.984 -> event:Subscribed address:ff03:0:0:0:0:0:0:fc origin:Thread +00:02:17.984 -> event:Subscribed address:ff03:0:0:0:0:0:0:1 origin:Thread +00:02:17.984 -> event:Subscribed address:ff02:0:0:0:0:0:0:1 origin:Thread +Done +``` + ### neighbor Usage `history neighbor [list] []` diff --git a/src/cli/cli_history.cpp b/src/cli/cli_history.cpp index ce3d33e29..c4e05863f 100644 --- a/src/cli/cli_history.cpp +++ b/src/cli/cli_history.cpp @@ -80,6 +80,130 @@ otError History::ParseArgs(Arg aArgs[], bool &aIsList, uint16_t &aNumEntries) co return aArgs[0].IsEmpty() ? OT_ERROR_NONE : OT_ERROR_INVALID_ARGS; } +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; + otHistoryTrackerIterator iterator; + const otHistoryTrackerUnicastAddressInfo *info; + uint32_t entryAge; + char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; + char addressString[OT_IP6_ADDRESS_STRING_SIZE + 4]; + + static_assert(0 == OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED, "ADDRESS_EVENT_ADDED is incorrect"); + static_assert(1 == OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED, "ADDRESS_EVENT_REMOVED is incorrect"); + + SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); + + if (!isList) + { + // | Age | Event | Address / PrefixLen /123 | Origin |Scope| P | V | R | + // +----------------------+---------+---------------------------------------------+--------+-----+---+---+---+ + + static const char *const kUnicastAddrInfoTitles[] = { + "Age", "Event", "Address / PrefixLength", "Origin", "Scope", "P", "V", "R"}; + + static const uint8_t kUnicastAddrInfoColumnWidths[] = {22, 9, 45, 8, 5, 3, 3, 3}; + + mInterpreter.OutputTableHeader(kUnicastAddrInfoTitles, kUnicastAddrInfoColumnWidths); + } + + otHistoryTrackerInitIterator(&iterator); + + for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) + { + info = otHistoryTrackerIterateUnicastAddressHistory(mInterpreter.mInstance, &iterator, &entryAge); + VerifyOrExit(info != nullptr); + + otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); + otIp6AddressToString(&info->mAddress, addressString, sizeof(addressString)); + + if (!isList) + { + sprintf(&addressString[strlen(addressString)], "/%d", info->mPrefixLength); + + mInterpreter.OutputLine("| %20s | %-7s | %-43s | %-6s | %3d | %c | %c | %c |", ageString, + kEventStrings[info->mEvent], addressString, + AddressOriginToString(info->mAddressOrigin), info->mScope, + info->mPreferred ? 'Y' : 'N', info->mValid ? 'Y' : 'N', info->mRloc ? 'Y' : 'N'); + } + else + { + mInterpreter.OutputLine( + "%s -> event:%s address:%s prefixlen:%d origin:%s scope:%d preferred:%s valid:%s rloc:%s", ageString, + kEventStrings[info->mEvent], addressString, info->mPrefixLength, + AddressOriginToString(info->mAddressOrigin), info->mScope, info->mPreferred ? "yes" : "no", + info->mValid ? "yes" : "no", info->mRloc ? "yes" : "no"); + } + } + +exit: + return error; +} + +otError History::ProcessIpMulticastAddr(Arg aArgs[]) +{ + static const char *const kEventStrings[] = { + "Subscribed", // (0) OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED + "Unsubscribed" // (1) OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED + }; + + otError error; + bool isList; + uint16_t numEntries; + otHistoryTrackerIterator iterator; + const otHistoryTrackerMulticastAddressInfo *info; + uint32_t entryAge; + char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; + char addressString[OT_IP6_ADDRESS_STRING_SIZE]; + + static_assert(0 == OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED, "ADDRESS_EVENT_ADDED is incorrect"); + static_assert(1 == OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED, "ADDRESS_EVENT_REMOVED is incorrect"); + + SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); + + if (!isList) + { + // | Age | Event | Multicast Address | Origin | + // +----------------------+--------------+-----------------------------------------+--------+ + + static const char *const kMulticastAddrInfoTitles[] = { + "Age", + "Event", + "Multicast Address", + "Origin", + }; + + static const uint8_t kMulticastAddrInfoColumnWidths[] = {22, 14, 42, 8}; + + mInterpreter.OutputTableHeader(kMulticastAddrInfoTitles, kMulticastAddrInfoColumnWidths); + } + + otHistoryTrackerInitIterator(&iterator); + + for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) + { + info = otHistoryTrackerIterateMulticastAddressHistory(mInterpreter.mInstance, &iterator, &entryAge); + VerifyOrExit(info != nullptr); + + otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); + otIp6AddressToString(&info->mAddress, addressString, sizeof(addressString)); + + mInterpreter.OutputLine(isList ? "%s -> event:%s address:%s origin:%s" : "| %20s | %-12s | %-39s | %-6s |", + ageString, kEventStrings[info->mEvent], addressString, + AddressOriginToString(info->mAddressOrigin)); + } + +exit: + return error; +} + otError History::ProcessNeighbor(Arg aArgs[]) { static const char *const kEventString[] = { @@ -203,6 +327,35 @@ otError History::ProcessTx(Arg aArgs[]) return ProcessRxTxHistory(kTx, aArgs); } +const char *History::AddressOriginToString(uint8_t aOrigin) +{ + const char *str = "Unknown"; + + switch (aOrigin) + { + case OT_ADDRESS_ORIGIN_THREAD: + str = "Thread"; + break; + + case OT_ADDRESS_ORIGIN_SLAAC: + str = "SLAAC"; + break; + + case OT_ADDRESS_ORIGIN_DHCPV6: + str = "DHCPv6"; + break; + + case OT_ADDRESS_ORIGIN_MANUAL: + str = "Manual"; + break; + + default: + break; + } + + return str; +} + const char *History::MessagePriorityToString(uint8_t aPriority) { const char *str = "unkn"; diff --git a/src/cli/cli_history.hpp b/src/cli/cli_history.hpp index 44bb7f787..c7a1bf512 100644 --- a/src/cli/cli_history.hpp +++ b/src/cli/cli_history.hpp @@ -96,6 +96,8 @@ private: }; otError ProcessHelp(Arg aArgs[]); + otError ProcessIpAddr(Arg aArgs[]); + otError ProcessIpMulticastAddr(Arg aArgs[]); otError ProcessNetInfo(Arg aArgs[]); otError ProcessNeighbor(Arg aArgs[]); otError ProcessRx(Arg aArgs[]); @@ -107,13 +109,20 @@ private: void OutputRxTxEntryListFormat(const otHistoryTrackerMessageInfo &aInfo, uint32_t aEntryAge, bool aIsRx); void OutputRxTxEntryTableFormat(const otHistoryTrackerMessageInfo &aInfo, uint32_t aEntryAge, bool aIsRx); + static const char *AddressOriginToString(uint8_t aOrigin); static const char *MessagePriorityToString(uint8_t aPriority); static const char *RadioTypeToString(const otHistoryTrackerMessageInfo &aInfo); static const char *MessageTypeToString(const otHistoryTrackerMessageInfo &aInfo); static constexpr Command sCommands[] = { - {"help", &History::ProcessHelp}, {"neighbor", &History::ProcessNeighbor}, {"netinfo", &History::ProcessNetInfo}, - {"rx", &History::ProcessRx}, {"rxtx", &History::ProcessRxTx}, {"tx", &History::ProcessTx}, + {"help", &History::ProcessHelp}, + {"ipaddr", &History::ProcessIpAddr}, + {"ipmaddr", &History::ProcessIpMulticastAddr}, + {"neighbor", &History::ProcessNeighbor}, + {"netinfo", &History::ProcessNetInfo}, + {"rx", &History::ProcessRx}, + {"rxtx", &History::ProcessRxTx}, + {"tx", &History::ProcessTx}, }; static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted"); diff --git a/src/core/api/history_tracker_api.cpp b/src/core/api/history_tracker_api.cpp index 4356ba753..a9714efcf 100644 --- a/src/core/api/history_tracker_api.cpp +++ b/src/core/api/history_tracker_api.cpp @@ -55,6 +55,24 @@ const otHistoryTrackerNetworkInfo *otHistoryTrackerIterateNetInfoHistory(otInsta return AsCoreType(aInstance).Get().IterateNetInfoHistory(AsCoreType(aIterator), *aEntryAge); } +const otHistoryTrackerUnicastAddressInfo *otHistoryTrackerIterateUnicastAddressHistory( + otInstance * aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t * aEntryAge) +{ + return AsCoreType(aInstance).Get().IterateUnicastAddressHistory(AsCoreType(aIterator), + *aEntryAge); +} + +const otHistoryTrackerMulticastAddressInfo *otHistoryTrackerIterateMulticastAddressHistory( + otInstance * aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t * aEntryAge) +{ + return AsCoreType(aInstance).Get().IterateMulticastAddressHistory(AsCoreType(aIterator), + *aEntryAge); +} + const otHistoryTrackerMessageInfo *otHistoryTrackerIterateRxHistory(otInstance * aInstance, otHistoryTrackerIterator *aIterator, uint32_t * aEntryAge) diff --git a/src/core/config/history_tracker.h b/src/core/config/history_tracker.h index e3f210e08..7cf65744f 100644 --- a/src/core/config/history_tracker.h +++ b/src/core/config/history_tracker.h @@ -57,6 +57,30 @@ #define OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_INFO_LIST_SIZE 32 #endif +/** + * @def OPENTHREAD_CONFIG_HISTORY_TRACKER_UNICAST_ADDRESS_LIST_SIZE + * + * Specifies the maximum number of entries in unicast IPv6 address history list. + * + * Can be set to zero to configure History Tracker module not to collect any entries. + * + */ +#ifndef OPENTHREAD_CONFIG_HISTORY_TRACKER_UNICAST_ADDRESS_LIST_SIZE +#define OPENTHREAD_CONFIG_HISTORY_TRACKER_UNICAST_ADDRESS_LIST_SIZE 20 +#endif + +/** + * @def OPENTHREAD_CONFIG_HISTORY_TRACKER_MULTICAST_ADDRESS_LIST_SIZE + * + * Specifies the maximum number of entries in multicast IPv6 address history list. + * + * Can be set to zero to configure History Tracker module not to collect any entries. + * + */ +#ifndef OPENTHREAD_CONFIG_HISTORY_TRACKER_MULTICAST_ADDRESS_LIST_SIZE +#define OPENTHREAD_CONFIG_HISTORY_TRACKER_MULTICAST_ADDRESS_LIST_SIZE 20 +#endif + /** * @def OPENTHREAD_CONFIG_HISTORY_TRACKER_RX_LIST_SIZE * diff --git a/src/core/net/dhcp6_client.cpp b/src/core/net/dhcp6_client.cpp index ede5cccfa..0fff15ea0 100644 --- a/src/core/net/dhcp6_client.cpp +++ b/src/core/net/dhcp6_client.cpp @@ -578,7 +578,7 @@ Error Client::ProcessIaAddress(Message &aMessage, uint16_t aOffset) idAssociation.mNetifAddress.mAddress = option.GetAddress(); idAssociation.mPreferredLifetime = option.GetPreferredLifetime(); idAssociation.mValidLifetime = option.GetValidLifetime(); - idAssociation.mNetifAddress.mAddressOrigin = OT_ADDRESS_ORIGIN_DHCPV6; + idAssociation.mNetifAddress.mAddressOrigin = Ip6::Netif::kOriginDhcp6; idAssociation.mNetifAddress.mPreferred = option.GetPreferredLifetime() != 0; idAssociation.mNetifAddress.mValid = option.GetValidLifetime() != 0; idAssociation.mStatus = kIaStatusSolicitReplied; diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp index dc9c52dd2..f640fb123 100644 --- a/src/core/net/netif.cpp +++ b/src/core/net/netif.cpp @@ -145,14 +145,22 @@ void Netif::SubscribeAllNodesMulticast(void) Get().Signal(kEventIp6MulticastSubscribed); +#if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE VerifyOrExit(mAddressCallback != nullptr); +#endif for (const MulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext()) { - AddressInfo addressInfo(*entry); +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressAdded, *entry, kOriginThread); - mAddressCallback(&addressInfo, - /* IsAdded */ true, mAddressCallbackContext); + if (mAddressCallback != nullptr) +#endif + { + AddressInfo addressInfo(*entry); + + mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext); + } } exit: @@ -194,14 +202,22 @@ void Netif::UnsubscribeAllNodesMulticast(void) Get().Signal(kEventIp6MulticastUnsubscribed); +#if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE VerifyOrExit(mAddressCallback != nullptr); +#endif for (const MulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext()) { - AddressInfo addressInfo(*entry); +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressRemoved, *entry, kOriginThread); - mAddressCallback(&addressInfo, - /* IsAdded */ false, mAddressCallbackContext); + if (mAddressCallback != nullptr) +#endif + { + AddressInfo addressInfo(*entry); + + mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext); + } } exit: @@ -254,15 +270,23 @@ void Netif::SubscribeAllRoutersMulticast(void) Get().Signal(kEventIp6MulticastSubscribed); +#if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE VerifyOrExit(mAddressCallback != nullptr); +#endif for (const MulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress; entry = entry->GetNext()) { - AddressInfo addressInfo(*entry); +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressAdded, *entry, kOriginThread); - mAddressCallback(&addressInfo, - /* IsAdded */ true, mAddressCallbackContext); + if (mAddressCallback != nullptr) +#endif + { + AddressInfo addressInfo(*entry); + + mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext); + } } exit: @@ -300,15 +324,23 @@ void Netif::UnsubscribeAllRoutersMulticast(void) Get().Signal(kEventIp6MulticastUnsubscribed); +#if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE VerifyOrExit(mAddressCallback != nullptr); +#endif for (const MulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress; entry = entry->GetNext()) { - AddressInfo addressInfo(*entry); +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressRemoved, *entry, kOriginThread); - mAddressCallback(&addressInfo, - /* IsAdded */ false, mAddressCallbackContext); + if (mAddressCallback != nullptr) +#endif + { + AddressInfo addressInfo(*entry); + + mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext); + } } exit: @@ -326,13 +358,15 @@ void Netif::SubscribeMulticast(MulticastAddress &aAddress) Get().Signal(kEventIp6MulticastSubscribed); - VerifyOrExit(mAddressCallback != nullptr); +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressAdded, aAddress, kOriginThread); +#endif + if (mAddressCallback != nullptr) { AddressInfo addressInfo(aAddress); - mAddressCallback(&addressInfo, - /* IsAdded */ true, mAddressCallbackContext); + mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext); } exit: @@ -345,12 +379,15 @@ void Netif::UnsubscribeMulticast(const MulticastAddress &aAddress) Get().Signal(kEventIp6MulticastUnsubscribed); - VerifyOrExit(mAddressCallback != nullptr); +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressRemoved, aAddress, kOriginThread); +#endif + + if (mAddressCallback != nullptr) { AddressInfo addressInfo(aAddress); - mAddressCallback(&addressInfo, - /* IsAdded */ false, mAddressCallbackContext); + mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext); } exit: @@ -384,6 +421,11 @@ Error Netif::SubscribeExternalMulticast(const Address &aAddress) entry->mMlrState = kMlrStateToRegister; #endif mMulticastAddresses.Push(*entry); + +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressAdded, *entry, kOriginManual); +#endif + Get().Signal(kEventIp6MulticastSubscribed); exit: @@ -403,6 +445,10 @@ Error Netif::UnsubscribeExternalMulticast(const Address &aAddress) mMulticastAddresses.PopAfter(prev); +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressRemoved, *entry, kOriginManual); +#endif + mExtMulticastAddressPool.Free(static_cast(*entry)); Get().Signal(kEventIp6MulticastUnsubscribed); @@ -438,13 +484,15 @@ void Netif::AddUnicastAddress(UnicastAddress &aAddress) Get().Signal(aAddress.mRloc ? kEventThreadRlocAdded : kEventIp6AddressAdded); - VerifyOrExit(mAddressCallback != nullptr); +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressAdded, aAddress); +#endif + if (mAddressCallback != nullptr) { AddressInfo addressInfo(aAddress); - mAddressCallback(&addressInfo, - /* IsAdded */ true, mAddressCallbackContext); + mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext); } exit: @@ -457,11 +505,15 @@ void Netif::RemoveUnicastAddress(const UnicastAddress &aAddress) Get().Signal(aAddress.mRloc ? kEventThreadRlocRemoved : kEventIp6AddressRemoved); - VerifyOrExit(mAddressCallback != nullptr); +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressRemoved, aAddress); +#endif + + if (mAddressCallback != nullptr) { AddressInfo addressInfo(aAddress); - mAddressCallback(&addressInfo, /* IsAdded */ false, mAddressCallbackContext); + mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext); } exit: @@ -495,6 +547,11 @@ Error Netif::AddExternalUnicastAddress(const UnicastAddress &aAddress) *entry = aAddress; mUnicastAddresses.Push(*entry); + +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressAdded, *entry); +#endif + Get().Signal(kEventIp6AddressAdded); exit: @@ -513,6 +570,11 @@ Error Netif::RemoveExternalUnicastAddress(const Address &aAddress) VerifyOrExit(IsUnicastAddressExternal(*entry), error = kErrorInvalidArgs); mUnicastAddresses.PopAfter(prev); + +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordAddressEvent(kAddressRemoved, *entry); +#endif + mExtUnicastAddressPool.Free(*entry); Get().Signal(kEventIp6AddressRemoved); @@ -552,7 +614,7 @@ void Netif::UnicastAddress::InitAsThreadOrigin(bool aPreferred) { Clear(); mPrefixLength = NetworkPrefix::kLength; - mAddressOrigin = OT_ADDRESS_ORIGIN_THREAD; + mAddressOrigin = kOriginThread; mPreferred = aPreferred; mValid = true; } @@ -566,7 +628,7 @@ void Netif::UnicastAddress::InitAsThreadOriginRealmLocalScope(void) void Netif::UnicastAddress::InitAsThreadOriginGlobalScope(void) { Clear(); - mAddressOrigin = OT_ADDRESS_ORIGIN_THREAD; + mAddressOrigin = kOriginThread; mValid = true; SetScopeOverride(Address::kGlobalScope); } @@ -575,7 +637,7 @@ void Netif::UnicastAddress::InitAsSlaacOrigin(uint8_t aPrefixLength, bool aPrefe { Clear(); mPrefixLength = aPrefixLength; - mAddressOrigin = OT_ADDRESS_ORIGIN_SLAAC; + mAddressOrigin = kOriginSlaac; mPreferred = aPreferred; mValid = true; } diff --git a/src/core/net/netif.hpp b/src/core/net/netif.hpp index 4ba995aa3..61ff2c281 100644 --- a/src/core/net/netif.hpp +++ b/src/core/net/netif.hpp @@ -75,6 +75,30 @@ class Netif : public InstanceLocator, private NonCopyable friend class Address; public: + /** + * This enumeration represent an address event (added or removed) + * + * The boolean values are used for `aIsAdded` parameter in the call of `otIp6AddressCallback`. + * + */ + enum AddressEvent : bool + { + kAddressRemoved = false, ///< Indicates that address was added. + kAddressAdded = true, ///< Indicates that address was removed. + }; + + /** + * This enumeration represents the address origin. + * + */ + enum AddressOrigin : uint8_t + { + kOriginThread = OT_ADDRESS_ORIGIN_THREAD, ///< Thread assigned address (ALOC, RLOC, MLEID, etc) + kOriginSlaac = OT_ADDRESS_ORIGIN_SLAAC, ///< SLAAC assigned address + kOriginDhcp6 = OT_ADDRESS_ORIGIN_DHCPV6, ///< DHCPv6 assigned address + kOriginManual = OT_ADDRESS_ORIGIN_MANUAL, ///< Manually assigned address + }; + /** * This class implements an IPv6 network interface unicast address. * @@ -181,6 +205,14 @@ public: mScopeOverrideValid = true; } + /** + * This method gets the IPv6 address origin. + * + * @returns The address origin. + * + */ + AddressOrigin GetOrigin(void) const { return static_cast(mAddressOrigin); } + private: bool Matches(const Address &aAddress) const { return GetAddress() == aAddress; } }; diff --git a/src/core/utils/history_tracker.cpp b/src/core/utils/history_tracker.cpp index 5212420cc..faec5f341 100644 --- a/src/core/utils/history_tracker.cpp +++ b/src/core/utils/history_tracker.cpp @@ -254,6 +254,42 @@ exit: return; } +void HistoryTracker::RecordAddressEvent(Ip6::Netif::AddressEvent aEvent, + const Ip6::Netif::UnicastAddress &aUnicastAddress) +{ + UnicastAddressInfo *entry = mUnicastAddressHistory.AddNewEntry(); + + VerifyOrExit(entry != nullptr); + + entry->mAddress = aUnicastAddress.GetAddress(); + entry->mPrefixLength = aUnicastAddress.GetPrefixLength(); + entry->mAddressOrigin = aUnicastAddress.GetOrigin(); + entry->mEvent = (aEvent == Ip6::Netif::kAddressAdded) ? kAddressAdded : kAddressRemoved; + entry->mScope = (aUnicastAddress.GetScope() & 0xf); + entry->mPreferred = aUnicastAddress.mPreferred; + entry->mValid = aUnicastAddress.mValid; + entry->mRloc = aUnicastAddress.mRloc; + +exit: + return; +} + +void HistoryTracker::RecordAddressEvent(Ip6::Netif::AddressEvent aEvent, + const Ip6::Netif::MulticastAddress &aMulticastAddress, + Ip6::Netif::AddressOrigin aAddressOrigin) +{ + MulticastAddressInfo *entry = mMulticastAddressHistory.AddNewEntry(); + + VerifyOrExit(entry != nullptr); + + entry->mAddress = aMulticastAddress.GetAddress(); + entry->mAddressOrigin = aAddressOrigin; + entry->mEvent = (aEvent == Ip6::Netif::kAddressAdded) ? kAddressAdded : kAddressRemoved; + +exit: + return; +} + void HistoryTracker::HandleNotifierEvents(Events aEvents) { if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadRlocAdded | kEventThreadRlocRemoved | @@ -271,6 +307,8 @@ void HistoryTracker::HandleTimer(Timer &aTimer) void HistoryTracker::HandleTimer(void) { mNetInfoHistory.UpdateAgedEntries(); + mUnicastAddressHistory.UpdateAgedEntries(); + mMulticastAddressHistory.UpdateAgedEntries(); mRxHistory.UpdateAgedEntries(); mTxHistory.UpdateAgedEntries(); mNeighborHistory.UpdateAgedEntries(); diff --git a/src/core/utils/history_tracker.hpp b/src/core/utils/history_tracker.hpp index 21713122c..181b76f4a 100644 --- a/src/core/utils/history_tracker.hpp +++ b/src/core/utils/history_tracker.hpp @@ -46,6 +46,7 @@ #include "common/non_copyable.hpp" #include "common/notifier.hpp" #include "common/timer.hpp" +#include "net/netif.hpp" #include "net/socket.hpp" #include "thread/mesh_forwarder.hpp" #include "thread/mle.hpp" @@ -65,6 +66,7 @@ class HistoryTracker : public InstanceLocator, private NonCopyable friend class ot::Notifier; friend class ot::Mle::Mle; friend class ot::NeighborTable; + friend class ot::Ip6::Netif; public: /** @@ -113,6 +115,18 @@ public: */ 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. * @@ -149,6 +163,38 @@ public: return mNetInfoHistory.Iterate(aIterator, aEntryAge); } + /** + * This method iterates over the entries in the unicast address history list. + * + * @param[inout] aIterator An iterator. MUST be initialized. + * @param[out] aEntryAge A reference to a variable to output the entry's age. + * Age is provided as the duration (in milliseconds) from when entry was recorded to + * @p aIterator initialization time. It is set to `kMaxAge` for entries older than max age. + * + * @returns A pointer to `UnicastAddress` entry or `nullptr` if no more entries in the list. + * + */ + const UnicastAddressInfo *IterateUnicastAddressHistory(Iterator &aIterator, uint32_t &aEntryAge) const + { + return mUnicastAddressHistory.Iterate(aIterator, aEntryAge); + } + + /** + * This method iterates over the entries in the multicast address history list. + * + * @param[inout] aIterator An iterator. MUST be initialized. + * @param[out] aEntryAge A reference to a variable to output the entry's age. + * Age is provided as the duration (in milliseconds) from when entry was recorded to + * @p aIterator initialization time. It is set to `kMaxAge` for entries older than max age. + * + * @returns A pointer to `MulticastAddress` entry or `nullptr` if no more entries in the list. + * + */ + const MulticastAddressInfo *IterateMulticastAddressHistory(Iterator &aIterator, uint32_t &aEntryAge) const + { + return mMulticastAddressHistory.Iterate(aIterator, aEntryAge); + } + /** * This method iterates over the entries in the RX history list. * @@ -214,10 +260,17 @@ private: static constexpr uint32_t kAgeCheckPeriod = 16 * kOneHourInMsec; - static constexpr uint16_t kNetInfoListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_INFO_LIST_SIZE; - 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 kNetInfoListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_INFO_LIST_SIZE; + static constexpr uint16_t kUnicastAddrListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_UNICAST_ADDRESS_LIST_SIZE; + static constexpr uint16_t kMulticastAddrListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_MULTICAST_ADDRESS_LIST_SIZE; + 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; + + typedef otHistoryTrackerAddressEvent AddressEvent; + + static constexpr AddressEvent kAddressAdded = OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED; + static constexpr AddressEvent kAddressRemoved = OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED; static constexpr int8_t kInvalidRss = OT_RADIO_RSSI_INVALID; static constexpr uint16_t kInvalidRloc16 = Mac::kShortAddrInvalid; @@ -323,15 +376,22 @@ private: void RecordNetworkInfo(void); void RecordMessage(const Message &aMessage, const Mac::Address &aMacAddress, MessageType aType); void RecordNeighborEvent(NeighborTable::Event aEvent, const NeighborTable::EntryInfo &aInfo); + void RecordAddressEvent(Ip6::Netif::AddressEvent aEvent, const Ip6::Netif::UnicastAddress &aUnicastAddress); + void RecordAddressEvent(Ip6::Netif::AddressEvent aEvent, + const Ip6::Netif::MulticastAddress &aMulticastAddress, + Ip6::Netif::AddressOrigin aAddressOrigin); void HandleNotifierEvents(Events aEvents); static void HandleTimer(Timer &aTimer); void HandleTimer(void); - EntryList mNetInfoHistory; - EntryList mRxHistory; - EntryList mTxHistory; - EntryList mNeighborHistory; - TimerMilli mTimer; + EntryList mNetInfoHistory; + EntryList mUnicastAddressHistory; + EntryList mMulticastAddressHistory; + EntryList mRxHistory; + EntryList mTxHistory; + EntryList mNeighborHistory; + + TimerMilli mTimer; }; } // namespace Utils