[mle] retransmit link request after router reset (#8704)

According to Thread 1.3.0 Section 4.7.1.3, router synchronization
after reset requires the multicast link request message to be
retransmitted until a response is received, or the maximum
transmission limit has been reached. If the router was a leader or had
more than 5 children prior to reset, it MUST be retransmitted as a
critical message. Each multicast retransmission of an MLE Message on a
TL1 or TL2 Link must be delayed by MLE_MULTICAST_RETRANSMISSION_DELAY
multiplied by a random value between 0.9 and 1.1 with a resolution of
at least 1 ms.

This commit implements the above requirement. With this requirement,
in the cases when a router is reset and there is no other router to
respond to the link request then it takes longer time for the router
to join thread network. Thus some test cases are modified to allow
longer router sync time in such cases.
This commit is contained in:
Yang Sun
2023-02-06 21:10:38 -08:00
committed by GitHub
parent 7aaf33478d
commit 82e816b82f
28 changed files with 451 additions and 32 deletions
+26
View File
@@ -83,6 +83,9 @@ Mle::Mle(Instance &aInstance)
, mAttachTimer(aInstance)
, mDelayedResponseTimer(aInstance)
, mMessageTransmissionTimer(aInstance)
#if OPENTHREAD_FTD
, mWasLeader(false)
#endif
, mAttachMode(kAnyPartition)
, mChildUpdateAttempts(0)
, mChildUpdateRequestState(kChildUpdateRequestNone)
@@ -435,6 +438,8 @@ void Mle::Restore(void)
Get<MleRouter>().SetPreviousPartitionId(networkInfo.GetPreviousPartitionId());
Get<ChildTable>().Restore();
}
mWasLeader = networkInfo.GetRole() == kRoleLeader;
#endif
// Successfully restored the network information from non-volatile settings after boot.
@@ -1885,6 +1890,14 @@ void Mle::ScheduleMessageTransmissionTimer(void)
{
uint32_t interval = 0;
#if OPENTHREAD_FTD
if (mRole == kRoleDetached && mLinkRequestAttempts > 0)
{
ExitNow(interval = Random::NonCrypto::GetUint32InRange(kMulticastTransmissionDelayMin,
kMulticastTransmissionDelayMax));
}
#endif
switch (mChildUpdateRequestState)
{
case kChildUpdateRequestNone:
@@ -1940,6 +1953,19 @@ void Mle::HandleMessageTransmissionTimer(void)
// - Retransmission of "Child Update Request",
// - Retransmission of "Data Request" on a child,
// - Sending periodic keep-alive "Child Update Request" messages on a non-sleepy (rx-on) child.
// - Retransmission of "Link Request" after router reset
#if OPENTHREAD_FTD
// Retransmit multicast link request if no response has been received
// and maximum transmission limit has not been reached.
if (mRole == kRoleDetached && mLinkRequestAttempts > 0)
{
IgnoreError(Get<MleRouter>().SendLinkRequest(nullptr));
mLinkRequestAttempts--;
ScheduleMessageTransmissionTimer();
ExitNow();
}
#endif
switch (mChildUpdateRequestState)
{
+6 -1
View File
@@ -1706,6 +1706,8 @@ protected:
#endif
void ScheduleMessageTransmissionTimer(void);
private:
// Declare early so we can use in as `TimerMilli` callbacks.
void HandleAttachTimer(void);
@@ -1733,6 +1735,10 @@ protected:
AttachTimer mAttachTimer; ///< The timer for driving the attach process.
DelayTimer mDelayedResponseTimer; ///< The timer to delay MLE responses.
MsgTxTimer mMessageTransmissionTimer; ///< The timer for (re-)sending of MLE messages (e.g. Child Update).
#if OPENTHREAD_FTD
uint8_t mLinkRequestAttempts; ///< Number of remaining link requests to send after reset.
bool mWasLeader; ///< Indicating if device was leader before reset.
#endif
private:
static constexpr uint8_t kMleHopLimit = 255;
@@ -1924,7 +1930,6 @@ private:
void SendDelayedResponse(TxMessage &aMessage, const DelayedResponseMetadata &aMetadata);
static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
void ScheduleMessageTransmissionTimer(void);
void ReestablishLinkWithNeighbor(Neighbor &aNeighbor);
static void HandleDetachGracefullyTimer(Timer &aTimer);
void HandleDetachGracefullyTimer(void);
+21 -2
View File
@@ -184,6 +184,20 @@ exit:
return error;
}
// If the router was a leader or had more than 5 children prior to reset,
// the multicast link request is retransmitted as a critical message.
void MleRouter::SetLinkRequestTransmissionCounter(void)
{
uint16_t numOfChildren = mChildTable.GetNumChildren(Child::kInStateValidOrRestoring);
mLinkRequestAttempts = kMaxTransmissionCount;
if (mWasLeader || numOfChildren >= kMinCriticalChildrenCount)
{
mLinkRequestAttempts = kMaxCriticalTransmissionCount;
}
}
Error MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus)
{
Error error = kErrorNone;
@@ -201,7 +215,10 @@ Error MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus)
switch (mRole)
{
case kRoleDetached:
SetLinkRequestTransmissionCounter();
SuccessOrExit(error = SendLinkRequest(nullptr));
mLinkRequestAttempts--;
ScheduleMessageTransmissionTimer();
Get<TimeTicker>().RegisterReceiver(TimeTicker::kMleRouter);
break;
@@ -865,7 +882,8 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
break;
case Neighbor::kStateInvalid:
VerifyOrExit((mChallengeTimeout > 0) && (response == mChallenge), error = kErrorSecurity);
VerifyOrExit((mLinkRequestAttempts > 0 || mChallengeTimeout > 0) && (response == mChallenge),
error = kErrorSecurity);
OT_FALL_THROUGH;
@@ -930,6 +948,7 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
SetStateRouter(GetRloc16());
}
mLinkRequestAttempts = 0; // completed router sync after reset, no more link request to retransmit
mRetrieveNewNetworkData = true;
IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr(), kDataRequestTlvs));
@@ -1541,7 +1560,7 @@ void MleRouter::HandleTimeTick(void)
switch (mRole)
{
case kRoleDetached:
if (mChallengeTimeout == 0)
if (mChallengeTimeout == 0 && mLinkRequestAttempts == 0)
{
IgnoreError(BecomeDetached());
ExitNow();
+4
View File
@@ -556,6 +556,8 @@ private:
static constexpr uint8_t kChildRouterLinks = OPENTHREAD_CONFIG_MLE_CHILD_ROUTER_LINKS;
static constexpr uint8_t kMaxChildIpAddresses = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD;
static constexpr uint8_t kMinCriticalChildrenCount = 6;
void HandleDetachStart(void);
void HandleChildStart(AttachMode aMode);
void HandleLinkRequest(RxInfo &aRxInfo);
@@ -627,6 +629,8 @@ private:
void HandleAdvertiseTrickleTimer(void);
void HandleTimeTick(void);
void SetLinkRequestTransmissionCounter(void);
TrickleTimer mAdvertiseTrickleTimer;
ChildTable mChildTable;
+7
View File
@@ -100,6 +100,13 @@ constexpr uint32_t kMaxResponseDelay = 1000; ///< Max response del
constexpr uint32_t kChildIdRequestTimeout = 5000; ///< Max delay to rx a Child ID Request (in msec)
constexpr uint32_t kLinkRequestTimeout = 2000; ///< Max delay to rx a Link Accept
constexpr uint8_t kMulticastLinkRequestDelay = 5; ///< Max delay for sending a mcast Link Request (in sec)
constexpr uint8_t kMaxCriticalTransmissionCount = 6; ///< Max number of times an critical MLE message may be transmitted
constexpr uint32_t kMulticastTransmissionDelay = 5000; ///< Delay for retransmitting a multicast packet (in msec)
constexpr uint32_t kMulticastTransmissionDelayMin =
kMulticastTransmissionDelay * 9 / 10; ///< Min delay for retransmitting a multicast packet (in msec)
constexpr uint32_t kMulticastTransmissionDelayMax =
kMulticastTransmissionDelay * 11 / 10; ///< Max delay for retransmitting a multicast packet (in msec)
constexpr uint32_t kMinTimeoutKeepAlive = (((kMaxChildKeepAliveAttempts + 1) * kUnicastRetransmissionDelay) / 1000);
constexpr uint32_t kMinPollPeriod = OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD;
+1 -1
View File
@@ -37,7 +37,7 @@ proc skip_on_macos {} {
proc wait_for {command success {failure {[\r\n]FAILURE_NOT_EXPECTED[\r\n]}}} {
set timeout 1
for {set i 0} {$i < 20} {incr i} {
for {set i 0} {$i < 40} {incr i} {
if {$command != ""} {
send "$command\n"
}
@@ -114,7 +114,7 @@ class Cert_5_3_6_RouterIdMask(thread_cert.TestCase):
# 5
self.nodes[ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.simulator.go(config.ROUTER_RESET_DELAY)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
@@ -84,7 +84,7 @@ class Cert_5_5_2_LeaderReboot(thread_cert.TestCase):
self.assertEqual(self.nodes[ROUTER].get_state(), 'leader')
self.nodes[LEADER].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.simulator.go(config.LEADER_RESET_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'router')
addrs = self.nodes[ED].get_addrs()
@@ -114,7 +114,7 @@ class Cert_5_5_3_SplitMergeChildren(thread_cert.TestCase):
self.assertEqual(self.nodes[ROUTER2].get_state(), 'leader')
self.nodes[LEADER].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.simulator.go(config.LEADER_RESET_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'router')
self.simulator.go(30)
@@ -102,7 +102,7 @@ class Cert_5_5_4_SplitMergeRouters(thread_cert.TestCase):
self.simulator.go(150)
self.nodes[LEADER].start()
self.simulator.go(50)
self.simulator.go(50 + config.LEADER_RESET_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'router')
@@ -93,7 +93,7 @@ class Cert_5_5_7_SplitMergeThreeWay(thread_cert.TestCase):
self.simulator.go(140)
self.nodes[LEADER1].start()
self.simulator.go(30)
self.simulator.go(30 + config.LEADER_RESET_DELAY)
addrs = self.nodes[LEADER1].get_addrs()
for addr in addrs:
@@ -143,7 +143,7 @@ class Cert_7_1_6_BorderRouterAsLeader(thread_cert.TestCase):
self.simulator.go(720)
self.nodes[ROUTER_1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.simulator.go(config.ROUTER_RESET_DELAY)
self.assertEqual(self.nodes[ROUTER_1].get_state(), 'router')
self.collect_rloc16s()
@@ -138,7 +138,7 @@ class Cert_9_2_15_PendingPartition(thread_cert.TestCase):
self.simulator.go(100)
self.nodes[ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.simulator.go(config.ROUTER_RESET_DELAY)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
self.simulator.go(100)
@@ -142,7 +142,7 @@ class Cert_9_2_16_ActivePendingPartition(thread_cert.TestCase):
self.simulator.go(100)
self.nodes[ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.simulator.go(config.ROUTER_RESET_DELAY)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
self.assertEqual(self.nodes[COMMISSIONER].get_network_name(), NETWORK_NAME_FINAL)
+8
View File
@@ -127,6 +127,7 @@ PANID = 0xface
LEADER_STARTUP_DELAY = 12
ROUTER_STARTUP_DELAY = 10
ED_STARTUP_DELAY = 5
BORDER_ROUTER_STARTUP_DELAY = 20
MAX_NEIGHBOR_AGE = 100
INFINITE_COST_TIMEOUT = 90
@@ -153,6 +154,13 @@ PACKET_VERIFICATION_NONE = 0
PACKET_VERIFICATION_DEFAULT = 1
PACKET_VERIFICATION_TREL = 2
# After leader reset it may retransmit link request 6 times with max 5.5s interval
LEADER_RESET_DELAY = 41
# After router reset it may retransmit link request 3 times with max 5.5s interval
ROUTER_RESET_DELAY = 23
MLE_MAX_CRITICAL_TRANSMISSION_COUNT = 6
MLE_MAX_TRANSMISSION_COUNT = 3
def create_default_network_data_prefix_sub_tlvs_factories():
return {
+5 -2
View File
@@ -145,10 +145,13 @@ class TestDetach(thread_cert.TestCase):
self.assertEqual(leader.get_state(), 'disabled')
leader.start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
# leader didn't become leader after the last start(), so it re-syncs in a non-critical manner thus taking ROUTER_RESET_DELAY to recover
self.simulator.go(config.ROUTER_RESET_DELAY / 2)
self.assertEqual(leader.get_state(), 'detached')
self.simulator.go(config.ROUTER_RESET_DELAY / 2)
self.assertEqual(leader.get_state(), 'leader')
router1.start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.simulator.go(config.ROUTER_RESET_DELAY)
self.assertEqual(router1.get_state(), 'router')
leader.thread_stop()
@@ -128,7 +128,7 @@ class TestHistoryTracker(thread_cert.TestCase):
# Start leader and child
leader.start()
self.simulator.go(SHORT_WAIT * 2)
self.simulator.go(config.LEADER_RESET_DELAY)
self.assertEqual(leader.get_state(), 'leader')
child.start()
@@ -0,0 +1,155 @@
#!/usr/bin/env python3
#
# 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.
#
import copy
import unittest
import command
import config
import mle
import thread_cert
from pktverify.consts import MLE_PARENT_REQUEST, MLE_LINK_REQUEST, MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, TLV_REQUEST_TLV, VERSION_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
DUT_LEADER = 1
DUT_ROUTER1 = 2
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to show that when the Leader is rebooted, it sends MLE_MAX_CRITICAL_TRANSMISSION_COUNT MLE link request packets if no response is received.
#
# Test Topology:
# -------------
# Leader
# |
# Router
#
# DUT Types:
# ----------
# Leader
# Router
class Test_LeaderRebootMultipleLinkRequest(thread_cert.TestCase):
#USE_MESSAGE_FACTORY = False
TOPOLOGY = {
DUT_LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [DUT_ROUTER1]
},
DUT_ROUTER1: {
'name': 'ROUTER',
'mode': 'rdn',
'allowlist': [DUT_LEADER]
},
}
def _setUpLeader(self):
self.nodes[DUT_LEADER].add_allowlist(self.nodes[DUT_ROUTER1].get_addr64())
self.nodes[DUT_LEADER].enable_allowlist()
def test(self):
self.nodes[DUT_LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_LEADER].get_state(), 'leader')
self.nodes[DUT_ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_ROUTER1].get_state(), 'router')
leader_rloc = self.nodes[DUT_LEADER].get_ip6_address(config.ADDRESS_TYPE.RLOC)
leader_rloc16 = self.nodes[DUT_LEADER].get_addr16()
self.nodes[DUT_LEADER].reset()
self.assertFalse(self.nodes[DUT_ROUTER1].ping(leader_rloc))
self._setUpLeader()
# Router1 will not reply to leader's link request
self.nodes[DUT_ROUTER1].clear_allowlist()
self.nodes[DUT_LEADER].start()
self.simulator.go(config.LEADER_RESET_DELAY)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER = pv.vars['ROUTER']
# Verify topology is formed correctly.
pv.verify_attached('ROUTER', 'LEADER')
# The DUT MUST send properly formatted MLE Advertisements with
# an IP Hop Limit of 255 to the Link-Local All Nodes multicast
# address (FF02::1).
# The following TLVs MUST be present in the MLE Advertisements:
# - Leader Data TLV
# - Route64 TLV
# - Source Address TLV
with pkts.save_index():
pkts.filter_wpan_src64(LEADER).\
filter_mle_advertisement('Leader').\
must_next()
pkts.filter_wpan_src64(ROUTER).\
filter_mle_advertisement('Router').\
must_next()
pkts.filter_ping_request().\
filter_wpan_src64(ROUTER).\
must_next()
# The Leader MUST send MLE_MAX_CRITICAL_TRANSMISSION_COUNT multicast Link Request
# The following TLVs MUST be present in the Link Request:
# - Challenge TLV
# - Version TLV
# - TLV Request TLV: Address16 TLV, Route64 TLV
for i in range(0, config.MLE_MAX_CRITICAL_TRANSMISSION_COUNT):
pkts.filter_wpan_src64(LEADER).\
filter_LLARMA().\
filter_mle_cmd(MLE_LINK_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
VERSION_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
ROUTE64_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is nullField and\
p.mle.tlv.route64.id_mask is nullField
).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,176 @@
#!/usr/bin/env python3
#
# 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.
#
import copy
import unittest
import command
import config
import mle
import thread_cert
from pktverify.consts import MLE_PARENT_REQUEST, MLE_LINK_REQUEST, MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, TLV_REQUEST_TLV, VERSION_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
DUT_ROUTER = 2
MED1 = 3
MED2 = 4
MED3 = 5
MED4 = 6
MED5 = 7
MED6 = 8
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to show that when a router with > 5 children is rebooted, it sends MLE_MAX_CRITICAL_TRANSMISSION_COUNT MLE link request packets if no response is received.
#
# Test Topology:
# -------------
# Leader
# |
# Router ------------------------+
# | | | | | |
# MED1 MED2 MED3 MED4 MED5 MED6
#
# DUT Types:
# ----------
# Router
class Test_LeaderRebootMultipleLinkRequest(thread_cert.TestCase):
#USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [DUT_ROUTER]
},
DUT_ROUTER: {
'name': 'ROUTER',
'mode': 'rdn',
'allowlist': [LEADER, MED1, MED2, MED3, MED4, MED5, MED6]
},
MED1: {
'name': 'MED1',
'mode': 'rn',
'allowlist': [DUT_ROUTER]
},
MED2: {
'name': 'MED2',
'mode': 'rn',
'allowlist': [DUT_ROUTER]
},
MED3: {
'name': 'MED3',
'mode': 'rn',
'allowlist': [DUT_ROUTER]
},
MED4: {
'name': 'MED4',
'mode': 'rn',
'allowlist': [DUT_ROUTER]
},
MED5: {
'name': 'MED5',
'mode': 'rn',
'allowlist': [DUT_ROUTER]
},
MED6: {
'name': 'MED6',
'mode': 'rn',
'allowlist': [DUT_ROUTER]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[DUT_ROUTER].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_ROUTER].get_state(), 'router')
for medid in range(MED1, MED6 + 1):
self.nodes[medid].start()
self.simulator.go(config.ED_STARTUP_DELAY)
self.assertEqual(self.nodes[medid].get_state(), 'child')
self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
self.nodes[DUT_ROUTER].reset()
# Leader will not reply to router's link request
self.nodes[LEADER].clear_allowlist()
self.nodes[DUT_ROUTER].start()
self.simulator.go(config.LEADER_RESET_DELAY)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER = pv.vars['ROUTER']
# Verify topology is formed correctly.
pv.verify_attached('ROUTER', 'LEADER')
for i in range(1, 7):
pv.verify_attached('MED%d' % i, 'ROUTER', 'MTD')
pkts.filter_wpan_src64(ROUTER).\
filter_mle_advertisement('Router').\
must_next()
# The router MUST send MLE_MAX_CRITICAL_TRANSMISSION_COUNT multicast Link Request
# The following TLVs MUST be present in the Link Request:
# - Challenge TLV
# - Version TLV
# - TLV Request TLV: Address16 TLV, Route64 TLV
for i in range(0, config.MLE_MAX_CRITICAL_TRANSMISSION_COUNT):
pkts.filter_wpan_src64(ROUTER).\
filter_LLARMA().\
filter_mle_cmd(MLE_LINK_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
VERSION_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
ROUTE64_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is nullField and\
p.mle.tlv.route64.id_mask is nullField
).\
must_next()
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -63,7 +63,7 @@ class Test_SetMlIid(thread_cert.TestCase):
self.nodes[LEADER].reset()
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.simulator.go(config.LEADER_RESET_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
# Ensure ML-IID is persistent after reset.
@@ -144,7 +144,7 @@ class TestBackboneRouterService(thread_cert.TestCase):
self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX)
self.nodes[BBR_1].enable_backbone_router()
self.nodes[BBR_1].start()
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
WAIT_TIME = config.ROUTER_RESET_DELAY
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_1].get_state(), 'router')
WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
@@ -229,7 +229,7 @@ class TestBackboneRouterService(thread_cert.TestCase):
self.nodes[BBR_2].enable_backbone_router()
self.nodes[BBR_2].interface_up()
self.nodes[BBR_2].thread_start()
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
WAIT_TIME = config.ROUTER_RESET_DELAY
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_2].get_state(), 'router')
WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
@@ -125,7 +125,7 @@ class SSED_CslTransmission(thread_cert.TestCase):
# Check if SSED is able to resynchronize with the parent after it is gone longer than the timeout
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.simulator.go(config.LEADER_RESET_DELAY)
self.nodes[SSED_1].set_csl_timeout(8)
self.nodes[SSED_1].set_timeout(10)
self.simulator.go(2)
@@ -133,7 +133,7 @@ class SSED_CslTransmission(thread_cert.TestCase):
self.simulator.go(25)
self.flush_all()
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.simulator.go(config.LEADER_RESET_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.simulator.go(5)
self.assertEqual(self.nodes[SSED_1].get_state(), 'child')
@@ -838,6 +838,7 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
# Turn off Router 1.1 and turn on Router 1.2
self.nodes[ROUTER_1_1].stop()
self.nodes[ROUTER_1_2].start()
self.simulator.go(config.ROUTER_RESET_DELAY)
for id in [FED_1, MED_1, SED_1]:
self.simulator.go(config.DEFAULT_CHILD_TIMEOUT + WAIT_REDUNDANCE)
@@ -148,7 +148,7 @@ def check_parent_is_associated():
verify(parent.is_associated())
wpan.verify_within(check_parent_is_associated, 10)
wpan.verify_within(check_parent_is_associated, 40)
# Verify that all the children are recovered and present in the parent's
# child table again.
@@ -97,6 +97,7 @@ children = [child1, child2]
# Test implementation
WAIT_INTERVAL = 6
LEADER_RESET_DELAY = 38
# Thread Mode for end-device and sleepy end-device
DEVICE_MODE_SLEEPY_END_DEVICE = (wpan.THREAD_MODE_FLAG_FULL_NETWORK_DATA)
@@ -144,7 +145,7 @@ wpan.verify_within(check_child_table, WAIT_INTERVAL)
# Reset parent and verify all children are recovered
parent.reset()
wpan.verify_within(check_child_table, WAIT_INTERVAL)
wpan.verify_within(check_child_table, WAIT_INTERVAL + LEADER_RESET_DELAY)
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
@@ -101,7 +101,7 @@ verify(node.get(wpan.WPAN_CHANNEL_MANAGER_AUTO_SELECT_ENABLED) == 'true')
node.reset()
start_time = time.time()
wait_time = 20
wait_time = 50
while node.get(wpan.WPAN_STATE) != wpan.STATE_ASSOCIATED:
if time.time() - start_time > wait_time:
+12 -6
View File
@@ -820,7 +820,7 @@ void VerifyPrefixTable(const OnLinkPrefix *aOnLinkPrefixes,
VerifyOrQuit(routePrefixCount == aNumRoutePrefixes);
}
void InitTest(bool aEnablBorderRouting = false)
void InitTest(bool aEnablBorderRouting = false, bool aAfterReset = false)
{
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Initialize OT instance.
@@ -828,6 +828,12 @@ void InitTest(bool aEnablBorderRouting = false)
sNow = 0;
sInstance = static_cast<Instance *>(testInitInstance());
uint32_t delay = 10000;
if (aAfterReset)
{
delay += 26000; // leader reset sync delay
}
memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame));
sRadioTxFrame.mPsdu = sRadioTxFramePsdu;
@@ -846,7 +852,7 @@ void InitTest(bool aEnablBorderRouting = false)
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ensure device starts as leader.
AdvanceTime(10000);
AdvanceTime(delay);
VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER);
@@ -2543,7 +2549,7 @@ void TestSavedOnLinkPrefixes(void)
testFreeInstance(sInstance);
InitTest(/* aEnablBorderRouting */ true);
InitTest(/* aEnablBorderRouting */ true, /* aAfterReset */ true);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(true));
@@ -2581,7 +2587,7 @@ void TestSavedOnLinkPrefixes(void)
testFreeInstance(sInstance);
InitTest(/* aEnablBorderRouting */ true);
InitTest(/* aEnablBorderRouting */ true, /* aAfterReset */ true);
sExpectedPio = kPioAdvertisingLocalOnLink;
@@ -2627,7 +2633,7 @@ void TestSavedOnLinkPrefixes(void)
testFreeInstance(sInstance);
InitTest();
InitTest(/* aEnablBorderRouting */ false, /* aAfterReset */ true);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Start Routing Manager.
@@ -2678,7 +2684,7 @@ void TestSavedOnLinkPrefixes(void)
Log("Disabling and re-enabling OT Instance again");
testFreeInstance(sInstance);
InitTest();
InitTest(/* aEnablBorderRouting */ false, /* aAfterReset */ true);
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(true));
AdvanceTime(100);
+11 -3
View File
@@ -208,6 +208,14 @@ void InitTest(void)
VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER);
}
void FinalizeTest(void)
{
SuccessOrQuit(otIp6SetEnabled(sInstance, false));
SuccessOrQuit(otThreadSetEnabled(sInstance, false));
SuccessOrQuit(otInstanceErasePersistentInfo(sInstance));
testFreeInstance(sInstance);
}
//---------------------------------------------------------------------------------------------------------------------
enum UpdateHandlerMode
@@ -458,7 +466,7 @@ void TestSrpServerBase(void)
// Finalize OT instance and validate all heap allocations are freed.
Log("Finalizing OT instance");
testFreeInstance(sInstance);
FinalizeTest();
VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
@@ -570,7 +578,7 @@ void TestSrpServerReject(void)
// Finalize OT instance and validate all heap allocations are freed.
Log("Finalizing OT instance");
testFreeInstance(sInstance);
FinalizeTest();
VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
@@ -682,7 +690,7 @@ void TestSrpServerIgnore(void)
// Finalize OT instance and validate all heap allocations are freed.
Log("Finalizing OT instance");
testFreeInstance(sInstance);
FinalizeTest();
VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());