From 8efa1ee4c672a6a00ca2d4f67d33ac367e759d6c Mon Sep 17 00:00:00 2001 From: Simon Lin Date: Sat, 18 Apr 2020 08:03:45 +0800 Subject: [PATCH] [mle] REED to handle rejected ADDR_SOL.rsp (#4834) Currently, a REED which expects to become Router soon does not send any SVR_DATA.ntf. This commit handles rejecting ADDR_SOL.rsp to allow REED to send SVR_DATA.ntf after it is rejected. --- src/core/thread/mle_router.cpp | 12 ++ src/core/thread/mle_router.hpp | 10 ++ src/core/thread/network_data_local.cpp | 3 +- tests/scripts/thread-cert/Makefile.am | 3 + .../test_reed_address_solicit_rejected.py | 122 ++++++++++++++++++ 5 files changed, 148 insertions(+), 2 deletions(-) create mode 100755 tests/scripts/thread-cert/test_reed_address_solicit_rejected.py diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index c3fd6d086..56e548403 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -74,6 +74,7 @@ MleRouter::MleRouter(Instance &aInstance) , mFixedLeaderPartitionId(0) , mRouterEligible(true) , mAddressSolicitPending(false) + , mAddressSolicitRejected(false) , mPreviousPartitionIdRouter(0) , mPreviousPartitionId(0) , mPreviousPartitionRouterIdSequence(0) @@ -232,6 +233,9 @@ otError MleRouter::HandleChildStart(AttachMode aMode) { otError error = OT_ERROR_NONE; + // reset `rejected` flag whenever REED becomes child. + mAddressSolicitRejected = false; + mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter); StopLeader(); @@ -4033,6 +4037,8 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Message * aMessage, if (status != ThreadStatusTlv::kSuccess) { + mAddressSolicitRejected = true; + if (IsRouterIdValid(mPreviousRouterId)) { if (HasChildren()) @@ -4099,6 +4105,12 @@ exit: InformPreviousChannel(); } +bool MleRouter::IsExpectedToBecomeRouter(void) const +{ + return IsRouterEligible() && !IsRouterOrLeader() && + (Get().GetActiveRouterCount() < GetRouterUpgradeThreshold()) && !mAddressSolicitRejected; +} + void MleRouter::HandleAddressSolicit(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) { static_cast(aContext)->HandleAddressSolicit(*static_cast(aMessage), diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 6e11c4332..4578032b6 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -319,6 +319,15 @@ public: */ void SetRouterDowngradeThreshold(uint8_t aThreshold) { mRouterDowngradeThreshold = aThreshold; } + /** + * This method returns if the REED is expected to become Router soon. + * + * @retval TRUE If the REED is going to become Router. + * @retval FALSE Otherwise. + * + */ + bool IsExpectedToBecomeRouter(void) const; + /** * This method removes a link to a neighbor. * @@ -830,6 +839,7 @@ private: uint32_t mFixedLeaderPartitionId; ///< only for certification testing bool mRouterEligible : 1; bool mAddressSolicitPending : 1; + bool mAddressSolicitRejected : 1; uint8_t mRouterId; uint8_t mPreviousRouterId; diff --git a/src/core/thread/network_data_local.cpp b/src/core/thread/network_data_local.cpp index 44fa3b3e5..ca47e09b6 100644 --- a/src/core/thread/network_data_local.cpp +++ b/src/core/thread/network_data_local.cpp @@ -359,8 +359,7 @@ otError Local::UpdateInconsistentServerData(Coap::ResponseHandler aHandler, void #if OPENTHREAD_FTD // Don't send this Server Data Notification if the device is going to upgrade to Router - if (Get().IsRouterEligible() && !Get().IsRouterOrLeader() && - (Get().GetActiveRouterCount() < Get().GetRouterUpgradeThreshold())) + if (Get().IsExpectedToBecomeRouter()) { ExitNow(error = OT_ERROR_INVALID_STATE); } diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index 908d2dad0..d59d60ce0 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -157,6 +157,7 @@ EXTRA_DIST = \ test_service.py \ test_network_data.py \ test_network_layer.py \ + test_reed_address_solicit_rejected.py \ tlvs_parsing.py \ $(NULL) @@ -179,6 +180,7 @@ check_SCRIPTS = \ test_service.py \ test_network_data.py \ test_network_layer.py \ + test_reed_address_solicit_rejected.py \ Cert_5_1_01_RouterAttach.py \ Cert_5_1_02_ChildAddressTimeout.py \ Cert_5_1_03_RouterAddressReallocation.py \ @@ -290,6 +292,7 @@ XFAIL_NCP_TESTS = \ test_diag.py \ test_ipv6_fragmentation.py \ test_ipv6_source_selection.py \ + test_reed_address_solicit_rejected.py \ test_service.py \ Cert_5_3_10_AddressQuery.py \ Cert_8_1_01_Commissioning.py \ diff --git a/tests/scripts/thread-cert/test_reed_address_solicit_rejected.py b/tests/scripts/thread-cert/test_reed_address_solicit_rejected.py new file mode 100755 index 000000000..d14fcd6a9 --- /dev/null +++ b/tests/scripts/thread-cert/test_reed_address_solicit_rejected.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2020, 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 config +import node +import re +import unittest + +LEADER = 1 +REED = 2 + +SRV_0_ID = 0 +SRV_0_ENT_NUMBER = '123' +SRV_0_SERVICE_DATA = 'foo' +SRV_0_SERVER_DATA = 'bar' + +# Topology: +# LEADER -- REED +# + + +class TestREEDAddressSolicitRejected(unittest.TestCase): + + def setUp(self): + self.simulator = config.create_default_simulator() + + self.nodes = {} + for i in [LEADER, REED]: + self.nodes[i] = node.Node(i, simulator=self.simulator) + + self.nodes[LEADER].set_panid(0xface) + self.nodes[LEADER].set_mode('rsdn') + + self.nodes[REED].set_panid(0xface) + self.nodes[REED].set_mode('rsdn') + self.nodes[REED].set_router_selection_jitter(1) + + def tearDown(self): + for n in list(self.nodes.values()): + n.stop() + n.destroy() + self.simulator.stop() + + def testAddressSolicitRejectedBeforeSvrData(self): + self.nodes[LEADER].start() + self.nodes[LEADER].set_router_upgrade_threshold(1) + self.simulator.go(5) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + self.nodes[REED].start() + self.simulator.go(5) + self.assertEqual(self.nodes[REED].get_state(), 'child') + + self.nodes[REED].add_service(SRV_0_ENT_NUMBER, SRV_0_SERVICE_DATA, + SRV_0_SERVER_DATA) + self.nodes[REED].register_netdata() + self.simulator.go(2) + + self.assertEqual(self.hasAloc(REED, SRV_0_ID), True) + + def testAddressSolicitRejectedAfterSvrData(self): + self.nodes[LEADER].start() + self.nodes[LEADER].set_router_upgrade_threshold(1) + self.simulator.go(5) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + self.nodes[REED].set_router_selection_jitter(120) + self.nodes[REED].start() + # set routerupgradethreshold=1 on REED so that it won't send ADDR_SOL.req + self.nodes[REED].set_router_upgrade_threshold(1) + self.simulator.go(5) + self.assertEqual(self.nodes[REED].get_state(), 'child') + + # restore routerupgradethreshold to 16 and add service + self.nodes[REED].set_router_upgrade_threshold(16) + self.nodes[REED].add_service(SRV_0_ENT_NUMBER, SRV_0_SERVICE_DATA, + SRV_0_SERVER_DATA) + self.nodes[REED].register_netdata() + self.simulator.go(130) + self.assertEqual(self.hasAloc(REED, SRV_0_ID), True) + + def hasAloc(self, node_id, service_id): + for addr in self.nodes[node_id].get_ip6_address( + config.ADDRESS_TYPE.ALOC): + m = re.match('.*:fc(..)$', addr, re.I) + if m is not None: + if m.group( + 1) == str(service_id + + 10): # for service_id=3 look for '...:fc13' + return True + + return False + + +if __name__ == '__main__': + unittest.main()