1 /* 2 * Copyright (c) 2021, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @file 31 * This file includes definitions to support History Tracker module. 32 */ 33 34 #ifndef HISTORY_TRACKER_HPP_ 35 #define HISTORY_TRACKER_HPP_ 36 37 #include "openthread-core-config.h" 38 39 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE 40 41 #include <openthread/history_tracker.h> 42 #include <openthread/platform/radio.h> 43 44 #include "common/as_core_type.hpp" 45 #include "common/clearable.hpp" 46 #include "common/locator.hpp" 47 #include "common/non_copyable.hpp" 48 #include "common/notifier.hpp" 49 #include "common/timer.hpp" 50 #include "net/netif.hpp" 51 #include "net/socket.hpp" 52 #include "thread/mesh_forwarder.hpp" 53 #include "thread/mle.hpp" 54 #include "thread/mle_types.hpp" 55 #include "thread/neighbor_table.hpp" 56 #include "thread/network_data.hpp" 57 #include "thread/router_table.hpp" 58 59 namespace ot { 60 namespace Utils { 61 62 #ifdef OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA 63 #error "OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA should not be defined directly." \ 64 "It is derived from other configs: on-mesh prefix and external route history list sizes" 65 #endif 66 67 #define OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA \ 68 ((OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE > 0) || \ 69 (OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE > 0)) 70 71 /** 72 * Implements History Tracker. 73 * 74 */ 75 class HistoryTracker : public InstanceLocator, private NonCopyable 76 { 77 friend class ot::MeshForwarder; 78 friend class ot::Notifier; 79 friend class ot::Mle::Mle; 80 friend class ot::NeighborTable; 81 friend class ot::Ip6::Netif; 82 #if OPENTHREAD_FTD 83 friend class ot::RouterTable; 84 #endif 85 86 public: 87 /** 88 * This constant specifies the maximum age of entries which is 49 days (value in msec). 89 * 90 * Entries older than the max age will give this value as their age. 91 * 92 */ 93 static constexpr uint32_t kMaxAge = OT_HISTORY_TRACKER_MAX_AGE; 94 95 /** 96 * This constant specifies the recommend string size to represent an entry age 97 * 98 */ 99 static constexpr uint16_t kEntryAgeStringSize = OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE; 100 101 /** 102 * This constants specified no next hop. 103 * 104 * Used for `mNextHop` in `RouteInfo` structure. 105 * 106 */ 107 static constexpr uint8_t kNoNextHop = OT_HISTORY_TRACKER_NO_NEXT_HOP; 108 109 /** 110 * Represents an iterator to iterate through a history list. 111 * 112 */ 113 class Iterator : public otHistoryTrackerIterator 114 { 115 friend class HistoryTracker; 116 117 public: 118 /** 119 * Initializes an `Iterator` 120 * 121 * An iterator MUST be initialized before it is used. An iterator can be initialized again to start from 122 * the beginning of the list. 123 * 124 */ Init(void)125 void Init(void) { ResetEntryNumber(), SetInitTime(); } 126 127 private: GetEntryNumber(void) const128 uint16_t GetEntryNumber(void) const { return mData16; } ResetEntryNumber(void)129 void ResetEntryNumber(void) { mData16 = 0; } IncrementEntryNumber(void)130 void IncrementEntryNumber(void) { mData16++; } GetInitTime(void) const131 TimeMilli GetInitTime(void) const { return TimeMilli(mData32); } SetInitTime(void)132 void SetInitTime(void) { mData32 = TimerMilli::GetNow().GetValue(); } 133 }; 134 135 typedef otHistoryTrackerNetworkInfo NetworkInfo; ///< Thread network info. 136 typedef otHistoryTrackerUnicastAddressInfo UnicastAddressInfo; ///< Unicast IPv6 address info. 137 typedef otHistoryTrackerMulticastAddressInfo MulticastAddressInfo; ///< Multicast IPv6 address info. 138 typedef otHistoryTrackerMessageInfo MessageInfo; ///< RX/TX IPv6 message info. 139 typedef otHistoryTrackerNeighborInfo NeighborInfo; ///< Neighbor info. 140 typedef otHistoryTrackerRouterInfo RouterInfo; ///< Router info. 141 typedef otHistoryTrackerOnMeshPrefixInfo OnMeshPrefixInfo; ///< Network Data on mesh prefix info. 142 typedef otHistoryTrackerExternalRouteInfo ExternalRouteInfo; ///< Network Data external route info 143 144 /** 145 * Initializes the `HistoryTracker`. 146 * 147 * @param[in] aInstance A reference to the OpenThread instance. 148 * 149 */ 150 explicit HistoryTracker(Instance &aInstance); 151 152 /** 153 * Iterates over the entries in the network info history list. 154 * 155 * @param[in,out] aIterator An iterator. MUST be initialized. 156 * @param[out] aEntryAge A reference to a variable to output the entry's age. 157 * Age is provided as the duration (in milliseconds) from when entry was recorded to 158 * @p aIterator initialization time. It is set to `kMaxAge` for entries older than max 159 * age. 160 * 161 * @returns A pointer to `NetworkInfo` entry or `nullptr` if no more entries in the list. 162 * 163 */ IterateNetInfoHistory(Iterator & aIterator,uint32_t & aEntryAge) const164 const NetworkInfo *IterateNetInfoHistory(Iterator &aIterator, uint32_t &aEntryAge) const 165 { 166 return mNetInfoHistory.Iterate(aIterator, aEntryAge); 167 } 168 169 /** 170 * Iterates over the entries in the unicast address history list. 171 * 172 * @param[in,out] aIterator An iterator. MUST be initialized. 173 * @param[out] aEntryAge A reference to a variable to output the entry's age. 174 * Age is provided as the duration (in milliseconds) from when entry was recorded to 175 * @p aIterator initialization time. It is set to `kMaxAge` for entries older than max 176 * age. 177 * 178 * @returns A pointer to `UnicastAddress` entry or `nullptr` if no more entries in the list. 179 * 180 */ IterateUnicastAddressHistory(Iterator & aIterator,uint32_t & aEntryAge) const181 const UnicastAddressInfo *IterateUnicastAddressHistory(Iterator &aIterator, uint32_t &aEntryAge) const 182 { 183 return mUnicastAddressHistory.Iterate(aIterator, aEntryAge); 184 } 185 186 /** 187 * Iterates over the entries in the multicast address history list. 188 * 189 * @param[in,out] aIterator An iterator. MUST be initialized. 190 * @param[out] aEntryAge A reference to a variable to output the entry's age. 191 * Age is provided as the duration (in milliseconds) from when entry was recorded to 192 * @p aIterator initialization time. It is set to `kMaxAge` for entries older than max 193 * age. 194 * 195 * @returns A pointer to `MulticastAddress` entry or `nullptr` if no more entries in the list. 196 * 197 */ IterateMulticastAddressHistory(Iterator & aIterator,uint32_t & aEntryAge) const198 const MulticastAddressInfo *IterateMulticastAddressHistory(Iterator &aIterator, uint32_t &aEntryAge) const 199 { 200 return mMulticastAddressHistory.Iterate(aIterator, aEntryAge); 201 } 202 203 /** 204 * Iterates over the entries in the RX history list. 205 * 206 * @param[in,out] aIterator An iterator. MUST be initialized. 207 * @param[out] aEntryAge A reference to a variable to output the entry's age. 208 * Age is provided as the duration (in milliseconds) from when entry was recorded to 209 * @p aIterator initialization time. It is set to `kMaxAge` for entries older than max 210 * age. 211 * 212 * @returns A pointer to `MessageInfo` entry or `nullptr` if no more entries in the list. 213 * 214 */ IterateRxHistory(Iterator & aIterator,uint32_t & aEntryAge) const215 const MessageInfo *IterateRxHistory(Iterator &aIterator, uint32_t &aEntryAge) const 216 { 217 return mRxHistory.Iterate(aIterator, aEntryAge); 218 } 219 220 /** 221 * Iterates over the entries in the TX history list. 222 * 223 * @param[in,out] aIterator An iterator. MUST be initialized. 224 * @param[out] aEntryAge A reference to a variable to output the entry's age. 225 * Age is provided as the duration (in milliseconds) from when entry was recorded to 226 * @p aIterator initialization time. It is set to `kMaxAge` for entries older than max 227 * age. 228 * 229 * @returns A pointer to `MessageInfo` entry or `nullptr` if no more entries in the list. 230 * 231 */ IterateTxHistory(Iterator & aIterator,uint32_t & aEntryAge) const232 const MessageInfo *IterateTxHistory(Iterator &aIterator, uint32_t &aEntryAge) const 233 { 234 return mTxHistory.Iterate(aIterator, aEntryAge); 235 } 236 IterateNeighborHistory(Iterator & aIterator,uint32_t & aEntryAge) const237 const NeighborInfo *IterateNeighborHistory(Iterator &aIterator, uint32_t &aEntryAge) const 238 { 239 return mNeighborHistory.Iterate(aIterator, aEntryAge); 240 } 241 IterateRouterHistory(Iterator & aIterator,uint32_t & aEntryAge) const242 const RouterInfo *IterateRouterHistory(Iterator &aIterator, uint32_t &aEntryAge) const 243 { 244 return mRouterHistory.Iterate(aIterator, aEntryAge); 245 } 246 IterateOnMeshPrefixHistory(Iterator & aIterator,uint32_t & aEntryAge) const247 const OnMeshPrefixInfo *IterateOnMeshPrefixHistory(Iterator &aIterator, uint32_t &aEntryAge) const 248 { 249 return mOnMeshPrefixHistory.Iterate(aIterator, aEntryAge); 250 } 251 IterateExternalRouteHistory(Iterator & aIterator,uint32_t & aEntryAge) const252 const ExternalRouteInfo *IterateExternalRouteHistory(Iterator &aIterator, uint32_t &aEntryAge) const 253 { 254 return mExternalRouteHistory.Iterate(aIterator, aEntryAge); 255 } 256 257 /** 258 * Converts a given entry age to a human-readable string. 259 * 260 * The entry age string follows the format "<hh>:<mm>:<ss>.<mmmm>" for hours, minutes, seconds and millisecond 261 * (if shorter than one day) or "<dd> days <hh>:<mm>:<ss>.<mmmm>" (if longer than one day). 262 * 263 * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be 264 * truncated but the outputted string is always null-terminated. 265 * 266 * @param[in] aEntryAge The entry age (duration in msec). 267 * @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be NULL). 268 * @param[in] aSize The size of @p aBuffer (in bytes). Recommended to use `OT_IP6_ADDRESS_STRING_SIZE`. 269 * 270 */ 271 static void EntryAgeToString(uint32_t aEntryAge, char *aBuffer, uint16_t aSize); 272 273 private: 274 // `Timestamp` uses `uint32_t` value. `2^32` msec is 49 days, 17 275 // hours, 2 minutes and 47 seconds and 296 msec. We use 49 days 276 // as `kMaxAge` and check for aged entries every 16 hours. 277 278 static constexpr uint32_t kAgeCheckPeriod = 16 * Time::kOneHourInMsec; 279 280 static constexpr uint16_t kNetInfoListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_INFO_LIST_SIZE; 281 static constexpr uint16_t kUnicastAddrListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_UNICAST_ADDRESS_LIST_SIZE; 282 static constexpr uint16_t kMulticastAddrListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_MULTICAST_ADDRESS_LIST_SIZE; 283 static constexpr uint16_t kRxListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_RX_LIST_SIZE; 284 static constexpr uint16_t kTxListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_TX_LIST_SIZE; 285 static constexpr uint16_t kNeighborListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_NEIGHBOR_LIST_SIZE; 286 static constexpr uint16_t kRouterListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE; 287 static constexpr uint16_t kOnMeshPrefixListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE; 288 static constexpr uint16_t kExternalRouteListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE; 289 290 typedef otHistoryTrackerAddressEvent AddressEvent; 291 292 static constexpr AddressEvent kAddressAdded = OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED; 293 static constexpr AddressEvent kAddressRemoved = OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED; 294 295 static constexpr uint16_t kInvalidRloc16 = Mac::kShortAddrInvalid; 296 297 typedef otHistoryTrackerNeighborEvent NeighborEvent; 298 299 static constexpr NeighborEvent kNeighborAdded = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_ADDED; 300 static constexpr NeighborEvent kNeighborRemoved = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_REMOVED; 301 static constexpr NeighborEvent kNeighborChanged = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_CHANGED; 302 static constexpr NeighborEvent kNeighborRestoring = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_RESTORING; 303 304 typedef otHistoryTrackerRouterEvent RouterEvent; 305 306 static constexpr RouterEvent kRouterAdded = OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED; 307 static constexpr RouterEvent kRouterRemoved = OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED; 308 static constexpr RouterEvent kRouterNextHopChanged = OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED; 309 static constexpr RouterEvent kRouterCostChanged = OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED; 310 311 typedef otHistoryTrackerNetDataEvent NetDataEvent; 312 313 static constexpr NetDataEvent kNetDataEntryAdded = OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED; 314 static constexpr NetDataEvent kNetDataEntryRemoved = OT_HISTORY_TRACKER_NET_DATA_ENTRY_REMOVED; 315 316 class Timestamp 317 { 318 public: 319 void SetToNow(void); 320 uint32_t GetDurationTill(TimeMilli aTime) const; IsDistantPast(void) const321 bool IsDistantPast(void) const { return (mTime.GetValue() == kDistantPast); } MarkAsDistantPast(void)322 void MarkAsDistantPast(void) { return mTime.SetValue(kDistantPast); } 323 324 private: 325 static constexpr uint32_t kDistantPast = 0; 326 327 TimeMilli mTime; 328 }; 329 330 // An ordered list of timestamped items (base class of `EntryList<Entry, kSize>`). 331 class List : private NonCopyable 332 { 333 public: 334 void Clear(void); GetSize(void) const335 uint16_t GetSize(void) const { return mSize; } 336 337 protected: 338 List(void); 339 uint16_t Add(uint16_t aMaxSize, Timestamp aTimestamps[]); 340 void UpdateAgedEntries(uint16_t aMaxSize, Timestamp aTimestamps[]); 341 uint16_t MapEntryNumberToListIndex(uint16_t aEntryNumber, uint16_t aMaxSize) const; 342 Error Iterate(uint16_t aMaxSize, 343 const Timestamp aTimestamps[], 344 Iterator &aIterator, 345 uint16_t &aListIndex, 346 uint32_t &aEntryAge) const; 347 348 private: 349 uint16_t mStartIndex; 350 uint16_t mSize; 351 }; 352 353 // A history list (with given max size) of timestamped `Entry` items. 354 template <typename Entry, uint16_t kMaxSize> class EntryList : public List 355 { 356 public: 357 // Adds a new entry to the list or overwrites the oldest entry 358 // if list is full. First version returns a pointer to the 359 // new `Entry` (for caller to populate). Second version copies 360 // the given `aEntry`. AddNewEntry(void)361 Entry *AddNewEntry(void) { return &mEntries[Add(kMaxSize, mTimestamps)]; } AddNewEntry(const Entry & aEntry)362 void AddNewEntry(const Entry &aEntry) { mEntries[Add(kMaxSize, mTimestamps)] = aEntry; } 363 UpdateAgedEntries(void)364 void UpdateAgedEntries(void) { List::UpdateAgedEntries(kMaxSize, mTimestamps); } 365 Iterate(Iterator & aIterator,uint32_t & aEntryAge) const366 const Entry *Iterate(Iterator &aIterator, uint32_t &aEntryAge) const 367 { 368 uint16_t index; 369 370 return (List::Iterate(kMaxSize, mTimestamps, aIterator, index, aEntryAge) == kErrorNone) ? &mEntries[index] 371 : nullptr; 372 } 373 374 private: 375 Timestamp mTimestamps[kMaxSize]; 376 Entry mEntries[kMaxSize]; 377 }; 378 379 // Partial specialization for `kMaxSize` zero. 380 template <typename Entry> class EntryList<Entry, 0> : private NonCopyable 381 { 382 public: Clear(void)383 void Clear(void) {} GetSize(void) const384 uint16_t GetSize(void) const { return 0; } AddNewEntry(void)385 Entry *AddNewEntry(void) { return nullptr; } AddNewEntry(const Entry &)386 void AddNewEntry(const Entry &) {} Iterate(Iterator &,uint32_t &) const387 const Entry *Iterate(Iterator &, uint32_t &) const { return nullptr; } RemoveAgedEntries(void)388 void RemoveAgedEntries(void) {} 389 }; 390 391 enum MessageType : uint8_t 392 { 393 kRxMessage, 394 kTxMessage, 395 }; 396 RecordRxMessage(const Message & aMessage,const Mac::Address & aMacSource)397 void RecordRxMessage(const Message &aMessage, const Mac::Address &aMacSource) 398 { 399 RecordMessage(aMessage, aMacSource, kRxMessage); 400 } 401 RecordTxMessage(const Message & aMessage,const Mac::Address & aMacDest)402 void RecordTxMessage(const Message &aMessage, const Mac::Address &aMacDest) 403 { 404 RecordMessage(aMessage, aMacDest, kTxMessage); 405 } 406 407 void RecordNetworkInfo(void); 408 void RecordMessage(const Message &aMessage, const Mac::Address &aMacAddress, MessageType aType); 409 void RecordNeighborEvent(NeighborTable::Event aEvent, const NeighborTable::EntryInfo &aInfo); 410 void RecordAddressEvent(Ip6::Netif::AddressEvent aEvent, const Ip6::Netif::UnicastAddress &aUnicastAddress); 411 void RecordAddressEvent(Ip6::Netif::AddressEvent aEvent, 412 const Ip6::Netif::MulticastAddress &aMulticastAddress, 413 Ip6::Netif::AddressOrigin aAddressOrigin); 414 void HandleNotifierEvents(Events aEvents); 415 void HandleTimer(void); 416 #if OPENTHREAD_FTD 417 void RecordRouterTableChange(void); 418 #endif 419 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA 420 void RecordNetworkDataChange(void); 421 void RecordOnMeshPrefixEvent(NetDataEvent aEvent, const NetworkData::OnMeshPrefixConfig &aPrefix); 422 void RecordExternalRouteEvent(NetDataEvent aEvent, const NetworkData::ExternalRouteConfig &aRoute); 423 #endif 424 425 using TrackerTimer = TimerMilliIn<HistoryTracker, &HistoryTracker::HandleTimer>; 426 427 EntryList<NetworkInfo, kNetInfoListSize> mNetInfoHistory; 428 EntryList<UnicastAddressInfo, kUnicastAddrListSize> mUnicastAddressHistory; 429 EntryList<MulticastAddressInfo, kMulticastAddrListSize> mMulticastAddressHistory; 430 EntryList<MessageInfo, kRxListSize> mRxHistory; 431 EntryList<MessageInfo, kTxListSize> mTxHistory; 432 EntryList<NeighborInfo, kNeighborListSize> mNeighborHistory; 433 EntryList<RouterInfo, kRouterListSize> mRouterHistory; 434 EntryList<OnMeshPrefixInfo, kOnMeshPrefixListSize> mOnMeshPrefixHistory; 435 EntryList<ExternalRouteInfo, kExternalRouteListSize> mExternalRouteHistory; 436 437 TrackerTimer mTimer; 438 439 #if OPENTHREAD_FTD && (OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0) 440 struct RouterEntry 441 { 442 bool mIsAllocated : 1; 443 uint8_t mNextHop : 6; 444 uint8_t mPathCost : 4; 445 }; 446 447 RouterEntry mRouterEntries[Mle::kMaxRouterId + 1]; 448 #endif 449 450 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA 451 NetworkData::MutableNetworkData mPreviousNetworkData; 452 453 uint8_t mNetworkDataTlvBuffer[NetworkData::NetworkData::kMaxSize]; 454 #endif 455 }; 456 457 } // namespace Utils 458 459 DefineCoreType(otHistoryTrackerIterator, Utils::HistoryTracker::Iterator); 460 DefineCoreType(otHistoryTrackerNetworkInfo, Utils::HistoryTracker::NetworkInfo); 461 DefineCoreType(otHistoryTrackerMessageInfo, Utils::HistoryTracker::MessageInfo); 462 DefineCoreType(otHistoryTrackerNeighborInfo, Utils::HistoryTracker::NeighborInfo); 463 DefineCoreType(otHistoryTrackerRouterInfo, Utils::HistoryTracker::RouterInfo); 464 DefineCoreType(otHistoryTrackerOnMeshPrefixInfo, Utils::HistoryTracker::OnMeshPrefixInfo); 465 DefineCoreType(otHistoryTrackerExternalRouteInfo, Utils::HistoryTracker::ExternalRouteInfo); 466 467 } // namespace ot 468 469 #endif // OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE 470 471 #endif // HISTORY_TRACKER_HPP_ 472