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 
ParseTlvs(Arg & aArg,otOperationalDatasetTlvs & aDatasetTlvs)511 otError Dataset::ParseTlvs(Arg &aArg, otOperationalDatasetTlvs &aDatasetTlvs)
512 {
513     otError  error;
514     uint16_t length;
515 
516     length = sizeof(aDatasetTlvs.mTlvs);
517     SuccessOrExit(error = aArg.ParseAsHexString(length, aDatasetTlvs.mTlvs));
518     aDatasetTlvs.mLength = static_cast<uint8_t>(length);
519 
520 exit:
521     return error;
522 }
523 
524 //---------------------------------------------------------------------------------------------------------------------
525 
ProcessCommand(const ComponentMapper & aMapper,Arg aArgs[])526 otError Dataset::ProcessCommand(const ComponentMapper &aMapper, Arg aArgs[])
527 {
528     otError              error = OT_ERROR_NONE;
529     otOperationalDataset dataset;
530 
531     if (aArgs[0].IsEmpty())
532     {
533         SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset));
534 
535         if (dataset.mComponents.*aMapper.mIsPresentPtr)
536         {
537             (this->*aMapper.mOutput)(dataset);
538         }
539     }
540     else
541     {
542         ClearAllBytes(dataset);
543         SuccessOrExit(error = (this->*aMapper.mParse)(aArgs, dataset));
544         dataset.mComponents.*aMapper.mIsPresentPtr = true;
545         SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs));
546     }
547 
548 exit:
549     return error;
550 }
551 
Print(otOperationalDatasetTlvs & aDatasetTlvs)552 otError Dataset::Print(otOperationalDatasetTlvs &aDatasetTlvs)
553 {
554     struct ComponentTitle
555     {
556         const char *mTitle; // Title to output.
557         const char *mName;  // To use with `LookupMapper()`.
558     };
559 
560     static const ComponentTitle kTitles[] = {
561         {"Pending Timestamp", "pendingtimestamp"},
562         {"Active Timestamp", "activetimestamp"},
563         {"Channel", "channel"},
564         {"Channel Mask", "channelmask"},
565         {"Delay", "delay"},
566         {"Ext PAN ID", "extpanid"},
567         {"Mesh Local Prefix", "meshlocalprefix"},
568         {"Network Key", "networkkey"},
569         {"Network Name", "networkname"},
570         {"PAN ID", "panid"},
571         {"PSKc", "pskc"},
572         {"Security Policy", "securitypolicy"},
573     };
574 
575     otError              error;
576     otOperationalDataset dataset;
577 
578     SuccessOrExit(error = otDatasetParseTlvs(&aDatasetTlvs, &dataset));
579 
580     for (const ComponentTitle &title : kTitles)
581     {
582         const ComponentMapper *mapper = LookupMapper(title.mName);
583 
584         if (dataset.mComponents.*mapper->mIsPresentPtr)
585         {
586             OutputFormat("%s: ", title.mTitle);
587             (this->*mapper->mOutput)(dataset);
588         }
589     }
590 
591 exit:
592     return error;
593 }
594 
595 /**
596  * @cli dataset init (active,new,pending,tlvs)
597  * @code
598  * dataset init new
599  * Done
600  * @endcode
601  * @cparam dataset init {@ca{active}|@ca{new}|@ca{pending}|@ca{tlvs}} [@ca{hex-encoded-tlvs}]
602  * Use `new` to initialize a new dataset, then enter the command `dataset commit active`.
603  * Use `tlvs` for hex-encoded TLVs.
604  * @par
605  * OT CLI checks for `active`, `pending`, or `tlvs` and returns the corresponding values. Otherwise,
606  * OT CLI creates a new, random network and returns a new dataset.
607  * @csa{dataset commit active}
608  * @csa{dataset active}
609  */
Process(Arg aArgs[])610 template <> otError Dataset::Process<Cmd("init")>(Arg aArgs[])
611 {
612     otError error = OT_ERROR_INVALID_ARGS;
613 
614     if (aArgs[0] == "active")
615     {
616         error = otDatasetGetActiveTlvs(GetInstancePtr(), &sDatasetTlvs);
617     }
618     else if (aArgs[0] == "pending")
619     {
620         error = otDatasetGetPendingTlvs(GetInstancePtr(), &sDatasetTlvs);
621     }
622 #if OPENTHREAD_FTD
623     else if (aArgs[0] == "new")
624     {
625         otOperationalDataset dataset;
626 
627         SuccessOrExit(error = otDatasetCreateNewNetwork(GetInstancePtr(), &dataset));
628         otDatasetConvertToTlvs(&dataset, &sDatasetTlvs);
629     }
630 #endif
631     else if (aArgs[0] == "tlvs")
632     {
633         ExitNow(error = ParseTlvs(aArgs[1], sDatasetTlvs));
634     }
635 
636 exit:
637     return error;
638 }
639 
640 /**
641  * @cli dataset active
642  * @code
643  * dataset active
644  * Active Timestamp: 1
645  * Channel: 13
646  * Channel Mask: 0x07fff800
647  * Ext PAN ID: d63e8e3e495ebbc3
648  * Mesh Local Prefix: fd3d:b50b:f96d:722d::/64
649  * Network Key: dfd34f0f05cad978ec4e32b0413038ff
650  * Network Name: OpenThread-8f28
651  * PAN ID: 0x8f28
652  * PSKc: c23a76e98f1a6483639b1ac1271e2e27
653  * Security Policy: 0, onrcb
654  * Done
655  * @endcode
656  * @code
657  * dataset active -x
658  * 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff
659  * Done
660  * @endcode
661  * @cparam dataset active [-x]
662  * The optional `-x` argument prints the Active Operational %Dataset values as hex-encoded TLVs.
663  * @par api_copy
664  * #otDatasetGetActive
665  * @par
666  * OT CLI uses #otOperationalDataset members to return dataset values to the console.
667  */
Process(Arg aArgs[])668 template <> otError Dataset::Process<Cmd("active")>(Arg aArgs[])
669 {
670     otError                  error;
671     otOperationalDatasetTlvs dataset;
672 
673     SuccessOrExit(error = otDatasetGetActiveTlvs(GetInstancePtr(), &dataset));
674 
675     if (aArgs[0].IsEmpty())
676     {
677         error = Print(dataset);
678     }
679     else if (aArgs[0] == "-x")
680     {
681         OutputBytesLine(dataset.mTlvs, dataset.mLength);
682     }
683     else
684     {
685         error = OT_ERROR_INVALID_ARGS;
686     }
687 
688 exit:
689     return error;
690 }
691 
Process(Arg aArgs[])692 template <> otError Dataset::Process<Cmd("pending")>(Arg aArgs[])
693 {
694     otError                  error;
695     otOperationalDatasetTlvs datasetTlvs;
696 
697     SuccessOrExit(error = otDatasetGetPendingTlvs(GetInstancePtr(), &datasetTlvs));
698 
699     if (aArgs[0].IsEmpty())
700     {
701         error = Print(datasetTlvs);
702     }
703     else if (aArgs[0] == "-x")
704     {
705         OutputBytesLine(datasetTlvs.mTlvs, datasetTlvs.mLength);
706     }
707     else
708     {
709         error = OT_ERROR_INVALID_ARGS;
710     }
711 
712 exit:
713     return error;
714 }
715 
716 /**
717  * @cli dataset clear
718  * @code
719  * dataset clear
720  * Done
721  * @endcode
722  * @par
723  * Reset the Operational %Dataset buffer.
724  */
Process(Arg aArgs[])725 template <> otError Dataset::Process<Cmd("clear")>(Arg aArgs[])
726 {
727     OT_UNUSED_VARIABLE(aArgs);
728 
729     ClearAllBytes(sDatasetTlvs);
730     return OT_ERROR_NONE;
731 }
732 
Process(Arg aArgs[])733 template <> otError Dataset::Process<Cmd("commit")>(Arg aArgs[])
734 {
735     otError error = OT_ERROR_INVALID_ARGS;
736 
737     /**
738      * @cli dataset commit active
739      * @code
740      * dataset commit active
741      * Done
742      * @endcode
743      * @par
744      * Commit the Operational %Dataset buffer to Active Operational %Dataset.
745      * @csa{dataset commit pending}
746      * @sa #otDatasetSetPending
747      */
748     if (aArgs[0] == "active")
749     {
750         error = otDatasetSetActiveTlvs(GetInstancePtr(), &sDatasetTlvs);
751     }
752     /**
753      * @cli dataset commit pending
754      * @code
755      * dataset commit pending
756      * Done
757      * @endcode
758      * @par
759      * Commit the Operational %Dataset buffer to Pending Operational %Dataset.
760      * @csa{dataset commit active}
761      * @sa #otDatasetSetActive
762      */
763     else if (aArgs[0] == "pending")
764     {
765         error = otDatasetSetPendingTlvs(GetInstancePtr(), &sDatasetTlvs);
766     }
767 
768     return error;
769 }
770 
Process(Arg aArgs[])771 template <> otError Dataset::Process<Cmd("mgmtsetcommand")>(Arg aArgs[])
772 {
773     otError                  error = OT_ERROR_NONE;
774     otOperationalDataset     dataset;
775     otOperationalDatasetTlvs tlvs;
776 
777     ClearAllBytes(dataset);
778     ClearAllBytes(tlvs);
779 
780     for (Arg *arg = &aArgs[1]; !arg->IsEmpty();)
781     {
782         const ComponentMapper *mapper = LookupMapper(arg->GetCString());
783 
784         if (mapper != nullptr)
785         {
786             arg++;
787             SuccessOrExit(error = (this->*mapper->mParse)(arg, dataset));
788             dataset.mComponents.*mapper->mIsPresentPtr = true;
789         }
790         else if (*arg == "-x")
791         {
792             arg++;
793             SuccessOrExit(error = ParseTlvs(*arg, tlvs));
794             arg++;
795         }
796         else
797         {
798             ExitNow(error = OT_ERROR_INVALID_ARGS);
799         }
800     }
801 
802     /**
803      * @cli dataset mgmtsetcommand active
804      * @code
805      * dataset mgmtsetcommand active activetimestamp 123 securitypolicy 1 onrcb
806      * Done
807      * @endcode
808      * @cparam dataset mgmtsetcommand active [@ca{dataset-components}] [-x @ca{tlv-list}]
809      * To learn more about these parameters and argument mappings, refer to @dataset.
810      * @par
811      * @note This command is primarily used for testing only.
812      * @par api_copy
813      * #otDatasetSendMgmtActiveSet
814      * @csa{dataset mgmtgetcommand active}
815      * @csa{dataset mgmtgetcommand pending}
816      * @csa{dataset mgmtsetcommand pending}
817      */
818     if (aArgs[0] == "active")
819     {
820         error =
821             otDatasetSendMgmtActiveSet(GetInstancePtr(), &dataset, tlvs.mTlvs, tlvs.mLength, /* aCallback */ nullptr,
822                                        /* aContext */ nullptr);
823     }
824     /**
825      * @cli dataset mgmtsetcommand pending
826      * @code
827      * dataset mgmtsetcommand pending activetimestamp 123 securitypolicy 1 onrcb
828      * Done
829      * @endcode
830      * @cparam dataset mgmtsetcommand pending [@ca{dataset-components}] [-x @ca{tlv-list}]
831      * To learn more about these parameters and argument mappings, refer to @dataset.
832      * @par
833      * @note This command is primarily used for testing only.
834      * @par api_copy
835      * #otDatasetSendMgmtPendingSet
836      * @csa{dataset mgmtgetcommand active}
837      * @csa{dataset mgmtgetcommand pending}
838      * @csa{dataset mgmtsetcommand active}
839      */
840     else if (aArgs[0] == "pending")
841     {
842         error =
843             otDatasetSendMgmtPendingSet(GetInstancePtr(), &dataset, tlvs.mTlvs, tlvs.mLength, /* aCallback */ nullptr,
844                                         /* aContext */ nullptr);
845     }
846     else
847     {
848         error = OT_ERROR_INVALID_ARGS;
849     }
850 
851 exit:
852     return error;
853 }
854 
Process(Arg aArgs[])855 template <> otError Dataset::Process<Cmd("mgmtgetcommand")>(Arg aArgs[])
856 {
857     otError                        error = OT_ERROR_NONE;
858     otOperationalDatasetComponents datasetComponents;
859     otOperationalDatasetTlvs       tlvs;
860     bool                           destAddrSpecified = false;
861     otIp6Address                   address;
862 
863     ClearAllBytes(datasetComponents);
864     ClearAllBytes(tlvs);
865 
866     for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
867     {
868         const ComponentMapper *mapper = LookupMapper(arg->GetCString());
869 
870         if (mapper != nullptr)
871         {
872             datasetComponents.*mapper->mIsPresentPtr = true;
873         }
874         else if (*arg == "-x")
875         {
876             arg++;
877             SuccessOrExit(error = ParseTlvs(*arg, tlvs));
878         }
879         else if (*arg == "address")
880         {
881             arg++;
882             SuccessOrExit(error = arg->ParseAsIp6Address(address));
883             destAddrSpecified = true;
884         }
885         else
886         {
887             ExitNow(error = OT_ERROR_INVALID_ARGS);
888         }
889     }
890 
891     /**
892      * @cli dataset mgmtgetcommand active
893      * @code
894      * dataset mgmtgetcommand active address fdde:ad00:beef:0:558:f56b:d688:799 activetimestamp securitypolicy
895      * Done
896      * @endcode
897      * @code
898      * dataset mgmtgetcommand active networkname
899      * Done
900      * @endcode
901      * @cparam dataset mgmtgetcommand active [address @ca{leader-address}] [@ca{dataset-components}] [-x @ca{tlv-list}]
902      * *    Use `address` to specify the IPv6 destination; otherwise, the Leader ALOC is used as default.
903      * *    For `dataset-components`, you can pass any combination of #otOperationalDatasetComponents, for
904      *      example `activetimestamp`, `pendingtimestamp`, or `networkkey`.
905      * *    The optional `-x` argument specifies raw TLVs to be requested.
906      * @par
907      * OT CLI sends a MGMT_ACTIVE_GET with the relevant arguments.
908      * To learn more about these parameters and argument mappings, refer to @dataset.
909      * @note This command is primarily used for testing only.
910      * @par api_copy
911      * #otDatasetSendMgmtActiveGet
912      * @csa{dataset mgmtgetcommand pending}
913      * @csa{dataset mgmtsetcommand active}
914      * @csa{dataset mgmtsetcommand pending}
915      */
916     if (aArgs[0] == "active")
917     {
918         error = otDatasetSendMgmtActiveGet(GetInstancePtr(), &datasetComponents, tlvs.mTlvs, tlvs.mLength,
919                                            destAddrSpecified ? &address : nullptr);
920     }
921     /**
922      * @cli dataset mgmtgetcommand pending
923      * @code
924      * dataset mgmtgetcommand pending address fdde:ad00:beef:0:558:f56b:d688:799 activetimestamp securitypolicy
925      * Done
926      * @endcode
927      * @code
928      * dataset mgmtgetcommand pending networkname
929      * Done
930      * @endcode
931      * @cparam dataset mgmtgetcommand pending [address @ca{leader-address}] [@ca{dataset-components}] [-x @ca{tlv-list}]
932      * To learn more about these parameters and argument mappings, refer to @dataset.
933      * @par
934      * @note This command is primarily used for testing only.
935      * @par api_copy
936      * #otDatasetSendMgmtPendingGet
937      * @csa{dataset mgmtgetcommand active}
938      * @csa{dataset mgmtsetcommand active}
939      * @csa{dataset mgmtsetcommand pending}
940      */
941     else if (aArgs[0] == "pending")
942     {
943         error = otDatasetSendMgmtPendingGet(GetInstancePtr(), &datasetComponents, tlvs.mTlvs, tlvs.mLength,
944                                             destAddrSpecified ? &address : nullptr);
945     }
946     else
947     {
948         error = OT_ERROR_INVALID_ARGS;
949     }
950 
951 exit:
952     return error;
953 }
954 
OutputSecurityPolicy(const otSecurityPolicy & aSecurityPolicy)955 void Dataset::OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy)
956 {
957     OutputFormat("%u ", aSecurityPolicy.mRotationTime);
958 
959     if (aSecurityPolicy.mObtainNetworkKeyEnabled)
960     {
961         OutputFormat("o");
962     }
963 
964     if (aSecurityPolicy.mNativeCommissioningEnabled)
965     {
966         OutputFormat("n");
967     }
968 
969     if (aSecurityPolicy.mRoutersEnabled)
970     {
971         OutputFormat("r");
972     }
973 
974     if (aSecurityPolicy.mExternalCommissioningEnabled)
975     {
976         OutputFormat("c");
977     }
978 
979     if (aSecurityPolicy.mCommercialCommissioningEnabled)
980     {
981         OutputFormat("C");
982     }
983 
984     if (aSecurityPolicy.mAutonomousEnrollmentEnabled)
985     {
986         OutputFormat("e");
987     }
988 
989     if (aSecurityPolicy.mNetworkKeyProvisioningEnabled)
990     {
991         OutputFormat("p");
992     }
993 
994     if (aSecurityPolicy.mNonCcmRoutersEnabled)
995     {
996         OutputFormat("R");
997     }
998 
999     OutputLine(" %u", aSecurityPolicy.mVersionThresholdForRouting);
1000 }
1001 
ParseSecurityPolicy(otSecurityPolicy & aSecurityPolicy,Arg * & aArgs)1002 otError Dataset::ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, Arg *&aArgs)
1003 {
1004     static constexpr uint8_t kMaxVersionThreshold = 7;
1005 
1006     otError          error;
1007     otSecurityPolicy policy;
1008     uint8_t          versionThreshold;
1009 
1010     ClearAllBytes(policy);
1011 
1012     SuccessOrExit(error = aArgs->ParseAsUint16(policy.mRotationTime));
1013     aArgs++;
1014 
1015     VerifyOrExit(!aArgs->IsEmpty());
1016 
1017     for (const char *flag = aArgs->GetCString(); *flag != '\0'; flag++)
1018     {
1019         switch (*flag)
1020         {
1021         case 'o':
1022             policy.mObtainNetworkKeyEnabled = true;
1023             break;
1024 
1025         case 'n':
1026             policy.mNativeCommissioningEnabled = true;
1027             break;
1028 
1029         case 'r':
1030             policy.mRoutersEnabled = true;
1031             break;
1032 
1033         case 'c':
1034             policy.mExternalCommissioningEnabled = true;
1035             break;
1036 
1037         case 'C':
1038             policy.mCommercialCommissioningEnabled = true;
1039             break;
1040 
1041         case 'e':
1042             policy.mAutonomousEnrollmentEnabled = true;
1043             break;
1044 
1045         case 'p':
1046             policy.mNetworkKeyProvisioningEnabled = true;
1047             break;
1048 
1049         case 'R':
1050             policy.mNonCcmRoutersEnabled = true;
1051             break;
1052 
1053         default:
1054             ExitNow(error = OT_ERROR_INVALID_ARGS);
1055         }
1056     }
1057 
1058     aArgs++;
1059     VerifyOrExit(!aArgs->IsEmpty());
1060 
1061     SuccessOrExit(error = aArgs->ParseAsUint8(versionThreshold));
1062     aArgs++;
1063     VerifyOrExit(versionThreshold <= kMaxVersionThreshold, error = OT_ERROR_INVALID_ARGS);
1064     policy.mVersionThresholdForRouting = versionThreshold;
1065 
1066 exit:
1067     if (error == OT_ERROR_NONE)
1068     {
1069         aSecurityPolicy = policy;
1070     }
1071 
1072     return error;
1073 }
1074 
1075 /**
1076  * @cli dataset set (active,pending)
1077  * @code
1078  * dataset set active 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff
1079  * Done
1080  * @endcode
1081  * @code
1082  * dataset set pending 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff
1083  * Done
1084  * @endcode
1085  * @cparam dataset set {active|pending} @ca{tlvs}
1086  * @par
1087  * The CLI `dataset set` command sets the Active Operational %Dataset using hex-encoded TLVs.
1088  * @par api_copy
1089  * #otDatasetSetActive
1090  */
Process(Arg aArgs[])1091 template <> otError Dataset::Process<Cmd("set")>(Arg aArgs[])
1092 {
1093     otError                  error = OT_ERROR_NONE;
1094     otOperationalDatasetTlvs datasetTlvs;
1095 
1096     SuccessOrExit(error = ParseTlvs(aArgs[1], datasetTlvs));
1097 
1098     if (aArgs[0] == "active")
1099     {
1100         error = otDatasetSetActiveTlvs(GetInstancePtr(), &datasetTlvs);
1101     }
1102     else if (aArgs[0] == "pending")
1103     {
1104         error = otDatasetSetPendingTlvs(GetInstancePtr(), &datasetTlvs);
1105     }
1106     else
1107     {
1108         error = OT_ERROR_INVALID_ARGS;
1109     }
1110 
1111 exit:
1112     return error;
1113 }
1114 
1115 /**
1116  * @cli dataset tlvs
1117  * @code
1118  * dataset tlvs
1119  * 0e080000000000010000000300001635060004001fffe0020...f7f8
1120  * Done
1121  * @endcode
1122  * @par api_copy
1123  * #otDatasetConvertToTlvs
1124  */
Process(Arg aArgs[])1125 template <> otError Dataset::Process<Cmd("tlvs")>(Arg aArgs[])
1126 {
1127     otError error = OT_ERROR_NONE;
1128 
1129     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
1130     OutputBytesLine(sDatasetTlvs.mTlvs, sDatasetTlvs.mLength);
1131 
1132 exit:
1133     return error;
1134 }
1135 
1136 #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
1137 
Process(Arg aArgs[])1138 template <> otError Dataset::Process<Cmd("updater")>(Arg aArgs[])
1139 {
1140     otError error = OT_ERROR_NONE;
1141 
1142     /**
1143      * @cli dataset updater
1144      * @code
1145      * dataset updater
1146      * Enabled
1147      * Done
1148      * @endcode
1149      * @par api_copy
1150      * #otDatasetUpdaterIsUpdateOngoing
1151      */
1152     if (aArgs[0].IsEmpty())
1153     {
1154         OutputEnabledDisabledStatus(otDatasetUpdaterIsUpdateOngoing(GetInstancePtr()));
1155     }
1156     /**
1157      * @cli dataset updater start
1158      * @code
1159      * channel
1160      * 19
1161      * Done
1162      * dataset clear
1163      * Done
1164      * dataset channel 15
1165      * Done
1166      * dataset
1167      * Channel: 15
1168      * Done
1169      * dataset updater start
1170      * Done
1171      * dataset updater
1172      * Enabled
1173      * Done
1174      * Dataset update complete: OK
1175      * channel
1176      * 15
1177      * Done
1178      * @endcode
1179      * @par api_copy
1180      * #otDatasetUpdaterRequestUpdate
1181      */
1182     else if (aArgs[0] == "start")
1183     {
1184         otOperationalDataset dataset;
1185 
1186         SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset));
1187         SuccessOrExit(
1188             error = otDatasetUpdaterRequestUpdate(GetInstancePtr(), &dataset, &Dataset::HandleDatasetUpdater, this));
1189     }
1190     /**
1191      * @cli dataset updater cancel
1192      * @code
1193      * @dataset updater cancel
1194      * Done
1195      * @endcode
1196      * @par api_copy
1197      * #otDatasetUpdaterCancelUpdate
1198      */
1199     else if (aArgs[0] == "cancel")
1200     {
1201         otDatasetUpdaterCancelUpdate(GetInstancePtr());
1202     }
1203     else
1204     {
1205         error = OT_ERROR_INVALID_ARGS;
1206     }
1207 
1208 exit:
1209     return error;
1210 }
1211 
HandleDatasetUpdater(otError aError,void * aContext)1212 void Dataset::HandleDatasetUpdater(otError aError, void *aContext)
1213 {
1214     static_cast<Dataset *>(aContext)->HandleDatasetUpdater(aError);
1215 }
1216 
HandleDatasetUpdater(otError aError)1217 void Dataset::HandleDatasetUpdater(otError aError)
1218 {
1219     OutputLine("Dataset update complete: %s", otThreadErrorToString(aError));
1220 }
1221 
1222 #endif // OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
1223 
Process(Arg aArgs[])1224 otError Dataset::Process(Arg aArgs[])
1225 {
1226 #define CmdEntry(aCommandString)                               \
1227     {                                                          \
1228         aCommandString, &Dataset::Process<Cmd(aCommandString)> \
1229     }
1230 
1231     static constexpr Command kCommands[] = {
1232         CmdEntry("active"),
1233         CmdEntry("clear"),
1234         CmdEntry("commit"),
1235         CmdEntry("init"),
1236         CmdEntry("mgmtgetcommand"),
1237         CmdEntry("mgmtsetcommand"),
1238         CmdEntry("pending"),
1239         CmdEntry("set"),
1240         CmdEntry("tlvs"),
1241 #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
1242         CmdEntry("updater"),
1243 #endif
1244     };
1245 
1246 #undef CmdEntry
1247 
1248     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
1249 
1250     otError                error = OT_ERROR_INVALID_COMMAND;
1251     const Command         *command;
1252     const ComponentMapper *mapper;
1253 
1254     if (aArgs[0].IsEmpty())
1255     {
1256         ExitNow(error = Print(sDatasetTlvs));
1257     }
1258 
1259     /**
1260      * @cli dataset help
1261      * @code
1262      * dataset help
1263      * help
1264      * active
1265      * activetimestamp
1266      * channel
1267      * channelmask
1268      * clear
1269      * commit
1270      * delay
1271      * extpanid
1272      * init
1273      * meshlocalprefix
1274      * mgmtgetcommand
1275      * mgmtsetcommand
1276      * networkkey
1277      * networkname
1278      * panid
1279      * pending
1280      * pendingtimestamp
1281      * pskc
1282      * securitypolicy
1283      * set
1284      * tlvs
1285      * Done
1286      * @endcode
1287      * @par
1288      * Gets a list of `dataset` CLI commands. @moreinfo{@dataset}.
1289      */
1290     if (aArgs[0] == "help")
1291     {
1292         OutputCommandTable(kCommands);
1293         ExitNow(error = OT_ERROR_NONE);
1294     }
1295 
1296     mapper = LookupMapper(aArgs[0].GetCString());
1297 
1298     if (mapper != nullptr)
1299     {
1300         error = ProcessCommand(*mapper, aArgs + 1);
1301         ExitNow();
1302     }
1303 
1304     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
1305     VerifyOrExit(command != nullptr);
1306 
1307     error = (this->*command->mHandler)(aArgs + 1);
1308 
1309 exit:
1310     return error;
1311 }
1312 
1313 } // namespace Cli
1314 } // namespace ot
1315