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 otOperationalDataset Dataset::sDataset;
49
Print(otOperationalDataset & aDataset)50 otError Dataset::Print(otOperationalDataset &aDataset)
51 {
52 if (aDataset.mComponents.mIsPendingTimestampPresent)
53 {
54 OutputLine("Pending Timestamp: %lu", aDataset.mPendingTimestamp.mSeconds);
55 }
56
57 if (aDataset.mComponents.mIsActiveTimestampPresent)
58 {
59 OutputLine("Active Timestamp: %lu", aDataset.mActiveTimestamp.mSeconds);
60 }
61
62 if (aDataset.mComponents.mIsChannelPresent)
63 {
64 OutputLine("Channel: %d", aDataset.mChannel);
65 }
66
67 if (aDataset.mComponents.mIsChannelMaskPresent)
68 {
69 OutputLine("Channel Mask: 0x%08x", aDataset.mChannelMask);
70 }
71
72 if (aDataset.mComponents.mIsDelayPresent)
73 {
74 OutputLine("Delay: %d", aDataset.mDelay);
75 }
76
77 if (aDataset.mComponents.mIsExtendedPanIdPresent)
78 {
79 OutputFormat("Ext PAN ID: ");
80 OutputBytesLine(aDataset.mExtendedPanId.m8);
81 }
82
83 if (aDataset.mComponents.mIsMeshLocalPrefixPresent)
84 {
85 OutputFormat("Mesh Local Prefix: ");
86 OutputIp6PrefixLine(aDataset.mMeshLocalPrefix);
87 }
88
89 if (aDataset.mComponents.mIsNetworkKeyPresent)
90 {
91 OutputFormat("Network Key: ");
92 OutputBytesLine(aDataset.mNetworkKey.m8);
93 }
94
95 if (aDataset.mComponents.mIsNetworkNamePresent)
96 {
97 OutputFormat("Network Name: ");
98 OutputLine("%s", aDataset.mNetworkName.m8);
99 }
100
101 if (aDataset.mComponents.mIsPanIdPresent)
102 {
103 OutputLine("PAN ID: 0x%04x", aDataset.mPanId);
104 }
105
106 if (aDataset.mComponents.mIsPskcPresent)
107 {
108 OutputFormat("PSKc: ");
109 OutputBytesLine(aDataset.mPskc.m8);
110 }
111
112 if (aDataset.mComponents.mIsSecurityPolicyPresent)
113 {
114 OutputFormat("Security Policy: ");
115 OutputSecurityPolicy(aDataset.mSecurityPolicy);
116 }
117
118 return OT_ERROR_NONE;
119 }
120
Process(Arg aArgs[])121 template <> otError Dataset::Process<Cmd("init")>(Arg aArgs[])
122 {
123 otError error = OT_ERROR_INVALID_ARGS;
124
125 if (aArgs[0] == "active")
126 {
127 error = otDatasetGetActive(GetInstancePtr(), &sDataset);
128 }
129 else if (aArgs[0] == "pending")
130 {
131 error = otDatasetGetPending(GetInstancePtr(), &sDataset);
132 }
133 #if OPENTHREAD_FTD
134 else if (aArgs[0] == "new")
135 {
136 error = otDatasetCreateNewNetwork(GetInstancePtr(), &sDataset);
137 }
138 #endif
139 else if (aArgs[0] == "tlvs")
140 {
141 otOperationalDatasetTlvs datasetTlvs;
142 uint16_t size = sizeof(datasetTlvs.mTlvs);
143
144 SuccessOrExit(error = aArgs[1].ParseAsHexString(size, datasetTlvs.mTlvs));
145 datasetTlvs.mLength = static_cast<uint8_t>(size);
146
147 SuccessOrExit(error = otDatasetParseTlvs(&datasetTlvs, &sDataset));
148 }
149
150 exit:
151 return error;
152 }
153
Process(Arg aArgs[])154 template <> otError Dataset::Process<Cmd("active")>(Arg aArgs[])
155 {
156 otError error = OT_ERROR_INVALID_ARGS;
157
158 if (aArgs[0].IsEmpty())
159 {
160 otOperationalDataset dataset;
161
162 SuccessOrExit(error = otDatasetGetActive(GetInstancePtr(), &dataset));
163 error = Print(dataset);
164 }
165 else if (aArgs[0] == "-x")
166 {
167 otOperationalDatasetTlvs dataset;
168
169 SuccessOrExit(error = otDatasetGetActiveTlvs(GetInstancePtr(), &dataset));
170 OutputBytesLine(dataset.mTlvs, dataset.mLength);
171 }
172
173 exit:
174 return error;
175 }
176
Process(Arg aArgs[])177 template <> otError Dataset::Process<Cmd("pending")>(Arg aArgs[])
178 {
179 otError error = OT_ERROR_INVALID_ARGS;
180
181 if (aArgs[0].IsEmpty())
182 {
183 otOperationalDataset dataset;
184
185 SuccessOrExit(error = otDatasetGetPending(GetInstancePtr(), &dataset));
186 error = Print(dataset);
187 }
188 else if (aArgs[0] == "-x")
189 {
190 otOperationalDatasetTlvs dataset;
191
192 SuccessOrExit(error = otDatasetGetPendingTlvs(GetInstancePtr(), &dataset));
193 OutputBytesLine(dataset.mTlvs, dataset.mLength);
194 }
195
196 exit:
197 return error;
198 }
199
Process(Arg aArgs[])200 template <> otError Dataset::Process<Cmd("activetimestamp")>(Arg aArgs[])
201 {
202 otError error = OT_ERROR_NONE;
203
204 if (aArgs[0].IsEmpty())
205 {
206 if (sDataset.mComponents.mIsActiveTimestampPresent)
207 {
208 OutputLine("%lu", sDataset.mActiveTimestamp.mSeconds);
209 }
210 }
211 else
212 {
213 SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mActiveTimestamp.mSeconds));
214 sDataset.mActiveTimestamp.mTicks = 0;
215 sDataset.mActiveTimestamp.mAuthoritative = false;
216 sDataset.mComponents.mIsActiveTimestampPresent = true;
217 }
218
219 exit:
220 return error;
221 }
222
Process(Arg aArgs[])223 template <> otError Dataset::Process<Cmd("channel")>(Arg aArgs[])
224 {
225 otError error = OT_ERROR_NONE;
226
227 if (aArgs[0].IsEmpty())
228 {
229 if (sDataset.mComponents.mIsChannelPresent)
230 {
231 OutputLine("%d", sDataset.mChannel);
232 }
233 }
234 else
235 {
236 SuccessOrExit(error = aArgs[0].ParseAsUint16(sDataset.mChannel));
237 sDataset.mComponents.mIsChannelPresent = true;
238 }
239
240 exit:
241 return error;
242 }
243
Process(Arg aArgs[])244 template <> otError Dataset::Process<Cmd("channelmask")>(Arg aArgs[])
245 {
246 otError error = OT_ERROR_NONE;
247
248 if (aArgs[0].IsEmpty())
249 {
250 if (sDataset.mComponents.mIsChannelMaskPresent)
251 {
252 OutputLine("0x%08x", sDataset.mChannelMask);
253 }
254 }
255 else
256 {
257 SuccessOrExit(error = aArgs[0].ParseAsUint32(sDataset.mChannelMask));
258 sDataset.mComponents.mIsChannelMaskPresent = true;
259 }
260
261 exit:
262 return error;
263 }
264
Process(Arg aArgs[])265 template <> otError Dataset::Process<Cmd("clear")>(Arg aArgs[])
266 {
267 OT_UNUSED_VARIABLE(aArgs);
268
269 memset(&sDataset, 0, sizeof(sDataset));
270 return OT_ERROR_NONE;
271 }
272
Process(Arg aArgs[])273 template <> otError Dataset::Process<Cmd("commit")>(Arg aArgs[])
274 {
275 otError error = OT_ERROR_INVALID_ARGS;
276
277 if (aArgs[0] == "active")
278 {
279 error = otDatasetSetActive(GetInstancePtr(), &sDataset);
280 }
281 else if (aArgs[0] == "pending")
282 {
283 error = otDatasetSetPending(GetInstancePtr(), &sDataset);
284 }
285
286 return error;
287 }
288
Process(Arg aArgs[])289 template <> otError Dataset::Process<Cmd("delay")>(Arg aArgs[])
290 {
291 otError error = OT_ERROR_NONE;
292
293 if (aArgs[0].IsEmpty())
294 {
295 if (sDataset.mComponents.mIsDelayPresent)
296 {
297 OutputLine("%d", sDataset.mDelay);
298 }
299 }
300 else
301 {
302 SuccessOrExit(error = aArgs[0].ParseAsUint32(sDataset.mDelay));
303 sDataset.mComponents.mIsDelayPresent = true;
304 }
305
306 exit:
307 return error;
308 }
309
Process(Arg aArgs[])310 template <> otError Dataset::Process<Cmd("extpanid")>(Arg aArgs[])
311 {
312 otError error = OT_ERROR_NONE;
313
314 if (aArgs[0].IsEmpty())
315 {
316 if (sDataset.mComponents.mIsExtendedPanIdPresent)
317 {
318 OutputBytesLine(sDataset.mExtendedPanId.m8);
319 }
320 }
321 else
322 {
323 SuccessOrExit(error = aArgs[0].ParseAsHexString(sDataset.mExtendedPanId.m8));
324 sDataset.mComponents.mIsExtendedPanIdPresent = true;
325 }
326
327 exit:
328 return error;
329 }
330
Process(Arg aArgs[])331 template <> otError Dataset::Process<Cmd("meshlocalprefix")>(Arg aArgs[])
332 {
333 otError error = OT_ERROR_NONE;
334
335 if (aArgs[0].IsEmpty())
336 {
337 if (sDataset.mComponents.mIsMeshLocalPrefixPresent)
338 {
339 OutputFormat("Mesh Local Prefix: ");
340 OutputIp6PrefixLine(sDataset.mMeshLocalPrefix);
341 }
342 }
343 else
344 {
345 otIp6Address prefix;
346
347 SuccessOrExit(error = aArgs[0].ParseAsIp6Address(prefix));
348
349 memcpy(sDataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(sDataset.mMeshLocalPrefix.m8));
350 sDataset.mComponents.mIsMeshLocalPrefixPresent = true;
351 }
352
353 exit:
354 return error;
355 }
356
Process(Arg aArgs[])357 template <> otError Dataset::Process<Cmd("networkkey")>(Arg aArgs[])
358 {
359 otError error = OT_ERROR_NONE;
360
361 if (aArgs[0].IsEmpty())
362 {
363 if (sDataset.mComponents.mIsNetworkKeyPresent)
364 {
365 OutputBytesLine(sDataset.mNetworkKey.m8);
366 }
367 }
368 else
369 {
370 SuccessOrExit(error = aArgs[0].ParseAsHexString(sDataset.mNetworkKey.m8));
371 sDataset.mComponents.mIsNetworkKeyPresent = true;
372 }
373
374 exit:
375 return error;
376 }
377
Process(Arg aArgs[])378 template <> otError Dataset::Process<Cmd("networkname")>(Arg aArgs[])
379 {
380 otError error = OT_ERROR_NONE;
381
382 if (aArgs[0].IsEmpty())
383 {
384 if (sDataset.mComponents.mIsNetworkNamePresent)
385 {
386 OutputLine("%s", sDataset.mNetworkName.m8);
387 }
388 }
389 else
390 {
391 SuccessOrExit(error = otNetworkNameFromString(&sDataset.mNetworkName, aArgs[0].GetCString()));
392 sDataset.mComponents.mIsNetworkNamePresent = true;
393 }
394
395 exit:
396 return error;
397 }
398
Process(Arg aArgs[])399 template <> otError Dataset::Process<Cmd("panid")>(Arg aArgs[])
400 {
401 otError error = OT_ERROR_NONE;
402
403 if (aArgs[0].IsEmpty())
404 {
405 if (sDataset.mComponents.mIsPanIdPresent)
406 {
407 OutputLine("0x%04x", sDataset.mPanId);
408 }
409 }
410 else
411 {
412 SuccessOrExit(error = aArgs[0].ParseAsUint16(sDataset.mPanId));
413 sDataset.mComponents.mIsPanIdPresent = true;
414 }
415
416 exit:
417 return error;
418 }
419
Process(Arg aArgs[])420 template <> otError Dataset::Process<Cmd("pendingtimestamp")>(Arg aArgs[])
421 {
422 otError error = OT_ERROR_NONE;
423
424 if (aArgs[0].IsEmpty())
425 {
426 if (sDataset.mComponents.mIsPendingTimestampPresent)
427 {
428 OutputLine("%lu", sDataset.mPendingTimestamp.mSeconds);
429 }
430 }
431 else
432 {
433 SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mPendingTimestamp.mSeconds));
434 sDataset.mPendingTimestamp.mTicks = 0;
435 sDataset.mPendingTimestamp.mAuthoritative = false;
436 sDataset.mComponents.mIsPendingTimestampPresent = true;
437 }
438
439 exit:
440 return error;
441 }
442
Process(Arg aArgs[])443 template <> otError Dataset::Process<Cmd("mgmtsetcommand")>(Arg aArgs[])
444 {
445 otError error = OT_ERROR_NONE;
446 otOperationalDataset dataset;
447 uint8_t tlvs[128];
448 uint8_t tlvsLength = 0;
449
450 memset(&dataset, 0, sizeof(dataset));
451
452 for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
453 {
454 if (*arg == "activetimestamp")
455 {
456 arg++;
457 SuccessOrExit(error = arg->ParseAsUint64(dataset.mActiveTimestamp.mSeconds));
458 dataset.mActiveTimestamp.mTicks = 0;
459 dataset.mActiveTimestamp.mAuthoritative = false;
460 dataset.mComponents.mIsActiveTimestampPresent = true;
461 }
462 else if (*arg == "pendingtimestamp")
463 {
464 arg++;
465 SuccessOrExit(error = arg->ParseAsUint64(dataset.mPendingTimestamp.mSeconds));
466 dataset.mPendingTimestamp.mTicks = 0;
467 dataset.mPendingTimestamp.mAuthoritative = false;
468 dataset.mComponents.mIsPendingTimestampPresent = true;
469 }
470 else if (*arg == "networkkey")
471 {
472 arg++;
473 dataset.mComponents.mIsNetworkKeyPresent = true;
474 SuccessOrExit(error = arg->ParseAsHexString(dataset.mNetworkKey.m8));
475 }
476 else if (*arg == "networkname")
477 {
478 arg++;
479 dataset.mComponents.mIsNetworkNamePresent = true;
480 VerifyOrExit(!arg->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
481 SuccessOrExit(error = otNetworkNameFromString(&dataset.mNetworkName, arg->GetCString()));
482 }
483 else if (*arg == "extpanid")
484 {
485 arg++;
486 dataset.mComponents.mIsExtendedPanIdPresent = true;
487 SuccessOrExit(error = arg->ParseAsHexString(dataset.mExtendedPanId.m8));
488 }
489 else if (*arg == "localprefix")
490 {
491 otIp6Address prefix;
492
493 arg++;
494 dataset.mComponents.mIsMeshLocalPrefixPresent = true;
495 SuccessOrExit(error = arg->ParseAsIp6Address(prefix));
496 memcpy(dataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(dataset.mMeshLocalPrefix.m8));
497 }
498 else if (*arg == "delaytimer")
499 {
500 arg++;
501 dataset.mComponents.mIsDelayPresent = true;
502 SuccessOrExit(error = arg->ParseAsUint32(dataset.mDelay));
503 }
504 else if (*arg == "panid")
505 {
506 arg++;
507 dataset.mComponents.mIsPanIdPresent = true;
508 SuccessOrExit(error = arg->ParseAsUint16(dataset.mPanId));
509 }
510 else if (*arg == "channel")
511 {
512 arg++;
513 dataset.mComponents.mIsChannelPresent = true;
514 SuccessOrExit(error = arg->ParseAsUint16(dataset.mChannel));
515 }
516 else if (*arg == "channelmask")
517 {
518 arg++;
519 dataset.mComponents.mIsChannelMaskPresent = true;
520 SuccessOrExit(error = arg->ParseAsUint32(dataset.mChannelMask));
521 }
522 else if (*arg == "securitypolicy")
523 {
524 arg++;
525 SuccessOrExit(error = ParseSecurityPolicy(dataset.mSecurityPolicy, arg));
526 dataset.mComponents.mIsSecurityPolicyPresent = true;
527 }
528 else if (*arg == "-x")
529 {
530 uint16_t length;
531
532 arg++;
533 length = sizeof(tlvs);
534 SuccessOrExit(error = arg->ParseAsHexString(length, tlvs));
535 tlvsLength = static_cast<uint8_t>(length);
536 }
537 else
538 {
539 ExitNow(error = OT_ERROR_INVALID_ARGS);
540 }
541 }
542
543 if (aArgs[0] == "active")
544 {
545 error = otDatasetSendMgmtActiveSet(GetInstancePtr(), &dataset, tlvs, tlvsLength, /* aCallback */ nullptr,
546 /* aContext */ nullptr);
547 }
548 else if (aArgs[0] == "pending")
549 {
550 error = otDatasetSendMgmtPendingSet(GetInstancePtr(), &dataset, tlvs, tlvsLength, /* aCallback */ nullptr,
551 /* aContext */ nullptr);
552 }
553 else
554 {
555 error = OT_ERROR_INVALID_ARGS;
556 }
557
558 exit:
559 return error;
560 }
561
Process(Arg aArgs[])562 template <> otError Dataset::Process<Cmd("mgmtgetcommand")>(Arg aArgs[])
563 {
564 otError error = OT_ERROR_NONE;
565 otOperationalDatasetComponents datasetComponents;
566 uint8_t tlvs[32];
567 uint8_t tlvsLength = 0;
568 bool destAddrSpecified = false;
569 otIp6Address address;
570
571 memset(&datasetComponents, 0, sizeof(datasetComponents));
572
573 for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
574 {
575 if (*arg == "activetimestamp")
576 {
577 datasetComponents.mIsActiveTimestampPresent = true;
578 }
579 else if (*arg == "pendingtimestamp")
580 {
581 datasetComponents.mIsPendingTimestampPresent = true;
582 }
583 else if (*arg == "networkkey")
584 {
585 datasetComponents.mIsNetworkKeyPresent = true;
586 }
587 else if (*arg == "networkname")
588 {
589 datasetComponents.mIsNetworkNamePresent = true;
590 }
591 else if (*arg == "extpanid")
592 {
593 datasetComponents.mIsExtendedPanIdPresent = true;
594 }
595 else if (*arg == "localprefix")
596 {
597 datasetComponents.mIsMeshLocalPrefixPresent = true;
598 }
599 else if (*arg == "delaytimer")
600 {
601 datasetComponents.mIsDelayPresent = true;
602 }
603 else if (*arg == "panid")
604 {
605 datasetComponents.mIsPanIdPresent = true;
606 }
607 else if (*arg == "channel")
608 {
609 datasetComponents.mIsChannelPresent = true;
610 }
611 else if (*arg == "securitypolicy")
612 {
613 datasetComponents.mIsSecurityPolicyPresent = true;
614 }
615 else if (*arg == "-x")
616 {
617 uint16_t length;
618
619 arg++;
620 length = sizeof(tlvs);
621 SuccessOrExit(error = arg->ParseAsHexString(length, tlvs));
622 tlvsLength = static_cast<uint8_t>(length);
623 }
624 else if (*arg == "address")
625 {
626 arg++;
627 SuccessOrExit(error = arg->ParseAsIp6Address(address));
628 destAddrSpecified = true;
629 }
630 else
631 {
632 ExitNow(error = OT_ERROR_INVALID_ARGS);
633 }
634 }
635
636 if (aArgs[0] == "active")
637 {
638 error = otDatasetSendMgmtActiveGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength,
639 destAddrSpecified ? &address : nullptr);
640 }
641 else if (aArgs[0] == "pending")
642 {
643 error = otDatasetSendMgmtPendingGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength,
644 destAddrSpecified ? &address : nullptr);
645 }
646 else
647 {
648 error = OT_ERROR_INVALID_ARGS;
649 }
650
651 exit:
652 return error;
653 }
654
Process(Arg aArgs[])655 template <> otError Dataset::Process<Cmd("pskc")>(Arg aArgs[])
656 {
657 otError error = OT_ERROR_NONE;
658
659 if (aArgs[0].IsEmpty())
660 {
661 // sDataset holds the key as a literal string, we don't
662 // need to export it from PSA ITS.
663 if (sDataset.mComponents.mIsPskcPresent)
664 {
665 OutputBytesLine(sDataset.mPskc.m8);
666 }
667 }
668 else
669 {
670 #if OPENTHREAD_FTD
671 if (aArgs[0] == "-p")
672 {
673 VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
674
675 SuccessOrExit(
676 error = otDatasetGeneratePskc(
677 aArgs[1].GetCString(),
678 (sDataset.mComponents.mIsNetworkNamePresent
679 ? &sDataset.mNetworkName
680 : reinterpret_cast<const otNetworkName *>(otThreadGetNetworkName(GetInstancePtr()))),
681 (sDataset.mComponents.mIsExtendedPanIdPresent ? &sDataset.mExtendedPanId
682 : otThreadGetExtendedPanId(GetInstancePtr())),
683 &sDataset.mPskc));
684 }
685 else
686 #endif
687 {
688 SuccessOrExit(error = aArgs[0].ParseAsHexString(sDataset.mPskc.m8));
689 }
690
691 sDataset.mComponents.mIsPskcPresent = true;
692 }
693
694 exit:
695 return error;
696 }
697
OutputSecurityPolicy(const otSecurityPolicy & aSecurityPolicy)698 void Dataset::OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy)
699 {
700 OutputFormat("%d ", aSecurityPolicy.mRotationTime);
701
702 if (aSecurityPolicy.mObtainNetworkKeyEnabled)
703 {
704 OutputFormat("o");
705 }
706
707 if (aSecurityPolicy.mNativeCommissioningEnabled)
708 {
709 OutputFormat("n");
710 }
711
712 if (aSecurityPolicy.mRoutersEnabled)
713 {
714 OutputFormat("r");
715 }
716
717 if (aSecurityPolicy.mExternalCommissioningEnabled)
718 {
719 OutputFormat("c");
720 }
721
722 if (aSecurityPolicy.mCommercialCommissioningEnabled)
723 {
724 OutputFormat("C");
725 }
726
727 if (aSecurityPolicy.mAutonomousEnrollmentEnabled)
728 {
729 OutputFormat("e");
730 }
731
732 if (aSecurityPolicy.mNetworkKeyProvisioningEnabled)
733 {
734 OutputFormat("p");
735 }
736
737 if (aSecurityPolicy.mNonCcmRoutersEnabled)
738 {
739 OutputFormat("R");
740 }
741
742 OutputLine("");
743 }
744
ParseSecurityPolicy(otSecurityPolicy & aSecurityPolicy,Arg * & aArgs)745 otError Dataset::ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, Arg *&aArgs)
746 {
747 otError error;
748 otSecurityPolicy policy;
749
750 memset(&policy, 0, sizeof(policy));
751
752 SuccessOrExit(error = aArgs->ParseAsUint16(policy.mRotationTime));
753 aArgs++;
754
755 VerifyOrExit(!aArgs->IsEmpty());
756
757 for (const char *flag = aArgs->GetCString(); *flag != '\0'; flag++)
758 {
759 switch (*flag)
760 {
761 case 'o':
762 policy.mObtainNetworkKeyEnabled = true;
763 break;
764
765 case 'n':
766 policy.mNativeCommissioningEnabled = true;
767 break;
768
769 case 'r':
770 policy.mRoutersEnabled = true;
771 break;
772
773 case 'c':
774 policy.mExternalCommissioningEnabled = true;
775 break;
776
777 case 'C':
778 policy.mCommercialCommissioningEnabled = true;
779 break;
780
781 case 'e':
782 policy.mAutonomousEnrollmentEnabled = true;
783 break;
784
785 case 'p':
786 policy.mNetworkKeyProvisioningEnabled = true;
787 break;
788
789 case 'R':
790 policy.mNonCcmRoutersEnabled = true;
791 break;
792
793 default:
794 ExitNow(error = OT_ERROR_INVALID_ARGS);
795 }
796 }
797
798 aArgs++;
799
800 exit:
801 if (error == OT_ERROR_NONE)
802 {
803 aSecurityPolicy = policy;
804 }
805
806 return error;
807 }
808
Process(Arg aArgs[])809 template <> otError Dataset::Process<Cmd("securitypolicy")>(Arg aArgs[])
810 {
811 otError error = OT_ERROR_NONE;
812
813 if (aArgs[0].IsEmpty())
814 {
815 if (sDataset.mComponents.mIsSecurityPolicyPresent)
816 {
817 OutputSecurityPolicy(sDataset.mSecurityPolicy);
818 }
819 }
820 else
821 {
822 Arg *arg = &aArgs[0];
823
824 SuccessOrExit(error = ParseSecurityPolicy(sDataset.mSecurityPolicy, arg));
825 sDataset.mComponents.mIsSecurityPolicyPresent = true;
826 }
827
828 exit:
829 return error;
830 }
831
Process(Arg aArgs[])832 template <> otError Dataset::Process<Cmd("set")>(Arg aArgs[])
833 {
834 otError error = OT_ERROR_NONE;
835 MeshCoP::Dataset::Type datasetType;
836
837 if (aArgs[0] == "active")
838 {
839 datasetType = MeshCoP::Dataset::Type::kActive;
840 }
841 else if (aArgs[0] == "pending")
842 {
843 datasetType = MeshCoP::Dataset::Type::kPending;
844 }
845 else
846 {
847 ExitNow(error = OT_ERROR_INVALID_ARGS);
848 }
849
850 {
851 MeshCoP::Dataset dataset;
852 MeshCoP::Dataset::Info datasetInfo;
853 uint16_t tlvsLength = MeshCoP::Dataset::kMaxSize;
854
855 SuccessOrExit(error = aArgs[1].ParseAsHexString(tlvsLength, dataset.GetBytes()));
856 dataset.SetSize(tlvsLength);
857 VerifyOrExit(dataset.IsValid(), error = OT_ERROR_INVALID_ARGS);
858 dataset.ConvertTo(datasetInfo);
859
860 switch (datasetType)
861 {
862 case MeshCoP::Dataset::Type::kActive:
863 SuccessOrExit(error = otDatasetSetActive(GetInstancePtr(), &datasetInfo));
864 break;
865 case MeshCoP::Dataset::Type::kPending:
866 SuccessOrExit(error = otDatasetSetPending(GetInstancePtr(), &datasetInfo));
867 break;
868 }
869 }
870
871 exit:
872 return error;
873 }
874
875 #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
876
Process(Arg aArgs[])877 template <> otError Dataset::Process<Cmd("updater")>(Arg aArgs[])
878 {
879 otError error = OT_ERROR_NONE;
880
881 if (aArgs[0].IsEmpty())
882 {
883 OutputEnabledDisabledStatus(otDatasetUpdaterIsUpdateOngoing(GetInstancePtr()));
884 }
885 else if (aArgs[0] == "start")
886 {
887 error = otDatasetUpdaterRequestUpdate(GetInstancePtr(), &sDataset, &Dataset::HandleDatasetUpdater, this);
888 }
889 else if (aArgs[0] == "cancel")
890 {
891 otDatasetUpdaterCancelUpdate(GetInstancePtr());
892 }
893 else
894 {
895 error = OT_ERROR_INVALID_ARGS;
896 }
897
898 return error;
899 }
900
HandleDatasetUpdater(otError aError,void * aContext)901 void Dataset::HandleDatasetUpdater(otError aError, void *aContext)
902 {
903 static_cast<Dataset *>(aContext)->HandleDatasetUpdater(aError);
904 }
905
HandleDatasetUpdater(otError aError)906 void Dataset::HandleDatasetUpdater(otError aError)
907 {
908 OutputLine("Dataset update complete: %s", otThreadErrorToString(aError));
909 }
910
911 #endif // OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
912
Process(Arg aArgs[])913 otError Dataset::Process(Arg aArgs[])
914 {
915 #define CmdEntry(aCommandString) \
916 { \
917 aCommandString, &Dataset::Process<Cmd(aCommandString)> \
918 }
919
920 static constexpr Command kCommands[] = {
921 CmdEntry("active"),
922 CmdEntry("activetimestamp"),
923 CmdEntry("channel"),
924 CmdEntry("channelmask"),
925 CmdEntry("clear"),
926 CmdEntry("commit"),
927 CmdEntry("delay"),
928 CmdEntry("extpanid"),
929 CmdEntry("init"),
930 CmdEntry("meshlocalprefix"),
931 CmdEntry("mgmtgetcommand"),
932 CmdEntry("mgmtsetcommand"),
933 CmdEntry("networkkey"),
934 CmdEntry("networkname"),
935 CmdEntry("panid"),
936 CmdEntry("pending"),
937 CmdEntry("pendingtimestamp"),
938 CmdEntry("pskc"),
939 CmdEntry("securitypolicy"),
940 CmdEntry("set"),
941 #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
942 CmdEntry("updater"),
943 #endif
944 };
945
946 #undef CmdEntry
947
948 static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
949
950 otError error = OT_ERROR_INVALID_COMMAND;
951 const Command *command;
952
953 if (aArgs[0].IsEmpty())
954 {
955 ExitNow(error = Print(sDataset));
956 }
957
958 if (aArgs[0] == "help")
959 {
960 OutputCommandTable(kCommands);
961 ExitNow(error = OT_ERROR_NONE);
962 }
963
964 command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
965 VerifyOrExit(command != nullptr);
966
967 error = (this->*command->mHandler)(aArgs + 1);
968
969 exit:
970 return error;
971 }
972
973 } // namespace Cli
974 } // namespace ot
975