mirror of
https://github.com/espressif/openthread.git
synced 2026-08-31 14:29:54 +00:00
[border-router] detect DHCPv6-PD prefix conflict with on-link prefixes (#12346)
This commit updates `RoutingManager` to detect if a delegated DHCPv6 PD prefix conflicts with any on-link prefix advertised on the infrastructure link. This protects against potential DHCPv6 server misbehavior and bugs where the same prefix might be assigned to multiple requesters. If a conflict is detected, the delegated PD prefix is marked as conflicted and is no longer used as the OMR prefix. Instead, we revert to using the locally generated OMR prefix. If the conflict is resolved, the delegated PD prefix is used again. A new unit test `TestDhcp6PdConflict()` is added to verify this behavior.
This commit is contained in:
@@ -865,7 +865,8 @@ void RoutingManager::OmrPrefixManager::UpdateLocalPrefix(void)
|
||||
{
|
||||
case kOmrConfigAuto:
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
|
||||
if (Get<RoutingManager>().mPdPrefixManager.HasPrefix())
|
||||
if (Get<RoutingManager>().mPdPrefixManager.HasPrefix() &&
|
||||
!Get<RoutingManager>().mPdPrefixManager.HasConflictWithOnLinkPrefixes())
|
||||
{
|
||||
if (mLocalPrefix.GetPrefix() != Get<RoutingManager>().mPdPrefixManager.GetPrefix())
|
||||
{
|
||||
@@ -877,6 +878,7 @@ void RoutingManager::OmrPrefixManager::UpdateLocalPrefix(void)
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
if (mLocalPrefix.GetPrefix() != mGeneratedPrefix)
|
||||
{
|
||||
RemoveLocalFromNetData();
|
||||
@@ -2504,6 +2506,7 @@ void RoutingManager::TxRaInfo::CalculateHash(const RouterAdvert::RxMessage &aRaM
|
||||
RoutingManager::PdPrefixManager::PdPrefixManager(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mState(kDhcp6PdStateDisabled)
|
||||
, mConflicted(false)
|
||||
, mNumPlatformPioProcessed(0)
|
||||
, mNumPlatformRaReceived(0)
|
||||
, mLastPlatformRaTime(0)
|
||||
@@ -2636,6 +2639,8 @@ void RoutingManager::PdPrefixManager::WithdrawPrefix(void)
|
||||
LogInfo("Withdrew DHCPv6 PD prefix %s", mPrefix.GetPrefix().ToString().AsCString());
|
||||
|
||||
mPrefix.Clear();
|
||||
mConflicted = false;
|
||||
|
||||
mTimer.Stop();
|
||||
|
||||
Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
|
||||
@@ -2727,7 +2732,10 @@ void RoutingManager::PdPrefixManager::ApplyFavoredPrefix(const PdPrefix &aFavore
|
||||
if (aFavoredPrefix.IsFavoredOver(mPrefix))
|
||||
{
|
||||
mPrefix = aFavoredPrefix;
|
||||
|
||||
LogInfo("DHCPv6 PD prefix set to %s", mPrefix.GetPrefix().ToString().AsCString());
|
||||
CheckConflictWithOnLinkPrefixes();
|
||||
|
||||
Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
|
||||
|
||||
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
|
||||
@@ -2783,6 +2791,35 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void RoutingManager::PdPrefixManager::CheckConflictWithOnLinkPrefixes(void)
|
||||
{
|
||||
// Checks if the delegated PD prefix is also seen as an on-link
|
||||
// prefix. This protects against DHCPv6-PD server misbehavior
|
||||
// assigning the same prefix to multiple requesters.
|
||||
//
|
||||
// If a conflict is detected, the delegated PD prefix is no longer
|
||||
// used as OMR prefix, reverting back to using the local OMR
|
||||
// prefix. Once the conflict is resolved, the PD prefix can be
|
||||
// used as OMR prefix again.
|
||||
|
||||
bool conflicted;
|
||||
|
||||
VerifyOrExit(HasPrefix());
|
||||
|
||||
conflicted = Get<RxRaTracker>().IsPrefixOnLink(mPrefix.GetPrefix());
|
||||
VerifyOrExit(conflicted != mConflicted);
|
||||
|
||||
mConflicted = conflicted;
|
||||
|
||||
LogInfo("DHCPv6 PD prefix %s %sconflicts with the advertised on-link prefixes",
|
||||
mPrefix.GetPrefix().ToString().AsCString(), mConflicted ? "" : "no longer ");
|
||||
|
||||
Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
|
||||
|
||||
void RoutingManager::PdPrefixManager::HandleRecordHistoryTask(void)
|
||||
|
||||
@@ -961,8 +961,10 @@ private:
|
||||
void Start(void) { Evaluate(); }
|
||||
void Stop(void) { Evaluate(); }
|
||||
bool HasPrefix(void) const { return !mPrefix.IsEmpty(); }
|
||||
bool HasConflictWithOnLinkPrefixes(void) const { return mConflicted; }
|
||||
const Ip6::Prefix &GetPrefix(void) const { return mPrefix.GetPrefix(); }
|
||||
State GetState(void) const { return mState; }
|
||||
void CheckConflictWithOnLinkPrefixes(void);
|
||||
|
||||
void ProcessPrefixesFromRa(const InfraIf::Icmp6Packet &aRaPacket);
|
||||
void ProcessPrefix(const Dhcp6PdPrefix &aPrefix);
|
||||
@@ -1000,6 +1002,7 @@ private:
|
||||
#endif
|
||||
|
||||
State mState;
|
||||
bool mConflicted;
|
||||
uint32_t mNumPlatformPioProcessed;
|
||||
uint32_t mNumPlatformRaReceived;
|
||||
TimeMilli mLastPlatformRaTime;
|
||||
|
||||
@@ -856,6 +856,16 @@ void RxRaTracker::Evaluate(void)
|
||||
mEventTask.Post();
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Check for possible conflict between delegated DHCPv6-PD prefix
|
||||
// and any of the observed on-link prefixes. This protects against
|
||||
// DHCPv6-PD server misbehavior (assigning same prefix to multiple
|
||||
// requesters).
|
||||
|
||||
Get<RoutingManager>().mPdPrefixManager.CheckConflictWithOnLinkPrefixes();
|
||||
#endif
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Schedule timers
|
||||
|
||||
@@ -1176,6 +1186,26 @@ exit:
|
||||
return isOnLink;
|
||||
}
|
||||
|
||||
bool RxRaTracker::IsPrefixOnLink(const Ip6::Prefix &aPrefix) const
|
||||
{
|
||||
bool isOnLink = false;
|
||||
|
||||
for (const Router &router : mRouters)
|
||||
{
|
||||
for (const OnLinkPrefix &onLinkPrefix : router.mOnLinkPrefixes)
|
||||
{
|
||||
if (aPrefix == onLinkPrefix.GetPrefix())
|
||||
{
|
||||
isOnLink = true;
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return isOnLink;
|
||||
}
|
||||
|
||||
bool RxRaTracker::IsAddressReachableThroughExplicitRoute(const Ip6::Address &aAddress) const
|
||||
{
|
||||
// Checks whether the `aAddress` matches any discovered route
|
||||
|
||||
@@ -307,6 +307,16 @@ public:
|
||||
*/
|
||||
bool IsAddressReachableThroughExplicitRoute(const Ip6::Address &aAddress) const;
|
||||
|
||||
/**
|
||||
* Indicates whether a given prefix is seen as an on-link prefix in any tracked RA.
|
||||
*
|
||||
* @param[in] aPrefix The IPv6 prefix to check.
|
||||
*
|
||||
* @retval TRUE The prefix is on-link.
|
||||
* @retval FALSE The prefix is not on-link.
|
||||
*/
|
||||
bool IsPrefixOnLink(const Ip6::Prefix &aPrefix) const;
|
||||
|
||||
// Callbacks notifying of changes
|
||||
void HandleLocalOnLinkPrefixChanged(void);
|
||||
|
||||
|
||||
@@ -5101,6 +5101,126 @@ void TestDhcp6Pd(void)
|
||||
FinalizeTest();
|
||||
}
|
||||
|
||||
void TestDhcp6PdConflict(void)
|
||||
{
|
||||
Ip6::Prefix localOmr;
|
||||
Ip6::Prefix pdPrefix = PrefixFromString("2001:db8:dead:beef::", 64);
|
||||
Ip6::Address routerAddressA = AddressFromString("fd00::aaaa");
|
||||
uint16_t heapAllocations;
|
||||
|
||||
Log("--------------------------------------------------------------------------------------------");
|
||||
Log("TestDhcp6PdConflict");
|
||||
|
||||
InitTest(/* aEnableBorderRouting */ true);
|
||||
heapAllocations = sHeapAllocatedPtrs.GetLength();
|
||||
|
||||
sInstance->Get<BorderRouter::RoutingManager>().SetDhcp6PdEnabled(true);
|
||||
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOmrPrefix(localOmr));
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Report a PD prefix and check that its used as OMR prefix
|
||||
|
||||
Log("Report DHCPv6-PD prefix");
|
||||
ReportPdPrefixesAsRa({Pio(pdPrefix, kValidLitime, kPreferredLifetime)});
|
||||
|
||||
sExpectedRios.Add(pdPrefix);
|
||||
AdvanceTime(10 * 1000);
|
||||
|
||||
VerifyPdOmrPrefix(pdPrefix);
|
||||
VerifyOrQuit(sExpectedRios.SawAll());
|
||||
VerifyOmrPrefixInNetData(pdPrefix, /* aDefaultRoute */ false);
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Advertise the same prefix as on-link from a router.
|
||||
|
||||
Log("Router A advertises PD prefix as on-link");
|
||||
SendRouterAdvert(routerAddressA, {Pio(pdPrefix, 200, 200)});
|
||||
|
||||
// Check that the PD prefix is no longer used as OMR prefix due to
|
||||
// conflict. The OMR should switch back to local OMR.
|
||||
|
||||
sExpectedRios.Clear();
|
||||
sExpectedRios.Add(localOmr);
|
||||
|
||||
AdvanceTime(10 * 1000);
|
||||
VerifyOrQuit(sExpectedRios.SawAll());
|
||||
|
||||
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ true);
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Wait for the PIO from Router A to expire. We renew the PD
|
||||
// prefix during this time to ensure it stays valid.
|
||||
|
||||
Log("Wait for Router A PIO to expire");
|
||||
|
||||
AdvanceTime(300 * 1000);
|
||||
ReportPdPrefixesAsRa({Pio(pdPrefix, kValidLitime, kPreferredLifetime)});
|
||||
AdvanceTime(300 * 1000);
|
||||
|
||||
// Router A entry should be expired and removed. The PD prefix is still
|
||||
// valid (renewed). The conflict should be resolved. Validate that PD
|
||||
// prefix is again being use as OMR prefix.
|
||||
|
||||
sExpectedRios.Clear();
|
||||
sExpectedRios.Add(pdPrefix);
|
||||
|
||||
AdvanceTime(100 * 1000);
|
||||
|
||||
VerifyPdOmrPrefix(pdPrefix);
|
||||
VerifyOrQuit(sExpectedRios.SawAll());
|
||||
VerifyOmrPrefixInNetData(pdPrefix, /* aDefaultRoute */ false);
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Remove the PD prefix
|
||||
|
||||
ReportPdPrefixesAsRa({Pio(pdPrefix, 0, 0)});
|
||||
|
||||
AdvanceTime(1 * 1000);
|
||||
VerifyNoPdOmrPrefix();
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Now Advertise the PD prefix as on-link from a router first.
|
||||
|
||||
Log("Router A advertises PD prefix as on-link before delegating the prefix");
|
||||
SendRouterAdvert(routerAddressA, {Pio(pdPrefix, 200, 200)});
|
||||
|
||||
// Check that local OMR is used.
|
||||
|
||||
sExpectedRios.Clear();
|
||||
sExpectedRios.Add(localOmr);
|
||||
|
||||
AdvanceTime(10 * 1000);
|
||||
VerifyOrQuit(sExpectedRios.SawAll());
|
||||
|
||||
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ true);
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Report the same PD prefix. Validate that local OMR prefix is
|
||||
// still being used.
|
||||
|
||||
Log("Delegate PD prefix which conflicts with already advertised on-link prefix from router A");
|
||||
ReportPdPrefixesAsRa({Pio(pdPrefix, kValidLitime, kPreferredLifetime)});
|
||||
|
||||
sExpectedRios.Clear();
|
||||
sExpectedRios.Add(localOmr);
|
||||
|
||||
AdvanceTime(100 * 1000);
|
||||
VerifyOrQuit(sExpectedRios.SawAll());
|
||||
|
||||
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ true);
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(false));
|
||||
AdvanceTime(3000);
|
||||
|
||||
VerifyOrQuit(sHeapAllocatedPtrs.GetLength() <= heapAllocations);
|
||||
|
||||
Log("End of TestDhcp6PdConflict");
|
||||
|
||||
FinalizeTest();
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
|
||||
|
||||
static void HandleRdnssChanged(void *aContext)
|
||||
@@ -5378,6 +5498,7 @@ int main(void)
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
|
||||
ot::TestDhcp6Pd();
|
||||
ot::TestDhcp6PdConflict();
|
||||
#endif
|
||||
ot::TestRdnss();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user