From 6f3b4317d61cb46bda4a4c1c398ff3369c34a778 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 20 Nov 2023 11:49:26 -0800 Subject: [PATCH] [routing-manager] new API to get list of discovered routers (#9601) This commit adds `otBorderRoutingGetNextRouterEntry` as a new API to iterate over discovered routers on the infrastructure link and get information about them such as their address, their Managed Address Configuration (`M`), Other Configuration (`O`), and Stub Router flags. The `otBorderRoutingPrefixTableEntry` is also updated to contain the same information. This commit also updates the CLI `br` sub-commands to provide this information, adding `br routers` to obtain a list of discovered routers. --- include/openthread/border_routing.h | 46 +++++++++--- include/openthread/instance.h | 2 +- src/cli/README_BR.md | 40 ++++++++++- src/cli/cli_br.cpp | 71 ++++++++++++++++--- src/cli/cli_br.hpp | 3 + src/core/api/border_routing_api.cpp | 10 +++ src/core/border_router/routing_manager.cpp | 82 +++++++++++++++++----- src/core/border_router/routing_manager.hpp | 35 ++++++++- tests/unit/test_routing_manager.cpp | 8 +-- 9 files changed, 253 insertions(+), 44 deletions(-) diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h index 36e83a6d0..3569a9e44 100644 --- a/include/openthread/border_routing.h +++ b/include/openthread/border_routing.h @@ -88,22 +88,34 @@ typedef struct otBorderRoutingPrefixTableIterator uint32_t mData32; } otBorderRoutingPrefixTableIterator; +/** + * Represents a discovered router on the infrastructure link. + * + */ +typedef struct otBorderRoutingRouterEntry +{ + otIp6Address mAddress; ///< IPv6 address of the router. + bool mManagedAddressConfigFlag : 1; ///< The router's Managed Address Config flag (`M` flag). + bool mOtherConfigFlag : 1; ///< The router's Other Config flag (`O` flag). + bool mStubRouterFlag : 1; ///< The router's Stub Router flag. +} otBorderRoutingRouterEntry; + /** * Represents an entry from the discovered prefix table. * * The entries in the discovered table track the Prefix/Route Info Options in the received Router Advertisement messages - * from other routers on infrastructure link. + * from other routers on the infrastructure link. * */ typedef struct otBorderRoutingPrefixTableEntry { - otIp6Address mRouterAddress; ///< IPv6 address of the router. - otIp6Prefix mPrefix; ///< The discovered IPv6 prefix. - bool mIsOnLink; ///< Indicates whether the prefix is on-link or route prefix. - uint32_t mMsecSinceLastUpdate; ///< Milliseconds since last update of this prefix. - uint32_t mValidLifetime; ///< Valid lifetime of the prefix (in seconds). - otRoutePreference mRoutePreference; ///< Route preference when `mIsOnlink` is false. - uint32_t mPreferredLifetime; ///< Preferred lifetime of the on-link prefix when `mIsOnLink` is true. + otBorderRoutingRouterEntry mRouter; ///< Information about the router advertising this prefix. + otIp6Prefix mPrefix; ///< The discovered IPv6 prefix. + bool mIsOnLink; ///< Indicates whether the prefix is on-link or route prefix. + uint32_t mMsecSinceLastUpdate; ///< Milliseconds since last update of this prefix. + uint32_t mValidLifetime; ///< Valid lifetime of the prefix (in seconds). + otRoutePreference mRoutePreference; ///< Route preference when `mIsOnlink` is false. + uint32_t mPreferredLifetime; ///< Preferred lifetime of the on-link prefix when `mIsOnLink`. } otBorderRoutingPrefixTableEntry; /** @@ -391,6 +403,9 @@ void otBorderRoutingPrefixTableInitIterator(otInstance *aInstance, otBorderRouti /** * Iterates over the entries in the Border Router's discovered prefix table. * + * Prefix entries associated with the same discovered router on an infrastructure link are guaranteed to be grouped + * together (retrieved back-to-back). + * * @param[in] aInstance The OpenThread instance. * @param[in,out] aIterator A pointer to the iterator. * @param[out] aEntry A pointer to the entry to populate. @@ -403,6 +418,21 @@ otError otBorderRoutingGetNextPrefixTableEntry(otInstance otBorderRoutingPrefixTableIterator *aIterator, otBorderRoutingPrefixTableEntry *aEntry); +/** + * Iterates over the discovered router entries on the infrastructure link. + * + * @param[in] aInstance The OpenThread instance. + * @param[in,out] aIterator A pointer to the iterator. + * @param[out] aEntry A pointer to the entry to populate. + * + * @retval OT_ERROR_NONE Iterated to the next router, @p aEntry and @p aIterator are updated. + * @retval OT_ERROR_NOT_FOUND No more router entries. + * + */ +otError otBorderRoutingGetNextRouterEntry(otInstance *aInstance, + otBorderRoutingPrefixTableIterator *aIterator, + otBorderRoutingRouterEntry *aEntry); + /** * Enables / Disables DHCPv6 Prefix Delegation. * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index e87e43bcc..fbcbb4329 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (374) +#define OPENTHREAD_API_VERSION (375) /** * @addtogroup api-instance diff --git a/src/cli/README_BR.md b/src/cli/README_BR.md index b48eec29d..dcc174cfa 100644 --- a/src/cli/README_BR.md +++ b/src/cli/README_BR.md @@ -15,6 +15,7 @@ Usage : `br [command] ...` - [prefixtable](#prefixtable) - [rioprf](#rioprf) - [routeprf](#routeprf) +- [routers](#routers) - [state](#state) ## Command Details @@ -35,6 +36,7 @@ onlinkprefix prefixtable rioprf routeprf +routers state Done ``` @@ -180,10 +182,24 @@ Usage: `br prefixtable` Get the discovered prefixes by Border Routing Manager on the infrastructure link. +Info per prefix entry: + +- The prefix +- Whether the prefix is on-link or route +- Milliseconds since last received Router Advertisement containing this prefix +- Prefix lifetime in seconds +- Preferred lifetime in seconds only if prefix is on-link +- Route preference (low, med, high) only if prefix is route (not on-link) +- The router IPv6 address which advertising this prefix +- Flags in received Router Advertisement header: + - M: Managed Address Config flag + - O: Other Config flag + - Stub: Stub Router flag (indicates whether the router is a stub router) + ```bash > br prefixtable -prefix:fd00:1234:5678:0::/64, on-link:no, ms-since-rx:29526, lifetime:1800, route-prf:med, router:ff02:0:0:0:0:0:0:1 -prefix:1200:abba:baba:0::/64, on-link:yes, ms-since-rx:29527, lifetime:1800, preferred:1800, router:ff02:0:0:0:0:0:0:1 +prefix:fd00:1234:5678:0::/64, on-link:no, ms-since-rx:29526, lifetime:1800, route-prf:med, router:ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) +prefix:1200:abba:baba:0::/64, on-link:yes, ms-since-rx:29527, lifetime:1800, preferred:1800, router:ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) Done ``` @@ -254,3 +270,23 @@ Clear a previously set preference value for publishing routes in Thread Network > br routeprf clear Done ``` + +### routers + +Usage: `br routers` + +Get the list of discovered routers by Border Routing Manager on the infrastructure link. + +Info per router: + +- The router IPv6 address +- Flags in received Router Advertisement header: + - M: Managed Address Config flag + - O: Other Config flag + - Stub: Stub Router flag (indicates whether the router is a stub router) + +```bash +> br routers +ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) +Done +``` diff --git a/src/cli/cli_br.cpp b/src/cli/cli_br.cpp index d1fd81b4d..bd4d0dfe5 100644 --- a/src/cli/cli_br.cpp +++ b/src/cli/cli_br.cpp @@ -37,8 +37,6 @@ #include -#include - #include "cli/cli.hpp" namespace ot { @@ -381,13 +379,26 @@ exit: * @code * br prefixtable * prefix:fd00:1234:5678:0::/64, on-link:no, ms-since-rx:29526, lifetime:1800, route-prf:med, - * router:ff02:0:0:0:0:0:0:1 + * router:ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) * prefix:1200:abba:baba:0::/64, on-link:yes, ms-since-rx:29527, lifetime:1800, preferred:1800, - * router:ff02:0:0:0:0:0:0:1 + * router:ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) * Done * @endcode - * @par api_copy - * #otBorderRoutingGetNextPrefixTableEntry + * @par + * Get the discovered prefixes by Border Routing Manager on the infrastructure link. + * Info per prefix entry: + * - The prefix + * - Whether the prefix is on-link or route + * - Milliseconds since last received Router Advertisement containing this prefix + * - Prefix lifetime in seconds + * - Preferred lifetime in seconds only if prefix is on-link + * - Route preference (low, med, high) only if prefix is route (not on-link) + * - The router IPv6 address which advertising this prefix + * - Flags in received Router Advertisement header: + * - M: Managed Address Config flag + * - O: Other Config flag + * - Stub: Stub Router flag (indicates whether the router is a stub router) + * @sa otBorderRoutingGetNextPrefixTableEntry */ template <> otError Br::Process(Arg aArgs[]) { @@ -416,14 +427,57 @@ template <> otError Br::Process(Arg aArgs[]) OutputFormat("route-prf:%s, ", Interpreter::PreferenceToString(entry.mRoutePreference)); } - otIp6AddressToString(&entry.mRouterAddress, string, sizeof(string)); - OutputLine("router:%s", string); + OutputFormat("router:"); + OutputRouterInfo(entry.mRouter); } exit: return error; } +/** + * @cli br routers + * @code + * br routers + * ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) + * Done + * @endcode + * @par + * Get the list of discovered routers by Border Routing Manager on the infrastructure link. + * Info per router: + * - The router IPv6 address + * - Flags in received Router Advertisement header: + * - M: Managed Address Config flag + * - O: Other Config flag + * - Stub: Stub Router flag (indicates whether the router is a stub router) + * @sa otBorderRoutingGetNextRouterEntry + */ +template <> otError Br::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + otBorderRoutingPrefixTableIterator iterator; + otBorderRoutingRouterEntry entry; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + otBorderRoutingPrefixTableInitIterator(GetInstancePtr(), &iterator); + + while (otBorderRoutingGetNextRouterEntry(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE) + { + OutputRouterInfo(entry); + } + +exit: + return error; +} + +void Br::OutputRouterInfo(const otBorderRoutingRouterEntry &aEntry) +{ + OutputIp6Address(aEntry.mAddress); + OutputLine(" (M:%u O:%u Stub:%u)", aEntry.mManagedAddressConfigFlag, aEntry.mOtherConfigFlag, + aEntry.mStubRouterFlag); +} + template <> otError Br::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -587,6 +641,7 @@ otError Br::Process(Arg aArgs[]) CmdEntry("prefixtable"), CmdEntry("rioprf"), CmdEntry("routeprf"), + CmdEntry("routers"), CmdEntry("state"), }; diff --git a/src/cli/cli_br.hpp b/src/cli/cli_br.hpp index f953344e3..01fcdcf94 100644 --- a/src/cli/cli_br.hpp +++ b/src/cli/cli_br.hpp @@ -36,6 +36,8 @@ #include "openthread-core-config.h" +#include + #include "cli/cli_config.h" #include "cli/cli_output.hpp" @@ -92,6 +94,7 @@ private: template otError Process(Arg aArgs[]); otError ParsePrefixTypeArgs(Arg aArgs[], PrefixType &aFlags); + void OutputRouterInfo(const otBorderRoutingRouterEntry &aEntry); }; } // namespace Cli diff --git a/src/core/api/border_routing_api.cpp b/src/core/api/border_routing_api.cpp index 3a76fa668..9fbfbcb4b 100644 --- a/src/core/api/border_routing_api.cpp +++ b/src/core/api/border_routing_api.cpp @@ -172,6 +172,16 @@ otError otBorderRoutingGetNextPrefixTableEntry(otInstance return AsCoreType(aInstance).Get().GetNextPrefixTableEntry(*aIterator, *aEntry); } +otError otBorderRoutingGetNextRouterEntry(otInstance *aInstance, + otBorderRoutingPrefixTableIterator *aIterator, + otBorderRoutingRouterEntry *aEntry) +{ + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aEntry); + + return AsCoreType(aInstance).Get().GetNextRouterEntry(*aIterator, *aEntry); +} + #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE void otBorderRoutingDhcp6PdSetEnabled(otInstance *aInstance, bool aEnabled) { diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index e5cd61da9..c8b3ade09 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1794,11 +1794,7 @@ exit: void RoutingManager::DiscoveredPrefixTable::InitIterator(PrefixTableIterator &aIterator) const { - Iterator &iterator = static_cast(aIterator); - - iterator.SetInitTime(); - iterator.SetRouter(mRouters.GetHead()); - iterator.SetEntry(mRouters.IsEmpty() ? nullptr : mRouters.GetHead()->mEntries.GetHead()); + static_cast(aIterator).Init(mRouters); } Error RoutingManager::DiscoveredPrefixTable::GetNextEntry(PrefixTableIterator &aIterator, @@ -1810,7 +1806,7 @@ Error RoutingManager::DiscoveredPrefixTable::GetNextEntry(PrefixTableIterator &a VerifyOrExit(iterator.GetRouter() != nullptr, error = kErrorNotFound); OT_ASSERT(iterator.GetEntry() != nullptr); - aEntry.mRouterAddress = iterator.GetRouter()->mAddress; + iterator.GetRouter()->CopyInfoTo(aEntry.mRouter); aEntry.mPrefix = iterator.GetEntry()->GetPrefix(); aEntry.mIsOnLink = iterator.GetEntry()->IsOnLinkPrefix(); aEntry.mMsecSinceLastUpdate = iterator.GetInitTime() - iterator.GetEntry()->GetLastUpdateTime(); @@ -1819,23 +1815,62 @@ Error RoutingManager::DiscoveredPrefixTable::GetNextEntry(PrefixTableIterator &a aEntry.mRoutePreference = static_cast(aEntry.mIsOnLink ? 0 : iterator.GetEntry()->GetRoutePreference()); - // Advance the iterator - iterator.SetEntry(iterator.GetEntry()->GetNext()); - - if (iterator.GetEntry() == nullptr) - { - iterator.SetRouter(iterator.GetRouter()->GetNext()); - - if (iterator.GetRouter() != nullptr) - { - iterator.SetEntry(iterator.GetRouter()->mEntries.GetHead()); - } - } + iterator.Advance(Iterator::kToNextEntry); exit: return error; } +Error RoutingManager::DiscoveredPrefixTable::GetNextRouter(PrefixTableIterator &aIterator, RouterEntry &aEntry) const +{ + Error error = kErrorNone; + Iterator &iterator = static_cast(aIterator); + + VerifyOrExit(iterator.GetRouter() != nullptr, error = kErrorNotFound); + + iterator.GetRouter()->CopyInfoTo(aEntry); + iterator.Advance(Iterator::kToNextRouter); + +exit: + return error; +} + +//--------------------------------------------------------------------------------------------------------------------- +// DiscoveredPrefixTable::Iterator + +void RoutingManager::DiscoveredPrefixTable::Iterator::Init(const LinkedList &aRouters) +{ + SetInitTime(); + SetRouter(aRouters.GetHead()); + SetEntry(aRouters.IsEmpty() ? nullptr : aRouters.GetHead()->mEntries.GetHead()); +} + +void RoutingManager::DiscoveredPrefixTable::Iterator::Advance(AdvanceMode aMode) +{ + switch (aMode) + { + case kToNextEntry: + SetEntry(GetEntry()->GetNext()); + + if (GetEntry() != nullptr) + { + break; + } + + OT_FALL_THROUGH; + + case kToNextRouter: + SetRouter(GetRouter()->GetNext()); + + if (GetRouter() != nullptr) + { + SetEntry(GetRouter()->mEntries.GetHead()); + } + + break; + } +} + //--------------------------------------------------------------------------------------------------------------------- // DiscoveredPrefixTable::Entry @@ -1957,6 +1992,17 @@ uint32_t RoutingManager::DiscoveredPrefixTable::Entry::CalculateExpireDelay(uint return delay; } +//--------------------------------------------------------------------------------------------------------------------- +// DiscoveredPrefixTable::Router + +void RoutingManager::DiscoveredPrefixTable::Router::CopyInfoTo(RouterEntry &aEntry) const +{ + aEntry.mAddress = mAddress; + aEntry.mManagedAddressConfigFlag = mManagedAddressConfigFlag; + aEntry.mOtherConfigFlag = mOtherConfigFlag; + aEntry.mStubRouterFlag = mStubRouterFlag; +} + //--------------------------------------------------------------------------------------------------------------------- // FavoredOmrPrefix diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 55da20191..db43f96a0 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -87,6 +87,7 @@ public: typedef NetworkData::RoutePreference RoutePreference; ///< Route preference (high, medium, low). typedef otBorderRoutingPrefixTableIterator PrefixTableIterator; ///< Prefix Table Iterator. typedef otBorderRoutingPrefixTableEntry PrefixTableEntry; ///< Prefix Table Entry. + typedef otBorderRoutingRouterEntry RouterEntry; ///< Router Entry. /** * This constant specifies the maximum number of route prefixes that may be published by `RoutingManager` @@ -466,6 +467,21 @@ public: return mDiscoveredPrefixTable.GetNextEntry(aIterator, aEntry); } + /** + * Iterates over discovered router entries on infrastructure link. + * + * @param[in,out] aIterator An iterator. + * @param[out] aEntry A reference to the entry to populate. + * + * @retval kErrorNone Got the next router info, @p aEntry is updated and @p aIterator is advanced. + * @retval kErrorNotFound No more routers. + * + */ + Error GetNextRouterEntry(PrefixTableIterator &aIterator, RouterEntry &aEntry) const + { + return mDiscoveredPrefixTable.GetNextRouter(aIterator, aEntry); + } + #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE /** * Determines whether to enable/disable SRP server when the auto-enable mode is changed on SRP server. @@ -621,6 +637,7 @@ private: void InitIterator(PrefixTableIterator &aIterator) const; Error GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const; + Error GetNextRouter(PrefixTableIterator &aIterator, RouterEntry &aEntry) const; void HandleEntryTimer(void); void HandleRouterTimer(void); @@ -758,6 +775,7 @@ private: bool Matches(const Ip6::Address &aAddress) const { return aAddress == mAddress; } bool Matches(EmptyChecker) const { return mEntries.IsEmpty(); } + void CopyInfoTo(RouterEntry &aEntry) const; Router *mNext; Ip6::Address mAddress; @@ -772,12 +790,23 @@ private: class Iterator : public PrefixTableIterator { public: + enum AdvanceMode : uint8_t + { + kToNextEntry, + kToNextRouter, + }; + + void Init(const LinkedList &aRouters); + void Advance(AdvanceMode aMode); + const Router *GetRouter(void) const { return static_cast(mPtr1); } - void SetRouter(const Router *aRouter) { mPtr1 = aRouter; } const Entry *GetEntry(void) const { return static_cast(mPtr2); } - void SetEntry(const Entry *aEntry) { mPtr2 = aEntry; } TimeMilli GetInitTime(void) const { return TimeMilli(mData32); } - void SetInitTime(void) { mData32 = TimerMilli::GetNow().GetValue(); } + + private: + void SetRouter(const Router *aRouter) { mPtr1 = aRouter; } + void SetEntry(const Entry *aEntry) { mPtr2 = aEntry; } + void SetInitTime(void) { mData32 = TimerMilli::GetNow().GetValue(); } }; void ProcessRaHeader(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader, Router &aRouter); diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp index 8ce2ffebd..b4982c030 100644 --- a/tests/unit/test_routing_manager.cpp +++ b/tests/unit/test_routing_manager.cpp @@ -945,7 +945,7 @@ void VerifyPrefixTable(const OnLinkPrefix *aOnLinkPrefixes, { Log(" on-link prefix:%s, valid:%u, preferred:%u, router:%s, age:%u", AsCoreType(&entry.mPrefix).ToString().AsCString(), entry.mValidLifetime, entry.mPreferredLifetime, - AsCoreType(&entry.mRouterAddress).ToString().AsCString(), entry.mMsecSinceLastUpdate / 1000); + AsCoreType(&entry.mRouter.mAddress).ToString().AsCString(), entry.mMsecSinceLastUpdate / 1000); onLinkPrefixCount++; @@ -954,7 +954,7 @@ void VerifyPrefixTable(const OnLinkPrefix *aOnLinkPrefixes, const OnLinkPrefix &onLinkPrefix = aOnLinkPrefixes[index]; if ((onLinkPrefix.mPrefix == AsCoreType(&entry.mPrefix)) && - (AsCoreType(&entry.mRouterAddress) == onLinkPrefix.mRouterAddress)) + (AsCoreType(&entry.mRouter.mAddress) == onLinkPrefix.mRouterAddress)) { VerifyOrQuit(entry.mValidLifetime == onLinkPrefix.mValidLifetime); VerifyOrQuit(entry.mPreferredLifetime == onLinkPrefix.mPreferredLifetime); @@ -967,7 +967,7 @@ void VerifyPrefixTable(const OnLinkPrefix *aOnLinkPrefixes, { Log(" route prefix:%s, valid:%u, prf:%s, router:%s, age:%u", AsCoreType(&entry.mPrefix).ToString().AsCString(), entry.mValidLifetime, - PreferenceToString(entry.mRoutePreference), AsCoreType(&entry.mRouterAddress).ToString().AsCString(), + PreferenceToString(entry.mRoutePreference), AsCoreType(&entry.mRouter.mAddress).ToString().AsCString(), entry.mMsecSinceLastUpdate / 1000); routePrefixCount++; @@ -977,7 +977,7 @@ void VerifyPrefixTable(const OnLinkPrefix *aOnLinkPrefixes, const RoutePrefix &routePrefix = aRoutePrefixes[index]; if ((routePrefix.mPrefix == AsCoreType(&entry.mPrefix)) && - (AsCoreType(&entry.mRouterAddress) == routePrefix.mRouterAddress)) + (AsCoreType(&entry.mRouter.mAddress) == routePrefix.mRouterAddress)) { VerifyOrQuit(entry.mValidLifetime == routePrefix.mValidLifetime); VerifyOrQuit(static_cast(entry.mRoutePreference) == routePrefix.mPreference);