From 9af7203ff38e365019851b9ef748197225570e18 Mon Sep 17 00:00:00 2001 From: Marius Preda Date: Mon, 22 Apr 2024 19:15:05 +0300 Subject: [PATCH] [routing-manager] add support for to use a prefix received from DHCPv6 PD (#10000) This commit adds support for adding a DHCPv6 delegated prefix to the Routing Manager via a new Open Thread API. Previously this feature was only supported using a platform generated RA on a Posix based BR. For platforms that can get the prefix directly from a DHCPv6 PD client API there is no point in creating a RA for setting the prefix. It's much easier to use a direct call to configure it. This new API uses an otBorderRoutingPrefixTableEntry structure as an input parameter and evaluates the prefix based on the same rules as one coming from a platform RA. The lifetime of the prefix can be updated by calling the function again with new time values. --- include/openthread/instance.h | 2 +- include/openthread/platform/border_routing.h | 19 +++ src/core/border_router/routing_manager.cpp | 165 +++++++++++++------ src/core/border_router/routing_manager.hpp | 19 ++- 4 files changed, 155 insertions(+), 50 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index a956397bb..ac135e860 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 (405) +#define OPENTHREAD_API_VERSION (406) /** * @addtogroup api-instance diff --git a/include/openthread/platform/border_routing.h b/include/openthread/platform/border_routing.h index eeb825c0d..aed4bdb1b 100644 --- a/include/openthread/platform/border_routing.h +++ b/include/openthread/platform/border_routing.h @@ -65,6 +65,25 @@ extern "C" { */ extern void otPlatBorderRoutingProcessIcmp6Ra(otInstance *aInstance, const uint8_t *aMessage, uint16_t aLength); +/** + * Process a prefix received from the DHCPv6 PD Server. The prefix is received on + * the DHCPv6 PD client callback and provided to the Routing Manager via this + * API. + * + * The prefix lifetime can be updated by calling the function again with updated time values. + * If the preferred lifetime of the prefix is set to 0, the prefix becomes deprecated. + * When this function is called multiple times, the smallest prefix is preferred as this rule allows + * choosing a GUA instead of a ULA. + * + * Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE`. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aPrefixInfo A pointer to the prefix information structure + * + */ +extern void otPlatBorderRoutingProcessDhcp6PdPrefix(otInstance *aInstance, + const otBorderRoutingPrefixTableEntry *aPrefixInfo); + #ifdef __cplusplus } #endif diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 3a596e6f6..61441084b 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -1744,6 +1744,15 @@ void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const RouteInfoOption mLastUpdateTime = TimerMilli::GetNow(); } +void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const PrefixTableEntry &aPrefixTableEntry) +{ + mPrefix = AsCoreType(&aPrefixTableEntry.mPrefix); + mType = aPrefixTableEntry.mIsOnLink ? kTypeOnLink : kTypeRoute; + mValidLifetime = aPrefixTableEntry.mValidLifetime; + mShared.mPreferredLifetime = aPrefixTableEntry.mPreferredLifetime; + mLastUpdateTime = TimerMilli::GetNow(); +} + bool RoutingManager::DiscoveredPrefixTable::Entry::operator==(const Entry &aOther) const { return (mType == aOther.mType) && (mPrefix == aOther.mPrefix); @@ -1776,6 +1785,11 @@ TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetStaleTime(void) const return mLastUpdateTime + TimeMilli::SecToMsec(delay); } +TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetStaleTimeFromPreferredLifetime(void) const +{ + return mLastUpdateTime + CalculateExpireDelay(GetPreferredLifetime()); +} + bool RoutingManager::DiscoveredPrefixTable::Entry::IsDeprecated(void) const { OT_ASSERT(IsOnLinkPrefix()); @@ -3681,70 +3695,74 @@ void RoutingManager::PdPrefixManager::ProcessPlatformGeneratedRa(const uint8_t * Error error = kErrorNone; RouterAdvert::Icmp6Packet packet; - VerifyOrExit(IsRunning(), LogWarn("Ignore platform generated RA since PD is disabled or not running.")); - packet.Init(aRouterAdvert, aLength); - error = Process(RouterAdvert::RxMessage(packet)); - mNumPlatformRaReceived++; - mLastPlatformRaTime = TimerMilli::GetNow(); + if (mEnabled) + { + packet.Init(aRouterAdvert, aLength); + RouterAdvert::RxMessage aMessage = RouterAdvert::RxMessage(packet); + + error = Process(&aMessage, nullptr); + mNumPlatformRaReceived++; + mLastPlatformRaTime = TimerMilli::GetNow(); + } + else + { + LogWarn("Ignore platform generated RA since PD is disabled."); + } -exit: if (error != kErrorNone) { LogCrit("Failed to process platform generated ND OnMeshPrefix: %s", ErrorToString(error)); } } -Error RoutingManager::PdPrefixManager::Process(const RouterAdvert::RxMessage &aMessage) +void RoutingManager::PdPrefixManager::ProcessDhcpPdPrefix(const PrefixTableEntry &aPrefixTableEntry) { - Error error = kErrorNone; + Error error = kErrorNone; + + VerifyOrExit(mEnabled, LogWarn("Ignore DHCPv6 delegated prefix since PD is disabled.")); + + error = Process(nullptr, &aPrefixTableEntry); + +exit: + + if (error != kErrorNone) + { + LogCrit("Failed to process DHCPv6 delegated prefix: %s", ErrorToString(error)); + } +} + +Error RoutingManager::PdPrefixManager::Process(const RouterAdvert::RxMessage *aMessage, + const PrefixTableEntry *aPrefixTableEntry) +{ + bool currentPrefixUpdated = false; + Error error = kErrorNone; DiscoveredPrefixTable::Entry favoredEntry; - bool currentPrefixUpdated = false; + DiscoveredPrefixTable::Entry entry; - VerifyOrExit(aMessage.IsValid(), error = kErrorParse); favoredEntry.Clear(); - for (const Option &option : aMessage) + // aMessage or aPrefixTableEntry must be different from null + if (aMessage != nullptr) { - DiscoveredPrefixTable::Entry entry; + VerifyOrExit(aMessage->IsValid(), error = kErrorParse); - if (option.GetType() != Option::kTypePrefixInfo || !static_cast(option).IsValid()) + for (const Option &option : *aMessage) { - continue; - } - - mNumPlatformPioProcessed++; - entry.SetFrom(static_cast(option)); - - if (!IsValidPdPrefix(entry.GetPrefix())) - { - LogWarn("PdPrefixManager: Ignore invalid PIO entry %s", entry.GetPrefix().ToString().AsCString()); - continue; - } - - entry.mPrefix.Tidy(); - entry.mPrefix.SetLength(kOmrPrefixLength); - - // The platform may send another RA message to announce that the current prefix we are using is no longer - // preferred or no longer valid. - if (entry.GetPrefix() == GetPrefix()) - { - currentPrefixUpdated = true; - mPrefix = entry; - } - - if (entry.IsDeprecated()) - { - continue; - } - - // Some platforms may delegate us more than one prefixes. We will pick the smallest one. This is a simple rule - // to pick the GUA prefix from the RA messages since GUA prefixes (2000::/3) are always smaller than ULA - // prefixes (fc00::/7). - if (favoredEntry.GetPrefix().GetLength() == 0 || entry.GetPrefix() < favoredEntry.GetPrefix()) - { - favoredEntry = entry; + if (option.GetType() != Option::kTypePrefixInfo || !static_cast(option).IsValid()) + { + continue; + } + + mNumPlatformPioProcessed++; + entry.SetFrom(static_cast(option)); + currentPrefixUpdated |= ProcessPrefixEntry(entry, favoredEntry); } } + else // aPrefixTableEntry != nullptr + { + entry.SetFrom(*aPrefixTableEntry); + currentPrefixUpdated = ProcessPrefixEntry(entry, favoredEntry); + } if (currentPrefixUpdated && mPrefix.IsDeprecated()) { @@ -3762,7 +3780,15 @@ Error RoutingManager::PdPrefixManager::Process(const RouterAdvert::RxMessage &aM exit: if (HasPrefix()) { - mTimer.FireAt(mPrefix.GetStaleTime()); + // If prefix has been set from aPrefixTableEntry use only preferred lifetime to calculate stale time + if (aPrefixTableEntry) + { + mTimer.FireAt(mPrefix.GetStaleTimeFromPreferredLifetime()); + } + else + { + mTimer.FireAt(mPrefix.GetStaleTime()); + } } else { @@ -3772,6 +3798,41 @@ exit: return error; } +bool RoutingManager::PdPrefixManager::ProcessPrefixEntry(DiscoveredPrefixTable::Entry &aEntry, + DiscoveredPrefixTable::Entry &aFavoredEntry) +{ + bool currentPrefixUpdated = false; + + if (!IsValidPdPrefix(aEntry.GetPrefix())) + { + LogWarn("PdPrefixManager: Ignore invalid prefix entry %s", aEntry.GetPrefix().ToString().AsCString()); + ExitNow(); + } + + aEntry.mPrefix.SetLength(kOmrPrefixLength); + aEntry.mPrefix.Tidy(); + + // Check if there is an update to the current prefix. The valid or preferred lifetime might change. + if (aEntry.GetPrefix() == GetPrefix()) + { + currentPrefixUpdated = true; + mPrefix = aEntry; + } + + VerifyOrExit(!aEntry.IsDeprecated()); + + // Some platforms may delegate us more than one prefix. We will pick the smallest one. This is a simple rule + // to pick the GUA prefix from the RA messages since GUA prefixes (2000::/3) are always smaller than ULA + // prefixes (fc00::/7). + if (aFavoredEntry.GetPrefix().GetLength() == 0 || aEntry.GetPrefix() < aFavoredEntry.GetPrefix()) + { + aFavoredEntry = aEntry; + } + +exit: + return currentPrefixUpdated; +} + void RoutingManager::PdPrefixManager::SetEnabled(bool aEnabled) { Dhcp6PdState oldState = GetState(); @@ -3788,6 +3849,14 @@ extern "C" void otPlatBorderRoutingProcessIcmp6Ra(otInstance *aInstance, const u { AsCoreType(aInstance).Get().ProcessPlatformGeneratedRa(aMessage, aLength); } + +extern "C" void otPlatBorderRoutingProcessDhcp6PdPrefix(otInstance *aInstance, + const otBorderRoutingPrefixTableEntry *aPrefixInfo) +{ + AssertPointerIsNotNull(aPrefixInfo); + + AsCoreType(aInstance).Get().ProcessDhcpPdPrefix(*aPrefixInfo); +} #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE } // namespace BorderRouter diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 37de6eca3..d0af0e2fc 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -540,6 +540,19 @@ public: mPdPrefixManager.ProcessPlatformGeneratedRa(aRouterAdvert, aLength); } + /** + * Handles a prefix delegated from a DHCPv6 PD server. The prefix is received on the DHCPv6 PD client callback and + * then this method can be used to configure the prefix in the Routing Manager module. + * + * Note: This method is a part of DHCPv6 PD support on Thread border routers. For platforms where it doesn't make + * sense to generate a RA to set a DHCPv6 PD prefix this method can be used to set the prefix directly. The lifetime + * of the prefix can be updated by calling the function again with updated values. + * + * @param[in] aPrefixInfo Prefix information structure received from the DHCPv6 PD server. + * + */ + void ProcessDhcpPdPrefix(const PrefixTableEntry &aPrefixInfo) { mPdPrefixManager.ProcessDhcpPdPrefix(aPrefixInfo); } + /** * Enables / Disables the functions for DHCPv6 PD. * @@ -753,6 +766,7 @@ private: void SetFrom(const RouterAdvert::Header &aRaHeader); void SetFrom(const PrefixInfoOption &aPio); void SetFrom(const RouteInfoOption &aRio); + void SetFrom(const PrefixTableEntry &aPrefixTableEntry); Type GetType(void) const { return mType; } bool IsOnLinkPrefix(void) const { return (mType == kTypeOnLink); } bool IsRoutePrefix(void) const { return (mType == kTypeRoute); } @@ -762,6 +776,7 @@ private: void ClearValidLifetime(void) { mValidLifetime = 0; } TimeMilli GetExpireTime(void) const; TimeMilli GetStaleTime(void) const; + TimeMilli GetStaleTimeFromPreferredLifetime(void) const; RoutePreference GetPreference(void) const; bool operator==(const Entry &aOther) const; bool Matches(const Matcher &aMatcher) const; @@ -1276,6 +1291,7 @@ private: Dhcp6PdState GetState(void) const; void ProcessPlatformGeneratedRa(const uint8_t *aRouterAdvert, uint16_t aLength); + void ProcessDhcpPdPrefix(const PrefixTableEntry &aPrefixTableEntry); Error GetPrefixInfo(PrefixTableEntry &aInfo) const; Error GetProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo) const; void HandleTimer(void) { WithdrawPrefix(); } @@ -1290,7 +1306,8 @@ private: } private: - Error Process(const RouterAdvert::RxMessage &aMessage); + Error Process(const RouterAdvert::RxMessage *aMessage, const PrefixTableEntry *aPrefixTableEntry); + bool ProcessPrefixEntry(DiscoveredPrefixTable::Entry &aEntry, DiscoveredPrefixTable::Entry &aFavoredEntry); void EvaluateStateChange(Dhcp6PdState aOldState); void WithdrawPrefix(void); void StartStop(bool aStart);