[routing-manager] initial PD support with platform generated RA (#9050)

This commit adds the following new API:
- otBorderRoutingProcessIcmp6Ra

And related APIs:
- otBorderRoutingDhcp6PdSetEnabled
- otBorderRoutingGetPdOmrPrefix

They are used to support the DHCPv6 PD on Thread Border Routers. On a
typical OS, the DHCPv6 Client of the platform network stack will
request a IPv6 subnet via DHCPv6-PD, then invoke the daemon for
sending router advertisements on the given interface.

With this commit, the RA will be catched by the processTransmit in
netif.cpp, which will then pass the message to
otBorderRoutingAddPrefixByRouterAdvertisement, RoutingManager will
then add the prefix according to the PIO in the router advertisement,
and take care of the lifetime of the prefix added.
This commit is contained in:
Song GUO
2023-06-08 13:52:39 +08:00
committed by GitHub
parent edc1a3fe23
commit 9cb667c01f
14 changed files with 789 additions and 26 deletions
+1
View File
@@ -89,6 +89,7 @@ ot_option(OT_BORDER_AGENT OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE "border agent")
ot_option(OT_BORDER_AGENT_ID OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE "create and save border agent ID")
ot_option(OT_BORDER_ROUTER OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE "border router")
ot_option(OT_BORDER_ROUTING OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE "border routing")
ot_option(OT_BORDER_ROUTING_DHCP6_PD OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE "dhcpv6 pd support in border routing")
ot_option(OT_BORDER_ROUTING_COUNTERS OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE "border routing counters")
ot_option(OT_CHANNEL_MANAGER OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE "channel manager")
ot_option(OT_CHANNEL_MONITOR OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE "channel monitor")
+1
View File
@@ -100,6 +100,7 @@ dist_openthread_HEADERS = $(openthread_headers)
ot_platform_headers = \
openthread/platform/alarm-micro.h \
openthread/platform/alarm-milli.h \
openthread/platform/border_routing.h \
openthread/platform/crypto.h \
openthread/platform/debug_uart.h \
openthread/platform/diag.h \
+1
View File
@@ -84,6 +84,7 @@ source_set("openthread") {
"ping_sender.h",
"platform/alarm-micro.h",
"platform/alarm-milli.h",
"platform/border_routing.h",
"platform/crypto.h",
"platform/debug_uart.h",
"platform/diag.h",
+47
View File
@@ -118,6 +118,17 @@ typedef enum
OT_BORDER_ROUTING_STATE_RUNNING, ///< Routing Manager is initialized, enabled, and running.
} otBorderRoutingState;
/**
* This enumeration represents the state of DHCPv6 Prefix Delegation State.
*
*/
typedef enum
{
OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED, ///< DHCPv6 PD is disabled on the border router.
OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED, ///< DHCPv6 PD in enabled but won't try to request and publish a prefix.
OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING, ///< DHCPv6 PD is enabled and will try to request and publish a prefix.
} otBorderRoutingDhcp6PdState;
/**
* Initializes the Border Routing Manager on given infrastructure interface.
*
@@ -209,15 +220,40 @@ void otBorderRoutingClearRouteInfoOptionPreference(otInstance *aInstance);
* Thread network if there isn't already an OMR prefix. This prefix can be reached
* from the local Wi-Fi or Ethernet network.
*
* Note: When DHCPv6 PD is enabled, the border router may publish the prefix from
* DHCPv6 PD.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[out] aPrefix A pointer to where the prefix will be output to.
*
* @retval OT_ERROR_INVALID_STATE The Border Routing Manager is not initialized yet.
* @retval OT_ERROR_NONE Successfully retrieved the OMR prefix.
*
* @sa otBorderRoutingGetPdOmrPrefix
*
*/
otError otBorderRoutingGetOmrPrefix(otInstance *aInstance, otIp6Prefix *aPrefix);
/**
* Gets the DHCPv6 Prefix Delegation (PD) provided off-mesh-routable (OMR) prefix.
*
* Only mPrefix, mValidLifetime and mPreferredLifetime fields are used in the returned prefix info.
*
* `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE` must be enabled.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[out] aPrefixInfo A pointer to where the prefix info will be output to.
*
* @retval OT_ERROR_NONE Successfully retrieved the OMR prefix.
* @retval OT_ERROR_INVALID_STATE The Border Routing Manager is not initialized yet.
* @retval OT_ERROR_NOT_FOUND There are no valid PD prefix on this BR.
*
* @sa otBorderRoutingGetOmrPrefix
* @sa otPlatBorderRoutingProcessIcmp6Ra
*
*/
otError otBorderRoutingGetPdOmrPrefix(otInstance *aInstance, otBorderRoutingPrefixTableEntry *aPrefixInfo);
/**
* Gets the currently favored Off-Mesh-Routable (OMR) Prefix.
*
@@ -326,6 +362,17 @@ otError otBorderRoutingGetNextPrefixTableEntry(otInstance
otBorderRoutingPrefixTableIterator *aIterator,
otBorderRoutingPrefixTableEntry *aEntry);
/**
* Enables / Disables DHCPv6 Prefix Delegation.
*
* `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE` must be enabled.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aEnabled Whether to accept platform generated RA messages.
*
*/
void otBorderRoutingDhcp6PdSetEnabled(otInstance *aInstance, bool aEnabled);
/**
* @}
*
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (328)
#define OPENTHREAD_API_VERSION (329)
/**
* @addtogroup api-instance
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2023, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* @brief
* This file includes the platform abstraction for border routing manager.
*/
#ifndef OPENTHREAD_PLATFORM_BORDER_ROUTER_H_
#define OPENTHREAD_PLATFORM_BORDER_ROUTER_H_
#include <stdint.h>
#include <openthread/error.h>
#include <openthread/instance.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Handles ICMP6 RA messages received on the Thread interface on the platform.
*
* The `aMessage` should point to a buffer of a valid ICMPv6 message (without IP headers) with router advertisement as
* the value of type field of the message.
*
* When DHCPv6 PD is disabled, the message will be dropped silently.
*
* Note: RA messages will not be forwarded into Thread networks, while for many platforms, RA messages is the way of
* distributing a prefix and other infomations to the downstream network. The typical usecase of this function is to
* handle the router advertisement messages sent by the platform as a result of DHCPv6 Prefix Delegation.
*
* Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE`.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aMessage A pointer to an ICMPv6 RouterAdvertisement message.
* @param[in] aLength The length of ICMPv6 RouterAdvertisement message.
*
*/
extern void otPlatBorderRoutingProcessIcmp6Ra(otInstance *aInstance, const uint8_t *aMessage, uint16_t aLength);
#ifdef __cplusplus
}
#endif
#endif // OPENTHREAD_PLATFORM_BORDER_ROUTER_H_
+1
View File
@@ -93,6 +93,7 @@ OT_CLANG_TIDY_BUILD_OPTS=(
'-DOT_BORDER_AGENT=ON'
'-DOT_BORDER_ROUTER=ON'
'-DOT_BORDER_ROUTING=ON'
'-DOT_BORDER_ROUTING_DHCP6_PD=ON'
'-DOT_CHANNEL_MANAGER=ON'
'-DOT_CHANNEL_MONITOR=ON'
'-DOT_COAP=ON'
+23
View File
@@ -36,6 +36,7 @@
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#include <openthread/border_routing.h>
#include <openthread/platform/border_routing.h>
#include "border_router/routing_manager.hpp"
#include "common/instance.hpp"
@@ -79,6 +80,15 @@ otError otBorderRoutingGetOmrPrefix(otInstance *aInstance, otIp6Prefix *aPrefix)
return AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().GetOmrPrefix(AsCoreType(aPrefix));
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
otError otBorderRoutingGetPdOmrPrefix(otInstance *aInstance, otBorderRoutingPrefixTableEntry *aPrefixInfo)
{
AssertPointerIsNotNull(aPrefixInfo);
return AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().GetPdOmrPrefix(*aPrefixInfo);
}
#endif
otError otBorderRoutingGetFavoredOmrPrefix(otInstance *aInstance, otIp6Prefix *aPrefix, otRoutePreference *aPreference)
{
otError error;
@@ -145,4 +155,17 @@ otError otBorderRoutingGetNextPrefixTableEntry(otInstance
return AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().GetNextPrefixTableEntry(*aIterator, *aEntry);
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
void otBorderRoutingDhcp6PdSetEnabled(otInstance *aInstance, bool aEnabled)
{
AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().SetDhcp6PdEnabled(aEnabled);
}
otBorderRoutingDhcp6PdState otBorderRoutingDhcp6PdGetState(otInstance *aInstance)
{
return static_cast<otBorderRoutingDhcp6PdState>(
AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().GetDhcp6PdState());
}
#endif
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+215 -11
View File
@@ -39,6 +39,7 @@
#include <string.h>
#include <openthread/border_router.h>
#include <openthread/platform/border_routing.h>
#include <openthread/platform/infra_if.h>
#include "common/code_utils.hpp"
@@ -47,11 +48,14 @@
#include "common/locator_getters.hpp"
#include "common/log.hpp"
#include "common/num_utils.hpp"
#include "common/numeric_limits.hpp"
#include "common/random.hpp"
#include "common/settings.hpp"
#include "meshcop/extended_panid.hpp"
#include "net/ip6.hpp"
#include "net/nat64_translator.hpp"
#include "net/nd6.hpp"
#include "thread/mle_router.hpp"
#include "thread/network_data_leader.hpp"
#include "thread/network_data_local.hpp"
#include "thread/network_data_notifier.hpp"
@@ -75,6 +79,9 @@ RoutingManager::RoutingManager(Instance &aInstance)
, mRoutePublisher(aInstance)
#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
, mNat64PrefixManager(aInstance)
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
, mPdPrefixManager(aInstance)
#endif
, mRsSender(aInstance)
, mDiscoveredPrefixStaleTimer(aInstance)
@@ -183,12 +190,25 @@ Error RoutingManager::GetOmrPrefix(Ip6::Prefix &aPrefix) const
Error error = kErrorNone;
VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
aPrefix = mOmrPrefixManager.GetLocalPrefix().GetPrefix();
aPrefix = mOmrPrefixManager.GetGeneratedPrefix();
exit:
return error;
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
Error RoutingManager::GetPdOmrPrefix(PrefixTableEntry &aPrefixInfo) const
{
Error error = kErrorNone;
VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
error = mPdPrefixManager.GetPrefixInfo(aPrefixInfo);
exit:
return error;
}
#endif
Error RoutingManager::GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference) const
{
Error error = kErrorNone;
@@ -1939,13 +1959,11 @@ RoutingManager::OmrPrefixManager::OmrPrefixManager(Instance &aInstance)
void RoutingManager::OmrPrefixManager::Init(const Ip6::Prefix &aBrUlaPrefix)
{
mLocalPrefix.mPrefix = aBrUlaPrefix;
mLocalPrefix.mPrefix.SetSubnetId(kOmrPrefixSubnetId);
mLocalPrefix.mPrefix.SetLength(kOmrPrefixLength);
mLocalPrefix.mPreference = NetworkData::kRoutePreferenceLow;
mLocalPrefix.mIsDomainPrefix = false;
mGeneratedPrefix = aBrUlaPrefix;
mGeneratedPrefix.SetSubnetId(kOmrPrefixSubnetId);
mGeneratedPrefix.SetLength(kOmrPrefixLength);
LogInfo("Generated local OMR prefix: %s", mLocalPrefix.mPrefix.ToString().AsCString());
LogInfo("Generated local OMR prefix: %s", mGeneratedPrefix.ToString().AsCString());
}
void RoutingManager::OmrPrefixManager::Start(void) { DetermineFavoredPrefix(); }
@@ -1985,11 +2003,42 @@ void RoutingManager::OmrPrefixManager::Evaluate(void)
DetermineFavoredPrefix();
// Decide if we need to add or remove our local OMR prefix.
if (mFavoredPrefix.IsEmpty())
// Determine the local prefix and remove outdated prefix published by us.
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
if (Get<RoutingManager>().mPdPrefixManager.HasPrefix())
{
LogInfo("No favored OMR prefix found in Thread network");
if (mLocalPrefix.GetPrefix() != Get<RoutingManager>().mPdPrefixManager.GetPrefix())
{
RemoveLocalFromNetData();
mLocalPrefix.mPrefix = Get<RoutingManager>().mPdPrefixManager.GetPrefix();
mLocalPrefix.mPreference = RoutePreference::kRoutePreferenceMedium;
mLocalPrefix.mIsDomainPrefix = false;
LogInfo("Setting local OMR prefix to PD prefix: %s", mLocalPrefix.GetPrefix().ToString().AsCString());
}
}
else
#endif
if (mLocalPrefix.GetPrefix() != mGeneratedPrefix)
{
RemoveLocalFromNetData();
mLocalPrefix.mPrefix = mGeneratedPrefix;
mLocalPrefix.mPreference = RoutePreference::kRoutePreferenceLow;
mLocalPrefix.mIsDomainPrefix = false;
LogInfo("Setting local OMR prefix to generated prefix: %s", mLocalPrefix.GetPrefix().ToString().AsCString());
}
// Decide if we need to add or remove our local OMR prefix.
if (mFavoredPrefix.IsEmpty() || mFavoredPrefix.GetPreference() < mLocalPrefix.GetPreference())
{
if (mFavoredPrefix.IsEmpty())
{
LogInfo("No favored OMR prefix found in Thread network.");
}
else
{
LogInfo("Replacing favored OMR prefix %s with higher preference local prefix %s.",
mFavoredPrefix.GetPrefix().ToString().AsCString(), mLocalPrefix.GetPrefix().ToString().AsCString());
}
// The `mFavoredPrefix` remains empty if we fail to publish
// the local OMR prefix.
@@ -3153,6 +3202,161 @@ exit:
return;
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
RoutingManager::PdPrefixManager::PdPrefixManager(Instance &aInstance)
: InstanceLocator(aInstance)
, mEnabled(false)
, mTimer(aInstance)
{
mPrefix.Clear();
}
Error RoutingManager::PdPrefixManager::GetPrefixInfo(PrefixTableEntry &aInfo) const
{
Error error = kErrorNone;
VerifyOrExit(IsRunning() && HasPrefix(), error = kErrorNotFound);
aInfo.mPrefix = mPrefix.GetPrefix();
aInfo.mValidLifetime = mPrefix.GetValidLifetime();
aInfo.mPreferredLifetime = mPrefix.GetPreferredLifetime();
aInfo.mMsecSinceLastUpdate = TimerMilli::GetNow() - mPrefix.GetLastUpdateTime();
exit:
return error;
}
void RoutingManager::PdPrefixManager::WithdrawPrefix(void)
{
VerifyOrExit(HasPrefix());
LogInfo("Withdrew platform provided outdated prefix: %s", mPrefix.GetPrefix().ToString().AsCString());
mPrefix.Clear();
mTimer.Stop();
Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
exit:
return;
}
void RoutingManager::PdPrefixManager::ProcessPlatformGeneratedRa(const uint8_t *aRouterAdvert, const uint16_t aLength)
{
Error error = kErrorNone;
Ip6::Nd::RouterAdvertMessage::Icmp6Packet packet;
VerifyOrExit(mEnabled, LogWarn("Ignore platform generated RA since PD is disabled."));
packet.Init(aRouterAdvert, aLength);
error = Process(Ip6::Nd::RouterAdvertMessage(packet));
exit:
if (error != kErrorNone)
{
LogCrit("Failed to process platform generated ND OnMeshPrefix: %s", ErrorToString(error));
}
}
Error RoutingManager::PdPrefixManager::Process(const Ip6::Nd::RouterAdvertMessage &aMessage)
{
Error error = kErrorNone;
DiscoveredPrefixTable::Entry favoredEntry;
bool currentPrefixUpdated = false;
VerifyOrExit(aMessage.IsValid(), error = kErrorParse);
favoredEntry.Clear();
for (const Ip6::Nd::Option &option : aMessage)
{
DiscoveredPrefixTable::Entry entry;
if (option.GetType() != Ip6::Nd::Option::Type::kTypePrefixInfo ||
!static_cast<const Ip6::Nd::PrefixInfoOption &>(option).IsValid())
{
continue;
}
entry.SetFrom(static_cast<const Ip6::Nd::PrefixInfoOption &>(option));
if (!IsValidPdPrefix(entry.GetPrefix()))
{
LogWarn("PdPrefixManager: Ignore invalid PIO entry %s", entry.GetPrefix().ToString().AsCString());
continue;
}
entry.mPrefix.SetLength(kOmrPrefixLength);
entry.mPrefix.Tidy();
// 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 (currentPrefixUpdated && mPrefix.IsDeprecated())
{
LogInfo("PdPrefixManager: Prefix %s is deprecated", mPrefix.GetPrefix().ToString().AsCString());
mPrefix.Clear();
Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
}
if (!HasPrefix() || (favoredEntry.GetPrefix().GetLength() != 0 && favoredEntry.GetPrefix() < mPrefix.GetPrefix()))
{
mPrefix = favoredEntry;
Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
}
exit:
if (HasPrefix())
{
mTimer.FireAt(mPrefix.GetStaleTime());
}
else
{
mTimer.Stop();
}
return error;
}
void RoutingManager::PdPrefixManager::SetEnabled(bool aEnabled)
{
VerifyOrExit(mEnabled != aEnabled);
mEnabled = aEnabled;
if (!aEnabled)
{
WithdrawPrefix();
}
LogInfo("PdPrefixManager is %s", aEnabled ? "enabled" : "disabled");
exit:
return;
}
extern "C" void otPlatBorderRoutingProcessIcmp6Ra(otInstance *aInstance, const uint8_t *aMessage, uint16_t aLength)
{
AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().ProcessPlatfromGeneratedRa(aMessage, aLength);
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
} // namespace BorderRouter
} // namespace ot
+128 -1
View File
@@ -55,6 +55,7 @@
#include "common/error.hpp"
#include "common/linked_list.hpp"
#include "common/locator.hpp"
#include "common/message.hpp"
#include "common/notifier.hpp"
#include "common/pool.hpp"
#include "common/string.hpp"
@@ -111,6 +112,17 @@ public:
kStateRunning = OT_BORDER_ROUTING_STATE_RUNNING, ///< Initialized, enabled, and running.
};
/**
* This enumeration represents the states of DHCPv6 PD in `RoutingManager`.
*
*/
enum Dhcp6PdState : uint8_t
{
kDhcp6PdStateDisabled = OT_BORDER_ROUTING_STATE_DISABLED, ///< Disabled.
kDhcp6PdStateStopped = OT_BORDER_ROUTING_STATE_STOPPED, ///< Enabled, but currently stopped.
kDhcp6PdStateRunning = OT_BORDER_ROUTING_STATE_RUNNING, ///< Enabled, and running.
};
/**
* Initializes the routing manager.
*
@@ -217,7 +229,7 @@ public:
void ClearRouteInfoOptionPreference(void);
/**
* Returns the local off-mesh-routable (OMR) prefix.
* Returns the local generated off-mesh-routable (OMR) prefix.
*
* The randomly generated 64-bit prefix will be added to the Thread Network Data if there isn't already an OMR
* prefix.
@@ -230,6 +242,22 @@ public:
*/
Error GetOmrPrefix(Ip6::Prefix &aPrefix) const;
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
/**
* Returns the platform provided off-mesh-routable (OMR) prefix.
*
* The prefix is extracted from the platform generated RA messages handled by `ProcessPlatfromGeneratedNd()`.
*
* @param[out] aPrefixInfo A reference to where the prefix info will be output to.
*
* @retval kErrorNone Successfully retrieved the OMR prefix.
* @retval kErrorNotFound There are no valid PD prefix on this BR.
* @retval kErrorInvalidState The Border Routing Manager is not initialized yet.
*
*/
Error GetPdOmrPrefix(PrefixTableEntry &aPrefixInfo) const;
#endif
/**
* Returns the currently favored off-mesh-routable (OMR) prefix.
*
@@ -413,6 +441,41 @@ public:
void HandleSrpServerAutoEnableMode(void);
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
/**
* Handles a router advertisement message received on platform's Thread interface.
*
* Note: This method is a part of DHCPv6 PD support on Thread border routers. The message should be generated by the
* software like dnamasq, radvd, systemd-networkd on the platform as a part of the DHCPv6 prefix delegation process
* for distributing the prefix to the interfaces (links).
*
* @param[in] aRouterAdvert A pointer to the buffer of the router advertisement message.
* @param[in] aLength The length of the router advertisement message.
*
*/
void ProcessPlatfromGeneratedRa(const uint8_t *aRouterAdvert, uint16_t aLength)
{
mPdPrefixManager.ProcessPlatformGeneratedRa(aRouterAdvert, aLength);
}
/**
* Enables / Disables the functions for DHCPv6 PD.
*
* @param[in] aEnabled Whether to accept platform generated RA messages.
*
*/
void SetDhcp6PdEnabled(bool aEnabled) { return mPdPrefixManager.SetEnabled(aEnabled); }
/**
* Returns the state of accpeting RouterAdvertisement messages on platform interface.
*
* @retval kDhcp6PdStateRunning DHCPv6 PD should be enabled and running on this border router.
* @retval kDhcp6PdStateDisabled DHCPv6 PD should be disabled on this border router..
*
*/
Dhcp6PdState GetDhcp6PdState(void) const { return mPdPrefixManager.GetState(); }
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
private:
static constexpr uint8_t kMaxOnMeshPrefixes = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES;
@@ -472,6 +535,10 @@ private:
void HandleDiscoveredPrefixTableEntryTimer(void) { mDiscoveredPrefixTable.HandleEntryTimer(); }
void HandleDiscoveredPrefixTableRouterTimer(void) { mDiscoveredPrefixTable.HandleRouterTimer(); }
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
class PdPrefixManager; // For DiscoveredPrefixTable::Entry
#endif
class DiscoveredPrefixTable : public InstanceLocator
{
// This class maintains the discovered on-link and route prefixes
@@ -492,6 +559,10 @@ private:
// changes within the same flow of execution, the callback is
// invoked after all the changes are processed.
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
friend class PdPrefixManager; // For DiscoveredPrefixTable::Entry
#endif
public:
explicit DiscoveredPrefixTable(Instance &aInstance);
@@ -527,6 +598,9 @@ private:
{
friend class LinkedListEntry<Entry>;
friend class Clearable<Entry>;
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
friend class PdPrefixManager;
#endif
public:
enum Type : uint8_t
@@ -728,6 +802,7 @@ private:
void Evaluate(void);
void UpdateDefaultRouteFlag(bool aDefaultRoute);
bool IsLocalAddedInNetData(void) const { return mIsLocalAddedInNetData; }
const Ip6::Prefix &GetGeneratedPrefix(void) const { return mGeneratedPrefix; }
const OmrPrefix &GetLocalPrefix(void) const { return mLocalPrefix; }
const FavoredOmrPrefix &GetFavoredPrefix(void) const { return mFavoredPrefix; }
@@ -743,6 +818,7 @@ private:
InfoString LocalToString(void) const;
OmrPrefix mLocalPrefix;
Ip6::Prefix mGeneratedPrefix;
FavoredOmrPrefix mFavoredPrefix;
bool mIsLocalAddedInNetData;
bool mDefaultRoute;
@@ -965,6 +1041,53 @@ private:
TimeMilli mStartTime;
};
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
void HandlePdPrefixManagerTimer(void) { mPdPrefixManager.HandleTimer(); }
class PdPrefixManager : public InstanceLocator
{
public:
// This class implements handling (including management of the lifetime) of the prefix obtained from platform's
// DHCPv6 PD client. We expect the platform will send ICMP6 RA messages to the platform's interface for the
// information of the prefix.
// This class manages the state of the PD prefix in OmrPrefixManager
explicit PdPrefixManager(Instance &aInstance);
void SetEnabled(bool aEnabled);
bool IsRunning(void) const { return GetState() == Dhcp6PdState::kDhcp6PdStateRunning; }
bool HasPrefix(void) const { return IsValidOmrPrefix(mPrefix.GetPrefix()); }
const Ip6::Prefix &GetPrefix(void) const { return mPrefix.GetPrefix(); }
Dhcp6PdState GetState(void) const
{
// TODO: We need to stop and inform the platform when there is already a GUA prefix advertised in the
// network.
return mEnabled ? kDhcp6PdStateRunning : kDhcp6PdStateDisabled;
}
void ProcessPlatformGeneratedRa(const uint8_t *aRouterAdvert, uint16_t aLength);
Error GetPrefixInfo(PrefixTableEntry &aInfo) const;
void HandleTimer(void) { WithdrawPrefix(); }
static bool IsValidPdPrefix(const Ip6::Prefix &aPrefix)
{
// We should accept ULA prefix since it could be used by the internet infrastructure like NAT64.
return aPrefix.GetLength() != 0 && aPrefix.GetLength() <= kOmrPrefixLength && !aPrefix.IsLinkLocal() &&
!aPrefix.IsMulticast();
}
private:
Error Process(const Ip6::Nd::RouterAdvertMessage &aMessage);
void WithdrawPrefix(void);
using PlatformOmrPrefixTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandlePdPrefixManagerTimer>;
bool mEnabled;
PlatformOmrPrefixTimer mTimer;
DiscoveredPrefixTable::Entry mPrefix;
};
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
void EvaluateState(void);
void Start(void);
void Stop(void);
@@ -1037,6 +1160,10 @@ private:
Nat64PrefixManager mNat64PrefixManager;
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
PdPrefixManager mPdPrefixManager;
#endif
RaInfo mRaInfo;
RsSender mRsSender;
+13
View File
@@ -104,4 +104,17 @@
#define OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT (60 * 1000) // (in msec).
#endif
/**
* @def OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
*
* Specifies whether to support handling platform generated ND messages.
*
* The desired use case is the prefix will be allocated by other software on the interface, and they will advertise the
* assigned prefix to the thread interface via router advertisement messages.
*
*/
#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 0
#endif
#endif // CONFIG_BORDER_ROUTING_H_
+13 -1
View File
@@ -532,6 +532,14 @@ public:
*/
RoutePreference GetDefaultRouterPreference(void) const;
/**
* This method returns the ICMPv6 message type.
*
* @returns The ICMPv6 message type.
*
*/
Icmp::Header::Type GetType(void) const { return static_cast<Icmp::Header::Type>(mType); }
private:
// Router Advertisement Message
//
@@ -613,7 +621,11 @@ public:
* @retval FALSE If the RA message is not valid.
*
*/
bool IsValid(void) const { return (mData.GetBytes() != nullptr) && (mData.GetLength() >= sizeof(Header)); }
bool IsValid(void) const
{
return (mData.GetBytes() != nullptr) && (mData.GetLength() >= sizeof(Header)) &&
(GetHeader().GetType() == Icmp::Header::kTypeRouterAdvert);
}
/**
* Gets the RA message's header.
@@ -83,6 +83,14 @@
*/
#define OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE 1
/**
* @def OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
*
* Define to 1 to enable ND handling on Thread interface on host.
*
*/
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 1
/**
* @def OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
*
+265 -12
View File
@@ -32,6 +32,7 @@
#include "test_util.hpp"
#include <openthread/thread.h>
#include <openthread/platform/border_routing.h>
#include "border_router/routing_manager.hpp"
#include "common/arg_macros.hpp"
@@ -466,6 +467,13 @@ void LogRouterAdvert(const Icmp6Packet &aPacket)
}
}
void LogRouterAdvert(const uint8_t *aBuffer, size_t aLength)
{
Icmp6Packet packet;
packet.Init(aBuffer, aLength);
LogRouterAdvert(packet);
}
const char *PreferenceToString(int8_t aPreference)
{
const char *str = "";
@@ -491,9 +499,14 @@ const char *PreferenceToString(int8_t aPreference)
return str;
}
void SendRouterAdvert(const Ip6::Address &aAddress, const uint8_t *aBuffer, uint16_t aLength)
{
otPlatInfraIfRecvIcmp6Nd(sInstance, kInfraIfIndex, &aAddress, aBuffer, aLength);
}
void SendRouterAdvert(const Ip6::Address &aAddress, const Icmp6Packet &aPacket)
{
otPlatInfraIfRecvIcmp6Nd(sInstance, kInfraIfIndex, &aAddress, aPacket.GetBytes(), aPacket.GetLength());
SendRouterAdvert(aAddress, aPacket.GetBytes(), aPacket.GetLength());
}
void SendNeighborAdvert(const Ip6::Address &aAddress, const Ip6::Nd::NeighborAdvertMessage &aNaMessage)
@@ -662,21 +675,22 @@ struct DefaultRoute
RoutePreference mPreference;
};
void SendRouterAdvert(const Ip6::Address &aRouterAddress,
const Pio *aPios,
uint16_t aNumPios,
const Rio *aRios,
uint16_t aNumRios,
const DefaultRoute &aDefaultRoute)
template <size_t N>
uint16_t BuildRouterAdvert(uint8_t (&aBuffer)[N],
const Pio *aPios,
uint16_t aNumPios,
const Rio *aRios,
uint16_t aNumRios,
const DefaultRoute &aDefaultRoute)
{
Ip6::Nd::RouterAdvertMessage::Header header;
uint8_t buffer[kMaxRaSize];
uint16_t length;
header.SetRouterLifetime(aDefaultRoute.mLifetime);
header.SetDefaultRouterPreference(aDefaultRoute.mPreference);
{
Ip6::Nd::RouterAdvertMessage raMsg(header, buffer);
Ip6::Nd::RouterAdvertMessage raMsg(header, aBuffer);
for (; aNumPios > 0; aPios++, aNumPios--)
{
@@ -689,10 +703,25 @@ void SendRouterAdvert(const Ip6::Address &aRouterAddress,
SuccessOrQuit(raMsg.AppendRouteInfoOption(aRios->mPrefix, aRios->mValidLifetime, aRios->mPreference));
}
SendRouterAdvert(aRouterAddress, raMsg.GetAsPacket());
Log("Sending RA from router %s", aRouterAddress.ToString().AsCString());
LogRouterAdvert(raMsg.GetAsPacket());
length = raMsg.GetAsPacket().GetLength();
}
return length;
}
void SendRouterAdvert(const Ip6::Address &aRouterAddress,
const Pio *aPios,
uint16_t aNumPios,
const Rio *aRios,
uint16_t aNumRios,
const DefaultRoute &aDefaultRoute)
{
uint8_t buffer[kMaxRaSize];
uint16_t length = BuildRouterAdvert(buffer, aPios, aNumPios, aRios, aNumRios, aDefaultRoute);
SendRouterAdvert(aRouterAddress, buffer, length);
Log("Sending RA from router %s", aRouterAddress.ToString().AsCString());
LogRouterAdvert(buffer, length);
}
template <uint16_t kNumPios, uint16_t kNumRios>
@@ -725,6 +754,17 @@ void SendRouterAdvert(const Ip6::Address &aRouterAddress, const DefaultRoute &aD
SendRouterAdvert(aRouterAddress, nullptr, 0, nullptr, 0, aDefaultRoute);
}
template <uint16_t kNumPios> void SendRouterAdvertToBorderRoutingProcessIcmp6Ra(const Pio (&aPios)[kNumPios])
{
uint8_t buffer[kMaxRaSize];
uint16_t length =
BuildRouterAdvert(buffer, aPios, kNumPios, nullptr, 0, DefaultRoute(0, NetworkData::kRoutePreferenceMedium));
otPlatBorderRoutingProcessIcmp6Ra(sInstance, buffer, length);
Log("Passing RA to otPlatBorderRoutingProcessIcmp6Ra");
LogRouterAdvert(buffer, length);
}
struct OnLinkPrefix : public Pio
{
OnLinkPrefix(const Ip6::Prefix &aPrefix,
@@ -3118,6 +3158,216 @@ void TestNat64PrefixSelection(void)
}
#endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
void VerifyPdOmrPrefix(const Ip6::Prefix &aPrefix)
{
otBorderRoutingPrefixTableEntry platformPrefixInfo;
VerifyOrQuit(otBorderRoutingGetPdOmrPrefix(sInstance, &platformPrefixInfo) == OT_ERROR_NONE);
VerifyOrQuit(AsCoreType(&platformPrefixInfo.mPrefix) == aPrefix);
}
void VerifyNoPdOmrPrefix()
{
otBorderRoutingPrefixTableEntry platformPrefixInfo;
VerifyOrQuit(otBorderRoutingGetPdOmrPrefix(sInstance, &platformPrefixInfo) == OT_ERROR_NOT_FOUND);
}
void TestBorderRoutingProcessPlatfromGeneratedNd(void)
{
Ip6::Prefix localOmr;
Log("--------------------------------------------------------------------------------------------");
Log("TestBorderRoutingProcessPlatfromGeneratedNd");
InitTest(/* aEnableBorderRouting */ true);
otBorderRoutingDhcp6PdSetEnabled(sInstance, true);
{
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOmrPrefix(localOmr));
}
// 0. Reject invalid RA.
Log("0. Invalid RA message.");
{
{
const uint8_t testInvalidRaMessage[] = {
0x86, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
otPlatBorderRoutingProcessIcmp6Ra(sInstance, testInvalidRaMessage, sizeof(testInvalidRaMessage));
VerifyNoPdOmrPrefix();
}
{
const uint8_t testInvalidRaMessage[] = {
0x87, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
otPlatBorderRoutingProcessIcmp6Ra(sInstance, testInvalidRaMessage, sizeof(testInvalidRaMessage));
VerifyNoPdOmrPrefix();
}
{
const uint8_t testRaMessageWithInvalidPrefix[] = {
0x86, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x04, 0x41, 0xc0, 0x00, 0x00, 0x10, 0xe1, 0x00, 0x00, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x00,
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
otPlatBorderRoutingProcessIcmp6Ra(sInstance, testRaMessageWithInvalidPrefix,
sizeof(testRaMessageWithInvalidPrefix));
VerifyNoPdOmrPrefix();
}
}
// 1. Publish a prefix, and wait until it expired.
Log("1. Simple RA message.");
{
Ip6::Prefix raPrefix = PrefixFromString("2001:db8:dead:beef::", 64);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(raPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(raPrefix);
AdvanceTime(10000);
VerifyPdOmrPrefix(raPrefix);
VerifyOrQuit(sExpectedRios.SawAll());
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1500000);
sExpectedRios.Clear();
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(400000);
// Deprecated prefixes will be removed.
VerifyNoPdOmrPrefix();
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false);
}
// 1.1. Publish a prefix, and wait until it expired.
// Multiple prefixes are advertised, only the smallest one will be used.
Log("1.1. RA message with multiple prefixes.");
{
Ip6::Prefix raPrefix = PrefixFromString("2001:db8:dead:beef::", 64);
Ip6::Prefix ulaRaPrefix = PrefixFromString("fd01:db8:deaf:beef::", 64);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(ulaRaPrefix, kValidLitime * 2, kPreferredLifetime * 2),
Pio(raPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(raPrefix);
AdvanceTime(10000);
VerifyPdOmrPrefix(raPrefix);
VerifyOrQuit(sExpectedRios.SawAll());
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1500000);
sExpectedRios.Clear();
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(400000);
// Deprecated prefixes will be removed.
VerifyNoPdOmrPrefix();
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false);
}
// 2. Publish a prefix, and renew it before it expired.
Log("2. Renew prefix lifetime.");
{
Ip6::Prefix raPrefix = PrefixFromString("2001:db8:1:2::", 64);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(raPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(raPrefix);
AdvanceTime(10000);
VerifyPdOmrPrefix(raPrefix);
VerifyOrQuit(sExpectedRios.SawAll());
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1500000);
sExpectedRios.Clear();
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(raPrefix, kValidLitime, kPreferredLifetime)});
AdvanceTime(400000);
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1500000);
VerifyNoPdOmrPrefix();
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false);
}
// 3. Publish a prefix, and publish another prefix to replace it (with goodbye ra).
Log("3. Update prefix.");
{
Ip6::Prefix raPrefix = PrefixFromString("2001:db8:1:2::", 64);
Ip6::Prefix newRaPrefix = PrefixFromString("2001:db8:3:4::", 64);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(raPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(raPrefix);
sExpectedRios.Clear();
AdvanceTime(10000);
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1000000);
VerifyPdOmrPrefix(raPrefix);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra(
{Pio(raPrefix, 0, 0), Pio(newRaPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(newRaPrefix);
AdvanceTime(1000000);
VerifyOrQuit(sExpectedRios.SawAll());
VerifyPdOmrPrefix(newRaPrefix);
AdvanceTime(1000000);
VerifyNoPdOmrPrefix();
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false);
}
// 4. Short prefix will be extended to /64.
Log("Short prefix");
{
// The prefix will be padded to a /64 prefix.
Ip6::Prefix raPrefix = PrefixFromString("2001:db8:cafe:0::", 64);
Ip6::Prefix realRaPrefix;
realRaPrefix.Set(raPrefix.GetBytes(), 48);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(realRaPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(raPrefix);
AdvanceTime(10000);
VerifyPdOmrPrefix(raPrefix);
VerifyOrQuit(sExpectedRios.SawAll());
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1500000);
sExpectedRios.Clear();
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(400000);
// Deprecated prefixes will be removed.
VerifyNoPdOmrPrefix();
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false);
}
Log("End of TestBorderRoutingProcessPlatfromGeneratedNd");
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
int main(void)
@@ -3143,6 +3393,9 @@ int main(void)
#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
TestNat64PrefixSelection();
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
TestBorderRoutingProcessPlatfromGeneratedNd();
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
printf("All tests passed\n");
#else