[history-tracker] track favored OMR prefix on a border router (#11775)

This commit adds support for tracking the history of the favored OMR
prefix on a border router. A new public API and the corresponding
`history omrprefix` CLI command are added to retrieve the recorded
history.

A new `otHistoryTrackerFavoredOmrPrefix` type is introduced to
represent an entry in the history. Each entry includes the OMR
prefix, its preference, and a boolean flag `mIsLocal` indicating
whether the prefix is the same as the one maintained locally by the
border router. The local OMR prefix can be based on either a randomly
generated ULA or a prefix delegated via DHCPv6-PD.
This commit is contained in:
Abtin Keshavarzian
2025-08-08 22:54:09 -07:00
committed by GitHub
parent e5d3f9e9c0
commit b09b791683
9 changed files with 226 additions and 4 deletions
+32
View File
@@ -289,6 +289,19 @@ typedef enum
OT_HISTORY_TRACKER_BORDER_AGENT_EPSKC_EVENT_DEACTIVATED_UNKNOWN, ///< Deactivated for an unknown reason.
} otHistoryTrackerBorderAgentEpskcEvent;
/**
* Represents a favored OMR prefix tracked by a device acting as a Border Router (BR).
*
* The `mIsLocal` field indicates whether the favored OMR prefix is the same as the local one maintained by this BR.
* The local OMR prefix can be either based on (random) ULA or a prefix delegated via DHCPv6-PD.
*/
typedef struct otHistoryTrackerFavoredOmrPrefix
{
otIp6Prefix mOmrPrefix; ///< The OMR prefix.
signed int mPreference : 2; ///< The 2-bit signed preference (`OT_ROUTE_PREFERENCE_*` values).
bool mIsLocal : 1; ///< `true` if the prefix is the local OMR prefix; `false` otherwise.
} otHistoryTrackerFavoredOmrPrefix;
/**
* Initializes an `otHistoryTrackerIterator`.
*
@@ -484,6 +497,25 @@ const otHistoryTrackerBorderAgentEpskcEvent *otHistoryTrackerIterateBorderAgentE
otHistoryTrackerIterator *aIterator,
uint32_t *aEntryAge);
/**
* Iterates over the entries in the favored OMR 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 entry was recorded to
* @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
* older than max age.
*
* @returns The `otHistoryTrackerFavoredOmrPrefix` entry or `NULL` if no more entries in the list.
*/
const otHistoryTrackerFavoredOmrPrefix *otHistoryTrackerIterateFavoredOmrPrefixHistory(
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 (523)
#define OPENTHREAD_API_VERSION (524)
/**
* @addtogroup api-instance
+35
View File
@@ -16,6 +16,7 @@ Usage : `history [command] ...`
- [ipmaddr](#ipmaddr)
- [neighbor](#neighbor)
- [netinfo](#netinfo)
- [omrprefix](#omrprefix)
- [prefix](#prefix)
- [route](#route)
- [router](#router)
@@ -292,6 +293,40 @@ Print only the latest 2 entries.
Done
```
### omrprefix
Usage `history omrprefix [list] [<num-entries>]`
Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE`.
Print the favored OMR prefix history. Each entry provides:
- The favored OMR prefix.
- Preference (`high`, `med`, `low`).
- IsLocal as boolean `yes`/`no` indicating whether the favored OMR prefix is the same as the local one maintained by this BR.
Print the OMR prefix history as a table.
```bash
> history omrprefix
| Age | OMR Prefix | Pref |IsLocal |
+----------------------+--------------------------------------------------+--------+--------+
| 00:00:10.110 | fd44:dc78:510b:1::/64 | low | yes |
| 00:06:17.604 | 2001:1a:12d5:23ae::/64 | med | no |
| 00:13:11.235 | fd44:dc78:510b:1::/64 | low | yes |
Done
```
Print the OMR prefix history as a list
```bash
> history omrprefix list
00:03:20.379 -> omr-prefix:fd44:dc78:510b:1::/64 prf:low is-local:yes
00:09:27.873 -> omr-prefix:2001:1a:12d5:23ae::/64 prf:med is-local:no
00:16:21.504 -> omr-prefix:fd44:dc78:510b:1::/64 prf:low is-local:yes
Done
```
### prefix
Usage `history prefix [list] [<num-entries>]`
+83 -3
View File
@@ -1539,6 +1539,83 @@ exit:
return error;
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
/**
* @cli history omrprefix
* @code
* history omrprefix
* | Age | OMR Prefix | Pref |IsLocal |
* +----------------------+--------------------------------------------------+--------+--------+
* | 00:00:10.110 | fd44:dc78:510b:1::/64 | low | yes |
* | 00:06:17.604 | 2001:1a:12d5:23ae::/64 | med | no |
* | 00:13:11.235 | fd44:dc78:510b:1::/64 | low | yes |
* Done
* @endcode
* @code
* history omrprefix list
* 00:03:20.379 -> omr-prefix:fd44:dc78:510b:1::/64 prf:low is-local:yes
* 00:09:27.873 -> omr-prefix:2001:1a:12d5:23ae::/64 prf:med is-local:no
* 00:16:21.504 -> omr-prefix:fd44:dc78:510b:1::/64 prf:low is-local:yes
* Done
* @endcode
* @cparam history omrprefix [@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 OMR 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`
* * OMR Prefix: The favored OMR prefix.
* * Preference: The prefix preference associated with the OMR prefix (`low`, `med`, `high`).
* * IsLocal: Indicates whether the favored OMR prefix is the same as the local one maintained by this BR.
* @sa otHistoryTrackerIterateFavoredOmrPrefixHistory
*/
template <> otError History::Process<Cmd("omrprefix")>(Arg aArgs[])
{
otError error;
bool isList;
uint16_t numEntries;
otHistoryTrackerIterator iterator;
const otHistoryTrackerFavoredOmrPrefix *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", "OMR Prefix", "Pref", "IsLocal"};
static const uint8_t kColumnWidths[] = {22, 50, 8, 8};
OutputTableHeader(kTitles, kColumnWidths);
}
otHistoryTrackerInitIterator(&iterator);
for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++)
{
info = otHistoryTrackerIterateFavoredOmrPrefixHistory(GetInstancePtr(), &iterator, &entryAge);
VerifyOrExit(info != nullptr);
otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString));
otIp6PrefixToString(&info->mOmrPrefix, prefixString, sizeof(prefixString));
OutputLine(isList ? "%s -> omr-prefix:%s prf:%s is-local:%s" : "| %20s | %-48s | %-6s | %-6s |", ageString,
prefixString, PreferenceToString(info->mPreference), info->mIsLocal ? "yes" : "no");
}
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
const char *History::DnsSrpAddrTypeToString(otHistoryTrackerDnsSrpAddrType aType)
{
static const char *const kAddrTypeStrings[] = {
@@ -1559,9 +1636,12 @@ 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("prefix"), CmdEntry("route"), CmdEntry("router"),
CmdEntry("rx"), CmdEntry("rxtx"), CmdEntry("tx"),
CmdEntry("dnssrpaddr"), CmdEntry("ipaddr"), CmdEntry("ipmaddr"), CmdEntry("neighbor"), CmdEntry("netinfo"),
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
CmdEntry("omrprefix"),
#endif
CmdEntry("prefix"), CmdEntry("route"), CmdEntry("router"), CmdEntry("rx"), CmdEntry("rxtx"),
CmdEntry("tx"),
};
#undef CmdEntry
+13
View File
@@ -152,6 +152,19 @@ const otHistoryTrackerBorderAgentEpskcEvent *otHistoryTrackerIterateBorderAgentE
}
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
const otHistoryTrackerFavoredOmrPrefix *otHistoryTrackerIterateFavoredOmrPrefixHistory(
otInstance *aInstance,
otHistoryTrackerIterator *aIterator,
uint32_t *aEntryAge)
{
AssertPointerIsNotNull(aEntryAge);
return AsCoreType(aInstance).Get<HistoryTracker::Local>().IterateFavoredOmrPrefixHistory(AsCoreType(aIterator),
*aEntryAge);
}
#endif
void otHistoryTrackerEntryAgeToString(uint32_t aEntryAge, char *aBuffer, uint16_t aSize)
{
HistoryTracker::Local::EntryAgeToString(aEntryAge, aBuffer, aSize);
@@ -2649,6 +2649,11 @@ void RoutingManager::OmrPrefixManager::SetFavoredPrefix(const OmrPrefix &aOmrPre
#endif
LogInfo("Favored OMR prefix: %s -> %s", FavoredToString(oldFavoredPrefix).AsCString(),
FavoredToString(mFavoredPrefix).AsCString());
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<HistoryTracker::Local>().RecordFavoredOmrPrefix(mFavoredPrefix.GetPrefix(), mFavoredPrefix.GetPreference(),
mFavoredPrefix.GetPrefix() == mLocalPrefix.GetPrefix());
#endif
}
}
+11
View File
@@ -182,6 +182,17 @@
#define OPENTHREAD_CONFIG_HISTORY_TRACKER_EPSKC_EVENT_SIZE 64
#endif
/**
* @def OPENTHREAD_CONFIG_HISTORY_TRACKER_OMR_PREFIX_LIST_SIZE
*
* Specifies the maximum number of entries in OMR Prefix history.
*
* Can be set to zero to configure History Tracker module not to collect any OMR prefix info.
*/
#ifndef OPENTHREAD_CONFIG_HISTORY_TRACKER_OMR_PREFIX_LIST_SIZE
#define OPENTHREAD_CONFIG_HISTORY_TRACKER_OMR_PREFIX_LIST_SIZE 16
#endif
/**
* @}
*/
+23
View File
@@ -577,6 +577,26 @@ exit:
}
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
void Local::RecordFavoredOmrPrefix(const Ip6::Prefix &aPrefix,
BorderRouter::RoutingManager::RoutePreference aPreference,
bool aIsLocal)
{
FavoredOmrPrefix *entry = mFavoredOmrPrefixHistory.AddNewEntry();
VerifyOrExit(entry != nullptr);
entry->mOmrPrefix = aPrefix;
entry->mPreference = NetworkData::RoutePreferenceToValue(aPreference);
entry->mIsLocal = aIsLocal;
exit:
return;
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
void Local::HandleNotifierEvents(Events aEvents)
{
if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadRlocAdded | kEventThreadRlocRemoved |
@@ -606,6 +626,9 @@ void Local::HandleTimer(void)
mDnsSrpAddrHistory.UpdateAgedEntries();
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
mEpskcEventHistory.UpdateAgedEntries();
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
mFavoredOmrPrefixHistory.UpdateAgedEntries();
#endif
mTimer.Start(kAgeCheckPeriod);
}
+23
View File
@@ -41,6 +41,7 @@
#include <openthread/history_tracker.h>
#include <openthread/platform/radio.h>
#include "border_router/routing_manager.hpp"
#include "common/as_core_type.hpp"
#include "common/clearable.hpp"
#include "common/locator.hpp"
@@ -109,6 +110,9 @@ typedef otHistoryTrackerDnsSrpAddrInfo DnsSrpAddrInfo; ///< Network
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
typedef otHistoryTrackerBorderAgentEpskcEvent EpskcEvent; ///< Border Agent ePSKc Event.
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
typedef otHistoryTrackerFavoredOmrPrefix FavoredOmrPrefix; ///< Favored OMR Prefix
#endif
/**
* This constant specifies the maximum age of entries which is 49 days (value in msec).
@@ -146,6 +150,9 @@ class Local : public InstanceLocator, private NonCopyable
friend class ot::MeshCoP::BorderAgent;
friend class ot::MeshCoP::BorderAgent::EphemeralKeyManager;
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
friend class ot::BorderRouter::RoutingManager;
#endif
public:
/**
@@ -267,6 +274,13 @@ public:
}
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
const FavoredOmrPrefix *IterateFavoredOmrPrefixHistory(Iterator &aIterator, uint32_t &aEntryAge) const
{
return mFavoredOmrPrefixHistory.Iterate(aIterator, aEntryAge);
}
#endif
/**
* Converts a given entry age to a human-readable string.
*
@@ -300,6 +314,7 @@ private:
static constexpr uint16_t kExternalRouteListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE;
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;
typedef otHistoryTrackerAddressEvent AddressEvent;
@@ -477,6 +492,11 @@ private:
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
void RecordEpskcEvent(EpskcEvent aEvent);
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
void RecordFavoredOmrPrefix(const Ip6::Prefix &aPrefix,
BorderRouter::RoutingManager::RoutePreference aPreference,
bool aIsLocal);
#endif
using TrackerTimer = TimerMilliIn<Local, &Local::HandleTimer>;
@@ -493,6 +513,9 @@ private:
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
EntryList<EpskcEvent, kEpskcEventListSize> mEpskcEventHistory;
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
EntryList<FavoredOmrPrefix, kOmrPrefixListSize> mFavoredOmrPrefixHistory;
#endif
TrackerTimer mTimer;