[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.
This commit is contained in:
Marius Preda
2024-04-22 09:15:05 -07:00
committed by GitHub
parent 7ec0257b0e
commit 9af7203ff3
4 changed files with 155 additions and 50 deletions
+1 -1
View File
@@ -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
@@ -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
+117 -48
View File
@@ -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<const PrefixInfoOption &>(option).IsValid())
for (const Option &option : *aMessage)
{
continue;
}
mNumPlatformPioProcessed++;
entry.SetFrom(static_cast<const PrefixInfoOption &>(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<const PrefixInfoOption &>(option).IsValid())
{
continue;
}
mNumPlatformPioProcessed++;
entry.SetFrom(static_cast<const PrefixInfoOption &>(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<BorderRouter::RoutingManager>().ProcessPlatformGeneratedRa(aMessage, aLength);
}
extern "C" void otPlatBorderRoutingProcessDhcp6PdPrefix(otInstance *aInstance,
const otBorderRoutingPrefixTableEntry *aPrefixInfo)
{
AssertPointerIsNotNull(aPrefixInfo);
AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().ProcessDhcpPdPrefix(*aPrefixInfo);
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
} // namespace BorderRouter
+18 -1
View File
@@ -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);