/* * 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. */ /** * @file * This file implements CLI for Backbone Router. */ #include "cli_bbr.hpp" #include "cli/cli.hpp" #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) namespace ot { namespace Cli { void Bbr::OutputConfig(const otBackboneRouterConfig &aConfig) { OutputLine("seqno: %u", aConfig.mSequenceNumber); OutputLine("delay: %u secs", aConfig.mReregistrationDelay); OutputLine("timeout: %lu secs", ToUlong(aConfig.mMlrTimeout)); } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE template <> otError Bbr::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_COMMAND; /** * @cli bbr mgmt mlr listener * @code * bbr mgmt mlr listener * ff04:0:0:0:0:0:0:abcd 3534000 * ff04:0:0:0:0:0:0:eeee 3537610 * Done * @endcode * @par * Returns the Multicast Listeners with the #otBackboneRouterMulticastListenerInfo * `mTimeout` in seconds. * @par * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` and * `OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE` are enabled. * @sa otBackboneRouterMulticastListenerGetNext */ if (aArgs[0] == "listener") { if (aArgs[1].IsEmpty()) { otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT; otBackboneRouterMulticastListenerInfo listenerInfo; while (otBackboneRouterMulticastListenerGetNext(GetInstancePtr(), &iter, &listenerInfo) == OT_ERROR_NONE) { OutputIp6Address(listenerInfo.mAddress); OutputLine(" %lu", ToUlong(listenerInfo.mTimeout)); } ExitNow(error = OT_ERROR_NONE); } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE /** * @cli bbr mgmt mlr listener clear * @code * bbr mgmt mlr listener clear * Done * @endcode * @par api_copy * #otBackboneRouterMulticastListenerClear */ if (aArgs[1] == "clear") { otBackboneRouterMulticastListenerClear(GetInstancePtr()); error = OT_ERROR_NONE; } /** * @cli bbr mgmt mlr listener add * @code * bbr mgmt mlr listener add ff04::1 * Done * @endcode * @code * bbr mgmt mlr listener add ff04::2 300 * Done * @endcode * @cparam bbr mgmt mlr listener add @ca{ipaddress} [@ca{timeout-seconds}] * @par api_copy * #otBackboneRouterMulticastListenerAdd */ else if (aArgs[1] == "add") { otIp6Address address; uint32_t timeout = 0; SuccessOrExit(error = aArgs[2].ParseAsIp6Address(address)); if (!aArgs[3].IsEmpty()) { SuccessOrExit(error = aArgs[3].ParseAsUint32(timeout)); } error = otBackboneRouterMulticastListenerAdd(GetInstancePtr(), &address, timeout); } } /** * @cli bbr mgmt mlr response * @code * bbr mgmt mlr response 2 * Done * @endcode * @cparam bbr mgmt mlr response @ca{status-code} * For `status-code`, use: * * 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 * @par api_copy * #otBackboneRouterConfigNextMulticastListenerRegistrationResponse */ else if (aArgs[0] == "response") { uint8_t status; SuccessOrExit(error = aArgs[1].ParseAsUint8(status)); otBackboneRouterConfigNextMulticastListenerRegistrationResponse(GetInstancePtr(), status); error = OT_ERROR_NONE; #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE } exit: return error; } #endif // #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE template <> otError Bbr::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_COMMAND; if (aArgs[0].IsEmpty()) { ExitNow(error = OT_ERROR_INVALID_COMMAND); } #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE /** * @cli bbr mgmt dua * @code * bbr mgmt dua 1 2f7c235e5025a2fd * Done * @endcode * @code * bbr mgmt dua 160 * Done * @endcode * @cparam bbr mgmt dua @ca{status|coap-code} [@ca{meshLocalIid}] * For `status` or `coap-code`, use: * * 0: ST_DUA_SUCCESS * * 1: ST_DUA_REREGISTER * * 2: ST_DUA_INVALID * * 3: ST_DUA_DUPLICATE * * 4: ST_DUA_NO_RESOURCES * * 5: ST_DUA_BBR_NOT_PRIMARY * * 6: ST_DUA_GENERAL_FAILURE * * 160: COAP code 5.00 * @par * With the `meshLocalIid` included, this command configures the response status * for the next DUA registration. Without `meshLocalIid`, respond to the next * DUA.req with the specified `status` or `coap-code`. * @par * Available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. * @sa otBackboneRouterConfigNextDuaRegistrationResponse */ if (aArgs[0] == "dua") { uint8_t status; otIp6InterfaceIdentifier *mlIid = nullptr; otIp6InterfaceIdentifier iid; SuccessOrExit(error = aArgs[1].ParseAsUint8(status)); if (!aArgs[2].IsEmpty()) { SuccessOrExit(error = aArgs[2].ParseAsHexString(iid.mFields.m8)); mlIid = &iid; VerifyOrExit(aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS); } otBackboneRouterConfigNextDuaRegistrationResponse(GetInstancePtr(), mlIid, status); ExitNow(); } #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE if (aArgs[0] == "mlr") { error = Process(aArgs + 1); ExitNow(); } #endif exit: return error; } /** * @cli bbr enable * @code * bbr enable * Done * @endcode * @par api_copy * #otBackboneRouterSetEnabled */ template <> otError Bbr::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); otBackboneRouterSetEnabled(GetInstancePtr(), true); return OT_ERROR_NONE; } /** * @cli bbr disable * @code * bbr disable * Done * @endcode * @par api_copy * #otBackboneRouterSetEnabled */ template <> otError Bbr::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); otBackboneRouterSetEnabled(GetInstancePtr(), false); return OT_ERROR_NONE; } /** * @cli bbr jitter (get,set) * @code * bbr jitter * 20 * Done * @endcode * @code * bbr jitter 10 * Done * @endcode * @cparam bbr jitter [@ca{jitter}] * @par * Gets or sets jitter (in seconds) for Backbone Router registration. * @par * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled. * @sa otBackboneRouterGetRegistrationJitter * @sa otBackboneRouterSetRegistrationJitter */ template <> otError Bbr::Process(Arg aArgs[]) { return ProcessGetSet(aArgs, otBackboneRouterGetRegistrationJitter, otBackboneRouterSetRegistrationJitter); } /** * @cli bbr register * @code * bbr register * Done * @endcode * @par api_copy * #otBackboneRouterRegister */ template <> otError Bbr::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); return otBackboneRouterRegister(GetInstancePtr()); } /** * @cli bbr state * @code * bbr state * Disabled * Done * @endcode * @code * bbr state * Primary * Done * @endcode * @code * bbr state * Secondary * Done * @endcode * @par * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled. * @par api_copy * #otBackboneRouterGetState */ template <> otError Bbr::Process(Arg aArgs[]) { static const char *const kStateStrings[] = { "Disabled", // (0) OT_BACKBONE_ROUTER_STATE_DISABLED "Secondary", // (1) OT_BACKBONE_ROUTER_STATE_SECONDARY "Primary", // (2) OT_BACKBONE_ROUTER_STATE_PRIMARY }; static_assert(0 == OT_BACKBONE_ROUTER_STATE_DISABLED, "OT_BACKBONE_ROUTER_STATE_DISABLED value is incorrect"); static_assert(1 == OT_BACKBONE_ROUTER_STATE_SECONDARY, "OT_BACKBONE_ROUTER_STATE_SECONDARY value is incorrect"); static_assert(2 == OT_BACKBONE_ROUTER_STATE_PRIMARY, "OT_BACKBONE_ROUTER_STATE_PRIMARY value is incorrect"); OT_UNUSED_VARIABLE(aArgs); OutputLine("%s", Stringify(otBackboneRouterGetState(GetInstancePtr()), kStateStrings)); return OT_ERROR_NONE; } /** * @cli bbr config * @code * bbr config * seqno: 10 * delay: 120 secs * timeout: 300 secs * Done * @endcode * @par api_copy * #otBackboneRouterGetConfig */ template <> otError Bbr::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; otBackboneRouterConfig config; otBackboneRouterGetConfig(GetInstancePtr(), &config); if (aArgs[0].IsEmpty()) { OutputConfig(config); } else { // Set local Backbone Router configuration. /** * @cli bbr config (set) * @code * bbr config seqno 20 delay 30 * Done * @endcode * @cparam bbr config [seqno @ca{seqno}] [delay @ca{delay}] [timeout @ca{timeout}] * @par * `bbr register` should be issued explicitly to register Backbone Router service to Leader * for Secondary Backbone Router. * @par api_copy * #otBackboneRouterSetConfig */ for (Arg *arg = &aArgs[0]; !arg->IsEmpty(); arg++) { if (*arg == "seqno") { arg++; SuccessOrExit(error = arg->ParseAsUint8(config.mSequenceNumber)); } else if (*arg == "delay") { arg++; SuccessOrExit(error = arg->ParseAsUint16(config.mReregistrationDelay)); } else if (*arg == "timeout") { arg++; SuccessOrExit(error = arg->ParseAsUint32(config.mMlrTimeout)); } else { ExitNow(error = OT_ERROR_INVALID_ARGS); } } error = otBackboneRouterSetConfig(GetInstancePtr(), &config); } exit: return error; } #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE otError Bbr::Process(Arg aArgs[]) { #define CmdEntry(aCommandString) {aCommandString, &Bbr::Process} otError error = OT_ERROR_INVALID_COMMAND; /** * @cli bbr * @code * bbr * BBR Primary: * server16: 0xE400 * seqno: 10 * delay: 120 secs * timeout: 300 secs * Done * @endcode * @code * bbr * BBR Primary: None * Done * @endcode * @par * Returns the current Primary Backbone Router information for the Thread device. */ if (aArgs[0].IsEmpty()) { otBackboneRouterConfig config; OutputFormat("BBR Primary:"); if (otBackboneRouterGetPrimary(GetInstancePtr(), &config) == OT_ERROR_NONE) { OutputNewLine(); OutputLine("server16: 0x%04X", config.mServer16); OutputConfig(config); } else { OutputLine(" None"); } error = OT_ERROR_NONE; ExitNow(); } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE { static constexpr Command kCommands[] = { CmdEntry("config"), CmdEntry("disable"), CmdEntry("enable"), CmdEntry("jitter"), CmdEntry("mgmt"), CmdEntry("register"), CmdEntry("state"), }; #undef CmdEntry static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); const Command *command; command = BinarySearch::Find(aArgs[0].GetCString(), kCommands); VerifyOrExit(command != nullptr); error = (this->*command->mHandler)(aArgs + 1); } #endif // #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE exit: return error; } } // namespace Cli } // namespace ot #endif // #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)