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