From c0babfe319a101b4a27a36789b0b8facde1fef43 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 2 Jun 2022 15:33:12 -0700 Subject: [PATCH] [routing-manager] add `OmrPrefix` class, include OMR preference in RIO (#7778) This commit updates `RoutingManager` to track the preference of the discovered OMR prefixes from Thread Network Data and advertise the OMR prefixes with the same preference in Route Info Options in the emitted Router Advertisements. This commit adds `OmrPrefix` class (which tracks an OMR prefix along with its preference) and provides helpers method (e.g., method to compare two OMR prefixes to determine which one is favored, `ToString ()` method). The `EvaluateOmrPrefix()` is updated to use the new `OmrPrefix` class. --- src/core/border_router/routing_manager.cpp | 162 ++++++++++++++------- src/core/border_router/routing_manager.hpp | 23 ++- src/core/thread/network_data_types.hpp | 8 + 3 files changed, 142 insertions(+), 51 deletions(-) diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 1ea7059f6..cf053a658 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -365,85 +365,98 @@ void RoutingManager::EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes) { NetworkData::Iterator iterator = NetworkData::kIteratorInit; NetworkData::OnMeshPrefixConfig onMeshPrefixConfig; - Ip6::Prefix * electedOmrPrefix = nullptr; - signed int electedOmrPrefixPreference = 0; - Ip6::Prefix * publishedLocalOmrPrefix = nullptr; + OmrPrefix * favoredOmrEntry = nullptr; + OmrPrefix * localOmrEntry = nullptr; OT_ASSERT(mIsRunning); while (Get().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == kErrorNone) { - const Ip6::Prefix &prefix = onMeshPrefixConfig.GetPrefix(); + OmrPrefix *entry; if (!IsValidOmrPrefix(onMeshPrefixConfig)) { continue; } - if (aNewOmrPrefixes.Contains(prefix)) - { - // Ignore duplicate prefixes. - continue; - } + entry = aNewOmrPrefixes.FindMatching(onMeshPrefixConfig.GetPrefix()); - if (aNewOmrPrefixes.PushBack(prefix) != kErrorNone) + if (entry != nullptr) { - LogWarn("EvaluateOmrPrefix: Too many OMR prefixes, ignoring prefix %s", prefix.ToString().AsCString()); - continue; + // Update the entry if we find the same prefix with higher + // preference in network data + + if (onMeshPrefixConfig.GetPreference() <= entry->GetPreference()) + { + continue; + } + + entry->SetPreference(onMeshPrefixConfig.GetPreference()); + } + else + { + entry = aNewOmrPrefixes.PushBack(); + + if (entry == nullptr) + { + LogWarn("EvaluateOmrPrefix: Too many OMR prefixes, ignoring prefix %s", + onMeshPrefixConfig.GetPrefix().ToString().AsCString()); + continue; + } + + entry->InitFrom(onMeshPrefixConfig); } if (onMeshPrefixConfig.mPreferred) { - if (electedOmrPrefix == nullptr || onMeshPrefixConfig.mPreference > electedOmrPrefixPreference || - (onMeshPrefixConfig.mPreference == electedOmrPrefixPreference && prefix < *electedOmrPrefix)) + if ((favoredOmrEntry == nullptr) || (entry->IsFavoredOver(*favoredOmrEntry))) { - electedOmrPrefix = aNewOmrPrefixes.Back(); - electedOmrPrefixPreference = onMeshPrefixConfig.mPreference; + favoredOmrEntry = entry; } } - if (prefix == mLocalOmrPrefix) + if (entry->GetPrefix() == mLocalOmrPrefix) { - publishedLocalOmrPrefix = aNewOmrPrefixes.Back(); + localOmrEntry = entry; } } // Decide if we need to add or remove our local OMR prefix. - if (electedOmrPrefix == nullptr) + if (favoredOmrEntry == nullptr) { LogInfo("EvaluateOmrPrefix: No preferred OMR prefixes found in Thread network"); - if (PublishLocalOmrPrefix() == kErrorNone) - { - IgnoreError(aNewOmrPrefixes.PushBack(mLocalOmrPrefix)); - } - // The `aNewOmrPrefixes` remains empty if we fail to publish // the local OMR prefix. + SuccessOrExit(PublishLocalOmrPrefix()); + + localOmrEntry = aNewOmrPrefixes.PushBack(); + VerifyOrExit(localOmrEntry != nullptr); + + localOmrEntry->Init(mLocalOmrPrefix, NetworkData::kRoutePreferenceMedium); } - else + else if (favoredOmrEntry == localOmrEntry) { - if (*electedOmrPrefix == mLocalOmrPrefix) - { - IgnoreError(PublishLocalOmrPrefix()); - } - else if (IsOmrPrefixAddedToLocalNetworkData()) - { - LogInfo("EvaluateOmrPrefix: There is already a preferred OMR prefix %s (pref=%d) in the Thread network", - electedOmrPrefix->ToString().AsCString(), electedOmrPrefixPreference); + IgnoreError(PublishLocalOmrPrefix()); + } + else if (IsOmrPrefixAddedToLocalNetworkData()) + { + LogInfo("EvaluateOmrPrefix: There is already a preferred OMR prefix %s in the Thread network", + favoredOmrEntry->ToString().AsCString()); - UnpublishLocalOmrPrefix(); + UnpublishLocalOmrPrefix(); + if (localOmrEntry != nullptr) + { // Remove the local OMR prefix from the list by overwriting it - // with the last element and then popping it from the list. - if (publishedLocalOmrPrefix != nullptr) - { - *publishedLocalOmrPrefix = *aNewOmrPrefixes.Back(); - aNewOmrPrefixes.PopBack(); - } + // with popped last entry in the list. + *localOmrEntry = *aNewOmrPrefixes.PopBack(); } } + +exit: + return; } Error RoutingManager::PublishLocalOmrPrefix(void) @@ -874,13 +887,14 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix // Invalidate the advertised OMR prefixes if they are no longer in the new OMR prefix array. - for (const Ip6::Prefix &advertisedOmrPrefix : mAdvertisedOmrPrefixes) + for (const OmrPrefix &advertisedOmrPrefix : mAdvertisedOmrPrefixes) { - if (!aNewOmrPrefixes.Contains(advertisedOmrPrefix)) + if (!aNewOmrPrefixes.ContainsMatching(advertisedOmrPrefix.GetPrefix())) { RouterAdv::RouteInfoOption *rio; - OT_ASSERT(bufferLength + RouterAdv::RouteInfoOption::OptionSizeForPrefix(advertisedOmrPrefix.GetLength()) <= + OT_ASSERT(bufferLength + RouterAdv::RouteInfoOption::OptionSizeForPrefix( + advertisedOmrPrefix.GetPrefix().GetLength()) <= sizeof(buffer)); rio = reinterpret_cast(buffer + bufferLength); @@ -888,7 +902,7 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix // Set zero route lifetime to immediately invalidate the advertised OMR prefix. rio->Init(); rio->SetRouteLifetime(0); - rio->SetPrefix(advertisedOmrPrefix); + rio->SetPrefix(advertisedOmrPrefix.GetPrefix()); bufferLength += rio->GetSize(); @@ -897,18 +911,20 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix } } - for (const Ip6::Prefix &newOmrPrefix : aNewOmrPrefixes) + for (const OmrPrefix &newOmrPrefix : aNewOmrPrefixes) { RouterAdv::RouteInfoOption *rio; - OT_ASSERT(bufferLength + RouterAdv::RouteInfoOption::OptionSizeForPrefix(newOmrPrefix.GetLength()) <= + OT_ASSERT(bufferLength + + RouterAdv::RouteInfoOption::OptionSizeForPrefix(newOmrPrefix.GetPrefix().GetLength()) <= sizeof(buffer)); rio = reinterpret_cast(buffer + bufferLength); rio->Init(); rio->SetRouteLifetime(kDefaultOmrPrefixLifetime); - rio->SetPrefix(newOmrPrefix); + rio->SetPreference(newOmrPrefix.GetPreference()); + rio->SetPrefix(newOmrPrefix.GetPrefix()); bufferLength += rio->GetSize(); @@ -1254,7 +1270,7 @@ void RoutingManager::UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption // messages are usually faster than Thread Network Data propagation). // They are the reasons why we need both the checks. - VerifyOrExit(!mAdvertisedOmrPrefixes.Contains(prefix)); + VerifyOrExit(!mAdvertisedOmrPrefixes.ContainsMatching(prefix)); VerifyOrExit(!NetworkDataContainsOmrPrefix(prefix)); LogInfo("Discovered OMR prefix (%s, %u seconds) from %s", prefix.ToString().AsCString(), aRio.GetRouteLifetime(), @@ -1315,8 +1331,8 @@ void RoutingManager::InvalidateDiscoveredPrefixes(void) // Data). if ((prefix.GetExpireTime() <= now) || - (!prefix.IsOnLinkPrefix() && - (mAdvertisedOmrPrefixes.Contains(prefix.GetPrefix()) || NetworkDataContainsOmrPrefix(prefix.GetPrefix())))) + (!prefix.IsOnLinkPrefix() && (mAdvertisedOmrPrefixes.ContainsMatching(prefix.GetPrefix()) || + NetworkDataContainsOmrPrefix(prefix.GetPrefix())))) { UnpublishExternalRoute(prefix.GetPrefix()); @@ -1560,6 +1576,52 @@ uint32_t RoutingManager::ExternalPrefix::GetPrefixExpireDelay(uint32_t aValidLif return delay; } +//--------------------------------------------------------------------------------------------------------------------- +// OmrPrefix + +void RoutingManager::OmrPrefix::Init(const Ip6::Prefix &aPrefix, RoutePreference aPreference) +{ + mPrefix = aPrefix; + mPreference = aPreference; +} + +void RoutingManager::OmrPrefix::InitFrom(NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig) +{ + Init(aOnMeshPrefixConfig.GetPrefix(), aOnMeshPrefixConfig.GetPreference()); +} + +bool RoutingManager::OmrPrefix::IsFavoredOver(const OmrPrefix &aOther) const +{ + // This method determines whether this OMR prefix is favored + // over `aOther` prefix. A prefix with higher preference is + // favored. If the preference is the same, then the smaller + // prefix (in the sense defined by `Ip6::Prefix`) is favored. + + return (mPreference > aOther.mPreference) || ((mPreference == aOther.mPreference) && (mPrefix < aOther.mPrefix)); +} + +RoutingManager::OmrPrefix::InfoString RoutingManager::OmrPrefix::ToString(void) const +{ + InfoString string; + + string.Append("%s (prf:", mPrefix.ToString().AsCString()); + + switch (mPreference) + { + case NetworkData::kRoutePreferenceHigh: + string.Append("high)"); + break; + case NetworkData::kRoutePreferenceMedium: + string.Append("med)"); + break; + case NetworkData::kRoutePreferenceLow: + string.Append("low)"); + break; + } + + return string; +} + } // namespace BorderRouter } // namespace ot diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index b4b501460..7afc06715 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -55,6 +55,7 @@ #include "common/error.hpp" #include "common/locator.hpp" #include "common/notifier.hpp" +#include "common/string.hpp" #include "common/timer.hpp" #include "net/ip6.hpp" #include "thread/network_data.hpp" @@ -278,7 +279,27 @@ private: bool mIsOnLinkPrefix; }; - typedef Array OmrPrefixArray; + class OmrPrefix // An OMR Prefix + { + public: + static constexpr uint16_t kInfoStringSize = 60; + typedef String InfoString; + + void Init(const Ip6::Prefix &aPrefix, RoutePreference aPreference); + void InitFrom(NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig); + const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } + RoutePreference GetPreference(void) const { return mPreference; } + void SetPreference(RoutePreference aPreference) { mPreference = aPreference; } + bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; } + bool IsFavoredOver(const OmrPrefix &aOther) const; + InfoString ToString(void) const; + + private: + Ip6::Prefix mPrefix; + RoutePreference mPreference; + }; + + typedef Array OmrPrefixArray; typedef Array ExternalPrefixArray; void EvaluateState(void); diff --git a/src/core/thread/network_data_types.hpp b/src/core/thread/network_data_types.hpp index 06ff0a942..821534394 100644 --- a/src/core/thread/network_data_types.hpp +++ b/src/core/thread/network_data_types.hpp @@ -190,6 +190,14 @@ public: */ Ip6::Prefix &GetPrefix(void) { return AsCoreType(&mPrefix); } + /** + * This method gets the preference. + * + * @return The preference. + * + */ + RoutePreference GetPreference(void) const { return RoutePreferenceFromValue(RoutePreferenceToValue(mPreference)); } + #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE /** * This method indicates whether or not the prefix configuration is valid.