From 5876ff79ae1cd73e91f233aa88afb92372e61e33 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Sun, 3 Jul 2022 12:35:39 -0700 Subject: [PATCH] [routing-manager] add public API to get discovered prefix table (#7877) This commit adds new public OT APIs to iterate over the Border Router discovered prefix table from received Router Advertisement messages. It also adds new CLI command to output the prefix table. The new APIs are primary intended for testing. --- include/openthread/border_routing.h | 65 ++++++++++++++++++++++ include/openthread/instance.h | 2 +- src/cli/cli.cpp | 46 ++++++++++++++- src/core/api/border_routing_api.cpp | 12 ++++ src/core/border_router/routing_manager.cpp | 47 ++++++++++++++++ src/core/border_router/routing_manager.hpp | 49 +++++++++++++++- 6 files changed, 218 insertions(+), 3 deletions(-) diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h index 1fae996a4..b815d9504 100644 --- a/include/openthread/border_routing.h +++ b/include/openthread/border_routing.h @@ -72,6 +72,40 @@ extern "C" { * */ +/** + * This structure represents an iterator to iterate through the Border Router's discovered prefix table. + * + * The fields in this type are opaque (intended for use by OpenThread core only) and therefore should not be + * accessed or used by caller. + * + * Before using an iterator, it MUST be initialized using `otBorderRoutingPrefixTableInitIterator()`. + * + */ +typedef struct otBorderRoutingPrefixTableIterator +{ + const void *mPtr1; + const void *mPtr2; + uint32_t mData32; +} otBorderRoutingPrefixTableIterator; + +/** + * This structure 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. + * + */ +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. +} otBorderRoutingPrefixTableEntry; + /** * This method initializes the Border Routing Manager on given infrastructure interface. * @@ -178,6 +212,37 @@ otError otBorderRoutingGetOnLinkPrefix(otInstance *aInstance, otIp6Prefix *aPref */ otError otBorderRoutingGetNat64Prefix(otInstance *aInstance, otIp6Prefix *aPrefix); +/** + * This function initializes an `otBorderRoutingPrefixTableIterator`. + * + * An iterator MUST be initialized before it is used. + * + * An iterator can be initialized again to restart from the beginning of the table. + * + * When iterating over entries in the table, to ensure the update times `mMsecSinceLastUpdate` of entries are + * consistent, they are given relative to the time the iterator was initialized. + * + * @param[in] aInstance The OpenThread instance. + * @param[out] aIterator A pointer to the iterator to initialize. + * + */ +void otBorderRoutingPrefixTableInitIterator(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator); + +/** + * This function iterates over the entries in the Border Router's discovered prefix table. + * + * @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 entry, @p aEntry and @p aIterator are updated. + * @retval OT_ERROR_NOT_FOUND No more entries in the table. + * + */ +otError otBorderRoutingGetNextPrefixTableEntry(otInstance * aInstance, + otBorderRoutingPrefixTableIterator *aIterator, + otBorderRoutingPrefixTableEntry * aEntry); + /** * @} * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index cf9ca66d4..136bd7273 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 (224) +#define OPENTHREAD_API_VERSION (225) /** * @addtogroup api-instance diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index e7dda6f45..5ea92fdda 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -605,7 +605,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) * #otBorderRoutingSetRouteInfoOptionPreference * */ - else if ((aArgs[0] == "rioprf")) + else if (aArgs[0] == "rioprf") { if (aArgs[1].IsEmpty()) { @@ -619,6 +619,50 @@ template <> otError Interpreter::Process(Arg aArgs[]) otBorderRoutingSetRouteInfoOptionPreference(GetInstancePtr(), preference); } } + /** + * @cli br prefixtable + * + * @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 + * 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 + * Done + * @endcode + * + * @par api_copy + * #otBorderRoutingGetNextPrefixTableEntry + * + */ + else if (aArgs[0] == "prefixtable") + { + otBorderRoutingPrefixTableIterator iterator; + otBorderRoutingPrefixTableEntry entry; + + otBorderRoutingPrefixTableInitIterator(GetInstancePtr(), &iterator); + + while (otBorderRoutingGetNextPrefixTableEntry(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE) + { + char string[OT_IP6_PREFIX_STRING_SIZE]; + + otIp6PrefixToString(&entry.mPrefix, string, sizeof(string)); + OutputFormat("prefix:%s, on-link:%s, ms-since-rx:%u, lifetime:%u, ", string, entry.mIsOnLink ? "yes" : "no", + entry.mMsecSinceLastUpdate, entry.mValidLifetime); + + if (entry.mIsOnLink) + { + OutputFormat("preferred:%u, ", entry.mPreferredLifetime); + } + else + { + OutputFormat("route-prf:%s, ", PreferenceToString(entry.mRoutePreference)); + } + + otIp6AddressToString(&entry.mRouterAddress, string, sizeof(string)); + OutputLine("router:%s", string); + } + } else { error = OT_ERROR_INVALID_COMMAND; diff --git a/src/core/api/border_routing_api.cpp b/src/core/api/border_routing_api.cpp index 17e21f038..38160401d 100644 --- a/src/core/api/border_routing_api.cpp +++ b/src/core/api/border_routing_api.cpp @@ -81,4 +81,16 @@ otError otBorderRoutingGetNat64Prefix(otInstance *aInstance, otIp6Prefix *aPrefi } #endif +void otBorderRoutingPrefixTableInitIterator(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator) +{ + AsCoreType(aInstance).Get().InitPrefixTableIterator(*aIterator); +} + +otError otBorderRoutingGetNextPrefixTableEntry(otInstance * aInstance, + otBorderRoutingPrefixTableIterator *aIterator, + otBorderRoutingPrefixTableEntry * aEntry) +{ + return AsCoreType(aInstance).Get().GetNextPrefixTableEntry(*aIterator, *aEntry); +} + #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 3645f3307..97f28e9f4 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1805,6 +1805,53 @@ void RoutingManager::DiscoveredPrefixTable::HandleSignalTask(Tasklet &aTasklet) aTasklet.Get().HandleDiscoveredPrefixTableChanged(); } +void RoutingManager::DiscoveredPrefixTable::InitIterator(PrefixTableIterator &aIterator) const +{ + Iterator &iterator = static_cast(aIterator); + + iterator.SetInitTime(); + iterator.SetRouter(mRouters.Front()); + iterator.SetEntry(mRouters.IsEmpty() ? nullptr : mRouters[0].mEntries.GetHead()); +} + +Error RoutingManager::DiscoveredPrefixTable::GetNextEntry(PrefixTableIterator &aIterator, + PrefixTableEntry & aEntry) const +{ + Error error = kErrorNone; + Iterator &iterator = static_cast(aIterator); + + VerifyOrExit(iterator.GetRouter() != nullptr, error = kErrorNotFound); + OT_ASSERT(iterator.GetEntry() != nullptr); + + aEntry.mRouterAddress = iterator.GetRouter()->mAddress; + aEntry.mPrefix = iterator.GetEntry()->GetPrefix(); + aEntry.mIsOnLink = iterator.GetEntry()->IsOnLinkPrefix(); + aEntry.mMsecSinceLastUpdate = iterator.GetInitTime() - iterator.GetEntry()->GetLastUpdateTime(); + aEntry.mValidLifetime = iterator.GetEntry()->GetValidLifetime(); + aEntry.mPreferredLifetime = aEntry.mIsOnLink ? iterator.GetEntry()->GetPreferredLifetime() : 0; + aEntry.mRoutePreference = + static_cast(aEntry.mIsOnLink ? 0 : iterator.GetEntry()->GetRoutePreference()); + + // Advance the iterator + iterator.SetEntry(iterator.GetEntry()->GetNext()); + + if (iterator.GetEntry() == nullptr) + { + if (iterator.GetRouter() != mRouters.Back()) + { + iterator.SetRouter(iterator.GetRouter() + 1); + iterator.SetEntry(iterator.GetRouter()->mEntries.GetHead()); + } + else + { + iterator.SetRouter(nullptr); + } + } + +exit: + return error; +} + //--------------------------------------------------------------------------------------------------------------------- // DiscoveredPrefixTable::Entry diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index a8f9dbb63..ab8d43c3c 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -79,7 +79,9 @@ class RoutingManager : public InstanceLocator friend class ot::Instance; public: - typedef NetworkData::RoutePreference RoutePreference; ///< Route preference (high, medium, low). + typedef NetworkData::RoutePreference RoutePreference; ///< Route preference (high, medium, low). + typedef otBorderRoutingPrefixTableIterator PrefixTableIterator; ///< Prefix Table Iterator. + typedef otBorderRoutingPrefixTableEntry PrefixTableEntry; ///< Prefix Table Entry. /** * This constructor initializes the routing manager. @@ -219,6 +221,37 @@ public: */ static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix); + /** + * This method initializes a `PrefixTableIterator`. + * + * An iterator can be initialized again to start from the beginning of the table. + * + * When iterating over entries in the table, to ensure the entry update times are consistent, they are given + * relative to the time the iterator was initialized. + * + * @param[out] aIterator The iterator to initialize. + * + */ + void InitPrefixTableIterator(PrefixTableIterator &aIterator) const + { + mDiscoveredPrefixTable.InitIterator(aIterator); + } + + /** + * This method iterates over entries in the discovered prefix table. + * + * @param[in,out] aIterator An iterator. + * @param[out] aEntry A reference to the entry to populate. + * + * @retval OT_ERROR_NONE Got the next entry, @p aEntry is updated and @p aIterator is advanced. + * @retval OT_ERROR_NOT_FOUND No more entries in the table. + * + */ + Error GetNextPrefixTableEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const + { + return mDiscoveredPrefixTable.GetNextEntry(aIterator, aEntry); + } + private: static constexpr uint16_t kMaxRouterAdvMessageLength = 256; // The maximum RA message length we can handle. @@ -312,6 +345,9 @@ private: TimeMilli CalculateNextStaleTime(TimeMilli aNow) const; + void InitIterator(PrefixTableIterator &aIterator) const; + Error GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const; + private: static constexpr uint16_t kMaxRouters = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS; static constexpr uint16_t kMaxEntries = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES; @@ -403,6 +439,17 @@ private: LinkedList mEntries; }; + class Iterator : public PrefixTableIterator + { + public: + 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(); } + }; + void ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader, Router &aRouter); void ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, Router &aRouter); void ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, Router &aRouter);