| /* |
| * Copyright (c) 2016, 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 the CLI interpreter. |
| */ |
| |
| #include "cli_dataset.hpp" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include <openthread/dataset.h> |
| #include <openthread/dataset_ftd.h> |
| #include <openthread/dataset_updater.h> |
| |
| #include "cli/cli.hpp" |
| |
| namespace ot { |
| namespace Cli { |
| |
| otOperationalDatasetTlvs Dataset::sDatasetTlvs; |
| |
| const Dataset::ComponentMapper *Dataset::LookupMapper(const char *aName) const |
| { |
| static constexpr ComponentMapper kMappers[] = { |
| { |
| "activetimestamp", |
| &Components::mIsActiveTimestampPresent, |
| &Dataset::OutputActiveTimestamp, |
| &Dataset::ParseActiveTimestamp, |
| }, |
| { |
| "channel", |
| &Components::mIsChannelPresent, |
| &Dataset::OutputChannel, |
| &Dataset::ParseChannel, |
| }, |
| { |
| "channelmask", |
| &Components::mIsChannelMaskPresent, |
| &Dataset::OutputChannelMask, |
| &Dataset::ParseChannelMask, |
| }, |
| { |
| "delay", |
| &Components::mIsDelayPresent, |
| &Dataset::OutputDelay, |
| &Dataset::ParseDelay, |
| }, |
| { |
| "delaytimer", // Alias for "delay "to ensure backward compatibility for "mgmtsetcommand" command |
| &Components::mIsDelayPresent, |
| &Dataset::OutputDelay, |
| &Dataset::ParseDelay, |
| }, |
| { |
| "extpanid", |
| &Components::mIsExtendedPanIdPresent, |
| &Dataset::OutputExtendedPanId, |
| &Dataset::ParseExtendedPanId, |
| }, |
| { |
| "localprefix", // Alias for "meshlocalprefix" to ensure backward compatibility in "mgmtsetcommand" command |
| &Components::mIsMeshLocalPrefixPresent, |
| &Dataset::OutputMeshLocalPrefix, |
| &Dataset::ParseMeshLocalPrefix, |
| }, |
| { |
| "meshlocalprefix", |
| &Components::mIsMeshLocalPrefixPresent, |
| &Dataset::OutputMeshLocalPrefix, |
| &Dataset::ParseMeshLocalPrefix, |
| }, |
| { |
| "networkkey", |
| &Components::mIsNetworkKeyPresent, |
| &Dataset::OutputNetworkKey, |
| &Dataset::ParseNetworkKey, |
| }, |
| { |
| "networkname", |
| &Components::mIsNetworkNamePresent, |
| &Dataset::OutputNetworkName, |
| &Dataset::ParseNetworkName, |
| }, |
| { |
| "panid", |
| &Components::mIsPanIdPresent, |
| &Dataset::OutputPanId, |
| &Dataset::ParsePanId, |
| }, |
| { |
| "pendingtimestamp", |
| &Components::mIsPendingTimestampPresent, |
| &Dataset::OutputPendingTimestamp, |
| &Dataset::ParsePendingTimestamp, |
| }, |
| { |
| "pskc", |
| &Components::mIsPskcPresent, |
| &Dataset::OutputPskc, |
| &Dataset::ParsePskc, |
| }, |
| { |
| "securitypolicy", |
| &Components::mIsSecurityPolicyPresent, |
| &Dataset::OutputSecurityPolicy, |
| &Dataset::ParseSecurityPolicy, |
| }, |
| }; |
| |
| static_assert(BinarySearch::IsSorted(kMappers), "kMappers is not sorted"); |
| |
| return BinarySearch::Find(aName, kMappers); |
| } |
| |
| //--------------------------------------------------------------------------------------------------------------------- |
| |
| /** |
| * @cli dataset activetimestamp (get, set) |
| * @code |
| * dataset activetimestamp |
| * 123456789 |
| * Done |
| * @endcode |
| * @code |
| * dataset activetimestamp 123456789 |
| * Done |
| * @endcode |
| * @cparam dataset activetimestamp [@ca{timestamp}] |
| * Pass the optional `timestamp` argument to set the active timestamp. |
| * @par |
| * Gets or sets #otOperationalDataset::mActiveTimestamp. |
| */ |
| void Dataset::OutputActiveTimestamp(const otOperationalDataset &aDataset) |
| { |
| OutputUint64Line(aDataset.mActiveTimestamp.mSeconds); |
| } |
| |
| /** |
| * @cli dataset channel (get,set) |
| * @code |
| * dataset channel |
| * 12 |
| * Done |
| * @endcode |
| * @code |
| * dataset channel 12 |
| * Done |
| * @endcode |
| * @cparam dataset channel [@ca{channel-num}] |
| * Use the optional `channel-num` argument to set the channel. |
| * @par |
| * Gets or sets #otOperationalDataset::mChannel. |
| */ |
| void Dataset::OutputChannel(const otOperationalDataset &aDataset) { OutputLine("%u", aDataset.mChannel); } |
| |
| /** |
| * @cli dataset channelmask (get,set) |
| * @code |
| * dataset channelmask |
| * 0x07fff800 |
| * Done |
| * @endcode |
| * @code |
| * dataset channelmask 0x07fff800 |
| * Done |
| * @endcode |
| * @cparam dataset channelmask [@ca{channel-mask}] |
| * Use the optional `channel-mask` argument to set the channel mask. |
| * @par |
| * Gets or sets #otOperationalDataset::mChannelMask |
| */ |
| void Dataset::OutputChannelMask(const otOperationalDataset &aDataset) |
| { |
| OutputLine("0x%08lx", ToUlong(aDataset.mChannelMask)); |
| } |
| |
| /** |
| * @cli dataset delay (get,set) |
| * @code |
| * dataset delay |
| * 1000 |
| * Done |
| * @endcode |
| * @code |
| * dataset delay 1000 |
| * Done |
| * @endcode |
| * @cparam dataset delay [@ca{delay}] |
| * Use the optional `delay` argument to set the delay timer value. |
| * @par |
| * Gets or sets #otOperationalDataset::mDelay. |
| * @sa otDatasetSetDelayTimerMinimal |
| */ |
| void Dataset::OutputDelay(const otOperationalDataset &aDataset) { OutputLine("%lu", ToUlong(aDataset.mDelay)); } |
| |
| /** |
| * @cli dataset extpanid (get,set) |
| * @code |
| * dataset extpanid |
| * 000db80123456789 |
| * Done |
| * @endcode |
| * @code |
| * dataset extpanid 000db80123456789 |
| * Done |
| * @endcode |
| * @cparam dataset extpanid [@ca{extpanid}] |
| * Use the optional `extpanid` argument to set the Extended Personal Area Network ID. |
| * @par |
| * Gets or sets #otOperationalDataset::mExtendedPanId. |
| * @note The commissioning credential in the dataset buffer becomes stale after changing |
| * this value. Use `dataset pskc` to reset. |
| * @csa{dataset pskc (get,set)} |
| */ |
| void Dataset::OutputExtendedPanId(const otOperationalDataset &aDataset) { OutputBytesLine(aDataset.mExtendedPanId.m8); } |
| |
| /** |
| * @cli dataset meshlocalprefix (get,set) |
| * @code |
| * dataset meshlocalprefix |
| * fd00:db8:0:0::/64 |
| * Done |
| * @endcode |
| * @code |
| * dataset meshlocalprefix fd00:db8:0:0:: |
| * Done |
| * @endcode |
| * @cparam dataset meshlocalprefix [@ca{meshlocalprefix}] |
| * Use the optional `meshlocalprefix` argument to set the Mesh-Local Prefix. |
| * @par |
| * Gets or sets #otOperationalDataset::mMeshLocalPrefix. |
| */ |
| void Dataset::OutputMeshLocalPrefix(const otOperationalDataset &aDataset) |
| { |
| OutputIp6PrefixLine(aDataset.mMeshLocalPrefix); |
| } |
| |
| /** |
| * @cli dataset networkkey (get,set) |
| * @code |
| * dataset networkkey |
| * 00112233445566778899aabbccddeeff |
| * Done |
| * @endcode |
| * @code |
| * dataset networkkey 00112233445566778899aabbccddeeff |
| * Done |
| * @endcode |
| * @cparam dataset networkkey [@ca{key}] |
| * Use the optional `key` argument to set the Network Key. |
| * @par |
| * Gets or sets #otOperationalDataset::mNetworkKey. |
| */ |
| void Dataset::OutputNetworkKey(const otOperationalDataset &aDataset) { OutputBytesLine(aDataset.mNetworkKey.m8); } |
| |
| /** |
| * @cli dataset networkname (get,set) |
| * @code |
| * dataset networkname |
| * OpenThread |
| * Done |
| * @endcode |
| * @code |
| * dataset networkname OpenThread |
| * Done |
| * @endcode |
| * @cparam dataset networkname [@ca{name}] |
| * Use the optional `name` argument to set the Network Name. |
| * @par |
| * Gets or sets #otOperationalDataset::mNetworkName. |
| * @note The Commissioning Credential in the dataset buffer becomes stale after changing this value. |
| * Use `dataset pskc` to reset. |
| * @csa{dataset pskc (get,set)} |
| */ |
| void Dataset::OutputNetworkName(const otOperationalDataset &aDataset) { OutputLine("%s", aDataset.mNetworkName.m8); } |
| |
| /** |
| * @cli dataset panid (get,set) |
| * @code |
| * dataset panid |
| * 0x1234 |
| * Done |
| * @endcode |
| * @code |
| * dataset panid 0x1234 |
| * Done |
| * @endcode |
| * @cparam dataset panid [@ca{panid}] |
| * Use the optional `panid` argument to set the PAN ID. |
| * @par |
| * Gets or sets #otOperationalDataset::mPanId. |
| */ |
| void Dataset::OutputPanId(const otOperationalDataset &aDataset) { OutputLine("0x%04x", aDataset.mPanId); } |
| |
| /** |
| * @cli dataset pendingtimestamp (get,set) |
| * @code |
| * dataset pendingtimestamp |
| * 123456789 |
| * Done |
| * @endcode |
| * @code |
| * dataset pendingtimestamp 123456789 |
| * Done |
| * @endcode |
| * @cparam dataset pendingtimestamp [@ca{timestamp}] |
| * Use the optional `timestamp` argument to set the pending timestamp seconds. |
| * @par |
| * Gets or sets #otOperationalDataset::mPendingTimestamp. |
| */ |
| void Dataset::OutputPendingTimestamp(const otOperationalDataset &aDataset) |
| { |
| OutputUint64Line(aDataset.mPendingTimestamp.mSeconds); |
| } |
| |
| /** |
| * @cli dataset pskc (get,set) |
| * @code |
| * dataset pskc |
| * 67c0c203aa0b042bfb5381c47aef4d9e |
| * Done |
| * @endcode |
| * @code |
| * dataset pskc -p 123456 |
| * Done |
| * @endcode |
| * @code |
| * dataset pskc 67c0c203aa0b042bfb5381c47aef4d9e |
| * Done |
| * @endcode |
| * @cparam dataset pskc [@ca{-p} @ca{passphrase}] | [@ca{key}] |
| * For FTD only, use `-p` with the `passphrase` argument. `-p` generates a pskc from |
| * the UTF-8 encoded `passphrase` that you provide, together with |
| * the network name and extended PAN ID. If set, `-p` uses the dataset buffer; |
| * otherwise, it uses the current stack. |
| * Alternatively, you can set pskc as `key` (hex format). |
| * @par |
| * Gets or sets #otOperationalDataset::mPskc. |
| */ |
| void Dataset::OutputPskc(const otOperationalDataset &aDataset) { OutputBytesLine(aDataset.mPskc.m8); } |
| |
| /** |
| * @cli dataset securitypolicy (get,set) |
| * @code |
| * dataset securitypolicy |
| * 672 onrc |
| * Done |
| * @endcode |
| * @code |
| * dataset securitypolicy 672 onrc |
| * Done |
| * @endcode |
| * @cparam dataset securitypolicy [@ca{rotationtime} [@ca{onrcCepR}]] |
| * * Use `rotationtime` for `thrKeyRotation`, in units of hours. |
| * * Security Policy commands use the `onrcCepR` argument mappings to get and set |
| * #otSecurityPolicy members, for example `o` represents |
| * #otSecurityPolicy::mObtainNetworkKeyEnabled. |
| * @moreinfo{@dataset}. |
| * @par |
| * Gets or sets the %Dataset security policy. |
| */ |
| void Dataset::OutputSecurityPolicy(const otOperationalDataset &aDataset) |
| { |
| OutputSecurityPolicy(aDataset.mSecurityPolicy); |
| } |
| |
| //--------------------------------------------------------------------------------------------------------------------- |
| |
| otError Dataset::ParseActiveTimestamp(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| otError error; |
| |
| SuccessOrExit(error = aArgs++->ParseAsUint64(aDataset.mActiveTimestamp.mSeconds)); |
| aDataset.mActiveTimestamp.mTicks = 0; |
| aDataset.mActiveTimestamp.mAuthoritative = false; |
| |
| exit: |
| return error; |
| } |
| |
| otError Dataset::ParseChannel(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| return aArgs++->ParseAsUint16(aDataset.mChannel); |
| } |
| |
| otError Dataset::ParseChannelMask(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| return aArgs++->ParseAsUint32(aDataset.mChannelMask); |
| } |
| |
| otError Dataset::ParseDelay(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| return aArgs++->ParseAsUint32(aDataset.mDelay); |
| } |
| |
| otError Dataset::ParseExtendedPanId(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| return aArgs++->ParseAsHexString(aDataset.mExtendedPanId.m8); |
| } |
| |
| otError Dataset::ParseMeshLocalPrefix(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| otError error; |
| otIp6Address prefix; |
| |
| SuccessOrExit(error = aArgs++->ParseAsIp6Address(prefix)); |
| |
| memcpy(aDataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(aDataset.mMeshLocalPrefix.m8)); |
| |
| exit: |
| return error; |
| } |
| |
| otError Dataset::ParseNetworkKey(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| return aArgs++->ParseAsHexString(aDataset.mNetworkKey.m8); |
| } |
| |
| otError Dataset::ParseNetworkName(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(!aArgs->IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| error = otNetworkNameFromString(&aDataset.mNetworkName, aArgs++->GetCString()); |
| |
| exit: |
| return error; |
| } |
| |
| otError Dataset::ParsePanId(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| return aArgs++->ParseAsUint16(aDataset.mPanId); |
| } |
| |
| otError Dataset::ParsePendingTimestamp(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| otError error; |
| |
| SuccessOrExit(error = aArgs++->ParseAsUint64(aDataset.mPendingTimestamp.mSeconds)); |
| aDataset.mPendingTimestamp.mTicks = 0; |
| aDataset.mPendingTimestamp.mAuthoritative = false; |
| |
| exit: |
| return error; |
| } |
| |
| otError Dataset::ParsePskc(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| otError error; |
| |
| #if OPENTHREAD_FTD |
| if (*aArgs == "-p") |
| { |
| aArgs++; |
| VerifyOrExit(!aArgs->IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| |
| SuccessOrExit(error = otDatasetGeneratePskc( |
| aArgs->GetCString(), |
| (aDataset.mComponents.mIsNetworkNamePresent |
| ? &aDataset.mNetworkName |
| : reinterpret_cast<const otNetworkName *>(otThreadGetNetworkName(GetInstancePtr()))), |
| (aDataset.mComponents.mIsExtendedPanIdPresent ? &aDataset.mExtendedPanId |
| : otThreadGetExtendedPanId(GetInstancePtr())), |
| &aDataset.mPskc)); |
| aArgs++; |
| } |
| else |
| #endif |
| { |
| ExitNow(error = aArgs++->ParseAsHexString(aDataset.mPskc.m8)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError Dataset::ParseSecurityPolicy(Arg *&aArgs, otOperationalDataset &aDataset) |
| { |
| return ParseSecurityPolicy(aDataset.mSecurityPolicy, aArgs); |
| } |
| |
| //--------------------------------------------------------------------------------------------------------------------- |
| |
| otError Dataset::ProcessCommand(const ComponentMapper &aMapper, Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| otOperationalDataset dataset; |
| |
| if (aArgs[0].IsEmpty()) |
| { |
| SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); |
| |
| if (dataset.mComponents.*aMapper.mIsPresentPtr) |
| { |
| (this->*aMapper.mOutput)(dataset); |
| } |
| } |
| else |
| { |
| ClearAllBytes(dataset); |
| SuccessOrExit(error = (this->*aMapper.mParse)(aArgs, dataset)); |
| dataset.mComponents.*aMapper.mIsPresentPtr = true; |
| SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError Dataset::Print(otOperationalDatasetTlvs &aDatasetTlvs) |
| { |
| struct ComponentTitle |
| { |
| const char *mTitle; // Title to output. |
| const char *mName; // To use with `LookupMapper()`. |
| }; |
| |
| static const ComponentTitle kTitles[] = { |
| {"Pending Timestamp", "pendingtimestamp"}, |
| {"Active Timestamp", "activetimestamp"}, |
| {"Channel", "channel"}, |
| {"Channel Mask", "channelmask"}, |
| {"Delay", "delay"}, |
| {"Ext PAN ID", "extpanid"}, |
| {"Mesh Local Prefix", "meshlocalprefix"}, |
| {"Network Key", "networkkey"}, |
| {"Network Name", "networkname"}, |
| {"PAN ID", "panid"}, |
| {"PSKc", "pskc"}, |
| {"Security Policy", "securitypolicy"}, |
| }; |
| |
| otError error; |
| otOperationalDataset dataset; |
| |
| SuccessOrExit(error = otDatasetParseTlvs(&aDatasetTlvs, &dataset)); |
| |
| for (const ComponentTitle &title : kTitles) |
| { |
| const ComponentMapper *mapper = LookupMapper(title.mName); |
| |
| if (dataset.mComponents.*mapper->mIsPresentPtr) |
| { |
| OutputFormat("%s: ", title.mTitle); |
| (this->*mapper->mOutput)(dataset); |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * @cli dataset init (active,new,pending,tlvs) |
| * @code |
| * dataset init new |
| * Done |
| * @endcode |
| * @cparam dataset init {@ca{active}|@ca{new}|@ca{pending}|@ca{tlvs}} [@ca{hex-encoded-tlvs}] |
| * Use `new` to initialize a new dataset, then enter the command `dataset commit active`. |
| * Use `tlvs` for hex-encoded TLVs. |
| * @par |
| * OT CLI checks for `active`, `pending`, or `tlvs` and returns the corresponding values. Otherwise, |
| * OT CLI creates a new, random network and returns a new dataset. |
| * @csa{dataset commit active} |
| * @csa{dataset active} |
| */ |
| template <> otError Dataset::Process<Cmd("init")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_INVALID_ARGS; |
| |
| if (aArgs[0] == "active") |
| { |
| error = otDatasetGetActiveTlvs(GetInstancePtr(), &sDatasetTlvs); |
| } |
| else if (aArgs[0] == "pending") |
| { |
| error = otDatasetGetPendingTlvs(GetInstancePtr(), &sDatasetTlvs); |
| } |
| #if OPENTHREAD_FTD |
| else if (aArgs[0] == "new") |
| { |
| otOperationalDataset dataset; |
| |
| SuccessOrExit(error = otDatasetCreateNewNetwork(GetInstancePtr(), &dataset)); |
| otDatasetConvertToTlvs(&dataset, &sDatasetTlvs); |
| } |
| #endif |
| else if (aArgs[0] == "tlvs") |
| { |
| uint16_t size = sizeof(sDatasetTlvs.mTlvs); |
| |
| SuccessOrExit(error = aArgs[1].ParseAsHexString(size, sDatasetTlvs.mTlvs)); |
| sDatasetTlvs.mLength = static_cast<uint8_t>(size); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * @cli dataset active |
| * @code |
| * dataset active |
| * Active Timestamp: 1 |
| * Channel: 13 |
| * Channel Mask: 0x07fff800 |
| * Ext PAN ID: d63e8e3e495ebbc3 |
| * Mesh Local Prefix: fd3d:b50b:f96d:722d::/64 |
| * Network Key: dfd34f0f05cad978ec4e32b0413038ff |
| * Network Name: OpenThread-8f28 |
| * PAN ID: 0x8f28 |
| * PSKc: c23a76e98f1a6483639b1ac1271e2e27 |
| * Security Policy: 0, onrcb |
| * Done |
| * @endcode |
| * @code |
| * dataset active -x |
| * 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff |
| * Done |
| * @endcode |
| * @cparam dataset active [-x] |
| * The optional `-x` argument prints the Active Operational %Dataset values as hex-encoded TLVs. |
| * @par api_copy |
| * #otDatasetGetActive |
| * @par |
| * OT CLI uses #otOperationalDataset members to return dataset values to the console. |
| */ |
| template <> otError Dataset::Process<Cmd("active")>(Arg aArgs[]) |
| { |
| otError error; |
| otOperationalDatasetTlvs dataset; |
| |
| SuccessOrExit(error = otDatasetGetActiveTlvs(GetInstancePtr(), &dataset)); |
| |
| if (aArgs[0].IsEmpty()) |
| { |
| error = Print(dataset); |
| } |
| else if (aArgs[0] == "-x") |
| { |
| OutputBytesLine(dataset.mTlvs, dataset.mLength); |
| } |
| else |
| { |
| error = OT_ERROR_INVALID_ARGS; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| template <> otError Dataset::Process<Cmd("pending")>(Arg aArgs[]) |
| { |
| otError error; |
| otOperationalDatasetTlvs datasetTlvs; |
| |
| SuccessOrExit(error = otDatasetGetPendingTlvs(GetInstancePtr(), &datasetTlvs)); |
| |
| if (aArgs[0].IsEmpty()) |
| { |
| error = Print(datasetTlvs); |
| } |
| else if (aArgs[0] == "-x") |
| { |
| OutputBytesLine(datasetTlvs.mTlvs, datasetTlvs.mLength); |
| } |
| else |
| { |
| error = OT_ERROR_INVALID_ARGS; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * @cli dataset clear |
| * @code |
| * dataset clear |
| * Done |
| * @endcode |
| * @par |
| * Reset the Operational %Dataset buffer. |
| */ |
| template <> otError Dataset::Process<Cmd("clear")>(Arg aArgs[]) |
| { |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| ClearAllBytes(sDatasetTlvs); |
| return OT_ERROR_NONE; |
| } |
| |
| template <> otError Dataset::Process<Cmd("commit")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_INVALID_ARGS; |
| |
| /** |
| * @cli dataset commit active |
| * @code |
| * dataset commit active |
| * Done |
| * @endcode |
| * @par |
| * Commit the Operational %Dataset buffer to Active Operational %Dataset. |
| * @csa{dataset commit pending} |
| * @sa #otDatasetSetPending |
| */ |
| if (aArgs[0] == "active") |
| { |
| error = otDatasetSetActiveTlvs(GetInstancePtr(), &sDatasetTlvs); |
| } |
| /** |
| * @cli dataset commit pending |
| * @code |
| * dataset commit pending |
| * Done |
| * @endcode |
| * @par |
| * Commit the Operational %Dataset buffer to Pending Operational %Dataset. |
| * @csa{dataset commit active} |
| * @sa #otDatasetSetActive |
| */ |
| else if (aArgs[0] == "pending") |
| { |
| error = otDatasetSetPendingTlvs(GetInstancePtr(), &sDatasetTlvs); |
| } |
| |
| return error; |
| } |
| |
| template <> otError Dataset::Process<Cmd("mgmtsetcommand")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| otOperationalDataset dataset; |
| uint8_t tlvs[128]; |
| uint8_t tlvsLength = 0; |
| |
| ClearAllBytes(dataset); |
| |
| for (Arg *arg = &aArgs[1]; !arg->IsEmpty();) |
| { |
| const ComponentMapper *mapper = LookupMapper(arg->GetCString()); |
| |
| if (mapper != nullptr) |
| { |
| arg++; |
| SuccessOrExit(error = (this->*mapper->mParse)(arg, dataset)); |
| dataset.mComponents.*mapper->mIsPresentPtr = true; |
| } |
| else if (*arg == "-x") |
| { |
| uint16_t length; |
| |
| arg++; |
| length = sizeof(tlvs); |
| SuccessOrExit(error = arg->ParseAsHexString(length, tlvs)); |
| tlvsLength = static_cast<uint8_t>(length); |
| arg++; |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| |
| /** |
| * @cli dataset mgmtsetcommand active |
| * @code |
| * dataset mgmtsetcommand active activetimestamp 123 securitypolicy 1 onrcb |
| * Done |
| * @endcode |
| * @cparam dataset mgmtsetcommand active [@ca{dataset-components}] [-x @ca{tlv-list}] |
| * To learn more about these parameters and argument mappings, refer to @dataset. |
| * @par |
| * @note This command is primarily used for testing only. |
| * @par api_copy |
| * #otDatasetSendMgmtActiveSet |
| * @csa{dataset mgmtgetcommand active} |
| * @csa{dataset mgmtgetcommand pending} |
| * @csa{dataset mgmtsetcommand pending} |
| */ |
| if (aArgs[0] == "active") |
| { |
| error = otDatasetSendMgmtActiveSet(GetInstancePtr(), &dataset, tlvs, tlvsLength, /* aCallback */ nullptr, |
| /* aContext */ nullptr); |
| } |
| /** |
| * @cli dataset mgmtsetcommand pending |
| * @code |
| * dataset mgmtsetcommand pending activetimestamp 123 securitypolicy 1 onrcb |
| * Done |
| * @endcode |
| * @cparam dataset mgmtsetcommand pending [@ca{dataset-components}] [-x @ca{tlv-list}] |
| * To learn more about these parameters and argument mappings, refer to @dataset. |
| * @par |
| * @note This command is primarily used for testing only. |
| * @par api_copy |
| * #otDatasetSendMgmtPendingSet |
| * @csa{dataset mgmtgetcommand active} |
| * @csa{dataset mgmtgetcommand pending} |
| * @csa{dataset mgmtsetcommand active} |
| */ |
| else if (aArgs[0] == "pending") |
| { |
| error = otDatasetSendMgmtPendingSet(GetInstancePtr(), &dataset, tlvs, tlvsLength, /* aCallback */ nullptr, |
| /* aContext */ nullptr); |
| } |
| else |
| { |
| error = OT_ERROR_INVALID_ARGS; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| template <> otError Dataset::Process<Cmd("mgmtgetcommand")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| otOperationalDatasetComponents datasetComponents; |
| uint8_t tlvs[32]; |
| uint8_t tlvsLength = 0; |
| bool destAddrSpecified = false; |
| otIp6Address address; |
| |
| ClearAllBytes(datasetComponents); |
| |
| for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++) |
| { |
| const ComponentMapper *mapper = LookupMapper(arg->GetCString()); |
| |
| if (mapper != nullptr) |
| { |
| datasetComponents.*mapper->mIsPresentPtr = true; |
| } |
| else if (*arg == "-x") |
| { |
| uint16_t length; |
| |
| arg++; |
| length = sizeof(tlvs); |
| SuccessOrExit(error = arg->ParseAsHexString(length, tlvs)); |
| tlvsLength = static_cast<uint8_t>(length); |
| } |
| else if (*arg == "address") |
| { |
| arg++; |
| SuccessOrExit(error = arg->ParseAsIp6Address(address)); |
| destAddrSpecified = true; |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| |
| /** |
| * @cli dataset mgmtgetcommand active |
| * @code |
| * dataset mgmtgetcommand active address fdde:ad00:beef:0:558:f56b:d688:799 activetimestamp securitypolicy |
| * Done |
| * @endcode |
| * @code |
| * dataset mgmtgetcommand active networkname |
| * Done |
| * @endcode |
| * @cparam dataset mgmtgetcommand active [address @ca{leader-address}] [@ca{dataset-components}] [-x @ca{tlv-list}] |
| * * Use `address` to specify the IPv6 destination; otherwise, the Leader ALOC is used as default. |
| * * For `dataset-components`, you can pass any combination of #otOperationalDatasetComponents, for |
| * example `activetimestamp`, `pendingtimestamp`, or `networkkey`. |
| * * The optional `-x` argument specifies raw TLVs to be requested. |
| * @par |
| * OT CLI sends a MGMT_ACTIVE_GET with the relevant arguments. |
| * To learn more about these parameters and argument mappings, refer to @dataset. |
| * @note This command is primarily used for testing only. |
| * @par api_copy |
| * #otDatasetSendMgmtActiveGet |
| * @csa{dataset mgmtgetcommand pending} |
| * @csa{dataset mgmtsetcommand active} |
| * @csa{dataset mgmtsetcommand pending} |
| */ |
| if (aArgs[0] == "active") |
| { |
| error = otDatasetSendMgmtActiveGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength, |
| destAddrSpecified ? &address : nullptr); |
| } |
| /** |
| * @cli dataset mgmtgetcommand pending |
| * @code |
| * dataset mgmtgetcommand pending address fdde:ad00:beef:0:558:f56b:d688:799 activetimestamp securitypolicy |
| * Done |
| * @endcode |
| * @code |
| * dataset mgmtgetcommand pending networkname |
| * Done |
| * @endcode |
| * @cparam dataset mgmtgetcommand pending [address @ca{leader-address}] [@ca{dataset-components}] [-x @ca{tlv-list}] |
| * To learn more about these parameters and argument mappings, refer to @dataset. |
| * @par |
| * @note This command is primarily used for testing only. |
| * @par api_copy |
| * #otDatasetSendMgmtPendingGet |
| * @csa{dataset mgmtgetcommand active} |
| * @csa{dataset mgmtsetcommand active} |
| * @csa{dataset mgmtsetcommand pending} |
| */ |
| else if (aArgs[0] == "pending") |
| { |
| error = otDatasetSendMgmtPendingGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength, |
| destAddrSpecified ? &address : nullptr); |
| } |
| else |
| { |
| error = OT_ERROR_INVALID_ARGS; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| void Dataset::OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy) |
| { |
| OutputFormat("%u ", aSecurityPolicy.mRotationTime); |
| |
| if (aSecurityPolicy.mObtainNetworkKeyEnabled) |
| { |
| OutputFormat("o"); |
| } |
| |
| if (aSecurityPolicy.mNativeCommissioningEnabled) |
| { |
| OutputFormat("n"); |
| } |
| |
| if (aSecurityPolicy.mRoutersEnabled) |
| { |
| OutputFormat("r"); |
| } |
| |
| if (aSecurityPolicy.mExternalCommissioningEnabled) |
| { |
| OutputFormat("c"); |
| } |
| |
| if (aSecurityPolicy.mCommercialCommissioningEnabled) |
| { |
| OutputFormat("C"); |
| } |
| |
| if (aSecurityPolicy.mAutonomousEnrollmentEnabled) |
| { |
| OutputFormat("e"); |
| } |
| |
| if (aSecurityPolicy.mNetworkKeyProvisioningEnabled) |
| { |
| OutputFormat("p"); |
| } |
| |
| if (aSecurityPolicy.mNonCcmRoutersEnabled) |
| { |
| OutputFormat("R"); |
| } |
| |
| OutputLine(" %u", aSecurityPolicy.mVersionThresholdForRouting); |
| } |
| |
| otError Dataset::ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, Arg *&aArgs) |
| { |
| static constexpr uint8_t kMaxVersionThreshold = 7; |
| |
| otError error; |
| otSecurityPolicy policy; |
| uint8_t versionThreshold; |
| |
| ClearAllBytes(policy); |
| |
| SuccessOrExit(error = aArgs->ParseAsUint16(policy.mRotationTime)); |
| aArgs++; |
| |
| VerifyOrExit(!aArgs->IsEmpty()); |
| |
| for (const char *flag = aArgs->GetCString(); *flag != '\0'; flag++) |
| { |
| switch (*flag) |
| { |
| case 'o': |
| policy.mObtainNetworkKeyEnabled = true; |
| break; |
| |
| case 'n': |
| policy.mNativeCommissioningEnabled = true; |
| break; |
| |
| case 'r': |
| policy.mRoutersEnabled = true; |
| break; |
| |
| case 'c': |
| policy.mExternalCommissioningEnabled = true; |
| break; |
| |
| case 'C': |
| policy.mCommercialCommissioningEnabled = true; |
| break; |
| |
| case 'e': |
| policy.mAutonomousEnrollmentEnabled = true; |
| break; |
| |
| case 'p': |
| policy.mNetworkKeyProvisioningEnabled = true; |
| break; |
| |
| case 'R': |
| policy.mNonCcmRoutersEnabled = true; |
| break; |
| |
| default: |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| } |
| |
| aArgs++; |
| VerifyOrExit(!aArgs->IsEmpty()); |
| |
| SuccessOrExit(error = aArgs->ParseAsUint8(versionThreshold)); |
| aArgs++; |
| VerifyOrExit(versionThreshold <= kMaxVersionThreshold, error = OT_ERROR_INVALID_ARGS); |
| policy.mVersionThresholdForRouting = versionThreshold; |
| |
| exit: |
| if (error == OT_ERROR_NONE) |
| { |
| aSecurityPolicy = policy; |
| } |
| |
| return error; |
| } |
| |
| /** |
| * @cli dataset set (active,pending) |
| * @code |
| * dataset set active 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff |
| * Done |
| * @endcode |
| * @code |
| * dataset set pending 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff |
| * Done |
| * @endcode |
| * @cparam dataset set {active|pending} @ca{tlvs} |
| * @par |
| * The CLI `dataset set` command sets the Active Operational %Dataset using hex-encoded TLVs. |
| * @par api_copy |
| * #otDatasetSetActive |
| */ |
| template <> otError Dataset::Process<Cmd("set")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| MeshCoP::Dataset::Type datasetType; |
| |
| if (aArgs[0] == "active") |
| { |
| datasetType = MeshCoP::Dataset::Type::kActive; |
| } |
| else if (aArgs[0] == "pending") |
| { |
| datasetType = MeshCoP::Dataset::Type::kPending; |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| } |
| |
| { |
| otOperationalDataset dataset; |
| otOperationalDatasetTlvs datasetTlvs; |
| uint16_t tlvsLength = OT_OPERATIONAL_DATASET_MAX_LENGTH; |
| |
| SuccessOrExit(error = aArgs[1].ParseAsHexString(tlvsLength, datasetTlvs.mTlvs)); |
| datasetTlvs.mLength = static_cast<uint8_t>(tlvsLength); |
| |
| SuccessOrExit(error = otDatasetParseTlvs(&datasetTlvs, &dataset)); |
| |
| switch (datasetType) |
| { |
| case MeshCoP::Dataset::Type::kActive: |
| SuccessOrExit(error = otDatasetSetActiveTlvs(GetInstancePtr(), &datasetTlvs)); |
| break; |
| case MeshCoP::Dataset::Type::kPending: |
| SuccessOrExit(error = otDatasetSetPendingTlvs(GetInstancePtr(), &datasetTlvs)); |
| break; |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * @cli dataset tlvs |
| * @code |
| * dataset tlvs |
| * 0e080000000000010000000300001635060004001fffe0020...f7f8 |
| * Done |
| * @endcode |
| * @par api_copy |
| * #otDatasetConvertToTlvs |
| */ |
| template <> otError Dataset::Process<Cmd("tlvs")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); |
| OutputBytesLine(sDatasetTlvs.mTlvs, sDatasetTlvs.mLength); |
| |
| exit: |
| return error; |
| } |
| |
| #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD |
| |
| template <> otError Dataset::Process<Cmd("updater")>(Arg aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| /** |
| * @cli dataset updater |
| * @code |
| * dataset updater |
| * Enabled |
| * Done |
| * @endcode |
| * @par api_copy |
| * #otDatasetUpdaterIsUpdateOngoing |
| */ |
| if (aArgs[0].IsEmpty()) |
| { |
| OutputEnabledDisabledStatus(otDatasetUpdaterIsUpdateOngoing(GetInstancePtr())); |
| } |
| /** |
| * @cli dataset updater start |
| * @code |
| * channel |
| * 19 |
| * Done |
| * dataset clear |
| * Done |
| * dataset channel 15 |
| * Done |
| * dataset |
| * Channel: 15 |
| * Done |
| * dataset updater start |
| * Done |
| * dataset updater |
| * Enabled |
| * Done |
| * Dataset update complete: OK |
| * channel |
| * 15 |
| * Done |
| * @endcode |
| * @par api_copy |
| * #otDatasetUpdaterRequestUpdate |
| */ |
| else if (aArgs[0] == "start") |
| { |
| otOperationalDataset dataset; |
| |
| SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); |
| SuccessOrExit( |
| error = otDatasetUpdaterRequestUpdate(GetInstancePtr(), &dataset, &Dataset::HandleDatasetUpdater, this)); |
| } |
| /** |
| * @cli dataset updater cancel |
| * @code |
| * @dataset updater cancel |
| * Done |
| * @endcode |
| * @par api_copy |
| * #otDatasetUpdaterCancelUpdate |
| */ |
| else if (aArgs[0] == "cancel") |
| { |
| otDatasetUpdaterCancelUpdate(GetInstancePtr()); |
| } |
| else |
| { |
| error = OT_ERROR_INVALID_ARGS; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| void Dataset::HandleDatasetUpdater(otError aError, void *aContext) |
| { |
| static_cast<Dataset *>(aContext)->HandleDatasetUpdater(aError); |
| } |
| |
| void Dataset::HandleDatasetUpdater(otError aError) |
| { |
| OutputLine("Dataset update complete: %s", otThreadErrorToString(aError)); |
| } |
| |
| #endif // OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD |
| |
| otError Dataset::Process(Arg aArgs[]) |
| { |
| #define CmdEntry(aCommandString) \ |
| { \ |
| aCommandString, &Dataset::Process<Cmd(aCommandString)> \ |
| } |
| |
| static constexpr Command kCommands[] = { |
| CmdEntry("active"), |
| CmdEntry("clear"), |
| CmdEntry("commit"), |
| CmdEntry("init"), |
| CmdEntry("mgmtgetcommand"), |
| CmdEntry("mgmtsetcommand"), |
| CmdEntry("pending"), |
| CmdEntry("set"), |
| CmdEntry("tlvs"), |
| #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD |
| CmdEntry("updater"), |
| #endif |
| }; |
| |
| #undef CmdEntry |
| |
| static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); |
| |
| otError error = OT_ERROR_INVALID_COMMAND; |
| const Command *command; |
| const ComponentMapper *mapper; |
| |
| if (aArgs[0].IsEmpty()) |
| { |
| ExitNow(error = Print(sDatasetTlvs)); |
| } |
| |
| /** |
| * @cli dataset help |
| * @code |
| * dataset help |
| * help |
| * active |
| * activetimestamp |
| * channel |
| * channelmask |
| * clear |
| * commit |
| * delay |
| * extpanid |
| * init |
| * meshlocalprefix |
| * mgmtgetcommand |
| * mgmtsetcommand |
| * networkkey |
| * networkname |
| * panid |
| * pending |
| * pendingtimestamp |
| * pskc |
| * securitypolicy |
| * set |
| * tlvs |
| * Done |
| * @endcode |
| * @par |
| * Gets a list of `dataset` CLI commands. @moreinfo{@dataset}. |
| */ |
| if (aArgs[0] == "help") |
| { |
| OutputCommandTable(kCommands); |
| ExitNow(error = OT_ERROR_NONE); |
| } |
| |
| mapper = LookupMapper(aArgs[0].GetCString()); |
| |
| if (mapper != nullptr) |
| { |
| error = ProcessCommand(*mapper, aArgs + 1); |
| ExitNow(); |
| } |
| |
| command = BinarySearch::Find(aArgs[0].GetCString(), kCommands); |
| VerifyOrExit(command != nullptr); |
| |
| error = (this->*command->mHandler)(aArgs + 1); |
| |
| exit: |
| return error; |
| } |
| |
| } // namespace Cli |
| } // namespace ot |