| /* |
| * Copyright (c) 2021, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * This file includes definitions to support History Tracker module. |
| */ |
| |
| #ifndef HISTORY_TRACKER_HPP_ |
| #define HISTORY_TRACKER_HPP_ |
| |
| #include "openthread-core-config.h" |
| |
| #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE |
| |
| #include <openthread/history_tracker.h> |
| #include <openthread/platform/radio.h> |
| |
| #include "common/as_core_type.hpp" |
| #include "common/clearable.hpp" |
| #include "common/locator.hpp" |
| #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" |
| #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. |
| * |
| */ |
| class HistoryTracker : public InstanceLocator, private NonCopyable |
| { |
| friend class ot::MeshForwarder; |
| friend class ot::Notifier; |
| friend class ot::Mle::Mle; |
| friend class ot::NeighborTable; |
| friend class ot::Ip6::Netif; |
| |
| public: |
| /** |
| * This constant specifies the maximum age of entries which is 49 days (value in msec). |
| * |
| * Entries older than the max age will give this value as their age. |
| * |
| */ |
| static constexpr uint32_t kMaxAge = OT_HISTORY_TRACKER_MAX_AGE; |
| |
| /** |
| * This constant specifies the recommend string size to represent an entry age |
| * |
| */ |
| static constexpr uint16_t kEntryAgeStringSize = OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE; |
| |
| /** |
| * This type represents an iterator to iterate through a history list. |
| * |
| */ |
| class Iterator : public otHistoryTrackerIterator |
| { |
| friend class HistoryTracker; |
| |
| public: |
| /** |
| * This method initializes an `Iterator` |
| * |
| * An iterator MUST be initialized before it is used. An iterator can be initialized again to start from |
| * the beginning of the list. |
| * |
| */ |
| void Init(void) { ResetEntryNumber(), SetInitTime(); } |
| |
| private: |
| uint16_t GetEntryNumber(void) const { return mData16; } |
| void ResetEntryNumber(void) { mData16 = 0; } |
| void IncrementEntryNumber(void) { mData16++; } |
| TimeMilli GetInitTime(void) const { return TimeMilli(mData32); } |
| void SetInitTime(void) { mData32 = TimerMilli::GetNow().GetValue(); } |
| }; |
| |
| 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`. |
| * |
| * @param[in] aInstance A reference to the OpenThread instance. |
| * |
| */ |
| explicit HistoryTracker(Instance &aInstance); |
| |
| /** |
| * This method iterates over the entries in the network info history list. |
| * |
| * @param[in,out] 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 `NetworkInfo` entry or `nullptr` if no more entries in the list. |
| * |
| */ |
| const NetworkInfo *IterateNetInfoHistory(Iterator &aIterator, uint32_t &aEntryAge) const |
| { |
| return mNetInfoHistory.Iterate(aIterator, aEntryAge); |
| } |
| |
| /** |
| * This method iterates over the entries in the unicast address history list. |
| * |
| * @param[in,out] 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[in,out] 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. |
| * |
| * @param[in,out] 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 `MessageInfo` entry or `nullptr` if no more entries in the list. |
| * |
| */ |
| const MessageInfo *IterateRxHistory(Iterator &aIterator, uint32_t &aEntryAge) const |
| { |
| return mRxHistory.Iterate(aIterator, aEntryAge); |
| } |
| |
| /** |
| * This method iterates over the entries in the TX history list. |
| * |
| * @param[in,out] 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 `MessageInfo` entry or `nullptr` if no more entries in the list. |
| * |
| */ |
| const MessageInfo *IterateTxHistory(Iterator &aIterator, uint32_t &aEntryAge) const |
| { |
| return mTxHistory.Iterate(aIterator, aEntryAge); |
| } |
| |
| const NeighborInfo *IterateNeighborHistory(Iterator &aIterator, uint32_t &aEntryAge) const |
| { |
| 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. |
| * |
| * The entry age string follows the format "<hh>:<mm>:<ss>.<mmmm>" for hours, minutes, seconds and millisecond |
| * (if shorter than one day) or "<dd> days <hh>:<mm>:<ss>.<mmmm>" (if longer than one day). |
| * |
| * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be |
| * truncated but the outputted string is always null-terminated. |
| * |
| * @param[in] aEntryAge The entry age (duration in msec). |
| * @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be NULL). |
| * @param[in] aSize The size of @p aBuffer (in bytes). Recommended to use `OT_IP6_ADDRESS_STRING_SIZE`. |
| * |
| */ |
| static void EntryAgeToString(uint32_t aEntryAge, char *aBuffer, uint16_t aSize); |
| |
| private: |
| // `Timestamp` uses `uint32_t` value. `2^32` msec is 49 days, 17 |
| // hours, 2 minutes and 47 seconds and 296 msec. We use 49 days |
| // as `kMaxAge` and check for aged entries every 16 hours. |
| |
| static constexpr uint32_t kAgeCheckPeriod = 16 * Time::kOneHourInMsec; |
| |
| 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; |
| 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; |
| |
| 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; |
| |
| typedef otHistoryTrackerNeighborEvent NeighborEvent; |
| |
| static constexpr NeighborEvent kNeighborAdded = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_ADDED; |
| static constexpr NeighborEvent kNeighborRemoved = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_REMOVED; |
| 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: |
| void SetToNow(void); |
| uint32_t GetDurationTill(TimeMilli aTime) const; |
| bool IsDistantPast(void) const { return (mTime.GetValue() == kDistantPast); } |
| void MarkAsDistantPast(void) { return mTime.SetValue(kDistantPast); } |
| |
| private: |
| static constexpr uint32_t kDistantPast = 0; |
| |
| TimeMilli mTime; |
| }; |
| |
| // An ordered list of timestamped items (base class of `EntryList<Entry, kSize>`). |
| class List : private NonCopyable |
| { |
| public: |
| void Clear(void); |
| uint16_t GetSize(void) const { return mSize; } |
| |
| protected: |
| List(void); |
| uint16_t Add(uint16_t aMaxSize, Timestamp aTimestamps[]); |
| void UpdateAgedEntries(uint16_t aMaxSize, Timestamp aTimestamps[]); |
| uint16_t MapEntryNumberToListIndex(uint16_t aEntryNumber, uint16_t aMaxSize) const; |
| Error Iterate(uint16_t aMaxSize, |
| const Timestamp aTimestamps[], |
| Iterator & aIterator, |
| uint16_t & aListIndex, |
| uint32_t & aEntryAge) const; |
| |
| private: |
| uint16_t mStartIndex; |
| uint16_t mSize; |
| }; |
| |
| // A history list (with given max size) of timestamped `Entry` items. |
| template <typename Entry, uint16_t kMaxSize> class EntryList : public List |
| { |
| public: |
| // Adds a new entry to the list or overwrites the oldest entry |
| // if list is full. First version returns a pointer to the |
| // new `Entry` (for caller to populate). Second version copies |
| // the given `aEntry`. |
| Entry *AddNewEntry(void) { return &mEntries[Add(kMaxSize, mTimestamps)]; } |
| void AddNewEntry(const Entry &aEntry) { mEntries[Add(kMaxSize, mTimestamps)] = aEntry; } |
| |
| void UpdateAgedEntries(void) { List::UpdateAgedEntries(kMaxSize, mTimestamps); } |
| |
| const Entry *Iterate(Iterator &aIterator, uint32_t &aEntryAge) const |
| { |
| uint16_t index; |
| |
| return (List::Iterate(kMaxSize, mTimestamps, aIterator, index, aEntryAge) == kErrorNone) ? &mEntries[index] |
| : nullptr; |
| } |
| |
| private: |
| Timestamp mTimestamps[kMaxSize]; |
| Entry mEntries[kMaxSize]; |
| }; |
| |
| // Partial specialization for `kMaxSize` zero. |
| template <typename Entry> class EntryList<Entry, 0> : private NonCopyable |
| { |
| public: |
| void Clear(void) {} |
| uint16_t GetSize(void) const { return 0; } |
| Entry * AddNewEntry(void) { return nullptr; } |
| void AddNewEntry(const Entry &) {} |
| const Entry *Iterate(Iterator &, uint32_t &) const { return nullptr; } |
| void RemoveAgedEntries(void) {} |
| }; |
| |
| enum MessageType : uint8_t |
| { |
| kRxMessage, |
| kTxMessage, |
| }; |
| |
| void RecordRxMessage(const Message &aMessage, const Mac::Address &aMacSource) |
| { |
| RecordMessage(aMessage, aMacSource, kRxMessage); |
| } |
| |
| void RecordTxMessage(const Message &aMessage, const Mac::Address &aMacDest) |
| { |
| RecordMessage(aMessage, aMacDest, kTxMessage); |
| } |
| |
| 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); |
| #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<NetworkInfo, kNetInfoListSize> mNetInfoHistory; |
| EntryList<UnicastAddressInfo, kUnicastAddrListSize> mUnicastAddressHistory; |
| EntryList<MulticastAddressInfo, kMulticastAddrListSize> mMulticastAddressHistory; |
| EntryList<MessageInfo, kRxListSize> mRxHistory; |
| EntryList<MessageInfo, kTxListSize> mTxHistory; |
| EntryList<NeighborInfo, kNeighborListSize> mNeighborHistory; |
| EntryList<OnMeshPrefixInfo, kOnMeshPrefixListSize> mOnMeshPrefixHistory; |
| EntryList<ExternalRouteInfo, kExternalRouteListSize> mExternalRouteHistory; |
| |
| TimerMilli mTimer; |
| |
| #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA |
| NetworkData::MutableNetworkData mPreviousNetworkData; |
| |
| uint8_t mNetworkDataTlvBuffer[NetworkData::NetworkData::kMaxSize]; |
| #endif |
| }; |
| |
| } // namespace Utils |
| |
| 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 |
| |
| #endif // OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE |
| |
| #endif // HISTORY_TRACKER_HPP_ |