1 /*
2  *  Copyright (c) 2016, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements the CLI interpreter.
32  */
33 
34 #include "cli_dataset.hpp"
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 
39 #include <openthread/dataset.h>
40 #include <openthread/dataset_ftd.h>
41 #include <openthread/dataset_updater.h>
42 
43 #include "cli/cli.hpp"
44 
45 namespace ot {
46 namespace Cli {
47 
48 otOperationalDatasetTlvs Dataset::sDatasetTlvs;
49 
LookupMapper(const char * aName) const50 const Dataset::ComponentMapper *Dataset::LookupMapper(const char *aName) const
51 {
52     static constexpr ComponentMapper kMappers[] = {
53         {
54             "activetimestamp",
55             &Components::mIsActiveTimestampPresent,
56             &Dataset::OutputActiveTimestamp,
57             &Dataset::ParseActiveTimestamp,
58         },
59         {
60             "channel",
61             &Components::mIsChannelPresent,
62             &Dataset::OutputChannel,
63             &Dataset::ParseChannel,
64         },
65         {
66             "channelmask",
67             &Components::mIsChannelMaskPresent,
68             &Dataset::OutputChannelMask,
69             &Dataset::ParseChannelMask,
70         },
71         {
72             "delay",
73             &Components::mIsDelayPresent,
74             &Dataset::OutputDelay,
75             &Dataset::ParseDelay,
76         },
77         {
78             "delaytimer", // Alias for "delay "to ensure backward compatibility for "mgmtsetcommand" command
79             &Components::mIsDelayPresent,
80             &Dataset::OutputDelay,
81             &Dataset::ParseDelay,
82         },
83         {
84             "extpanid",
85             &Components::mIsExtendedPanIdPresent,
86             &Dataset::OutputExtendedPanId,
87             &Dataset::ParseExtendedPanId,
88         },
89         {
90             "localprefix", // Alias for "meshlocalprefix" to ensure backward compatibility in "mgmtsetcommand" command
91             &Components::mIsMeshLocalPrefixPresent,
92             &Dataset::OutputMeshLocalPrefix,
93             &Dataset::ParseMeshLocalPrefix,
94         },
95         {
96             "meshlocalprefix",
97             &Components::mIsMeshLocalPrefixPresent,
98             &Dataset::OutputMeshLocalPrefix,
99             &Dataset::ParseMeshLocalPrefix,
100         },
101         {
102             "networkkey",
103             &Components::mIsNetworkKeyPresent,
104             &Dataset::OutputNetworkKey,
105             &Dataset::ParseNetworkKey,
106         },
107         {
108             "networkname",
109             &Components::mIsNetworkNamePresent,
110             &Dataset::OutputNetworkName,
111             &Dataset::ParseNetworkName,
112         },
113         {
114             "panid",
115             &Components::mIsPanIdPresent,
116             &Dataset::OutputPanId,
117             &Dataset::ParsePanId,
118         },
119         {
120             "pendingtimestamp",
121             &Components::mIsPendingTimestampPresent,
122             &Dataset::OutputPendingTimestamp,
123             &Dataset::ParsePendingTimestamp,
124         },
125         {
126             "pskc",
127             &Components::mIsPskcPresent,
128             &Dataset::OutputPskc,
129             &Dataset::ParsePskc,
130         },
131         {
132             "securitypolicy",
133             &Components::mIsSecurityPolicyPresent,
134             &Dataset::OutputSecurityPolicy,
135             &Dataset::ParseSecurityPolicy,
136         },
137     };
138 
139     static_assert(BinarySearch::IsSorted(kMappers), "kMappers is not sorted");
140 
141     return BinarySearch::Find(aName, kMappers);
142 }
143 
144 //---------------------------------------------------------------------------------------------------------------------
145 
146 /**
147  * @cli dataset activetimestamp (get, set)
148  * @code
149  * dataset activetimestamp
150  * 123456789
151  * Done
152  * @endcode
153  * @code
154  * dataset activetimestamp 123456789
155  * Done
156  * @endcode
157  * @cparam dataset activetimestamp [@ca{timestamp}]
158  * Pass the optional `timestamp` argument to set the active timestamp.
159  * @par
160  * Gets or sets #otOperationalDataset::mActiveTimestamp.
161  */
OutputActiveTimestamp(const otOperationalDataset & aDataset)162 void Dataset::OutputActiveTimestamp(const otOperationalDataset &aDataset)
163 {
164     OutputUint64Line(aDataset.mActiveTimestamp.mSeconds);
165 }
166 
167 /**
168  * @cli dataset channel (get,set)
169  * @code
170  * dataset channel
171  * 12
172  * Done
173  * @endcode
174  * @code
175  * dataset channel 12
176  * Done
177  * @endcode
178  * @cparam dataset channel [@ca{channel-num}]
179  * Use the optional `channel-num` argument to set the channel.
180  * @par
181  * Gets or sets #otOperationalDataset::mChannel.
182  */
OutputChannel(const otOperationalDataset & aDataset)183 void Dataset::OutputChannel(const otOperationalDataset &aDataset) { OutputLine("%u", aDataset.mChannel); }
184 
185 /**
186  * @cli dataset channelmask (get,set)
187  * @code
188  * dataset channelmask
189  * 0x07fff800
190  * Done
191  * @endcode
192  * @code
193  * dataset channelmask 0x07fff800
194  * Done
195  * @endcode
196  * @cparam dataset channelmask [@ca{channel-mask}]
197  * Use the optional `channel-mask` argument to set the channel mask.
198  * @par
199  * Gets or sets #otOperationalDataset::mChannelMask
200  */
OutputChannelMask(const otOperationalDataset & aDataset)201 void Dataset::OutputChannelMask(const otOperationalDataset &aDataset)
202 {
203     OutputLine("0x%08lx", ToUlong(aDataset.mChannelMask));
204 }
205 
206 /**
207  * @cli dataset delay (get,set)
208  * @code
209  * dataset delay
210  * 1000
211  * Done
212  * @endcode
213  * @code
214  * dataset delay 1000
215  * Done
216  * @endcode
217  * @cparam dataset delay [@ca{delay}]
218  * Use the optional `delay` argument to set the delay timer value.
219  * @par
220  * Gets or sets #otOperationalDataset::mDelay.
221  * @sa otDatasetSetDelayTimerMinimal
222  */
OutputDelay(const otOperationalDataset & aDataset)223 void Dataset::OutputDelay(const otOperationalDataset &aDataset) { OutputLine("%lu", ToUlong(aDataset.mDelay)); }
224 
225 /**
226  * @cli dataset extpanid (get,set)
227  * @code
228  * dataset extpanid
229  * 000db80123456789
230  * Done
231  * @endcode
232  * @code
233  * dataset extpanid 000db80123456789
234  * Done
235  * @endcode
236  * @cparam dataset extpanid [@ca{extpanid}]
237  * Use the optional `extpanid` argument to set the Extended Personal Area Network ID.
238  * @par
239  * Gets or sets #otOperationalDataset::mExtendedPanId.
240  * @note The commissioning credential in the dataset buffer becomes stale after changing
241  * this value. Use `dataset pskc` to reset.
242  * @csa{dataset pskc (get,set)}
243  */
OutputExtendedPanId(const otOperationalDataset & aDataset)244 void Dataset::OutputExtendedPanId(const otOperationalDataset &aDataset) { OutputBytesLine(aDataset.mExtendedPanId.m8); }
245 
246 /**
247  * @cli dataset meshlocalprefix (get,set)
248  * @code
249  * dataset meshlocalprefix
250  * fd00:db8:0:0::/64
251  * Done
252  * @endcode
253  * @code
254  * dataset meshlocalprefix fd00:db8:0:0::
255  * Done
256  * @endcode
257  * @cparam dataset meshlocalprefix [@ca{meshlocalprefix}]
258  * Use the optional `meshlocalprefix` argument to set the Mesh-Local Prefix.
259  * @par
260  * Gets or sets #otOperationalDataset::mMeshLocalPrefix.
261  */
OutputMeshLocalPrefix(const otOperationalDataset & aDataset)262 void Dataset::OutputMeshLocalPrefix(const otOperationalDataset &aDataset)
263 {
264     OutputIp6PrefixLine(aDataset.mMeshLocalPrefix);
265 }
266 
267 /**
268  * @cli dataset networkkey (get,set)
269  * @code
270  * dataset networkkey
271  * 00112233445566778899aabbccddeeff
272  * Done
273  * @endcode
274  * @code
275  * dataset networkkey 00112233445566778899aabbccddeeff
276  * Done
277  * @endcode
278  * @cparam dataset networkkey [@ca{key}]
279  * Use the optional `key` argument to set the Network Key.
280  * @par
281  * Gets or sets #otOperationalDataset::mNetworkKey.
282  */
OutputNetworkKey(const otOperationalDataset & aDataset)283 void Dataset::OutputNetworkKey(const otOperationalDataset &aDataset) { OutputBytesLine(aDataset.mNetworkKey.m8); }
284 
285 /**
286  * @cli dataset networkname (get,set)
287  * @code
288  * dataset networkname
289  * OpenThread
290  * Done
291  * @endcode
292  * @code
293  * dataset networkname OpenThread
294  * Done
295  * @endcode
296  * @cparam dataset networkname [@ca{name}]
297  * Use the optional `name` argument to set the Network Name.
298  * @par
299  * Gets or sets #otOperationalDataset::mNetworkName.
300  * @note The Commissioning Credential in the dataset buffer becomes stale after changing this value.
301  * Use `dataset pskc` to reset.
302  * @csa{dataset pskc (get,set)}
303  */
OutputNetworkName(const otOperationalDataset & aDataset)304 void Dataset::OutputNetworkName(const otOperationalDataset &aDataset) { OutputLine("%s", aDataset.mNetworkName.m8); }
305 
306 /**
307  * @cli dataset panid (get,set)
308  * @code
309  * dataset panid
310  * 0x1234
311  * Done
312  * @endcode
313  * @code
314  * dataset panid 0x1234
315  * Done
316  * @endcode
317  * @cparam dataset panid [@ca{panid}]
318  * Use the optional `panid` argument to set the PAN ID.
319  * @par
320  * Gets or sets #otOperationalDataset::mPanId.
321  */
OutputPanId(const otOperationalDataset & aDataset)322 void Dataset::OutputPanId(const otOperationalDataset &aDataset) { OutputLine("0x%04x", aDataset.mPanId); }
323 
324 /**
325  * @cli dataset pendingtimestamp (get,set)
326  * @code
327  * dataset pendingtimestamp
328  * 123456789
329  * Done
330  * @endcode
331  * @code
332  * dataset pendingtimestamp 123456789
333  * Done
334  * @endcode
335  * @cparam dataset pendingtimestamp [@ca{timestamp}]
336  * Use the optional `timestamp` argument to set the pending timestamp seconds.
337  * @par
338  * Gets or sets #otOperationalDataset::mPendingTimestamp.
339  */
OutputPendingTimestamp(const otOperationalDataset & aDataset)340 void Dataset::OutputPendingTimestamp(const otOperationalDataset &aDataset)
341 {
342     OutputUint64Line(aDataset.mPendingTimestamp.mSeconds);
343 }
344 
345 /**
346  * @cli dataset pskc (get,set)
347  * @code
348  * dataset pskc
349  * 67c0c203aa0b042bfb5381c47aef4d9e
350  * Done
351  * @endcode
352  * @code
353  * dataset pskc -p 123456
354  * Done
355  * @endcode
356  * @code
357  * dataset pskc 67c0c203aa0b042bfb5381c47aef4d9e
358  * Done
359  * @endcode
360  * @cparam dataset pskc [@ca{-p} @ca{passphrase}] | [@ca{key}]
361  * For FTD only, use `-p` with the `passphrase` argument. `-p` generates a pskc from
362  * the UTF-8 encoded `passphrase` that you provide, together with
363  * the network name and extended PAN ID. If set, `-p` uses the dataset buffer;
364  * otherwise, it uses the current stack.
365  * Alternatively, you can set pskc as `key` (hex format).
366  * @par
367  * Gets or sets #otOperationalDataset::mPskc.
368  */
OutputPskc(const otOperationalDataset & aDataset)369 void Dataset::OutputPskc(const otOperationalDataset &aDataset) { OutputBytesLine(aDataset.mPskc.m8); }
370 
371 /**
372  * @cli dataset securitypolicy (get,set)
373  * @code
374  * dataset securitypolicy
375  * 672 onrc
376  * Done
377  * @endcode
378  * @code
379  * dataset securitypolicy 672 onrc
380  * Done
381  * @endcode
382  * @cparam dataset securitypolicy [@ca{rotationtime} [@ca{onrcCepR}]]
383  * *   Use `rotationtime` for `thrKeyRotation`, in units of hours.
384  * *   Security Policy commands use the `onrcCepR` argument mappings to get and set
385  * #otSecurityPolicy members, for example `o` represents
386  * #otSecurityPolicy::mObtainNetworkKeyEnabled.
387  * @moreinfo{@dataset}.
388  * @par
389  * Gets or sets the %Dataset security policy.
390  */
OutputSecurityPolicy(const otOperationalDataset & aDataset)391 void Dataset::OutputSecurityPolicy(const otOperationalDataset &aDataset)
392 {
393     OutputSecurityPolicy(aDataset.mSecurityPolicy);
394 }
395 
396 //---------------------------------------------------------------------------------------------------------------------
397 
ParseActiveTimestamp(Arg * & aArgs,otOperationalDataset & aDataset)398 otError Dataset::ParseActiveTimestamp(Arg *&aArgs, otOperationalDataset &aDataset)
399 {
400     otError error;
401 
402     SuccessOrExit(error = aArgs++->ParseAsUint64(aDataset.mActiveTimestamp.mSeconds));
403     aDataset.mActiveTimestamp.mTicks         = 0;
404     aDataset.mActiveTimestamp.mAuthoritative = false;
405 
406 exit:
407     return error;
408 }
409 
ParseChannel(Arg * & aArgs,otOperationalDataset & aDataset)410 otError Dataset::ParseChannel(Arg *&aArgs, otOperationalDataset &aDataset)
411 {
412     return aArgs++->ParseAsUint16(aDataset.mChannel);
413 }
414 
ParseChannelMask(Arg * & aArgs,otOperationalDataset & aDataset)415 otError Dataset::ParseChannelMask(Arg *&aArgs, otOperationalDataset &aDataset)
416 {
417     return aArgs++->ParseAsUint32(aDataset.mChannelMask);
418 }
419 
ParseDelay(Arg * & aArgs,otOperationalDataset & aDataset)420 otError Dataset::ParseDelay(Arg *&aArgs, otOperationalDataset &aDataset)
421 {
422     return aArgs++->ParseAsUint32(aDataset.mDelay);
423 }
424 
ParseExtendedPanId(Arg * & aArgs,otOperationalDataset & aDataset)425 otError Dataset::ParseExtendedPanId(Arg *&aArgs, otOperationalDataset &aDataset)
426 {
427     return aArgs++->ParseAsHexString(aDataset.mExtendedPanId.m8);
428 }
429 
ParseMeshLocalPrefix(Arg * & aArgs,otOperationalDataset & aDataset)430 otError Dataset::ParseMeshLocalPrefix(Arg *&aArgs, otOperationalDataset &aDataset)
431 {
432     otError      error;
433     otIp6Address prefix;
434 
435     SuccessOrExit(error = aArgs++->ParseAsIp6Address(prefix));
436 
437     memcpy(aDataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(aDataset.mMeshLocalPrefix.m8));
438 
439 exit:
440     return error;
441 }
442 
ParseNetworkKey(Arg * & aArgs,otOperationalDataset & aDataset)443 otError Dataset::ParseNetworkKey(Arg *&aArgs, otOperationalDataset &aDataset)
444 {
445     return aArgs++->ParseAsHexString(aDataset.mNetworkKey.m8);
446 }
447 
ParseNetworkName(Arg * & aArgs,otOperationalDataset & aDataset)448 otError Dataset::ParseNetworkName(Arg *&aArgs, otOperationalDataset &aDataset)
449 {
450     otError error = OT_ERROR_NONE;
451 
452     VerifyOrExit(!aArgs->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
453     error = otNetworkNameFromString(&aDataset.mNetworkName, aArgs++->GetCString());
454 
455 exit:
456     return error;
457 }
458 
ParsePanId(Arg * & aArgs,otOperationalDataset & aDataset)459 otError Dataset::ParsePanId(Arg *&aArgs, otOperationalDataset &aDataset)
460 {
461     return aArgs++->ParseAsUint16(aDataset.mPanId);
462 }
463 
ParsePendingTimestamp(Arg * & aArgs,otOperationalDataset & aDataset)464 otError Dataset::ParsePendingTimestamp(Arg *&aArgs, otOperationalDataset &aDataset)
465 {
466     otError error;
467 
468     SuccessOrExit(error = aArgs++->ParseAsUint64(aDataset.mPendingTimestamp.mSeconds));
469     aDataset.mPendingTimestamp.mTicks         = 0;
470     aDataset.mPendingTimestamp.mAuthoritative = false;
471 
472 exit:
473     return error;
474 }
475 
ParsePskc(Arg * & aArgs,otOperationalDataset & aDataset)476 otError Dataset::ParsePskc(Arg *&aArgs, otOperationalDataset &aDataset)
477 {
478     otError error;
479 
480 #if OPENTHREAD_FTD
481     if (*aArgs == "-p")
482     {
483         aArgs++;
484         VerifyOrExit(!aArgs->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
485 
486         SuccessOrExit(error = otDatasetGeneratePskc(
487                           aArgs->GetCString(),
488                           (aDataset.mComponents.mIsNetworkNamePresent
489                                ? &aDataset.mNetworkName
490                                : reinterpret_cast<const otNetworkName *>(otThreadGetNetworkName(GetInstancePtr()))),
491                           (aDataset.mComponents.mIsExtendedPanIdPresent ? &aDataset.mExtendedPanId
492                                                                         : otThreadGetExtendedPanId(GetInstancePtr())),
493                           &aDataset.mPskc));
494         aArgs++;
495     }
496     else
497 #endif
498     {
499         ExitNow(error = aArgs++->ParseAsHexString(aDataset.mPskc.m8));
500     }
501 
502 exit:
503     return error;
504 }
505 
ParseSecurityPolicy(Arg * & aArgs,otOperationalDataset & aDataset)506 otError Dataset::ParseSecurityPolicy(Arg *&aArgs, otOperationalDataset &aDataset)
507 {
508     return ParseSecurityPolicy(aDataset.mSecurityPolicy, aArgs);
509 }
510 
511 //---------------------------------------------------------------------------------------------------------------------
512 
ProcessCommand(const ComponentMapper & aMapper,Arg aArgs[])513 otError Dataset::ProcessCommand(const ComponentMapper &aMapper, Arg aArgs[])
514 {
515     otError              error = OT_ERROR_NONE;
516     otOperationalDataset dataset;
517 
518     if (aArgs[0].IsEmpty())
519     {
520         SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset));
521 
522         if (dataset.mComponents.*aMapper.mIsPresentPtr)
523         {
524             (this->*aMapper.mOutput)(dataset);
525         }
526     }
527     else
528     {
529         ClearAllBytes(dataset);
530         SuccessOrExit(error = (this->*aMapper.mParse)(aArgs, dataset));
531         dataset.mComponents.*aMapper.mIsPresentPtr = true;
532         SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs));
533     }
534 
535 exit:
536     return error;
537 }
538 
Print(otOperationalDatasetTlvs & aDatasetTlvs)539 otError Dataset::Print(otOperationalDatasetTlvs &aDatasetTlvs)
540 {
541     struct ComponentTitle
542     {
543         const char *mTitle; // Title to output.
544         const char *mName;  // To use with `LookupMapper()`.
545     };
546 
547     static const ComponentTitle kTitles[] = {
548         {"Pending Timestamp", "pendingtimestamp"},
549         {"Active Timestamp", "activetimestamp"},
550         {"Channel", "channel"},
551         {"Channel Mask", "channelmask"},
552         {"Delay", "delay"},
553         {"Ext PAN ID", "extpanid"},
554         {"Mesh Local Prefix", "meshlocalprefix"},
555         {"Network Key", "networkkey"},
556         {"Network Name", "networkname"},
557         {"PAN ID", "panid"},
558         {"PSKc", "pskc"},
559         {"Security Policy", "securitypolicy"},
560     };
561 
562     otError              error;
563     otOperationalDataset dataset;
564 
565     SuccessOrExit(error = otDatasetParseTlvs(&aDatasetTlvs, &dataset));
566 
567     for (const ComponentTitle &title : kTitles)
568     {
569         const ComponentMapper *mapper = LookupMapper(title.mName);
570 
571         if (dataset.mComponents.*mapper->mIsPresentPtr)
572         {
573             OutputFormat("%s: ", title.mTitle);
574             (this->*mapper->mOutput)(dataset);
575         }
576     }
577 
578 exit:
579     return error;
580 }
581 
582 /**
583  * @cli dataset init (active,new,pending,tlvs)
584  * @code
585  * dataset init new
586  * Done
587  * @endcode
588  * @cparam dataset init {@ca{active}|@ca{new}|@ca{pending}|@ca{tlvs}} [@ca{hex-encoded-tlvs}]
589  * Use `new` to initialize a new dataset, then enter the command `dataset commit active`.
590  * Use `tlvs` for hex-encoded TLVs.
591  * @par
592  * OT CLI checks for `active`, `pending`, or `tlvs` and returns the corresponding values. Otherwise,
593  * OT CLI creates a new, random network and returns a new dataset.
594  * @csa{dataset commit active}
595  * @csa{dataset active}
596  */
Process(Arg aArgs[])597 template <> otError Dataset::Process<Cmd("init")>(Arg aArgs[])
598 {
599     otError error = OT_ERROR_INVALID_ARGS;
600 
601     if (aArgs[0] == "active")
602     {
603         error = otDatasetGetActiveTlvs(GetInstancePtr(), &sDatasetTlvs);
604     }
605     else if (aArgs[0] == "pending")
606     {
607         error = otDatasetGetPendingTlvs(GetInstancePtr(), &sDatasetTlvs);
608     }
609 #if OPENTHREAD_FTD
610     else if (aArgs[0] == "new")
611     {
612         otOperationalDataset dataset;
613 
614         SuccessOrExit(error = otDatasetCreateNewNetwork(GetInstancePtr(), &dataset));
615         otDatasetConvertToTlvs(&dataset, &sDatasetTlvs);
616     }
617 #endif
618     else if (aArgs[0] == "tlvs")
619     {
620         uint16_t size = sizeof(sDatasetTlvs.mTlvs);
621 
622         SuccessOrExit(error = aArgs[1].ParseAsHexString(size, sDatasetTlvs.mTlvs));
623         sDatasetTlvs.mLength = static_cast<uint8_t>(size);
624     }
625 
626 exit:
627     return error;
628 }
629 
630 /**
631  * @cli dataset active
632  * @code
633  * dataset active
634  * Active Timestamp: 1
635  * Channel: 13
636  * Channel Mask: 0x07fff800
637  * Ext PAN ID: d63e8e3e495ebbc3
638  * Mesh Local Prefix: fd3d:b50b:f96d:722d::/64
639  * Network Key: dfd34f0f05cad978ec4e32b0413038ff
640  * Network Name: OpenThread-8f28
641  * PAN ID: 0x8f28
642  * PSKc: c23a76e98f1a6483639b1ac1271e2e27
643  * Security Policy: 0, onrcb
644  * Done
645  * @endcode
646  * @code
647  * dataset active -x
648  * 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff
649  * Done
650  * @endcode
651  * @cparam dataset active [-x]
652  * The optional `-x` argument prints the Active Operational %Dataset values as hex-encoded TLVs.
653  * @par api_copy
654  * #otDatasetGetActive
655  * @par
656  * OT CLI uses #otOperationalDataset members to return dataset values to the console.
657  */
Process(Arg aArgs[])658 template <> otError Dataset::Process<Cmd("active")>(Arg aArgs[])
659 {
660     otError                  error;
661     otOperationalDatasetTlvs dataset;
662 
663     SuccessOrExit(error = otDatasetGetActiveTlvs(GetInstancePtr(), &dataset));
664 
665     if (aArgs[0].IsEmpty())
666     {
667         error = Print(dataset);
668     }
669     else if (aArgs[0] == "-x")
670     {
671         OutputBytesLine(dataset.mTlvs, dataset.mLength);
672     }
673     else
674     {
675         error = OT_ERROR_INVALID_ARGS;
676     }
677 
678 exit:
679     return error;
680 }
681 
Process(Arg aArgs[])682 template <> otError Dataset::Process<Cmd("pending")>(Arg aArgs[])
683 {
684     otError                  error;
685     otOperationalDatasetTlvs datasetTlvs;
686 
687     SuccessOrExit(error = otDatasetGetPendingTlvs(GetInstancePtr(), &datasetTlvs));
688 
689     if (aArgs[0].IsEmpty())
690     {
691         error = Print(datasetTlvs);
692     }
693     else if (aArgs[0] == "-x")
694     {
695         OutputBytesLine(datasetTlvs.mTlvs, datasetTlvs.mLength);
696     }
697     else
698     {
699         error = OT_ERROR_INVALID_ARGS;
700     }
701 
702 exit:
703     return error;
704 }
705 
706 /**
707  * @cli dataset clear
708  * @code
709  * dataset clear
710  * Done
711  * @endcode
712  * @par
713  * Reset the Operational %Dataset buffer.
714  */
Process(Arg aArgs[])715 template <> otError Dataset::Process<Cmd("clear")>(Arg aArgs[])
716 {
717     OT_UNUSED_VARIABLE(aArgs);
718 
719     ClearAllBytes(sDatasetTlvs);
720     return OT_ERROR_NONE;
721 }
722 
Process(Arg aArgs[])723 template <> otError Dataset::Process<Cmd("commit")>(Arg aArgs[])
724 {
725     otError error = OT_ERROR_INVALID_ARGS;
726 
727     /**
728      * @cli dataset commit active
729      * @code
730      * dataset commit active
731      * Done
732      * @endcode
733      * @par
734      * Commit the Operational %Dataset buffer to Active Operational %Dataset.
735      * @csa{dataset commit pending}
736      * @sa #otDatasetSetPending
737      */
738     if (aArgs[0] == "active")
739     {
740         error = otDatasetSetActiveTlvs(GetInstancePtr(), &sDatasetTlvs);
741     }
742     /**
743      * @cli dataset commit pending
744      * @code
745      * dataset commit pending
746      * Done
747      * @endcode
748      * @par
749      * Commit the Operational %Dataset buffer to Pending Operational %Dataset.
750      * @csa{dataset commit active}
751      * @sa #otDatasetSetActive
752      */
753     else if (aArgs[0] == "pending")
754     {
755         error = otDatasetSetPendingTlvs(GetInstancePtr(), &sDatasetTlvs);
756     }
757 
758     return error;
759 }
760 
Process(Arg aArgs[])761 template <> otError Dataset::Process<Cmd("mgmtsetcommand")>(Arg aArgs[])
762 {
763     otError              error = OT_ERROR_NONE;
764     otOperationalDataset dataset;
765     uint8_t              tlvs[128];
766     uint8_t              tlvsLength = 0;
767 
768     ClearAllBytes(dataset);
769 
770     for (Arg *arg = &aArgs[1]; !arg->IsEmpty();)
771     {
772         const ComponentMapper *mapper = LookupMapper(arg->GetCString());
773 
774         if (mapper != nullptr)
775         {
776             arg++;
777             SuccessOrExit(error = (this->*mapper->mParse)(arg, dataset));
778             dataset.mComponents.*mapper->mIsPresentPtr = true;
779         }
780         else if (*arg == "-x")
781         {
782             uint16_t length;
783 
784             arg++;
785             length = sizeof(tlvs);
786             SuccessOrExit(error = arg->ParseAsHexString(length, tlvs));
787             tlvsLength = static_cast<uint8_t>(length);
788             arg++;
789         }
790         else
791         {
792             ExitNow(error = OT_ERROR_INVALID_ARGS);
793         }
794     }
795 
796     /**
797      * @cli dataset mgmtsetcommand active
798      * @code
799      * dataset mgmtsetcommand active activetimestamp 123 securitypolicy 1 onrcb
800      * Done
801      * @endcode
802      * @cparam dataset mgmtsetcommand active [@ca{dataset-components}] [-x @ca{tlv-list}]
803      * To learn more about these parameters and argument mappings, refer to @dataset.
804      * @par
805      * @note This command is primarily used for testing only.
806      * @par api_copy
807      * #otDatasetSendMgmtActiveSet
808      * @csa{dataset mgmtgetcommand active}
809      * @csa{dataset mgmtgetcommand pending}
810      * @csa{dataset mgmtsetcommand pending}
811      */
812     if (aArgs[0] == "active")
813     {
814         error = otDatasetSendMgmtActiveSet(GetInstancePtr(), &dataset, tlvs, tlvsLength, /* aCallback */ nullptr,
815                                            /* aContext */ nullptr);
816     }
817     /**
818      * @cli dataset mgmtsetcommand pending
819      * @code
820      * dataset mgmtsetcommand pending activetimestamp 123 securitypolicy 1 onrcb
821      * Done
822      * @endcode
823      * @cparam dataset mgmtsetcommand pending [@ca{dataset-components}] [-x @ca{tlv-list}]
824      * To learn more about these parameters and argument mappings, refer to @dataset.
825      * @par
826      * @note This command is primarily used for testing only.
827      * @par api_copy
828      * #otDatasetSendMgmtPendingSet
829      * @csa{dataset mgmtgetcommand active}
830      * @csa{dataset mgmtgetcommand pending}
831      * @csa{dataset mgmtsetcommand active}
832      */
833     else if (aArgs[0] == "pending")
834     {
835         error = otDatasetSendMgmtPendingSet(GetInstancePtr(), &dataset, tlvs, tlvsLength, /* aCallback */ nullptr,
836                                             /* aContext */ nullptr);
837     }
838     else
839     {
840         error = OT_ERROR_INVALID_ARGS;
841     }
842 
843 exit:
844     return error;
845 }
846 
Process(Arg aArgs[])847 template <> otError Dataset::Process<Cmd("mgmtgetcommand")>(Arg aArgs[])
848 {
849     otError                        error = OT_ERROR_NONE;
850     otOperationalDatasetComponents datasetComponents;
851     uint8_t                        tlvs[32];
852     uint8_t                        tlvsLength        = 0;
853     bool                           destAddrSpecified = false;
854     otIp6Address                   address;
855 
856     ClearAllBytes(datasetComponents);
857 
858     for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
859     {
860         const ComponentMapper *mapper = LookupMapper(arg->GetCString());
861 
862         if (mapper != nullptr)
863         {
864             datasetComponents.*mapper->mIsPresentPtr = true;
865         }
866         else if (*arg == "-x")
867         {
868             uint16_t length;
869 
870             arg++;
871             length = sizeof(tlvs);
872             SuccessOrExit(error = arg->ParseAsHexString(length, tlvs));
873             tlvsLength = static_cast<uint8_t>(length);
874         }
875         else if (*arg == "address")
876         {
877             arg++;
878             SuccessOrExit(error = arg->ParseAsIp6Address(address));
879             destAddrSpecified = true;
880         }
881         else
882         {
883             ExitNow(error = OT_ERROR_INVALID_ARGS);
884         }
885     }
886 
887     /**
888      * @cli dataset mgmtgetcommand active
889      * @code
890      * dataset mgmtgetcommand active address fdde:ad00:beef:0:558:f56b:d688:799 activetimestamp securitypolicy
891      * Done
892      * @endcode
893      * @code
894      * dataset mgmtgetcommand active networkname
895      * Done
896      * @endcode
897      * @cparam dataset mgmtgetcommand active [address @ca{leader-address}] [@ca{dataset-components}] [-x @ca{tlv-list}]
898      * *    Use `address` to specify the IPv6 destination; otherwise, the Leader ALOC is used as default.
899      * *    For `dataset-components`, you can pass any combination of #otOperationalDatasetComponents, for
900      *      example `activetimestamp`, `pendingtimestamp`, or `networkkey`.
901      * *    The optional `-x` argument specifies raw TLVs to be requested.
902      * @par
903      * OT CLI sends a MGMT_ACTIVE_GET with the relevant arguments.
904      * To learn more about these parameters and argument mappings, refer to @dataset.
905      * @note This command is primarily used for testing only.
906      * @par api_copy
907      * #otDatasetSendMgmtActiveGet
908      * @csa{dataset mgmtgetcommand pending}
909      * @csa{dataset mgmtsetcommand active}
910      * @csa{dataset mgmtsetcommand pending}
911      */
912     if (aArgs[0] == "active")
913     {
914         error = otDatasetSendMgmtActiveGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength,
915                                            destAddrSpecified ? &address : nullptr);
916     }
917     /**
918      * @cli dataset mgmtgetcommand pending
919      * @code
920      * dataset mgmtgetcommand pending address fdde:ad00:beef:0:558:f56b:d688:799 activetimestamp securitypolicy
921      * Done
922      * @endcode
923      * @code
924      * dataset mgmtgetcommand pending networkname
925      * Done
926      * @endcode
927      * @cparam dataset mgmtgetcommand pending [address @ca{leader-address}] [@ca{dataset-components}] [-x @ca{tlv-list}]
928      * To learn more about these parameters and argument mappings, refer to @dataset.
929      * @par
930      * @note This command is primarily used for testing only.
931      * @par api_copy
932      * #otDatasetSendMgmtPendingGet
933      * @csa{dataset mgmtgetcommand active}
934      * @csa{dataset mgmtsetcommand active}
935      * @csa{dataset mgmtsetcommand pending}
936      */
937     else if (aArgs[0] == "pending")
938     {
939         error = otDatasetSendMgmtPendingGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength,
940                                             destAddrSpecified ? &address : nullptr);
941     }
942     else
943     {
944         error = OT_ERROR_INVALID_ARGS;
945     }
946 
947 exit:
948     return error;
949 }
950 
OutputSecurityPolicy(const otSecurityPolicy & aSecurityPolicy)951 void Dataset::OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy)
952 {
953     OutputFormat("%u ", aSecurityPolicy.mRotationTime);
954 
955     if (aSecurityPolicy.mObtainNetworkKeyEnabled)
956     {
957         OutputFormat("o");
958     }
959 
960     if (aSecurityPolicy.mNativeCommissioningEnabled)
961     {
962         OutputFormat("n");
963     }
964 
965     if (aSecurityPolicy.mRoutersEnabled)
966     {
967         OutputFormat("r");
968     }
969 
970     if (aSecurityPolicy.mExternalCommissioningEnabled)
971     {
972         OutputFormat("c");
973     }
974 
975     if (aSecurityPolicy.mCommercialCommissioningEnabled)
976     {
977         OutputFormat("C");
978     }
979 
980     if (aSecurityPolicy.mAutonomousEnrollmentEnabled)
981     {
982         OutputFormat("e");
983     }
984 
985     if (aSecurityPolicy.mNetworkKeyProvisioningEnabled)
986     {
987         OutputFormat("p");
988     }
989 
990     if (aSecurityPolicy.mNonCcmRoutersEnabled)
991     {
992         OutputFormat("R");
993     }
994 
995     OutputLine(" %u", aSecurityPolicy.mVersionThresholdForRouting);
996 }
997 
ParseSecurityPolicy(otSecurityPolicy & aSecurityPolicy,Arg * & aArgs)998 otError Dataset::ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, Arg *&aArgs)
999 {
1000     static constexpr uint8_t kMaxVersionThreshold = 7;
1001 
1002     otError          error;
1003     otSecurityPolicy policy;
1004     uint8_t          versionThreshold;
1005 
1006     ClearAllBytes(policy);
1007 
1008     SuccessOrExit(error = aArgs->ParseAsUint16(policy.mRotationTime));
1009     aArgs++;
1010 
1011     VerifyOrExit(!aArgs->IsEmpty());
1012 
1013     for (const char *flag = aArgs->GetCString(); *flag != '\0'; flag++)
1014     {
1015         switch (*flag)
1016         {
1017         case 'o':
1018             policy.mObtainNetworkKeyEnabled = true;
1019             break;
1020 
1021         case 'n':
1022             policy.mNativeCommissioningEnabled = true;
1023             break;
1024 
1025         case 'r':
1026             policy.mRoutersEnabled = true;
1027             break;
1028 
1029         case 'c':
1030             policy.mExternalCommissioningEnabled = true;
1031             break;
1032 
1033         case 'C':
1034             policy.mCommercialCommissioningEnabled = true;
1035             break;
1036 
1037         case 'e':
1038             policy.mAutonomousEnrollmentEnabled = true;
1039             break;
1040 
1041         case 'p':
1042             policy.mNetworkKeyProvisioningEnabled = true;
1043             break;
1044 
1045         case 'R':
1046             policy.mNonCcmRoutersEnabled = true;
1047             break;
1048 
1049         default:
1050             ExitNow(error = OT_ERROR_INVALID_ARGS);
1051         }
1052     }
1053 
1054     aArgs++;
1055     VerifyOrExit(!aArgs->IsEmpty());
1056 
1057     SuccessOrExit(error = aArgs->ParseAsUint8(versionThreshold));
1058     aArgs++;
1059     VerifyOrExit(versionThreshold <= kMaxVersionThreshold, error = OT_ERROR_INVALID_ARGS);
1060     policy.mVersionThresholdForRouting = versionThreshold;
1061 
1062 exit:
1063     if (error == OT_ERROR_NONE)
1064     {
1065         aSecurityPolicy = policy;
1066     }
1067 
1068     return error;
1069 }
1070 
1071 /**
1072  * @cli dataset set (active,pending)
1073  * @code
1074  * dataset set active 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff
1075  * Done
1076  * @endcode
1077  * @code
1078  * dataset set pending 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff
1079  * Done
1080  * @endcode
1081  * @cparam dataset set {active|pending} @ca{tlvs}
1082  * @par
1083  * The CLI `dataset set` command sets the Active Operational %Dataset using hex-encoded TLVs.
1084  * @par api_copy
1085  * #otDatasetSetActive
1086  */
Process(Arg aArgs[])1087 template <> otError Dataset::Process<Cmd("set")>(Arg aArgs[])
1088 {
1089     otError                error = OT_ERROR_NONE;
1090     MeshCoP::Dataset::Type datasetType;
1091 
1092     if (aArgs[0] == "active")
1093     {
1094         datasetType = MeshCoP::Dataset::Type::kActive;
1095     }
1096     else if (aArgs[0] == "pending")
1097     {
1098         datasetType = MeshCoP::Dataset::Type::kPending;
1099     }
1100     else
1101     {
1102         ExitNow(error = OT_ERROR_INVALID_ARGS);
1103     }
1104 
1105     {
1106         otOperationalDataset     dataset;
1107         otOperationalDatasetTlvs datasetTlvs;
1108         uint16_t                 tlvsLength = OT_OPERATIONAL_DATASET_MAX_LENGTH;
1109 
1110         SuccessOrExit(error = aArgs[1].ParseAsHexString(tlvsLength, datasetTlvs.mTlvs));
1111         datasetTlvs.mLength = static_cast<uint8_t>(tlvsLength);
1112 
1113         SuccessOrExit(error = otDatasetParseTlvs(&datasetTlvs, &dataset));
1114 
1115         switch (datasetType)
1116         {
1117         case MeshCoP::Dataset::Type::kActive:
1118             SuccessOrExit(error = otDatasetSetActiveTlvs(GetInstancePtr(), &datasetTlvs));
1119             break;
1120         case MeshCoP::Dataset::Type::kPending:
1121             SuccessOrExit(error = otDatasetSetPendingTlvs(GetInstancePtr(), &datasetTlvs));
1122             break;
1123         }
1124     }
1125 
1126 exit:
1127     return error;
1128 }
1129 
1130 /**
1131  * @cli dataset tlvs
1132  * @code
1133  * dataset tlvs
1134  * 0e080000000000010000000300001635060004001fffe0020...f7f8
1135  * Done
1136  * @endcode
1137  * @par api_copy
1138  * #otDatasetConvertToTlvs
1139  */
Process(Arg aArgs[])1140 template <> otError Dataset::Process<Cmd("tlvs")>(Arg aArgs[])
1141 {
1142     otError error = OT_ERROR_NONE;
1143 
1144     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
1145     OutputBytesLine(sDatasetTlvs.mTlvs, sDatasetTlvs.mLength);
1146 
1147 exit:
1148     return error;
1149 }
1150 
1151 #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
1152 
Process(Arg aArgs[])1153 template <> otError Dataset::Process<Cmd("updater")>(Arg aArgs[])
1154 {
1155     otError error = OT_ERROR_NONE;
1156 
1157     /**
1158      * @cli dataset updater
1159      * @code
1160      * dataset updater
1161      * Enabled
1162      * Done
1163      * @endcode
1164      * @par api_copy
1165      * #otDatasetUpdaterIsUpdateOngoing
1166      */
1167     if (aArgs[0].IsEmpty())
1168     {
1169         OutputEnabledDisabledStatus(otDatasetUpdaterIsUpdateOngoing(GetInstancePtr()));
1170     }
1171     /**
1172      * @cli dataset updater start
1173      * @code
1174      * channel
1175      * 19
1176      * Done
1177      * dataset clear
1178      * Done
1179      * dataset channel 15
1180      * Done
1181      * dataset
1182      * Channel: 15
1183      * Done
1184      * dataset updater start
1185      * Done
1186      * dataset updater
1187      * Enabled
1188      * Done
1189      * Dataset update complete: OK
1190      * channel
1191      * 15
1192      * Done
1193      * @endcode
1194      * @par api_copy
1195      * #otDatasetUpdaterRequestUpdate
1196      */
1197     else if (aArgs[0] == "start")
1198     {
1199         otOperationalDataset dataset;
1200 
1201         SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset));
1202         SuccessOrExit(
1203             error = otDatasetUpdaterRequestUpdate(GetInstancePtr(), &dataset, &Dataset::HandleDatasetUpdater, this));
1204     }
1205     /**
1206      * @cli dataset updater cancel
1207      * @code
1208      * @dataset updater cancel
1209      * Done
1210      * @endcode
1211      * @par api_copy
1212      * #otDatasetUpdaterCancelUpdate
1213      */
1214     else if (aArgs[0] == "cancel")
1215     {
1216         otDatasetUpdaterCancelUpdate(GetInstancePtr());
1217     }
1218     else
1219     {
1220         error = OT_ERROR_INVALID_ARGS;
1221     }
1222 
1223 exit:
1224     return error;
1225 }
1226 
HandleDatasetUpdater(otError aError,void * aContext)1227 void Dataset::HandleDatasetUpdater(otError aError, void *aContext)
1228 {
1229     static_cast<Dataset *>(aContext)->HandleDatasetUpdater(aError);
1230 }
1231 
HandleDatasetUpdater(otError aError)1232 void Dataset::HandleDatasetUpdater(otError aError)
1233 {
1234     OutputLine("Dataset update complete: %s", otThreadErrorToString(aError));
1235 }
1236 
1237 #endif // OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
1238 
Process(Arg aArgs[])1239 otError Dataset::Process(Arg aArgs[])
1240 {
1241 #define CmdEntry(aCommandString)                               \
1242     {                                                          \
1243         aCommandString, &Dataset::Process<Cmd(aCommandString)> \
1244     }
1245 
1246     static constexpr Command kCommands[] = {
1247         CmdEntry("active"),
1248         CmdEntry("clear"),
1249         CmdEntry("commit"),
1250         CmdEntry("init"),
1251         CmdEntry("mgmtgetcommand"),
1252         CmdEntry("mgmtsetcommand"),
1253         CmdEntry("pending"),
1254         CmdEntry("set"),
1255         CmdEntry("tlvs"),
1256 #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
1257         CmdEntry("updater"),
1258 #endif
1259     };
1260 
1261 #undef CmdEntry
1262 
1263     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
1264 
1265     otError                error = OT_ERROR_INVALID_COMMAND;
1266     const Command         *command;
1267     const ComponentMapper *mapper;
1268 
1269     if (aArgs[0].IsEmpty())
1270     {
1271         ExitNow(error = Print(sDatasetTlvs));
1272     }
1273 
1274     /**
1275      * @cli dataset help
1276      * @code
1277      * dataset help
1278      * help
1279      * active
1280      * activetimestamp
1281      * channel
1282      * channelmask
1283      * clear
1284      * commit
1285      * delay
1286      * extpanid
1287      * init
1288      * meshlocalprefix
1289      * mgmtgetcommand
1290      * mgmtsetcommand
1291      * networkkey
1292      * networkname
1293      * panid
1294      * pending
1295      * pendingtimestamp
1296      * pskc
1297      * securitypolicy
1298      * set
1299      * tlvs
1300      * Done
1301      * @endcode
1302      * @par
1303      * Gets a list of `dataset` CLI commands. @moreinfo{@dataset}.
1304      */
1305     if (aArgs[0] == "help")
1306     {
1307         OutputCommandTable(kCommands);
1308         ExitNow(error = OT_ERROR_NONE);
1309     }
1310 
1311     mapper = LookupMapper(aArgs[0].GetCString());
1312 
1313     if (mapper != nullptr)
1314     {
1315         error = ProcessCommand(*mapper, aArgs + 1);
1316         ExitNow();
1317     }
1318 
1319     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
1320     VerifyOrExit(command != nullptr);
1321 
1322     error = (this->*command->mHandler)(aArgs + 1);
1323 
1324 exit:
1325     return error;
1326 }
1327 
1328 } // namespace Cli
1329 } // namespace ot
1330