diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 36f9541bd..62c90b6d0 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1159,6 +1159,7 @@ void RoutingManager::RxRaTracker::ProcessPrefixInfoOption(const PrefixInfoOption { Ip6::Prefix prefix; Entry *entry; + bool disregard; VerifyOrExit(aPio.IsValid()); aPio.GetPrefix(prefix); @@ -1169,7 +1170,15 @@ void RoutingManager::RxRaTracker::ProcessPrefixInfoOption(const PrefixInfoOption ExitNow(); } - VerifyOrExit(prefix != Get().mOnLinkPrefixManager.GetLocalPrefix()); + // Disregard the PIO prefix if it matches our local on-link prefix, + // as this indicates it's likely from a peer Border Router connected + // to the same Thread mesh. + + disregard = (prefix == Get().mOnLinkPrefixManager.GetLocalPrefix()); + +#if !OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + VerifyOrExit(!disregard); +#endif LogPrefixInfoOption(prefix, aPio.GetValidLifetime(), aPio.GetPreferredLifetime()); @@ -1198,6 +1207,8 @@ void RoutingManager::RxRaTracker::ProcessPrefixInfoOption(const PrefixInfoOption entry->AdoptValidAndPreferredLifetimesFrom(newPrefix); } + entry->SetDisregardFlag(disregard); + exit: return; } @@ -1206,16 +1217,16 @@ void RoutingManager::RxRaTracker::ProcessRouteInfoOption(const RouteInfoOption & { Ip6::Prefix prefix; Entry *entry; + bool disregard; VerifyOrExit(aRio.IsValid()); aRio.GetPrefix(prefix); VerifyOrExit(!prefix.IsLinkLocal() && !prefix.IsMulticast()); - VerifyOrExit(Get().mOmrPrefixManager.GetLocalPrefix().GetPrefix() != prefix); - // Disregard our own advertised OMR prefixes and those currently - // present in the Thread Network Data. + // present in the Thread Network Data. This implies it is likely + // from a peer Thread BR connected to the same Thread mesh. // // There should be eventual parity between the `RioAdvertiser` // prefixes and the OMR prefixes in Network Data, but temporary @@ -1223,8 +1234,13 @@ void RoutingManager::RxRaTracker::ProcessRouteInfoOption(const RouteInfoOption & // required to update Network Data (registering with leader). So // both checks are necessary. - VerifyOrExit(!Get().mRioAdvertiser.HasAdvertised(prefix)); - VerifyOrExit(!Get().ContainsOmrPrefix(prefix)); + disregard = (Get().mOmrPrefixManager.GetLocalPrefix().GetPrefix() == prefix) || + Get().mRioAdvertiser.HasAdvertised(prefix) || + Get().ContainsOmrPrefix(prefix); + +#if !OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + VerifyOrExit(!disregard); +#endif LogRouteInfoOption(prefix, aRio.GetRouteLifetime(), aRio.GetPreference()); @@ -1250,6 +1266,8 @@ void RoutingManager::RxRaTracker::ProcessRouteInfoOption(const RouteInfoOption & entry->SetFrom(aRio); } + entry->SetDisregardFlag(disregard); + exit: return; } @@ -1319,14 +1337,29 @@ template void RoutingManager::RxRaTracker::Entry: void RoutingManager::RxRaTracker::HandleLocalOnLinkPrefixChanged(void) { const Ip6::Prefix &prefix = Get().mOnLinkPrefixManager.GetLocalPrefix(); - bool didRemove = false; + bool didChange = false; + + // When `TRACK_PEER_BR_INFO_ENABLE` is enabled, we mark + // to disregard any on-link prefix entries matching the new + // local on-link prefix. Otherwise, we can remove and free + // them. for (Router &router : mRouters) { - didRemove |= router.mOnLinkPrefixes.RemoveAndFreeAllMatching(prefix); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + OnLinkPrefix *entry = router.mOnLinkPrefixes.FindMatching(prefix); + + if ((entry != nullptr) && !entry->ShouldDisregard()) + { + entry->SetDisregardFlag(true); + didChange = true; + } +#else + didChange |= router.mOnLinkPrefixes.RemoveAndFreeAllMatching(prefix); +#endif } - VerifyOrExit(didRemove); + VerifyOrExit(didChange); Evaluate(); @@ -1338,7 +1371,7 @@ void RoutingManager::RxRaTracker::HandleNetDataChange(void) { NetworkData::Iterator iterator = NetworkData::kIteratorInit; NetworkData::OnMeshPrefixConfig prefixConfig; - bool didRemove = false; + bool didChange = false; while (Get().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone) { @@ -1349,11 +1382,21 @@ void RoutingManager::RxRaTracker::HandleNetDataChange(void) for (Router &router : mRouters) { - didRemove |= router.mRoutePrefixes.RemoveAndFreeAllMatching(prefixConfig.GetPrefix()); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + RoutePrefix *entry = router.mRoutePrefixes.FindMatching(prefixConfig.GetPrefix()); + + if ((entry != nullptr) && !entry->ShouldDisregard()) + { + entry->SetDisregardFlag(true); + didChange = true; + } +#else + didChange |= router.mRoutePrefixes.RemoveAndFreeAllMatching(prefixConfig.GetPrefix()); +#endif } } - if (didRemove) + if (didChange) { Evaluate(); } @@ -1395,13 +1438,13 @@ void RoutingManager::RxRaTracker::Evaluate(void) { DecisionFactors oldFactors = mDecisionFactors; TimeMilli now = TimerMilli::GetNow(); - NextFireTime routerTimeout(now); + NextFireTime routerTimeoutTime(now); NextFireTime entryExpireTime(now); NextFireTime staleTime(now); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Remove expired prefix entries in routers and then remove any - // router that has with no prefix entries or flags. + // router that has no prefix entries or flags. mRouters.RemoveAndFreeAllMatching(Router::EmptyChecker(now)); @@ -1413,18 +1456,24 @@ void RoutingManager::RxRaTracker::Evaluate(void) for (Router &router : mRouters) { + router.mAllEntriesDisregarded = true; + mDecisionFactors.UpdateFlagsFrom(router); for (OnLinkPrefix &entry : router.mOnLinkPrefixes) { mDecisionFactors.UpdateFrom(entry); entry.SetStaleTimeCalculated(false); + + router.mAllEntriesDisregarded &= entry.ShouldDisregard(); } for (RoutePrefix &entry : router.mRoutePrefixes) { mDecisionFactors.UpdateFrom(entry); entry.SetStaleTimeCalculated(false); + + router.mAllEntriesDisregarded &= entry.ShouldDisregard(); } } @@ -1446,11 +1495,12 @@ void RoutingManager::RxRaTracker::Evaluate(void) // their entries, `DetermineStaleTimeFor()` will consider all // matching entries and mark "StaleTimeCalculated" flag on them. - for (const Router &router : mRouters) + for (Router &router : mRouters) { if (router.ShouldCheckReachability()) { - routerTimeout.UpdateIfEarlier(router.mTimeout); + router.DetermineReachabilityTimeout(); + routerTimeoutTime.UpdateIfEarlier(router.mTimeoutTime); } for (const OnLinkPrefix &entry : router.mOnLinkPrefixes) @@ -1486,7 +1536,7 @@ void RoutingManager::RxRaTracker::Evaluate(void) staleTime.UpdateIfEarlier(CalculateExpirationTime(mLocalRaHeaderUpdateTime, interval)); } - mRouterTimer.FireAt(routerTimeout); + mRouterTimer.FireAt(routerTimeoutTime); mExpirationTimer.FireAt(entryExpireTime); mStaleTimer.FireAt(staleTime); } @@ -1590,7 +1640,7 @@ void RoutingManager::RxRaTracker::HandleRouterTimer(void) for (Router &router : mRouters) { - if (!router.ShouldCheckReachability() || (router.mTimeout > now)) + if (!router.ShouldCheckReachability() || (router.mTimeoutTime > now)) { continue; } @@ -1599,8 +1649,8 @@ void RoutingManager::RxRaTracker::HandleRouterTimer(void) if (router.IsReachable()) { - router.mTimeout = now + ((router.mNsProbeCount < Router::kMaxNsProbes) ? Router::kNsProbeRetryInterval - : Router::kNsProbeTimeout); + router.mTimeoutTime = now + ((router.mNsProbeCount < Router::kMaxNsProbes) ? Router::kNsProbeRetryInterval + : Router::kNsProbeTimeout); SendNeighborSolicitToRouter(router); } else @@ -1817,7 +1867,27 @@ void RoutingManager::RxRaTracker::Router::ResetReachabilityState(void) mNsProbeCount = 0; mLastUpdateTime = TimerMilli::GetNow(); - mTimeout = mLastUpdateTime + Random::NonCrypto::AddJitter(kReachableTimeout, kJitter); + mTimeoutTime = mLastUpdateTime + Random::NonCrypto::AddJitter(kReachableInterval, kJitter); +} + +void RoutingManager::RxRaTracker::Router::DetermineReachabilityTimeout(void) +{ + uint32_t interval; + + VerifyOrExit(ShouldCheckReachability()); + VerifyOrExit(mNsProbeCount == 0); + + // If all of the router's prefix entries are marked as + // disregarded (excluded from any decisions), it indicates that + // this router is likely a peer BR connected to the same Thread + // mesh. We use a longer reachability check interval for such + // peer BRs. + + interval = mAllEntriesDisregarded ? kPeerBrReachableInterval : kReachableInterval; + mTimeoutTime = mLastUpdateTime + Random::NonCrypto::AddJitter(interval, kJitter); + +exit: + return; } bool RoutingManager::RxRaTracker::Router::Matches(const EmptyChecker &aChecker) @@ -1886,6 +1956,8 @@ exit: void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const OnLinkPrefix &aOnLinkPrefix) { + VerifyOrExit(!aOnLinkPrefix.ShouldDisregard()); + if (aOnLinkPrefix.GetPrefix().IsUniqueLocal()) { mHasUlaOnLink = true; @@ -1903,10 +1975,15 @@ void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const OnLinkPrefix void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const RoutePrefix &aRoutePrefix) { + VerifyOrExit(!aRoutePrefix.ShouldDisregard()); + if (!mHasNonUlaRoute) { mHasNonUlaRoute = !aRoutePrefix.GetPrefix().IsUniqueLocal(); } + +exit: + return; } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 5c036a58f..17bcbd4b2 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -677,12 +677,16 @@ private: void SetStaleTimeCalculated(bool aFlag) { mStaleTimeCalculated = aFlag; } bool IsStaleTimeCalculated(void) const { return mStaleTimeCalculated; } + void SetDisregardFlag(bool aFlag) { mDisregard = aFlag; } + bool ShouldDisregard(void) const { return mDisregard; } + protected: LifetimedPrefix(void) = default; TimeMilli CalculateExpirationTime(uint32_t aLifetime) const; Ip6::Prefix mPrefix; + bool mDisregard : 1; bool mStaleTimeCalculated : 1; uint32_t mValidLifetime; TimeMilli mLastUpdateTime; @@ -808,9 +812,15 @@ private: struct Router : public Clearable { - // The timeout (in msec) for router to be considered reachable - // before starting the Neighbor Solicitation (NS) probes. - static constexpr uint32_t kReachableTimeout = OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT; + // Reachability timeout intervals before starting Neighbor + // Solicitation (NS) probes. Generally 60 seconds (± 2 + // seconds jitter) is used. For peer BRs a longer timeout + // of 200 seconds (3 min and 20 seconds) is used. This is + // selected to be longer than regular RA beacon interval + // of 3 minutes (± 15 seconds jitter). + + static constexpr uint32_t kReachableInterval = OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT; + static constexpr uint32_t kPeerBrReachableInterval = Time::kOneSecondInMsec * 200; static constexpr uint8_t kMaxNsProbes = 5; // Max number of NS probe attempts. static constexpr uint32_t kNsProbeRetryInterval = 1000; // In msec. Time between NS probe attempts. @@ -824,6 +834,7 @@ private: bool IsReachable(void) const { return mNsProbeCount <= kMaxNsProbes; } bool ShouldCheckReachability(void) const; void ResetReachabilityState(void); + void DetermineReachabilityTimeout(void); bool Matches(const Ip6::Address &aAddress) const { return aAddress == mAddress; } bool Matches(const EmptyChecker &aChecker); void CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow) const; @@ -835,12 +846,13 @@ private: OnLinkPrefixList mOnLinkPrefixes; RoutePrefixList mRoutePrefixes; TimeMilli mLastUpdateTime; - TimeMilli mTimeout; + TimeMilli mTimeoutTime; uint8_t mNsProbeCount; bool mManagedAddressConfigFlag : 1; bool mOtherConfigFlag : 1; bool mStubRouterFlag : 1; bool mIsLocalDevice : 1; + bool mAllEntriesDisregarded : 1; }; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/core/config/border_routing.h b/src/core/config/border_routing.h index 344cde4a5..7a91d6a39 100644 --- a/src/core/config/border_routing.h +++ b/src/core/config/border_routing.h @@ -71,6 +71,23 @@ #define OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE 1 #endif +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE + * + * Define to 1 to allow the Routing Manager to track information (e.g., advertised prefixes) about peer Thread + * Border Routers that are connected to the same Thread network. + * + * When enabled, the Routing Manager will maintain a record of advertised RIO/PIO prefixes discovered from received + * Router Advertisements of peer BRs. These entries are disregarded in decision-making (e.g., selecting favored + * on-link prefix or determining which route to publish in the Thread Network Data). + * + * It is recommended to enable this feature alongside `OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE`. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE +#define OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE +#endif + /** * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS *