blob: 86399c3fa6298e39cb0fb23b2230ddefafde0b73 [file] [log] [blame]
/*
* 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