• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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