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