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