From c3845e72ff9092f2cf870b34f6542bdd84ba60e4 Mon Sep 17 00:00:00 2001 From: Simon Lin Date: Fri, 28 Aug 2020 05:13:07 +0800 Subject: [PATCH] [mlr] configure MLR.req response status (#5350) --- include/openthread/backbone_router_ftd.h | 12 +++++ src/cli/README.md | 20 +++++++ src/cli/cli.cpp | 13 +++++ src/core/api/backbone_router_ftd_api.cpp | 10 ++++ src/core/backbone_router/bbr_manager.cpp | 18 ++++++- src/core/backbone_router/bbr_manager.hpp | 14 +++++ src/core/thread/thread_tlvs.hpp | 1 + tests/scripts/thread-cert/node.py | 9 +++- ..._2_test_multicast_listener_registration.py | 54 +++++++++++++++++++ 9 files changed, 148 insertions(+), 3 deletions(-) diff --git a/include/openthread/backbone_router_ftd.h b/include/openthread/backbone_router_ftd.h index dda75b007..3b4668b9e 100644 --- a/include/openthread/backbone_router_ftd.h +++ b/include/openthread/backbone_router_ftd.h @@ -193,6 +193,18 @@ void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance * const otIp6InterfaceIdentifier *aMlIid, uint8_t aStatus); +/** + * This method configures response status for next Multicast Listener Registration. + * + * Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. + * Only used for test and certification. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aStatus The status to respond. + * + */ +void otBackboneRouterConfigNextMulticastListenerRegistrationResponse(otInstance *aInstance, uint8_t aStatus); + /** * Represents the Multicast Listener events. * diff --git a/src/cli/README.md b/src/cli/README.md index 3f1a6e0af..7dbcca34c 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -185,6 +185,26 @@ Done Done ``` +### bbr mgmt mlr response \ + +Configure the response status for the next MLR.req. + +Only for testing/reference device. + +Known status values: + +- 0: ST_MLR_SUCCESS +- 2: ST_MLR_INVALID +- 3: ST_MLR_NO_PERSISTENT +- 4: ST_MLR_NO_RESOURCES +- 5: ST_MLR_BBR_NOT_PRIMARY +- 6: ST_MLR_GENERAL_FAILURE + +```bash +> bbr mgmt mlr response 2 +Done +``` + ### bbr state Show local Backbone state ([`Disabled`,`Primary`, `Secondary`]) for Thread 1.2 FTD. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index dffab51f8..38716e9ce 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -605,6 +605,19 @@ otError Interpreter::ProcessBackboneRouterMgmtMlr(uint8_t aArgsLength, char **aA error = otBackboneRouterMulticastListenerAdd(mInstance, &address, timeout); } } + else if (!strcmp(aArgs[0], "response")) + { + unsigned long value; + + VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); + SuccessOrExit(error = ParseUnsignedLong(aArgs[1], value)); + + otBackboneRouterConfigNextMulticastListenerRegistrationResponse(mInstance, static_cast(value)); + } + else + { + error = OT_ERROR_INVALID_COMMAND; + } exit: return error; diff --git a/src/core/api/backbone_router_ftd_api.cpp b/src/core/api/backbone_router_ftd_api.cpp index bffec15bb..23785951d 100644 --- a/src/core/api/backbone_router_ftd_api.cpp +++ b/src/core/api/backbone_router_ftd_api.cpp @@ -131,6 +131,16 @@ void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance * static_cast(aMlIid), aStatus); } +void otBackboneRouterConfigNextMulticastListenerRegistrationResponse(otInstance *aInstance, uint8_t aStatus) +{ + Instance &instance = *static_cast(aInstance); + + OT_ASSERT(aStatus <= ThreadStatusTlv::kMlrStatusMax); + + instance.Get().ConfigNextMulticastListenerRegistrationResponse( + static_cast(aStatus)); +} + void otBackboneRouterMulticastListenerClear(otInstance *aInstance) { Instance &instance = *static_cast(aInstance); diff --git a/src/core/backbone_router/bbr_manager.cpp b/src/core/backbone_router/bbr_manager.cpp index 24f00c673..686094002 100644 --- a/src/core/backbone_router/bbr_manager.cpp +++ b/src/core/backbone_router/bbr_manager.cpp @@ -57,7 +57,9 @@ Manager::Manager(Instance &aInstance) , mTimer(aInstance, Manager::HandleTimer, this) #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE , mDuaResponseStatus(ThreadStatusTlv::kDuaSuccess) + , mMlrResponseStatus(ThreadStatusTlv::kMlrSuccess) , mDuaResponseIsSpecified(false) + , mMlrResponseIsSpecified(false) #endif { } @@ -106,9 +108,17 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, VerifyOrExit(aMessage.IsConfirmable() && aMessage.GetCode() == OT_COAP_CODE_POST, error = OT_ERROR_PARSE); +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + // Required by Test Specification 5.10.22 DUA-TC-26, only for certification purpose + if (mMlrResponseIsSpecified) + { + mMlrResponseIsSpecified = false; + ExitNow(status = mMlrResponseStatus); + } +#endif + VerifyOrExit(isPrimary, status = ThreadStatusTlv::kMlrBbrNotPrimary); - // TODO: (MLR) send configured MLR response for Reference Device // TODO: (MLR) handle Commissioner Session TLV // TODO: (MLR) handle Timeout TLV @@ -292,6 +302,12 @@ void Manager::ConfigNextDuaRegistrationResponse(const Ip6::InterfaceIdentifier * mDuaResponseStatus = static_cast(aStatus); } + +void Manager::ConfigNextMulticastListenerRegistrationResponse(ThreadStatusTlv::MlrStatus aStatus) +{ + mMlrResponseIsSpecified = true; + mMlrResponseStatus = aStatus; +} #endif } // namespace BackboneRouter diff --git a/src/core/backbone_router/bbr_manager.hpp b/src/core/backbone_router/bbr_manager.hpp index c63e1f3f8..f21f74fb5 100644 --- a/src/core/backbone_router/bbr_manager.hpp +++ b/src/core/backbone_router/bbr_manager.hpp @@ -80,6 +80,18 @@ public: * */ void ConfigNextDuaRegistrationResponse(const Ip6::InterfaceIdentifier *aMlIid, uint8_t aStatus); + + /** + * This method configures response status for next Multicast Listener Registration. + * + * Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. + * Only used for test and certification. + * + * @param[in] aStatus The status to respond. + * + */ + void ConfigNextMulticastListenerRegistrationResponse(ThreadStatusTlv::MlrStatus aStatus); + #endif /** @@ -135,7 +147,9 @@ private: #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE Ip6::InterfaceIdentifier mDuaResponseTargetMlIid; ThreadStatusTlv::DuaStatus mDuaResponseStatus; + ThreadStatusTlv::MlrStatus mMlrResponseStatus; bool mDuaResponseIsSpecified : 1; + bool mMlrResponseIsSpecified : 1; #endif }; diff --git a/src/core/thread/thread_tlvs.hpp b/src/core/thread/thread_tlvs.hpp index 8b5480791..a769b3820 100644 --- a/src/core/thread/thread_tlvs.hpp +++ b/src/core/thread/thread_tlvs.hpp @@ -134,6 +134,7 @@ public: kMlrNoResources = 4, ///< BBR resource shortage. kMlrBbrNotPrimary = 5, ///< BBR is not Primary at this moment. kMlrGeneralFailure = 6, ///< Reason(s) for failure are not further specified. + kMlrStatusMax = 6, ///< Max MLR status. }; /** diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 5c57fe1c8..97c09580e 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -40,7 +40,7 @@ import time import unittest import binascii -from typing import Union +from typing import Union, Dict class Node: @@ -530,7 +530,7 @@ class Node: self.send_command(cmd) self._expect('Done') - def multicast_listener_list(self): + def multicast_listener_list(self) -> Dict[ipaddress.IPv6Address, int]: cmd = 'bbr mgmt mlr listener' self.send_command(cmd) @@ -559,6 +559,11 @@ class Node: self.send_command(cmd) self._expect(r"(Done|Error .*)") + def set_next_mlr_response(self, status: int): + cmd = 'bbr mgmt mlr response {}'.format(status) + self.send_command(cmd) + self._expect('Done') + def set_link_quality(self, addr, lqi): cmd = 'macfilter rss add-lqi %s %s' % (addr, lqi) self.send_command(cmd) diff --git a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py index 9d939747c..f32952e87 100755 --- a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py +++ b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py @@ -285,6 +285,60 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): self._bootstrap() self.__test_multicast_listeners_table_api() + def testMlrConfigResponse(self): + self._bootstrap() + self.__test_mlr_config_response() + + def __test_mlr_config_response(self): + bbr = self.nodes[BBR_1] + router = self.nodes[ROUTER_1_2] + + self.flush_all() + + # Configure next response to 0 + bbr.set_next_mlr_response(0) + router.add_ipmaddr("ff04::1") + self.simulator.go(WAIT_REDUNDANCE) + self.__check_send_mlr_req(ROUTER_1_2, ["ff04::1"], + should_send=True, + expect_mlr_rsp=True, + expect_mlr_rsp_status=0) + self.assertNotIn(ipaddress.IPv6Address("ff04::1"), bbr.multicast_listener_list()) + + router.del_ipmaddr("ff04::1") + self.simulator.go(WAIT_REDUNDANCE) + self.flush_all() + + # Configure next response to 2 + bbr.set_next_mlr_response(2) + router.add_ipmaddr("ff04::2") + self.simulator.go(WAIT_REDUNDANCE) + self.__check_send_mlr_req(ROUTER_1_2, ["ff04::2"], + should_send=True, + expect_mlr_rsp=True, + expect_mlr_rsp_status=2) + + router.del_ipmaddr("ff04::2") + self.simulator.go(WAIT_REDUNDANCE) + self.flush_all() + + # Configure next response to 4 + bbr.set_next_mlr_response(4) + router.add_ipmaddr("ff04::4") + self.simulator.go(WAIT_REDUNDANCE) + self.__check_send_mlr_req(ROUTER_1_2, ["ff04::4"], + should_send=True, + expect_mlr_rsp=True, + expect_mlr_rsp_status=4) + + # The MA should be eventually registered after reregistration + self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE) + self.assertIn(ipaddress.IPv6Address("ff04::4"), bbr.multicast_listener_list()) + + router.del_ipmaddr("ff04::4") + self.simulator.go(WAIT_REDUNDANCE) + self.flush_all() + def __test_multicast_listeners_table_api(self): self.assertTrue(self.nodes[BBR_1].multicast_listener_list() == {})