[border-routing] fix stale prefixes checking timer (#7241)
Current implementation will reschedule the Stale Timer when discovered
on-link or OMR prefixes or RA messages are updated, but it has a few
issues:
1. It's counting the maximum stale time of all on-link prefixes so
that we will schedule Router Solicitation when all on-link
prefixes become stale. But it failed to filter out OMR prefixes in
the `mDiscoveredPrefixes` list. This results in incorrect stale
time calculation.
2. A deprecated on-link-prefix is not removed from
`mDiscoveredPrefixes` directly but the preferred lifetime is set
to zero. We are not filtering out deprecated on-link prefixes when
calculating stale time and this results in zero stale timer delay
if there are no non-deprecated on-link prefixes.
This commit fixes those issues by having a dedicated
`ResetDiscoveredPrefixStaleTimer` to reschedule the stale timer with
following rules whenever discovered prefixes or learnt RA messages are
updated:
1. If BR learns RA header from Host daemons, it should send RS when
the RA header is stale.
2. If BR discovered any on-link prefix, it should send RS when all
on-link prefixes are stale.
3. If BR discovered any OMR prefix, it should send RS when the first
OMR prefix is stale.
`ResetDiscoveredPrefixStaleTimer` is supposed to correctly calculate
stale timer timeout whenever it's called.
diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp
index 28ee8ae..5128b2e 100644
--- a/src/core/border_router/routing_manager.cpp
+++ b/src/core/border_router/routing_manager.cpp
@@ -65,6 +65,7 @@
, mIsAdvertisingLocalOnLinkPrefix(false)
, mOnLinkPrefixDeprecateTimer(aInstance, HandleOnLinkPrefixDeprecateTimer)
, mTimeRouterAdvMessageLastUpdate(TimerMilli::GetNow())
+ , mLearntRouterAdvMessageFromHost(false)
, mDiscoveredPrefixInvalidTimer(aInstance, HandleDiscoveredPrefixInvalidTimer)
, mDiscoveredPrefixStaleTimer(aInstance, HandleDiscoveredPrefixStaleTimer)
, mRouterAdvertisementTimer(aInstance, HandleRouterAdvertisementTimer)
@@ -917,6 +918,9 @@
// Re-evaluate our routing policy and send Router Advertisement if necessary.
EvaluateRoutingPolicy();
+
+ // Reset prefix stale timer because `mDiscoveredPrefixes` may change.
+ ResetDiscoveredPrefixStaleTimer();
}
}
@@ -927,7 +931,7 @@
void RoutingManager::HandleDiscoveredPrefixStaleTimer(void)
{
- otLogInfoBr("All on-link prefixes are stale");
+ otLogInfoBr("Stale On-Link or OMR Prefixes or RA messages are detected");
StartRouterSolicitationDelay();
}
@@ -1072,7 +1076,6 @@
{
Ip6::Prefix prefix = aPio.GetPrefix();
bool needReevaluate = false;
- TimeMilli staleTime;
ExternalPrefix onLinkPrefix;
ExternalPrefix *existingPrefix = nullptr;
@@ -1093,16 +1096,12 @@
onLinkPrefix.mPreferredLifetime = aPio.GetPreferredLifetime();
onLinkPrefix.mTimeLastUpdate = TimerMilli::GetNow();
- staleTime = TimerMilli::GetNow();
-
for (ExternalPrefix &externalPrefix : mDiscoveredPrefixes)
{
if (externalPrefix == onLinkPrefix)
{
existingPrefix = &externalPrefix;
}
-
- staleTime = OT_MAX(staleTime, externalPrefix.GetStaleTime());
}
if (existingPrefix == nullptr)
@@ -1158,8 +1157,7 @@
}
mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(existingPrefix->GetExpireTime());
- staleTime = OT_MAX(staleTime, existingPrefix->GetStaleTime());
- mDiscoveredPrefixStaleTimer.FireAtIfEarlier(staleTime);
+ ResetDiscoveredPrefixStaleTimer();
exit:
return needReevaluate;
@@ -1213,8 +1211,6 @@
{
existingPrefix = &externalPrefix;
}
-
- mDiscoveredPrefixStaleTimer.FireAtIfEarlier(externalPrefix.GetStaleTime());
}
if (existingPrefix == nullptr)
@@ -1239,7 +1235,7 @@
*existingPrefix = omrPrefix;
mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(existingPrefix->GetExpireTime());
- mDiscoveredPrefixStaleTimer.FireAtIfEarlier(existingPrefix->GetStaleTime());
+ ResetDiscoveredPrefixStaleTimer();
exit:
return;
@@ -1325,17 +1321,82 @@
if (aRouterAdvMessage == nullptr || aRouterAdvMessage->GetRouterLifetime() == 0)
{
mRouterAdvMessage.SetToDefault();
+ mLearntRouterAdvMessageFromHost = false;
}
else
{
- mRouterAdvMessage = *aRouterAdvMessage;
- mDiscoveredPrefixStaleTimer.FireAtIfEarlier(mTimeRouterAdvMessageLastUpdate +
- Time::SecToMsec(kRtrAdvStaleTime));
+ mRouterAdvMessage = *aRouterAdvMessage;
+ mLearntRouterAdvMessageFromHost = true;
}
+ ResetDiscoveredPrefixStaleTimer();
+
return (mRouterAdvMessage != oldRouterAdvMessage);
}
+void RoutingManager::ResetDiscoveredPrefixStaleTimer(void)
+{
+ TimeMilli now = TimerMilli::GetNow();
+ TimeMilli nextStaleTime = now.GetDistantFuture();
+ TimeMilli maxOnlinkPrefixStaleTime = now;
+ bool requireCheckStaleOnlinkPrefix = false;
+
+ OT_ASSERT(mIsRunning);
+
+ // The stale timer triggers sending RS to check the state of On-Link/OMR prefixes and host RA messages.
+ // The rules for calculating the next stale time:
+ // 1. If BR learns RA header from Host daemons, it should send RS when the RA header is stale.
+ // 2. If BR discovered any on-link prefix, it should send RS when all on-link prefixes are stale.
+ // 3. If BR discovered any OMR prefix, it should send RS when the first OMR prefix is stale.
+
+ // Check for stale Router Advertisement Message if learnt from Host.
+ if (mLearntRouterAdvMessageFromHost)
+ {
+ TimeMilli routerAdvMessageStaleTime = mTimeRouterAdvMessageLastUpdate + Time::SecToMsec(kRtrAdvStaleTime);
+
+ nextStaleTime = OT_MIN(nextStaleTime, routerAdvMessageStaleTime);
+ }
+
+ for (ExternalPrefix &externalPrefix : mDiscoveredPrefixes)
+ {
+ TimeMilli prefixStaleTime = externalPrefix.GetStaleTime();
+
+ if (externalPrefix.mIsOnLinkPrefix)
+ {
+ if (!externalPrefix.IsDeprecated())
+ {
+ // Check for least recent stale On-Link Prefixes if BR is not advertising local On-Link Prefix.
+ maxOnlinkPrefixStaleTime = OT_MAX(maxOnlinkPrefixStaleTime, prefixStaleTime);
+ requireCheckStaleOnlinkPrefix = true;
+ }
+ }
+ else
+ {
+ // Check for most recent stale OMR Prefixes
+ nextStaleTime = OT_MIN(nextStaleTime, prefixStaleTime);
+ }
+ }
+
+ if (requireCheckStaleOnlinkPrefix)
+ {
+ nextStaleTime = OT_MIN(nextStaleTime, maxOnlinkPrefixStaleTime);
+ }
+
+ if (nextStaleTime == now.GetDistantFuture())
+ {
+ if (mDiscoveredPrefixStaleTimer.IsRunning())
+ {
+ otLogDebgBr("Prefix stale timer stopped");
+ }
+ mDiscoveredPrefixStaleTimer.Stop();
+ }
+ else
+ {
+ mDiscoveredPrefixStaleTimer.FireAt(nextStaleTime);
+ otLogDebgBr("Prefix stale timer scheduled in %lu ms", nextStaleTime - now);
+ }
+}
+
} // namespace BorderRouter
} // namespace ot
diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp
index 4e4354e..37c89b4 100644
--- a/src/core/border_router/routing_manager.hpp
+++ b/src/core/border_router/routing_manager.hpp
@@ -315,6 +315,7 @@
void InvalidateAllDiscoveredPrefixes(void);
bool NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const;
bool UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage *aRouterAdvMessage);
+ void ResetDiscoveredPrefixStaleTimer(void);
static bool IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig);
static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix);
@@ -370,6 +371,7 @@
// and updated with RA messages initiated from infra interface.
RouterAdv::RouterAdvMessage mRouterAdvMessage;
TimeMilli mTimeRouterAdvMessageLastUpdate;
+ bool mLearntRouterAdvMessageFromHost;
TimerMilli mDiscoveredPrefixInvalidTimer;
TimerMilli mDiscoveredPrefixStaleTimer;