blob: 3a0a2ee2dae8211174cc9b81e210d1c062934d9e [file] [log] [blame]
/*
* 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_