975 lines
26 KiB
C++
975 lines
26 KiB
C++
/*
|
|
* 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 {
|
|
|
|
otOperationalDataset Dataset::sDataset;
|
|
|
|
otError Dataset::Print(otOperationalDataset &aDataset)
|
|
{
|
|
if (aDataset.mComponents.mIsPendingTimestampPresent)
|
|
{
|
|
OutputLine("Pending Timestamp: %lu", aDataset.mPendingTimestamp.mSeconds);
|
|
}
|
|
|
|
if (aDataset.mComponents.mIsActiveTimestampPresent)
|
|
{
|
|
OutputLine("Active Timestamp: %lu", aDataset.mActiveTimestamp.mSeconds);
|
|
}
|
|
|
|
if (aDataset.mComponents.mIsChannelPresent)
|
|
{
|
|
OutputLine("Channel: %d", aDataset.mChannel);
|
|
}
|
|
|
|
if (aDataset.mComponents.mIsChannelMaskPresent)
|
|
{
|
|
OutputLine("Channel Mask: 0x%08x", aDataset.mChannelMask);
|
|
}
|
|
|
|
if (aDataset.mComponents.mIsDelayPresent)
|
|
{
|
|
OutputLine("Delay: %d", aDataset.mDelay);
|
|
}
|
|
|
|
if (aDataset.mComponents.mIsExtendedPanIdPresent)
|
|
{
|
|
OutputFormat("Ext PAN ID: ");
|
|
OutputBytesLine(aDataset.mExtendedPanId.m8);
|
|
}
|
|
|
|
if (aDataset.mComponents.mIsMeshLocalPrefixPresent)
|
|
{
|
|
OutputFormat("Mesh Local Prefix: ");
|
|
OutputIp6PrefixLine(aDataset.mMeshLocalPrefix);
|
|
}
|
|
|
|
if (aDataset.mComponents.mIsNetworkKeyPresent)
|
|
{
|
|
OutputFormat("Network Key: ");
|
|
OutputBytesLine(aDataset.mNetworkKey.m8);
|
|
}
|
|
|
|
if (aDataset.mComponents.mIsNetworkNamePresent)
|
|
{
|
|
OutputFormat("Network Name: ");
|
|
OutputLine("%s", aDataset.mNetworkName.m8);
|
|
}
|
|
|
|
if (aDataset.mComponents.mIsPanIdPresent)
|
|
{
|
|
OutputLine("PAN ID: 0x%04x", aDataset.mPanId);
|
|
}
|
|
|
|
if (aDataset.mComponents.mIsPskcPresent)
|
|
{
|
|
OutputFormat("PSKc: ");
|
|
OutputBytesLine(aDataset.mPskc.m8);
|
|
}
|
|
|
|
if (aDataset.mComponents.mIsSecurityPolicyPresent)
|
|
{
|
|
OutputFormat("Security Policy: ");
|
|
OutputSecurityPolicy(aDataset.mSecurityPolicy);
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("init")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_ARGS;
|
|
|
|
if (aArgs[0] == "active")
|
|
{
|
|
error = otDatasetGetActive(GetInstancePtr(), &sDataset);
|
|
}
|
|
else if (aArgs[0] == "pending")
|
|
{
|
|
error = otDatasetGetPending(GetInstancePtr(), &sDataset);
|
|
}
|
|
#if OPENTHREAD_FTD
|
|
else if (aArgs[0] == "new")
|
|
{
|
|
error = otDatasetCreateNewNetwork(GetInstancePtr(), &sDataset);
|
|
}
|
|
#endif
|
|
else if (aArgs[0] == "tlvs")
|
|
{
|
|
otOperationalDatasetTlvs datasetTlvs;
|
|
uint16_t size = sizeof(datasetTlvs.mTlvs);
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsHexString(size, datasetTlvs.mTlvs));
|
|
datasetTlvs.mLength = static_cast<uint8_t>(size);
|
|
|
|
SuccessOrExit(error = otDatasetParseTlvs(&datasetTlvs, &sDataset));
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("active")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_ARGS;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
otOperationalDataset dataset;
|
|
|
|
SuccessOrExit(error = otDatasetGetActive(GetInstancePtr(), &dataset));
|
|
error = Print(dataset);
|
|
}
|
|
else if (aArgs[0] == "-x")
|
|
{
|
|
otOperationalDatasetTlvs dataset;
|
|
|
|
SuccessOrExit(error = otDatasetGetActiveTlvs(GetInstancePtr(), &dataset));
|
|
OutputBytesLine(dataset.mTlvs, dataset.mLength);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("pending")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_ARGS;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
otOperationalDataset dataset;
|
|
|
|
SuccessOrExit(error = otDatasetGetPending(GetInstancePtr(), &dataset));
|
|
error = Print(dataset);
|
|
}
|
|
else if (aArgs[0] == "-x")
|
|
{
|
|
otOperationalDatasetTlvs dataset;
|
|
|
|
SuccessOrExit(error = otDatasetGetPendingTlvs(GetInstancePtr(), &dataset));
|
|
OutputBytesLine(dataset.mTlvs, dataset.mLength);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("activetimestamp")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (sDataset.mComponents.mIsActiveTimestampPresent)
|
|
{
|
|
OutputLine("%lu", sDataset.mActiveTimestamp.mSeconds);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mActiveTimestamp.mSeconds));
|
|
sDataset.mActiveTimestamp.mTicks = 0;
|
|
sDataset.mActiveTimestamp.mAuthoritative = false;
|
|
sDataset.mComponents.mIsActiveTimestampPresent = true;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("channel")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (sDataset.mComponents.mIsChannelPresent)
|
|
{
|
|
OutputLine("%d", sDataset.mChannel);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint16(sDataset.mChannel));
|
|
sDataset.mComponents.mIsChannelPresent = true;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("channelmask")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (sDataset.mComponents.mIsChannelMaskPresent)
|
|
{
|
|
OutputLine("0x%08x", sDataset.mChannelMask);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint32(sDataset.mChannelMask));
|
|
sDataset.mComponents.mIsChannelMaskPresent = true;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("clear")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
memset(&sDataset, 0, sizeof(sDataset));
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("commit")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_ARGS;
|
|
|
|
if (aArgs[0] == "active")
|
|
{
|
|
error = otDatasetSetActive(GetInstancePtr(), &sDataset);
|
|
}
|
|
else if (aArgs[0] == "pending")
|
|
{
|
|
error = otDatasetSetPending(GetInstancePtr(), &sDataset);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("delay")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (sDataset.mComponents.mIsDelayPresent)
|
|
{
|
|
OutputLine("%d", sDataset.mDelay);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint32(sDataset.mDelay));
|
|
sDataset.mComponents.mIsDelayPresent = true;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("extpanid")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (sDataset.mComponents.mIsExtendedPanIdPresent)
|
|
{
|
|
OutputBytesLine(sDataset.mExtendedPanId.m8);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsHexString(sDataset.mExtendedPanId.m8));
|
|
sDataset.mComponents.mIsExtendedPanIdPresent = true;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("meshlocalprefix")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (sDataset.mComponents.mIsMeshLocalPrefixPresent)
|
|
{
|
|
OutputFormat("Mesh Local Prefix: ");
|
|
OutputIp6PrefixLine(sDataset.mMeshLocalPrefix);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
otIp6Address prefix;
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsIp6Address(prefix));
|
|
|
|
memcpy(sDataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(sDataset.mMeshLocalPrefix.m8));
|
|
sDataset.mComponents.mIsMeshLocalPrefixPresent = true;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("networkkey")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (sDataset.mComponents.mIsNetworkKeyPresent)
|
|
{
|
|
OutputBytesLine(sDataset.mNetworkKey.m8);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsHexString(sDataset.mNetworkKey.m8));
|
|
sDataset.mComponents.mIsNetworkKeyPresent = true;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("networkname")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (sDataset.mComponents.mIsNetworkNamePresent)
|
|
{
|
|
OutputLine("%s", sDataset.mNetworkName.m8);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = otNetworkNameFromString(&sDataset.mNetworkName, aArgs[0].GetCString()));
|
|
sDataset.mComponents.mIsNetworkNamePresent = true;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("panid")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (sDataset.mComponents.mIsPanIdPresent)
|
|
{
|
|
OutputLine("0x%04x", sDataset.mPanId);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint16(sDataset.mPanId));
|
|
sDataset.mComponents.mIsPanIdPresent = true;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("pendingtimestamp")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (sDataset.mComponents.mIsPendingTimestampPresent)
|
|
{
|
|
OutputLine("%lu", sDataset.mPendingTimestamp.mSeconds);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mPendingTimestamp.mSeconds));
|
|
sDataset.mPendingTimestamp.mTicks = 0;
|
|
sDataset.mPendingTimestamp.mAuthoritative = false;
|
|
sDataset.mComponents.mIsPendingTimestampPresent = true;
|
|
}
|
|
|
|
exit:
|
|
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;
|
|
|
|
memset(&dataset, 0, sizeof(dataset));
|
|
|
|
for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
|
|
{
|
|
if (*arg == "activetimestamp")
|
|
{
|
|
arg++;
|
|
SuccessOrExit(error = arg->ParseAsUint64(dataset.mActiveTimestamp.mSeconds));
|
|
dataset.mActiveTimestamp.mTicks = 0;
|
|
dataset.mActiveTimestamp.mAuthoritative = false;
|
|
dataset.mComponents.mIsActiveTimestampPresent = true;
|
|
}
|
|
else if (*arg == "pendingtimestamp")
|
|
{
|
|
arg++;
|
|
SuccessOrExit(error = arg->ParseAsUint64(dataset.mPendingTimestamp.mSeconds));
|
|
dataset.mPendingTimestamp.mTicks = 0;
|
|
dataset.mPendingTimestamp.mAuthoritative = false;
|
|
dataset.mComponents.mIsPendingTimestampPresent = true;
|
|
}
|
|
else if (*arg == "networkkey")
|
|
{
|
|
arg++;
|
|
dataset.mComponents.mIsNetworkKeyPresent = true;
|
|
SuccessOrExit(error = arg->ParseAsHexString(dataset.mNetworkKey.m8));
|
|
}
|
|
else if (*arg == "networkname")
|
|
{
|
|
arg++;
|
|
dataset.mComponents.mIsNetworkNamePresent = true;
|
|
VerifyOrExit(!arg->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
SuccessOrExit(error = otNetworkNameFromString(&dataset.mNetworkName, arg->GetCString()));
|
|
}
|
|
else if (*arg == "extpanid")
|
|
{
|
|
arg++;
|
|
dataset.mComponents.mIsExtendedPanIdPresent = true;
|
|
SuccessOrExit(error = arg->ParseAsHexString(dataset.mExtendedPanId.m8));
|
|
}
|
|
else if (*arg == "localprefix")
|
|
{
|
|
otIp6Address prefix;
|
|
|
|
arg++;
|
|
dataset.mComponents.mIsMeshLocalPrefixPresent = true;
|
|
SuccessOrExit(error = arg->ParseAsIp6Address(prefix));
|
|
memcpy(dataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(dataset.mMeshLocalPrefix.m8));
|
|
}
|
|
else if (*arg == "delaytimer")
|
|
{
|
|
arg++;
|
|
dataset.mComponents.mIsDelayPresent = true;
|
|
SuccessOrExit(error = arg->ParseAsUint32(dataset.mDelay));
|
|
}
|
|
else if (*arg == "panid")
|
|
{
|
|
arg++;
|
|
dataset.mComponents.mIsPanIdPresent = true;
|
|
SuccessOrExit(error = arg->ParseAsUint16(dataset.mPanId));
|
|
}
|
|
else if (*arg == "channel")
|
|
{
|
|
arg++;
|
|
dataset.mComponents.mIsChannelPresent = true;
|
|
SuccessOrExit(error = arg->ParseAsUint16(dataset.mChannel));
|
|
}
|
|
else if (*arg == "channelmask")
|
|
{
|
|
arg++;
|
|
dataset.mComponents.mIsChannelMaskPresent = true;
|
|
SuccessOrExit(error = arg->ParseAsUint32(dataset.mChannelMask));
|
|
}
|
|
else if (*arg == "securitypolicy")
|
|
{
|
|
arg++;
|
|
SuccessOrExit(error = ParseSecurityPolicy(dataset.mSecurityPolicy, arg));
|
|
dataset.mComponents.mIsSecurityPolicyPresent = 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
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
}
|
|
|
|
if (aArgs[0] == "active")
|
|
{
|
|
error = otDatasetSendMgmtActiveSet(GetInstancePtr(), &dataset, tlvs, tlvsLength, /* aCallback */ nullptr,
|
|
/* aContext */ nullptr);
|
|
}
|
|
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;
|
|
|
|
memset(&datasetComponents, 0, sizeof(datasetComponents));
|
|
|
|
for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
|
|
{
|
|
if (*arg == "activetimestamp")
|
|
{
|
|
datasetComponents.mIsActiveTimestampPresent = true;
|
|
}
|
|
else if (*arg == "pendingtimestamp")
|
|
{
|
|
datasetComponents.mIsPendingTimestampPresent = true;
|
|
}
|
|
else if (*arg == "networkkey")
|
|
{
|
|
datasetComponents.mIsNetworkKeyPresent = true;
|
|
}
|
|
else if (*arg == "networkname")
|
|
{
|
|
datasetComponents.mIsNetworkNamePresent = true;
|
|
}
|
|
else if (*arg == "extpanid")
|
|
{
|
|
datasetComponents.mIsExtendedPanIdPresent = true;
|
|
}
|
|
else if (*arg == "localprefix")
|
|
{
|
|
datasetComponents.mIsMeshLocalPrefixPresent = true;
|
|
}
|
|
else if (*arg == "delaytimer")
|
|
{
|
|
datasetComponents.mIsDelayPresent = true;
|
|
}
|
|
else if (*arg == "panid")
|
|
{
|
|
datasetComponents.mIsPanIdPresent = true;
|
|
}
|
|
else if (*arg == "channel")
|
|
{
|
|
datasetComponents.mIsChannelPresent = true;
|
|
}
|
|
else if (*arg == "securitypolicy")
|
|
{
|
|
datasetComponents.mIsSecurityPolicyPresent = 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);
|
|
}
|
|
}
|
|
|
|
if (aArgs[0] == "active")
|
|
{
|
|
error = otDatasetSendMgmtActiveGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength,
|
|
destAddrSpecified ? &address : nullptr);
|
|
}
|
|
else if (aArgs[0] == "pending")
|
|
{
|
|
error = otDatasetSendMgmtPendingGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength,
|
|
destAddrSpecified ? &address : nullptr);
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("pskc")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
// sDataset holds the key as a literal string, we don't
|
|
// need to export it from PSA ITS.
|
|
if (sDataset.mComponents.mIsPskcPresent)
|
|
{
|
|
OutputBytesLine(sDataset.mPskc.m8);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if OPENTHREAD_FTD
|
|
if (aArgs[0] == "-p")
|
|
{
|
|
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
|
|
SuccessOrExit(
|
|
error = otDatasetGeneratePskc(
|
|
aArgs[1].GetCString(),
|
|
(sDataset.mComponents.mIsNetworkNamePresent
|
|
? &sDataset.mNetworkName
|
|
: reinterpret_cast<const otNetworkName *>(otThreadGetNetworkName(GetInstancePtr()))),
|
|
(sDataset.mComponents.mIsExtendedPanIdPresent ? &sDataset.mExtendedPanId
|
|
: otThreadGetExtendedPanId(GetInstancePtr())),
|
|
&sDataset.mPskc));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsHexString(sDataset.mPskc.m8));
|
|
}
|
|
|
|
sDataset.mComponents.mIsPskcPresent = true;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Dataset::OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy)
|
|
{
|
|
OutputFormat("%d ", 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("");
|
|
}
|
|
|
|
otError Dataset::ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, Arg *&aArgs)
|
|
{
|
|
otError error;
|
|
otSecurityPolicy policy;
|
|
|
|
memset(&policy, 0, sizeof(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++;
|
|
|
|
exit:
|
|
if (error == OT_ERROR_NONE)
|
|
{
|
|
aSecurityPolicy = policy;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
template <> otError Dataset::Process<Cmd("securitypolicy")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (sDataset.mComponents.mIsSecurityPolicyPresent)
|
|
{
|
|
OutputSecurityPolicy(sDataset.mSecurityPolicy);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Arg *arg = &aArgs[0];
|
|
|
|
SuccessOrExit(error = ParseSecurityPolicy(sDataset.mSecurityPolicy, arg));
|
|
sDataset.mComponents.mIsSecurityPolicyPresent = true;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
{
|
|
MeshCoP::Dataset dataset;
|
|
MeshCoP::Dataset::Info datasetInfo;
|
|
uint16_t tlvsLength = MeshCoP::Dataset::kMaxSize;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsHexString(tlvsLength, dataset.GetBytes()));
|
|
dataset.SetSize(tlvsLength);
|
|
VerifyOrExit(dataset.IsValid(), error = OT_ERROR_INVALID_ARGS);
|
|
dataset.ConvertTo(datasetInfo);
|
|
|
|
switch (datasetType)
|
|
{
|
|
case MeshCoP::Dataset::Type::kActive:
|
|
SuccessOrExit(error = otDatasetSetActive(GetInstancePtr(), &datasetInfo));
|
|
break;
|
|
case MeshCoP::Dataset::Type::kPending:
|
|
SuccessOrExit(error = otDatasetSetPending(GetInstancePtr(), &datasetInfo));
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
|
|
|
|
template <> otError Dataset::Process<Cmd("updater")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputEnabledDisabledStatus(otDatasetUpdaterIsUpdateOngoing(GetInstancePtr()));
|
|
}
|
|
else if (aArgs[0] == "start")
|
|
{
|
|
error = otDatasetUpdaterRequestUpdate(GetInstancePtr(), &sDataset, &Dataset::HandleDatasetUpdater, this);
|
|
}
|
|
else if (aArgs[0] == "cancel")
|
|
{
|
|
otDatasetUpdaterCancelUpdate(GetInstancePtr());
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
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("activetimestamp"),
|
|
CmdEntry("channel"),
|
|
CmdEntry("channelmask"),
|
|
CmdEntry("clear"),
|
|
CmdEntry("commit"),
|
|
CmdEntry("delay"),
|
|
CmdEntry("extpanid"),
|
|
CmdEntry("init"),
|
|
CmdEntry("meshlocalprefix"),
|
|
CmdEntry("mgmtgetcommand"),
|
|
CmdEntry("mgmtsetcommand"),
|
|
CmdEntry("networkkey"),
|
|
CmdEntry("networkname"),
|
|
CmdEntry("panid"),
|
|
CmdEntry("pending"),
|
|
CmdEntry("pendingtimestamp"),
|
|
CmdEntry("pskc"),
|
|
CmdEntry("securitypolicy"),
|
|
CmdEntry("set"),
|
|
#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;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
ExitNow(error = Print(sDataset));
|
|
}
|
|
|
|
if (aArgs[0] == "help")
|
|
{
|
|
OutputCommandTable(kCommands);
|
|
ExitNow(error = OT_ERROR_NONE);
|
|
}
|
|
|
|
command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
|
|
VerifyOrExit(command != nullptr);
|
|
|
|
error = (this->*command->mHandler)(aArgs + 1);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
} // namespace Cli
|
|
} // namespace ot
|