[netdata] add feature for BR to request router role upgrade (#7597)
This commit adds a new mechanism to allow border routers to request router role upgrade. A border router which provides IP connectivity (either adding an external route prefix or an on-mesh prefix with default route flag) and is acting as a REED is eligible to request a router role upgrade by sending an "Address Solicit" request to leader with status reason `BorderRouterRequest`. This reason is used when the number of active routers in the Thread mesh is above the threshold, and only if the number of existing eligible BRs (determined from the network data) that are acting as router is less than two. This mechanism allows up to two eligible border routers to request router role upgrade when the number of routers is already above the threshold. This commit also adds a new test-case `test_br_upgrade_router_role` which validates the behavior of the new mechanism.
This commit is contained in:
parent
4164a5029d
commit
64e837861f
|
@ -94,6 +94,13 @@ void TimeTicker::HandleTimer(void)
|
|||
Get<AddressResolver>().HandleTimeTick();
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
if (mReceivers & Mask(kNetworkDataNotifier))
|
||||
{
|
||||
Get<NetworkData::Notifier>().HandleTimeTick();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
|
||||
if (mReceivers & Mask(kChildSupervisor))
|
||||
{
|
||||
|
|
|
@ -71,6 +71,7 @@ public:
|
|||
kIp6FragmentReassembler, ///< `Ip6::Ip6` (handling of fragmented messages)
|
||||
kDuaManager, ///< `DuaManager`
|
||||
kMlrManager, ///< `MlrManager`
|
||||
kNetworkDataNotifier, ///< `NetworkData::Notifier`
|
||||
|
||||
kNumReceivers, ///< Number of receivers.
|
||||
};
|
||||
|
|
|
@ -55,6 +55,32 @@
|
|||
#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
*
|
||||
* Define to 1 to enable mechanism on a Border Router which provides IP connectivity to request router role upgrade.
|
||||
*
|
||||
* This config is applicable on an `OPENTHREAD_FTD` build and when `OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE` is also
|
||||
* enabled.
|
||||
*
|
||||
* A Border Router is considered to provide external IP connectivity if at least one of the below conditions hold:
|
||||
*
|
||||
* - It has added at least one external route entry.
|
||||
* - It has added at least one prefix entry with default-route and on-mesh flags set.
|
||||
* - It has added at least one domain prefix (domain and on-mesh flags set).
|
||||
*
|
||||
* A Border Router which provides IP connectivity and is acting as a REED is eligible to request a router role upgrade
|
||||
* by sending an "Address Solicit" request to leader with status reason `BorderRouterRequest`. This reason is used when
|
||||
* the number of active routers in the Thread mesh is above the threshold, and only if the number of existing eligible
|
||||
* BRs (determined from the Thread Network Data) that are acting as router is less than two. This mechanism allows up
|
||||
* to two eligible Border Routers to request router role upgrade when the number of routers is already above the
|
||||
* threshold.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
*
|
||||
|
|
|
@ -1453,7 +1453,11 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo)
|
|||
|
||||
if (routerCount > mRouterDowngradeThreshold && mRouterSelectionJitterTimeout == 0 &&
|
||||
HasMinDowngradeNeighborRouters() && HasSmallNumberOfChildren() &&
|
||||
HasOneNeighborWithComparableConnectivity(route, routerId))
|
||||
HasOneNeighborWithComparableConnectivity(route, routerId)
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
&& !Get<NetworkData::Notifier>().IsEligibleForRouterRoleUpgradeAsBorderRouter()
|
||||
#endif
|
||||
)
|
||||
{
|
||||
mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter);
|
||||
}
|
||||
|
@ -3920,7 +3924,7 @@ void MleRouter::HandleAddressSolicit(Coap::Message &aMessage, const Ip6::Message
|
|||
case ThreadStatusTlv::kParentPartitionChange:
|
||||
break;
|
||||
|
||||
case ThreadStatusTlv::kBorderRouterRequst:
|
||||
case ThreadStatusTlv::kBorderRouterRequest:
|
||||
if ((mRouterTable.GetActiveRouterCount() >= mRouterUpgradeThreshold) &&
|
||||
(Get<NetworkData::Leader>().CountBorderRouters(NetworkData::kRouterRoleOnly) >=
|
||||
kRouterUpgradeBorderRouterRequestThreshold))
|
||||
|
|
|
@ -585,7 +585,7 @@ private:
|
|||
static constexpr uint16_t kUnsolicitedDataResponseJitter = 500; // Max delay for unsol Data Response (in msec).
|
||||
|
||||
// Threshold to accept a router upgrade request with reason
|
||||
// `kBorderRouterRequst` (number of BRs acting as router in
|
||||
// `kBorderRouterRequest` (number of BRs acting as router in
|
||||
// Network Data).
|
||||
static constexpr uint8_t kRouterUpgradeBorderRouterRequestThreshold = 2;
|
||||
|
||||
|
|
|
@ -162,6 +162,13 @@ constexpr uint8_t kRouterSelectionJitter = 120; ///< (in sec)
|
|||
constexpr uint8_t kRouterDowngradeThreshold = 23;
|
||||
constexpr uint8_t kRouterUpgradeThreshold = 16;
|
||||
|
||||
/**
|
||||
* Threshold to accept a router upgrade request with reason `kBorderRouterRequest` (number of BRs acting as router in
|
||||
* Network Data).
|
||||
*
|
||||
*/
|
||||
constexpr uint8_t kRouterUpgradeBorderRouterRequestThreshold = 2;
|
||||
|
||||
constexpr uint32_t kMaxLeaderToRouterTimeout = 90; ///< (in sec)
|
||||
constexpr uint32_t kReedAdvertiseInterval = 570; ///< (in sec)
|
||||
constexpr uint32_t kReedAdvertiseJitter = 60; ///< (in sec)
|
||||
|
|
|
@ -814,5 +814,34 @@ uint8_t NetworkData::CountBorderRouters(RoleFilter aRoleFilter) const
|
|||
return rlocsLength;
|
||||
}
|
||||
|
||||
bool NetworkData::ContainsBorderRouterWithRloc(uint16_t aRloc16) const
|
||||
{
|
||||
bool contains = false;
|
||||
Iterator iterator = kIteratorInit;
|
||||
ExternalRouteConfig route;
|
||||
OnMeshPrefixConfig prefix;
|
||||
|
||||
while (GetNextExternalRoute(iterator, route) == kErrorNone)
|
||||
{
|
||||
if (route.mRloc16 == aRloc16)
|
||||
{
|
||||
ExitNow(contains = true);
|
||||
}
|
||||
}
|
||||
|
||||
iterator = kIteratorInit;
|
||||
|
||||
while (GetNextOnMeshPrefix(iterator, prefix) == kErrorNone)
|
||||
{
|
||||
if ((prefix.mRloc16 == aRloc16) && prefix.mOnMesh && (prefix.mDefaultRoute || prefix.mDp))
|
||||
{
|
||||
ExitNow(contains = true);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return contains;
|
||||
}
|
||||
|
||||
} // namespace NetworkData
|
||||
} // namespace ot
|
||||
|
|
|
@ -325,9 +325,9 @@ public:
|
|||
Error GetNextServer(Iterator &aIterator, uint16_t &aRloc16) const;
|
||||
|
||||
/**
|
||||
* This method finds and returns the list of RLOCs of border routers providing external IPv6 connectivity.
|
||||
* This method finds and returns the list of RLOCs of border routers providing external IP connectivity.
|
||||
*
|
||||
* A border router is considered to provide external IPv6 connectivity if it has added at least one external route
|
||||
* A border router is considered to provide external IP connectivity if it has added at least one external route
|
||||
* entry, or an on-mesh prefix with default-route and on-mesh flags set.
|
||||
*
|
||||
* This method should be used when the RLOC16s are present in the Network Data (when the Network Data contains the
|
||||
|
@ -346,10 +346,13 @@ public:
|
|||
Error FindBorderRouters(RoleFilter aRoleFilter, uint16_t aRlocs[], uint8_t &aRlocsLength) const;
|
||||
|
||||
/**
|
||||
* This method counts the number of border routers providing external IPv6 connectivity.
|
||||
* This method counts the number of border routers providing external IP connectivity.
|
||||
*
|
||||
* A border router is considered to provide external IPv6 connectivity if it has added at least one external route
|
||||
* entry, or an on-mesh prefix with default-route and on-mesh flags set.
|
||||
* A border router is considered to provide external IP connectivity if at least one of the below conditions hold
|
||||
*
|
||||
* - It has added at least one external route entry.
|
||||
* - It has added at least one prefix entry with default-route and on-mesh flags set.
|
||||
* - It has added at least one domain prefix (domain and on-mesh flags set).
|
||||
*
|
||||
* This method should be used when the RLOC16s are present in the Network Data (when the Network Data contains the
|
||||
* full set and not the stable subset).
|
||||
|
@ -361,6 +364,27 @@ public:
|
|||
*/
|
||||
uint8_t CountBorderRouters(RoleFilter aRoleFilter) const;
|
||||
|
||||
/**
|
||||
* This method indicates whether the network data contains a border providing external IP connectivity with a given
|
||||
* RLOC16.
|
||||
*
|
||||
* A border router is considered to provide external IP connectivity if at least one of the below conditions hold
|
||||
*
|
||||
* - It has added at least one external route entry.
|
||||
* - It has added at least one prefix entry with default-route and on-mesh flags set.
|
||||
* - It has added at least one domain prefix (domain and on-mesh flags set).
|
||||
*
|
||||
* This method should be used when the RLOC16s are present in the Network Data (when the Network Data contains the
|
||||
* full set and not the stable subset).
|
||||
*
|
||||
* @param[in] aRloc16 The RLOC16 to check.
|
||||
*
|
||||
* @returns TRUE If the network data contains a border router with @p aRloc16 providing IP connectivity.
|
||||
* @returns FALSE If the network data does not contain a border router with @p aRloc16 providing IP connectivity.
|
||||
*
|
||||
*/
|
||||
bool ContainsBorderRouterWithRloc(uint16_t aRloc16) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* This enumeration defines Service Data match mode.
|
||||
|
|
|
@ -38,23 +38,35 @@
|
|||
#include "common/code_utils.hpp"
|
||||
#include "common/instance.hpp"
|
||||
#include "common/locator_getters.hpp"
|
||||
#include "common/log.hpp"
|
||||
#include "thread/network_data_leader.hpp"
|
||||
#include "thread/network_data_local.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace NetworkData {
|
||||
|
||||
RegisterLogModule("NetworkData");
|
||||
|
||||
Notifier::Notifier(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mTimer(aInstance, HandleTimer)
|
||||
, mSynchronizeDataTask(aInstance, HandleSynchronizeDataTask)
|
||||
, mNextDelay(0)
|
||||
, mWaitingForResponse(false)
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
, mDidRequestRouterRoleUpgrade(false)
|
||||
, mRouterRoleUpgradeTimeout(0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
void Notifier::HandleServerDataUpdated(void)
|
||||
{
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
mDidRequestRouterRoleUpgrade = false;
|
||||
ScheduleRouterRoleUpgradeIfEligible();
|
||||
#endif
|
||||
|
||||
mNextDelay = 0;
|
||||
mSynchronizeDataTask.Post();
|
||||
}
|
||||
|
@ -113,6 +125,18 @@ void Notifier::HandleNotifierEvents(Events aEvents)
|
|||
mNextDelay = 0;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
if (aEvents.Contains(kEventThreadPartitionIdChanged))
|
||||
{
|
||||
mDidRequestRouterRoleUpgrade = false;
|
||||
}
|
||||
|
||||
if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadNetdataChanged | kEventThreadPartitionIdChanged))
|
||||
{
|
||||
ScheduleRouterRoleUpgradeIfEligible();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged | kEventThreadChildRemoved))
|
||||
{
|
||||
SynchronizeServerData();
|
||||
|
@ -158,6 +182,90 @@ void Notifier::HandleCoapResponse(Error aResult)
|
|||
}
|
||||
}
|
||||
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
|
||||
bool Notifier::IsEligibleForRouterRoleUpgradeAsBorderRouter(void) const
|
||||
{
|
||||
bool isEligible = false;
|
||||
uint16_t rloc16 = Get<Mle::Mle>().GetRloc16();
|
||||
uint8_t activeRouterCount;
|
||||
|
||||
VerifyOrExit(Get<Mle::MleRouter>().IsRouterEligible());
|
||||
|
||||
// RouterUpgradeThreshold can be explicitly set to zero in some of
|
||||
// cert tests to disallow device to become router.
|
||||
|
||||
VerifyOrExit(Get<Mle::MleRouter>().GetRouterUpgradeThreshold() != 0);
|
||||
|
||||
// Check that we are a border router providing IP connectivity and already
|
||||
// in the leader's network data and therefore eligible to request router
|
||||
// role upgrade with `kBorderRouterRequest` status.
|
||||
|
||||
VerifyOrExit(Get<Local>().ContainsBorderRouterWithRloc(rloc16) &&
|
||||
Get<Leader>().ContainsBorderRouterWithRloc(rloc16));
|
||||
|
||||
activeRouterCount = Get<RouterTable>().GetActiveRouterCount();
|
||||
VerifyOrExit((activeRouterCount >= Get<Mle::MleRouter>().GetRouterUpgradeThreshold()) &&
|
||||
(activeRouterCount < Mle::kMaxRouters));
|
||||
|
||||
VerifyOrExit(Get<Leader>().CountBorderRouters(kRouterRoleOnly) < Mle::kRouterUpgradeBorderRouterRequestThreshold);
|
||||
isEligible = true;
|
||||
|
||||
exit:
|
||||
return isEligible;
|
||||
}
|
||||
|
||||
void Notifier::ScheduleRouterRoleUpgradeIfEligible(void)
|
||||
{
|
||||
// We allow device to request router role upgrade using status
|
||||
// reason `kBorderRouterRequest` once while its local network data
|
||||
// remains unchanged. This ensures if the leader is running an
|
||||
// older version of Thread stack which does not support
|
||||
// `kBorderRouterRequest` reason, we do not keep trying (on no
|
||||
// response). The boolean `mDidRequestRouterRoleUpgrade` tracks
|
||||
// this. It is set to `false` when local network data gets changed
|
||||
// or when partition ID gets changed (indicating a potential
|
||||
// leader change).
|
||||
|
||||
VerifyOrExit(!mDidRequestRouterRoleUpgrade);
|
||||
|
||||
VerifyOrExit(Get<Mle::MleRouter>().IsChild());
|
||||
VerifyOrExit(IsEligibleForRouterRoleUpgradeAsBorderRouter() && (mRouterRoleUpgradeTimeout == 0));
|
||||
|
||||
mRouterRoleUpgradeTimeout = Random::NonCrypto::GetUint8InRange(1, kRouterRoleUpgradeMaxTimeout + 1);
|
||||
Get<TimeTicker>().RegisterReceiver(TimeTicker::kNetworkDataNotifier);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Notifier::HandleTimeTick(void)
|
||||
{
|
||||
VerifyOrExit(mRouterRoleUpgradeTimeout > 0);
|
||||
|
||||
mRouterRoleUpgradeTimeout--;
|
||||
|
||||
if (mRouterRoleUpgradeTimeout == 0)
|
||||
{
|
||||
Get<TimeTicker>().UnregisterReceiver(TimeTicker::kNetworkDataNotifier);
|
||||
|
||||
// Check that we are still eligible for requesting router role
|
||||
// upgrade (note that state can change since the last time we
|
||||
// checked and registered to receive time ticks).
|
||||
|
||||
if (Get<Mle::MleRouter>().IsChild() && IsEligibleForRouterRoleUpgradeAsBorderRouter())
|
||||
{
|
||||
LogInfo("Requesting router role as BR");
|
||||
mDidRequestRouterRoleUpgrade = true;
|
||||
IgnoreError(Get<Mle::MleRouter>().BecomeRouter(ThreadStatusTlv::kBorderRouterRequest));
|
||||
}
|
||||
}
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE &&
|
||||
// OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
|
||||
} // namespace NetworkData
|
||||
} // namespace ot
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "common/non_copyable.hpp"
|
||||
#include "common/notifier.hpp"
|
||||
#include "common/tasklet.hpp"
|
||||
#include "common/time_ticker.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace NetworkData {
|
||||
|
@ -54,6 +55,7 @@ namespace NetworkData {
|
|||
class Notifier : public InstanceLocator, private NonCopyable
|
||||
{
|
||||
friend class ot::Notifier;
|
||||
friend class ot::TimeTicker;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
@ -74,10 +76,33 @@ public:
|
|||
*/
|
||||
void HandleServerDataUpdated(void);
|
||||
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
/**
|
||||
* This method indicates whether the device as border router is eligible for router role upgrade request using the
|
||||
* status reason `kBorderRouterRequest`.
|
||||
*
|
||||
* This method checks whether device is providing IP connectivity and that there are fewer than two border routers
|
||||
* in network data acting as router. Device is considered to provide external IP connectivity if at least one of
|
||||
* the below conditions hold:
|
||||
*
|
||||
* - It has added at least one external route entry.
|
||||
* - It has added at least one prefix entry with default-route and on-mesh flags set.
|
||||
* - It has added at least one domain prefix (domain and on-mesh flags set).
|
||||
*
|
||||
* This method does not check the current role of device.
|
||||
*
|
||||
* @retval TRUE Device is eligible to request router role upgrade as a border router.
|
||||
* @retval FALSE Device is not eligible to request router role upgrade as a border router.
|
||||
*
|
||||
*/
|
||||
bool IsEligibleForRouterRoleUpgradeAsBorderRouter(void) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
static constexpr uint32_t kDelayNoBufs = 1000; // in msec
|
||||
static constexpr uint32_t kDelayRemoveStaleChildren = 5000; // in msec
|
||||
static constexpr uint32_t kDelaySynchronizeServerData = 300000; // in msec
|
||||
static constexpr uint32_t kDelayNoBufs = 1000; // in msec
|
||||
static constexpr uint32_t kDelayRemoveStaleChildren = 5000; // in msec
|
||||
static constexpr uint32_t kDelaySynchronizeServerData = 300000; // in msec
|
||||
static constexpr uint8_t kRouterRoleUpgradeMaxTimeout = 10; // in sec
|
||||
|
||||
void HandleNotifierEvents(Events aEvents);
|
||||
|
||||
|
@ -93,11 +118,20 @@ private:
|
|||
static void HandleSynchronizeDataTask(Tasklet &aTasklet);
|
||||
|
||||
void SynchronizeServerData(void);
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
void ScheduleRouterRoleUpgradeIfEligible(void);
|
||||
void HandleTimeTick(void);
|
||||
#endif
|
||||
|
||||
TimerMilli mTimer;
|
||||
Tasklet mSynchronizeDataTask;
|
||||
uint32_t mNextDelay;
|
||||
bool mWaitingForResponse;
|
||||
bool mWaitingForResponse : 1;
|
||||
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
|
||||
bool mDidRequestRouterRoleUpgrade : 1;
|
||||
uint8_t mRouterRoleUpgradeTimeout;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace NetworkData
|
||||
|
|
|
@ -166,7 +166,7 @@ public:
|
|||
kTooFewRouters = 2, ///< Address Solicit due to too few routers.
|
||||
kHaveChildIdRequest = 3, ///< Address Solicit due to child ID request.
|
||||
kParentPartitionChange = 4, ///< Address Solicit due to parent partition change
|
||||
kBorderRouterRequst = 5, ///< Address Solicit from Border Router request.
|
||||
kBorderRouterRequest = 5, ///< Address Solicit from Border Router request.
|
||||
kUnrecognizedStatus = 6, ///< The requested status is unrecognized or not meaningful in a request.
|
||||
};
|
||||
|
||||
|
|
|
@ -153,6 +153,7 @@ EXTRA_DIST = \
|
|||
sniffer_transport.py \
|
||||
test_anycast.py \
|
||||
test_anycast_locator.py \
|
||||
test_br_upgrade_router_role.py \
|
||||
test_coap.py \
|
||||
test_coap_block.py \
|
||||
test_coap_observe.py \
|
||||
|
@ -232,6 +233,7 @@ check_PROGRAMS = \
|
|||
check_SCRIPTS = \
|
||||
test_anycast.py \
|
||||
test_anycast_locator.py \
|
||||
test_br_upgrade_router_role.py \
|
||||
test_coap.py \
|
||||
test_coap_block.py \
|
||||
test_coap_observe.py \
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2022, 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.
|
||||
#
|
||||
|
||||
import ipaddress
|
||||
import unittest
|
||||
|
||||
import command
|
||||
import thread_cert
|
||||
|
||||
# Test description:
|
||||
#
|
||||
# This test verifies behavior of Border Routers (which provide IP connectivity) requesting router role within Thread
|
||||
# mesh.
|
||||
|
||||
# Topology:
|
||||
#
|
||||
# 5 FTD nodes, all connected.
|
||||
#
|
||||
# Topology is expected to change during execution of test.
|
||||
#
|
||||
|
||||
LEADER = 1
|
||||
ROUTER = 2
|
||||
BR1 = 3
|
||||
BR2 = 4
|
||||
BR3 = 5
|
||||
|
||||
|
||||
class BrUpgradeRouterRole(thread_cert.TestCase):
|
||||
USE_MESSAGE_FACTORY = False
|
||||
SUPPORT_NCP = False
|
||||
|
||||
TOPOLOGY = {
|
||||
LEADER: {
|
||||
'name': 'leader',
|
||||
'mode': 'rdn',
|
||||
},
|
||||
ROUTER: {
|
||||
'name': 'reader',
|
||||
'mode': 'rdn',
|
||||
},
|
||||
BR1: {
|
||||
'name': 'br-1',
|
||||
'mode': 'rdn',
|
||||
},
|
||||
BR2: {
|
||||
'name': 'br-2',
|
||||
'mode': 'rdn',
|
||||
},
|
||||
BR3: {
|
||||
'name': 'br-3',
|
||||
'mode': 'rdn',
|
||||
},
|
||||
}
|
||||
|
||||
def test(self):
|
||||
leader = self.nodes[LEADER]
|
||||
router = self.nodes[ROUTER]
|
||||
br1 = self.nodes[BR1]
|
||||
br2 = self.nodes[BR2]
|
||||
br3 = self.nodes[BR3]
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Set the router upgrade threshold to 2 on all nodes.
|
||||
|
||||
for node in [leader, router, br1, br2, br3]:
|
||||
node.set_router_upgrade_threshold(2)
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Start the leader and router.
|
||||
|
||||
leader.start()
|
||||
self.simulator.go(10)
|
||||
self.assertEqual(leader.get_state(), 'leader')
|
||||
|
||||
router.start()
|
||||
self.simulator.go(125)
|
||||
self.assertEqual(router.get_state(), 'router')
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Start all three BRs, we expect all to stay as child since there are already two
|
||||
# routers in the Thread mesh.
|
||||
|
||||
br1.start()
|
||||
br2.start()
|
||||
br3.start()
|
||||
self.simulator.go(125)
|
||||
self.assertEqual(br1.get_state(), 'child')
|
||||
self.assertEqual(br2.get_state(), 'child')
|
||||
self.assertEqual(br3.get_state(), 'child')
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Add an external route on `br1`, it should now try to become a router requesting
|
||||
# with status BorderRouterRequest. Verify that leader allows it to become router.
|
||||
|
||||
br1.add_route('2001:dead:beef:cafe::/64', stable=True)
|
||||
br1.register_netdata()
|
||||
self.simulator.go(15)
|
||||
self.assertEqual(br1.get_state(), 'router')
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Add a prefix with default route on `br2`, it should also become a router.
|
||||
|
||||
br2.add_prefix('2001:dead:beef:2222::/64', 'paros')
|
||||
br2.register_netdata()
|
||||
self.simulator.go(15)
|
||||
self.assertEqual(br2.get_state(), 'router')
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Add an external route on `br3`, it should not become a router since we already have
|
||||
# two that requested router role upgrade as border router reason.
|
||||
|
||||
br3.add_route('2001:dead:beef:cafe::/64', stable=True)
|
||||
br3.register_netdata()
|
||||
self.simulator.go(120)
|
||||
self.assertEqual(br3.get_state(), 'child')
|
||||
|
||||
#-------------------------------------------------------------------------------------
|
||||
# Remove the external route on `br1`. This should now trigger `br3` to request a
|
||||
# router role upgrade since number of BRs acting as router in network data is now
|
||||
# below the threshold of two.
|
||||
|
||||
br1.remove_route('2001:dead:beef:cafe::/64')
|
||||
br1.register_netdata()
|
||||
self.simulator.go(15)
|
||||
self.assertEqual(br1.get_state(), 'router')
|
||||
self.assertEqual(br3.get_state(), 'router')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -149,7 +149,8 @@ void TestNetworkDataIterator(void)
|
|||
},
|
||||
};
|
||||
|
||||
const uint16_t kRlocs[] = {0xc800, 0x5400};
|
||||
const uint16_t kRlocs[] = {0xc800, 0x5400};
|
||||
const uint16_t kNonExistingRlocs[] = {0xc700, 0x0000, 0x5401};
|
||||
|
||||
NetworkData netData(*instance, kNetworkData, sizeof(kNetworkData));
|
||||
|
||||
|
@ -179,6 +180,16 @@ void TestNetworkDataIterator(void)
|
|||
SuccessOrQuit(netData.FindBorderRouters(kChildRoleOnly, rlocs, rlocsLength));
|
||||
VerifyOrQuit(rlocsLength == 0);
|
||||
VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == 0);
|
||||
|
||||
for (uint16_t rloc16 : kRlocs)
|
||||
{
|
||||
VerifyOrQuit(netData.ContainsBorderRouterWithRloc(rloc16));
|
||||
}
|
||||
|
||||
for (uint16_t rloc16 : kNonExistingRlocs)
|
||||
{
|
||||
VerifyOrQuit(!netData.ContainsBorderRouterWithRloc(rloc16));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -252,9 +263,10 @@ void TestNetworkDataIterator(void)
|
|||
},
|
||||
};
|
||||
|
||||
const uint16_t kRlocsAnyRole[] = {0x1000, 0x5400, 0x0401};
|
||||
const uint16_t kRlocsRouterRole[] = {0x1000, 0x5400};
|
||||
const uint16_t kRlocsChildRole[] = {0x0401};
|
||||
const uint16_t kRlocsAnyRole[] = {0x1000, 0x5400, 0x0401};
|
||||
const uint16_t kRlocsRouterRole[] = {0x1000, 0x5400};
|
||||
const uint16_t kRlocsChildRole[] = {0x0401};
|
||||
const uint16_t kNonExistingRlocs[] = {0x6000, 0x0000, 0x0402};
|
||||
|
||||
NetworkData netData(*instance, kNetworkData, sizeof(kNetworkData));
|
||||
|
||||
|
@ -297,6 +309,16 @@ void TestNetworkDataIterator(void)
|
|||
rlocsLength = GetArrayLength(kRlocsAnyRole);
|
||||
SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
|
||||
VerifyRlocsArray(rlocs, rlocsLength, kRlocsAnyRole);
|
||||
|
||||
for (uint16_t rloc16 : kRlocsAnyRole)
|
||||
{
|
||||
VerifyOrQuit(netData.ContainsBorderRouterWithRloc(rloc16));
|
||||
}
|
||||
|
||||
for (uint16_t rloc16 : kNonExistingRlocs)
|
||||
{
|
||||
VerifyOrQuit(!netData.ContainsBorderRouterWithRloc(rloc16));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -410,9 +432,10 @@ void TestNetworkDataIterator(void)
|
|||
},
|
||||
};
|
||||
|
||||
const uint16_t kRlocsAnyRole[] = {0xec00, 0x2801, 0x2800};
|
||||
const uint16_t kRlocsRouterRole[] = {0xec00, 0x2800};
|
||||
const uint16_t kRlocsChildRole[] = {0x2801};
|
||||
const uint16_t kRlocsAnyRole[] = {0xec00, 0x2801, 0x2800};
|
||||
const uint16_t kRlocsRouterRole[] = {0xec00, 0x2800};
|
||||
const uint16_t kRlocsChildRole[] = {0x2801};
|
||||
const uint16_t kNonExistingRlocs[] = {0x6000, 0x0000, 0x2806, 0x4c00};
|
||||
|
||||
NetworkData netData(*instance, kNetworkData, sizeof(kNetworkData));
|
||||
|
||||
|
@ -451,6 +474,16 @@ void TestNetworkDataIterator(void)
|
|||
SuccessOrQuit(netData.FindBorderRouters(kChildRoleOnly, rlocs, rlocsLength));
|
||||
VerifyRlocsArray(rlocs, rlocsLength, kRlocsChildRole);
|
||||
VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == GetArrayLength(kRlocsChildRole));
|
||||
|
||||
for (uint16_t rloc16 : kRlocsAnyRole)
|
||||
{
|
||||
VerifyOrQuit(netData.ContainsBorderRouterWithRloc(rloc16));
|
||||
}
|
||||
|
||||
for (uint16_t rloc16 : kNonExistingRlocs)
|
||||
{
|
||||
VerifyOrQuit(!netData.ContainsBorderRouterWithRloc(rloc16));
|
||||
}
|
||||
}
|
||||
|
||||
testFreeInstance(instance);
|
||||
|
|
Loading…
Reference in New Issue