[history-tracker] add support for tracking favored on-link prefix (#11807)

This commit adds support to `HistoryTracker` to record the history of
favored on-link prefixes on the AIL.

It introduces the `otHistoryTrackerFavoredOnLinkPrefix` structure and
a new API, `otHistoryTrackerIterateFavoredOnLinkPrefixHistory()`, to
access the recorded data. The new CLI command `history onlinkprefix`
is added to display the tracked history, showing the prefix, its age,
and whether it is local to the Border Router.
This commit is contained in:
Abtin Keshavarzian
2025-08-15 11:03:31 -07:00
committed by GitHub
parent a3cd859502
commit 2d19b13aaf
10 changed files with 196 additions and 9 deletions
+31
View File
@@ -302,6 +302,18 @@ typedef struct otHistoryTrackerFavoredOmrPrefix
bool mIsLocal : 1; ///< `true` if the prefix is the local OMR prefix; `false` otherwise.
} otHistoryTrackerFavoredOmrPrefix;
/**
* Represents a favored on-link prefix on AIL tracked by a device acting as a Border Router (BR).
*
* The `mIsLocal` field indicates whether the favored on-link prefix is the same as the local one maintained by this
* BR.
*/
typedef struct otHistoryTrackerFavoredOnLinkPrefix
{
otIp6Prefix mOnLinkPrefix; ///< The on-link prefix.
bool mIsLocal : 1; ///< `true` if the prefix is the local on-link prefix; `false` otherwise.
} otHistoryTrackerFavoredOnLinkPrefix;
/**
* Initializes an `otHistoryTrackerIterator`.
*
@@ -516,6 +528,25 @@ const otHistoryTrackerFavoredOmrPrefix *otHistoryTrackerIterateFavoredOmrPrefixH
otHistoryTrackerIterator *aIterator,
uint32_t *aEntryAge);
/**
* Iterates over the entries in the favored on-link prefix history list.
*
* Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` (device acting as Border Router).
*
* @param[in] aInstance A pointer to the OpenThread instance.
* @param[in,out] 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 the entry was recorded to
* @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
* older than the max age.
*
* @returns The `otHistoryTrackerFavoredOnLinkPrefix` entry or `NULL` if no more entries in the list.
*/
const otHistoryTrackerFavoredOnLinkPrefix *otHistoryTrackerIterateFavoredOnLinkPrefixHistory(
otInstance *aInstance,
otHistoryTrackerIterator *aIterator,
uint32_t *aEntryAge);
/**
* Converts a given entry age to a human-readable string.
*
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
*
* @note This number versions both OpenThread platform and user APIs.
*/
#define OPENTHREAD_API_VERSION (525)
#define OPENTHREAD_API_VERSION (526)
/**
* @addtogroup api-instance
+33 -1
View File
@@ -17,6 +17,7 @@ Usage : `history [command] ...`
- [neighbor](#neighbor)
- [netinfo](#netinfo)
- [omrprefix](#omrprefix)
- [onlinkprefix](#onlinkprefix)
- [prefix](#prefix)
- [route](#route)
- [router](#router)
@@ -317,7 +318,7 @@ Print the OMR prefix history as a table.
Done
```
Print the OMR prefix history as a list
Print the OMR prefix history as a list.
```bash
> history omrprefix list
@@ -327,6 +328,37 @@ Print the OMR prefix history as a list
Done
```
### onlinkprefix
Usage `history onlinkprefix [list] [<num-entries>]`
Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE`.
Print the favored on-link prefix history. Each entry provides:
- The favored on-link prefix (on AIL).
- IsLocal as boolean `yes`/`no` indicating whether the favored on-link prefix is the same as the local one maintained by this BR.
Print the on-link prefix history as a table.
```bash
> history onlinkprefix
| Age | On-link Prefix |IsLocal |
+----------------------+--------------------------------------------------+--------+
| 00:00:50.600 | 2001:efc6:75a8:efee::/64 | no |
| 00:11:04.327 | fd74:fe69:9f21:437::/64 | yes |
Done
```
Print the on-link prefix history as a list.
```bash
> history onlinkprefix list
00:00:50.600 -> on-link-prefix:2001:efc6:75a8:efee::/64 is-local:no
00:11:04.327 -> on-link-prefix:fd74:fe69:9f21:437::/64 is-local:yes
Done
```
### prefix
Usage `history prefix [list] [<num-entries>]`
+75 -4
View File
@@ -1614,6 +1614,76 @@ exit:
return error;
}
/**
* @cli history onlinkprefix
* @code
* history onlinkprefix
* | Age | On-link Prefix |IsLocal |
* +----------------------+--------------------------------------------------+--------+
* | 00:00:50.600 | 2001:efc6:75a8:efee::/64 | no |
* | 00:11:04.327 | fd74:fe69:9f21:437::/64 | yes |
* Done
* @endcode
* @code
* history onlinkprefix list
* 00:00:50.600 -> on-link-prefix:2001:efc6:75a8:efee::/64 is-local:no
* 00:11:04.327 -> on-link-prefix:fd74:fe69:9f21:437::/64 is-local:yes
* Done
* @endcode
* @cparam history onlinkprefix [@ca{list}] [@ca{num-entries}]
* * Use the `list` option to display the output in list format. Otherwise, the output is shown in table format.
* * Use the `num-entries` option to limit the output to the number of most-recent entries specified. If this option
* is not used, all stored entries are shown in the output.
* @par
* Displays the favored on-link prefix history in table or list format.
* @par
* Each table or list entry provides:
* * Age: Time elapsed since the command was issued, and given in the format:
* `hours`:`minutes`:`seconds`.`milliseconds`
* * On-link Prefix: The favored on-link prefix on AIL.
* * IsLocal: Indicates whether the favored on-link prefix is the same as the local one maintained by this BR.
* @sa otHistoryTrackerIterateFavoredOnLinkPrefixHistory
*/
template <> otError History::Process<Cmd("onlinkprefix")>(Arg aArgs[])
{
otError error;
bool isList;
uint16_t numEntries;
otHistoryTrackerIterator iterator;
const otHistoryTrackerFavoredOnLinkPrefix *info;
uint32_t entryAge;
char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE];
char prefixString[OT_IP6_PREFIX_STRING_SIZE];
SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries));
if (!isList)
{
static const char *const kTitles[] = {"Age", "On-link Prefix", "IsLocal"};
static const uint8_t kColumnWidths[] = {22, 50, 8};
OutputTableHeader(kTitles, kColumnWidths);
}
otHistoryTrackerInitIterator(&iterator);
for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++)
{
info = otHistoryTrackerIterateFavoredOnLinkPrefixHistory(GetInstancePtr(), &iterator, &entryAge);
VerifyOrExit(info != nullptr);
otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString));
otIp6PrefixToString(&info->mOnLinkPrefix, prefixString, sizeof(prefixString));
OutputLine(isList ? "%s -> on-link-prefix:%s is-local:%s" : "| %20s | %-48s | %-6s |", ageString, prefixString,
info->mIsLocal ? "yes" : "no");
}
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
const char *History::DnsSrpAddrTypeToString(otHistoryTrackerDnsSrpAddrType aType)
@@ -1636,12 +1706,13 @@ otError History::Process(Arg aArgs[])
#define CmdEntry(aCommandString) {aCommandString, &History::Process<Cmd(aCommandString)>}
static constexpr Command kCommands[] = {
CmdEntry("dnssrpaddr"), CmdEntry("ipaddr"), CmdEntry("ipmaddr"), CmdEntry("neighbor"), CmdEntry("netinfo"),
CmdEntry("dnssrpaddr"), CmdEntry("ipaddr"), CmdEntry("ipmaddr"),
CmdEntry("neighbor"), CmdEntry("netinfo"),
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
CmdEntry("omrprefix"),
CmdEntry("omrprefix"), CmdEntry("onlinkprefix"),
#endif
CmdEntry("prefix"), CmdEntry("route"), CmdEntry("router"), CmdEntry("rx"), CmdEntry("rxtx"),
CmdEntry("tx"),
CmdEntry("prefix"), CmdEntry("route"), CmdEntry("router"),
CmdEntry("rx"), CmdEntry("rxtx"), CmdEntry("tx"),
};
#undef CmdEntry
+13
View File
@@ -153,6 +153,7 @@ const otHistoryTrackerBorderAgentEpskcEvent *otHistoryTrackerIterateBorderAgentE
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
const otHistoryTrackerFavoredOmrPrefix *otHistoryTrackerIterateFavoredOmrPrefixHistory(
otInstance *aInstance,
otHistoryTrackerIterator *aIterator,
@@ -163,6 +164,18 @@ const otHistoryTrackerFavoredOmrPrefix *otHistoryTrackerIterateFavoredOmrPrefixH
return AsCoreType(aInstance).Get<HistoryTracker::Local>().IterateFavoredOmrPrefixHistory(AsCoreType(aIterator),
*aEntryAge);
}
const otHistoryTrackerFavoredOnLinkPrefix *otHistoryTrackerIterateFavoredOnLinkPrefixHistory(
otInstance *aInstance,
otHistoryTrackerIterator *aIterator,
uint32_t *aEntryAge)
{
AssertPointerIsNotNull(aEntryAge);
return AsCoreType(aInstance).Get<HistoryTracker::Local>().IterateFavoredOnLinkPrefixHistory(AsCoreType(aIterator),
*aEntryAge);
}
#endif
void otHistoryTrackerEntryAgeToString(uint32_t aEntryAge, char *aBuffer, uint16_t aSize)
@@ -3072,6 +3072,10 @@ void RoutingManager::OnLinkPrefixManager::SetFavoredPrefix(const Ip6::Prefix &aP
mFavoredPrefix = aPrefix;
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<HistoryTracker::Local>().RecordFavoredOnLinkPrefix(mFavoredPrefix, mFavoredPrefix == mLocalPrefix);
#endif
exit:
return;
}
+11
View File
@@ -193,6 +193,17 @@
#define OPENTHREAD_CONFIG_HISTORY_TRACKER_OMR_PREFIX_LIST_SIZE 16
#endif
/**
* @def OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_LINK_PREFIX_LIST_SIZE
*
* Specifies the maximum number of entries in On-Link Prefix (on AIL) history.
*
* Can be set to zero to configure History Tracker module not to collect any on-link prefix info.
*/
#ifndef OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_LINK_PREFIX_LIST_SIZE
#define OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_LINK_PREFIX_LIST_SIZE 16
#endif
/**
* @}
*/
+14
View File
@@ -595,6 +595,19 @@ exit:
return;
}
void Local::RecordFavoredOnLinkPrefix(const Ip6::Prefix &aPrefix, bool aIsLocal)
{
FavoredOnLinkPrefix *entry = mFavoredOnLinkPrefixHistory.AddNewEntry();
VerifyOrExit(entry != nullptr);
entry->mOnLinkPrefix = aPrefix;
entry->mIsLocal = aIsLocal;
exit:
return;
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
void Local::HandleNotifierEvents(Events aEvents)
@@ -629,6 +642,7 @@ void Local::HandleTimer(void)
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
mFavoredOmrPrefixHistory.UpdateAgedEntries();
mFavoredOnLinkPrefixHistory.UpdateAgedEntries();
#endif
mTimer.Start(kAgeCheckPeriod);
}
+11 -2
View File
@@ -111,7 +111,8 @@ typedef otHistoryTrackerDnsSrpAddrInfo DnsSrpAddrInfo; ///< Network
typedef otHistoryTrackerBorderAgentEpskcEvent EpskcEvent; ///< Border Agent ePSKc Event.
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
typedef otHistoryTrackerFavoredOmrPrefix FavoredOmrPrefix; ///< Favored OMR Prefix
typedef otHistoryTrackerFavoredOmrPrefix FavoredOmrPrefix; ///< Favored OMR Prefix
typedef otHistoryTrackerFavoredOnLinkPrefix FavoredOnLinkPrefix; ///< Favored On-link Prefix
#endif
/**
@@ -279,6 +280,11 @@ public:
{
return mFavoredOmrPrefixHistory.Iterate(aIterator, aEntryAge);
}
const FavoredOnLinkPrefix *IterateFavoredOnLinkPrefixHistory(Iterator &aIterator, uint32_t &aEntryAge) const
{
return mFavoredOnLinkPrefixHistory.Iterate(aIterator, aEntryAge);
}
#endif
/**
@@ -315,6 +321,7 @@ private:
static constexpr uint16_t kDnsSrpAddrListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_DNSSRP_ADDR_LIST_SIZE;
static constexpr uint16_t kEpskcEventListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_EPSKC_EVENT_SIZE;
static constexpr uint16_t kOmrPrefixListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_OMR_PREFIX_LIST_SIZE;
static constexpr uint16_t kOnLinkPrefixListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_LINK_PREFIX_LIST_SIZE;
typedef otHistoryTrackerAddressEvent AddressEvent;
@@ -496,6 +503,7 @@ private:
void RecordFavoredOmrPrefix(const Ip6::Prefix &aPrefix,
BorderRouter::RoutingManager::RoutePreference aPreference,
bool aIsLocal);
void RecordFavoredOnLinkPrefix(const Ip6::Prefix &aPrefix, bool aIsLocal);
#endif
using TrackerTimer = TimerMilliIn<Local, &Local::HandleTimer>;
@@ -514,7 +522,8 @@ private:
EntryList<EpskcEvent, kEpskcEventListSize> mEpskcEventHistory;
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
EntryList<FavoredOmrPrefix, kOmrPrefixListSize> mFavoredOmrPrefixHistory;
EntryList<FavoredOmrPrefix, kOmrPrefixListSize> mFavoredOmrPrefixHistory;
EntryList<FavoredOnLinkPrefix, kOnLinkPrefixListSize> mFavoredOnLinkPrefixHistory;
#endif
TrackerTimer mTimer;
@@ -100,13 +100,15 @@ verify(br1_favored_onlink == br2_favored_onlink)
# Check that the two BRs discover and track each other (not as peer BR since
# connected to different networks).
for br in [br1, br1]:
for br in [br1, br2]:
routers = br.br_get_routers()
verify(len(routers) > 0)
for router in routers:
verify('reachable:yes' in router)
verify('S:1' in router)
verify(not router.endswith('(peer BR)'))
hist_list = br.cli('history onlinkprefix list')
verify(len(hist_list) == 1)
# -----------------------------------------------------------------------------------------------------------------------
# Test finished