/* * 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 #include #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 "::." for hours, minutes, seconds and millisecond * (if shorter than one day) or "
days ::." (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`). 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 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 class EntryList : 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 mNetInfoHistory; EntryList mUnicastAddressHistory; EntryList mMulticastAddressHistory; 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 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_