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);