diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 203dd8cc7..ff9632787 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (113) +#define OPENTHREAD_API_VERSION (114) /** * @addtogroup api-instance diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h index 95f748b0a..f2f093e35 100644 --- a/include/openthread/ip6.h +++ b/include/openthread/ip6.h @@ -719,6 +719,20 @@ otError otIp6RegisterMulticastListeners(otInstance * otIp6RegisterMulticastListenersCallback aCallback, void * aContext); +/** + * This function sets the Mesh Local IID (for test purpose). + * + * Only available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aIid A pointer to the Mesh Local IID to set. + * + * @retval OT_ERROR_NONE Successfully set the Mesh Local IID. + * @retval OT_ERROR_INVALID_STATE Thread protocols are enabled. + * + */ +otError otIp6SetMeshLocalIid(otInstance *aInstance, const otIp6InterfaceIdentifier *aIid); + /** * @} * diff --git a/script/test b/script/test index 4adfbc84c..4b46b450d 100755 --- a/script/test +++ b/script/test @@ -272,8 +272,6 @@ do_build_otbr_docker() "-DOT_SRP_CLIENT=ON" "-DOT_TREL=OFF" "-DOTBR_DUA_ROUTING=ON" - "-DOTBR_REST=OFF" - "-DOTBR_WEB=OFF" ) local otbr_docker_image=${OTBR_DOCKER_IMAGE:-otbr-ot12-backbone-ci} @@ -293,6 +291,8 @@ do_build_otbr_docker() --build-arg REFERENCE_DEVICE=1 \ --build-arg OT_BACKBONE_CI=1 \ --build-arg NAT64=0 \ + --build-arg REST_API=0 \ + --build-arg WEB_GUI=0 \ --build-arg OTBR_OPTIONS="${otbr_options[*]}" ) diff --git a/src/cli/README.md b/src/cli/README.md index c3613fc30..756a4e593 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -66,6 +66,7 @@ Done - [mac](#mac-retries-direct) - [macfilter](#macfilter) - [masterkey](#masterkey) +- [mliid](#mliid-iid) - [mlr](#mlr-reg-ipaddr--timeout) - [mode](#mode) - [multiradio](#multiradio) @@ -1626,6 +1627,19 @@ Set the Thread Master Key value. Done ``` +### mliid \ + +Set the Mesh Local IID. + +It must be used before Thread stack is enabled. + +Only for testing/reference device. + +```bash +> mliid 1122334455667788 +Done +``` + ### mlr reg \ ... [timeout] Register Multicast Listeners to Primary Backbone Router, with an optional `timeout` (in seconds). diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 5a737a30f..f8b1e9431 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -2638,6 +2638,22 @@ exit: return error; } +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE +otError Interpreter::ProcessMlIid(uint8_t aArgsLength, Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + otIp6InterfaceIdentifier iid; + + VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); + + SuccessOrExit(error = aArgs[0].ParseAsHexString(iid.mFields.m8)); + SuccessOrExit(error = otIp6SetMeshLocalIid(mInstance, &iid)); + +exit: + return error; +} +#endif + #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE otError Interpreter::ProcessMlr(uint8_t aArgsLength, Arg aArgs[]) diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 61c7286a1..1d3081155 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -432,6 +432,9 @@ private: otError ProcessLeaderWeight(uint8_t aArgsLength, Arg aArgs[]); #endif otError ProcessMasterKey(uint8_t aArgsLength, Arg aArgs[]); +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + otError ProcessMlIid(uint8_t aArgsLength, Arg aArgs[]); +#endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE otError ProcessLinkMetrics(uint8_t aArgsLength, Arg aArgs[]); otError ProcessLinkMetricsQuery(uint8_t aArgsLength, Arg aArgs[]); @@ -733,6 +736,9 @@ private: {"macfilter", &Interpreter::ProcessMacFilter}, #endif {"masterkey", &Interpreter::ProcessMasterKey}, +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + {"mliid", &Interpreter::ProcessMlIid}, +#endif #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE {"mlr", &Interpreter::ProcessMlr}, #endif diff --git a/src/core/api/ip6_api.cpp b/src/core/api/ip6_api.cpp index 4debf7098..577b601ba 100644 --- a/src/core/api/ip6_api.cpp +++ b/src/core/api/ip6_api.cpp @@ -302,3 +302,14 @@ void otIp6SetSlaacPrefixFilter(otInstance *aInstance, otIp6SlaacPrefixFilter aFi } #endif // OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE + +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + +otError otIp6SetMeshLocalIid(otInstance *aInstance, const otIp6InterfaceIdentifier *aIid) +{ + Instance &instance = *static_cast(aInstance); + + return instance.Get().SetMeshLocalIid(static_cast(*aIid)); +} + +#endif diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 8910a0647..66d227d16 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -823,6 +823,19 @@ exit: return; } +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE +Error Mle::SetMeshLocalIid(const Ip6::InterfaceIdentifier &aMlIid) +{ + Error error = kErrorNone; + + VerifyOrExit(!Get().HasUnicastAddress(mMeshLocal64), error = kErrorInvalidState); + + mMeshLocal64.GetAddress().SetIid(aMlIid); +exit: + return error; +} +#endif + void Mle::ApplyMeshLocalPrefix(void) { mLinkLocalAllThreadNodes.GetAddress().SetMulticastNetworkPrefix(GetMeshLocalPrefix()); diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 69d20df2e..46ebd9971 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -348,6 +348,21 @@ public: */ void SetMeshLocalPrefix(const MeshLocalPrefix &aMeshLocalPrefix); +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + /** + * This method sets the Mesh Local IID. + * + * Available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. + * + * @param[in] aMlIid The Mesh Local IID. + * + * @retval kErrorNone Successfully configured Mesh Local IID. + * @retval kErrorInvalidState If the Thread stack is already enabled. + * + */ + Error SetMeshLocalIid(const Ip6::InterfaceIdentifier &aMlIid); +#endif + /** * This method applies the Mesh Local Prefix. * diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 0bc1af0ff..fd3e59c46 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -2622,6 +2622,11 @@ class NodeImpl: return result + def set_mliid(self, mliid: str): + cmd = f'mliid {mliid}' + self.send_command(cmd) + self._expect_command_output(cmd) + class Node(NodeImpl, OtCli): pass diff --git a/tests/scripts/thread-cert/test_set_mliid.py b/tests/scripts/thread-cert/test_set_mliid.py new file mode 100644 index 000000000..468d47568 --- /dev/null +++ b/tests/scripts/thread-cert/test_set_mliid.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021, 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 unittest +import ipaddress + +import config +import thread_cert + +LEADER = 1 + + +class Test_SetMlIid(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + + TOPOLOGY = { + LEADER: { + 'mode': 'rdn', + }, + } + + def test(self): + # Set ML-IID before Thread is enabled. + self.nodes[LEADER].set_mliid('1122334455667788') + + self.nodes[LEADER].start() + self.simulator.go(4) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + # Ensure set ML-IID was effective. + mleid = self.nodes[LEADER].get_ip6_address(config.ADDRESS_TYPE.ML_EID) + self.assertEqual(b'\x11\x22\x33\x44\x55\x66\x77\x88', ipaddress.IPv6Address(mleid).packed[-8:]) + + # Ensure set ML-IID fail after Thread is enabled. + self.assertRaises(Exception, lambda: self.nodes[LEADER].set_mliid('5566778811223344')) + + self.nodes[LEADER].reset() + + self.nodes[LEADER].start() + self.simulator.go(10) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + # Ensure ML-IID is persistent after reset. + mleid = self.nodes[LEADER].get_ip6_address(config.ADDRESS_TYPE.ML_EID) + self.assertEqual(b'\x11\x22\x33\x44\x55\x66\x77\x88', ipaddress.IPv6Address(mleid).packed[-8:]) + + +if __name__ == '__main__': + unittest.main()