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.hpp"
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include <openthread/diag.h>
41 #include <openthread/dns.h>
42 #include <openthread/icmp6.h>
43 #include <openthread/link.h>
44 #include <openthread/logging.h>
45 #include <openthread/ncp.h>
46 #include <openthread/thread.h>
47 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
48 #include <openthread/network_time.h>
49 #endif
50 #if OPENTHREAD_FTD
51 #include <openthread/dataset_ftd.h>
52 #include <openthread/thread_ftd.h>
53 #endif
54 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
55 #include <openthread/border_router.h>
56 #endif
57 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
58 #include <openthread/server.h>
59 #endif
60 #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
61 #include <openthread/child_supervision.h>
62 #endif
63 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
64 #include <openthread/platform/misc.h>
65 #endif
66 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
67 #include <openthread/backbone_router.h>
68 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
69 #include <openthread/backbone_router_ftd.h>
70 #endif
71 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
72 #include <openthread/link_metrics.h>
73 #endif
74 #endif
75 #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
76 #include <openthread/channel_manager.h>
77 #endif
78 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
79 #include <openthread/channel_monitor.h>
80 #endif
81 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX
82 #include <openthread/platform/debug_uart.h>
83 #endif
84 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
85 #include <openthread/trel.h>
86 #endif
87
88 #include "common/new.hpp"
89 #include "common/string.hpp"
90 #include "mac/channel_mask.hpp"
91
92 namespace ot {
93 namespace Cli {
94
95 Interpreter *Interpreter::sInterpreter = nullptr;
96 static OT_DEFINE_ALIGNED_VAR(sInterpreterRaw, sizeof(Interpreter), uint64_t);
97
Interpreter(Instance * aInstance,otCliOutputCallback aCallback,void * aContext)98 Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, void *aContext)
99 : Output(aInstance, aCallback, aContext)
100 , mUserCommands(nullptr)
101 , mUserCommandsLength(0)
102 , mCommandIsPending(false)
103 , mTimer(*aInstance, HandleTimer, this)
104 #if OPENTHREAD_FTD || OPENTHREAD_MTD
105 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
106 , mSntpQueryingInProgress(false)
107 #endif
108 , mDataset(*this)
109 , mNetworkData(*this)
110 , mUdp(*this)
111 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
112 , mTcp(*this)
113 #endif
114 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
115 , mCoap(*this)
116 #endif
117 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
118 , mCoapSecure(*this)
119 #endif
120 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
121 , mCommissioner(*this)
122 #endif
123 #if OPENTHREAD_CONFIG_JOINER_ENABLE
124 , mJoiner(*this)
125 #endif
126 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
127 , mSrpClient(*this)
128 #endif
129 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
130 , mSrpServer(*this)
131 #endif
132 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
133 , mHistory(*this)
134 #endif
135 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
136 , mLocateInProgress(false)
137 #endif
138 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
139 {
140 #if OPENTHREAD_FTD
141 otThreadSetDiscoveryRequestCallback(GetInstancePtr(), &Interpreter::HandleDiscoveryRequest, this);
142 #endif
143
144 OutputPrompt();
145 }
146
OutputResult(otError aError)147 void Interpreter::OutputResult(otError aError)
148 {
149 OT_ASSERT(mCommandIsPending);
150
151 VerifyOrExit(aError != OT_ERROR_PENDING);
152
153 if (aError == OT_ERROR_NONE)
154 {
155 OutputLine("Done");
156 }
157 else
158 {
159 OutputLine("Error %d: %s", aError, otThreadErrorToString(aError));
160 }
161
162 mCommandIsPending = false;
163 mTimer.Stop();
164 OutputPrompt();
165
166 exit:
167 return;
168 }
169
LinkModeToString(const otLinkModeConfig & aLinkMode,char (& aStringBuffer)[kLinkModeStringSize])170 const char *Interpreter::LinkModeToString(const otLinkModeConfig &aLinkMode, char (&aStringBuffer)[kLinkModeStringSize])
171 {
172 char *flagsPtr = &aStringBuffer[0];
173
174 if (aLinkMode.mRxOnWhenIdle)
175 {
176 *flagsPtr++ = 'r';
177 }
178
179 if (aLinkMode.mDeviceType)
180 {
181 *flagsPtr++ = 'd';
182 }
183
184 if (aLinkMode.mNetworkData)
185 {
186 *flagsPtr++ = 'n';
187 }
188
189 if (flagsPtr == &aStringBuffer[0])
190 {
191 *flagsPtr++ = '-';
192 }
193
194 *flagsPtr = '\0';
195
196 return aStringBuffer;
197 }
198
199 #if OPENTHREAD_CONFIG_DIAG_ENABLE
Process(Arg aArgs[])200 template <> otError Interpreter::Process<Cmd("diag")>(Arg aArgs[])
201 {
202 otError error;
203 char * args[kMaxArgs];
204 char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE];
205
206 // all diagnostics related features are processed within diagnostics module
207 Arg::CopyArgsToStringArray(aArgs, args);
208
209 error = otDiagProcessCmd(GetInstancePtr(), Arg::GetArgsLength(aArgs), args, output, sizeof(output));
210
211 OutputFormat("%s", output);
212
213 return error;
214 }
215 #endif
216
Process(Arg aArgs[])217 template <> otError Interpreter::Process<Cmd("version")>(Arg aArgs[])
218 {
219 otError error = OT_ERROR_NONE;
220
221 if (aArgs[0].IsEmpty())
222 {
223 OutputLine("%s", otGetVersionString());
224 }
225 else if (aArgs[0] == "api")
226 {
227 OutputLine("%d", OPENTHREAD_API_VERSION);
228 }
229 else
230 {
231 error = OT_ERROR_INVALID_COMMAND;
232 }
233
234 return error;
235 }
236
Process(Arg aArgs[])237 template <> otError Interpreter::Process<Cmd("reset")>(Arg aArgs[])
238 {
239 OT_UNUSED_VARIABLE(aArgs);
240
241 otInstanceReset(GetInstancePtr());
242
243 return OT_ERROR_NONE;
244 }
245
ProcessLine(char * aBuf)246 void Interpreter::ProcessLine(char *aBuf)
247 {
248 Arg args[kMaxArgs + 1];
249 otError error = OT_ERROR_NONE;
250
251 OT_ASSERT(aBuf != nullptr);
252
253 // Ignore the command if another command is pending.
254 VerifyOrExit(!mCommandIsPending, args[0].Clear());
255 mCommandIsPending = true;
256
257 VerifyOrExit(StringLength(aBuf, kMaxLineLength) <= kMaxLineLength - 1, error = OT_ERROR_PARSE);
258
259 SuccessOrExit(error = Utils::CmdLineParser::ParseCmd(aBuf, args, kMaxArgs));
260 VerifyOrExit(!args[0].IsEmpty(), mCommandIsPending = false);
261
262 LogInput(args);
263
264 #if OPENTHREAD_CONFIG_DIAG_ENABLE
265 if (otDiagIsEnabled(GetInstancePtr()) && (args[0] != "diag") && (args[0] != "factoryreset"))
266 {
267 OutputLine("under diagnostics mode, execute 'diag stop' before running any other commands.");
268 ExitNow(error = OT_ERROR_INVALID_STATE);
269 }
270 #endif
271
272 error = ProcessCommand(args);
273
274 exit:
275 if ((error != OT_ERROR_NONE) || !args[0].IsEmpty())
276 {
277 OutputResult(error);
278 }
279 else if (!mCommandIsPending)
280 {
281 OutputPrompt();
282 }
283 }
284
ProcessUserCommands(Arg aArgs[])285 otError Interpreter::ProcessUserCommands(Arg aArgs[])
286 {
287 otError error = OT_ERROR_INVALID_COMMAND;
288
289 for (uint8_t i = 0; i < mUserCommandsLength; i++)
290 {
291 if (aArgs[0] == mUserCommands[i].mName)
292 {
293 char *args[kMaxArgs];
294
295 Arg::CopyArgsToStringArray(aArgs, args);
296 mUserCommands[i].mCommand(mUserCommandsContext, Arg::GetArgsLength(aArgs) - 1, args + 1);
297 error = OT_ERROR_NONE;
298 break;
299 }
300 }
301
302 return error;
303 }
304
SetUserCommands(const otCliCommand * aCommands,uint8_t aLength,void * aContext)305 void Interpreter::SetUserCommands(const otCliCommand *aCommands, uint8_t aLength, void *aContext)
306 {
307 mUserCommands = aCommands;
308 mUserCommandsLength = aLength;
309 mUserCommandsContext = aContext;
310 }
311
312 #if OPENTHREAD_FTD || OPENTHREAD_MTD
ParseEnableOrDisable(const Arg & aArg,bool & aEnable)313 otError Interpreter::ParseEnableOrDisable(const Arg &aArg, bool &aEnable)
314 {
315 otError error = OT_ERROR_NONE;
316
317 if (aArg == "enable")
318 {
319 aEnable = true;
320 }
321 else if (aArg == "disable")
322 {
323 aEnable = false;
324 }
325 else
326 {
327 error = OT_ERROR_INVALID_COMMAND;
328 }
329
330 return error;
331 }
332
ParseJoinerDiscerner(Arg & aArg,otJoinerDiscerner & aDiscerner)333 otError Interpreter::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner)
334 {
335 otError error;
336 char * separator;
337
338 VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
339
340 separator = strstr(aArg.GetCString(), "/");
341
342 VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND);
343
344 SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(separator + 1, aDiscerner.mLength));
345 VerifyOrExit(aDiscerner.mLength > 0 && aDiscerner.mLength <= 64, error = OT_ERROR_INVALID_ARGS);
346 *separator = '\0';
347 error = aArg.ParseAsUint64(aDiscerner.mValue);
348
349 exit:
350 return error;
351 }
352
353 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
354
ParsePingInterval(const Arg & aArg,uint32_t & aInterval)355 otError Interpreter::ParsePingInterval(const Arg &aArg, uint32_t &aInterval)
356 {
357 otError error = OT_ERROR_NONE;
358 const char * string = aArg.GetCString();
359 const uint32_t msFactor = 1000;
360 uint32_t factor = msFactor;
361
362 aInterval = 0;
363
364 while (*string)
365 {
366 if ('0' <= *string && *string <= '9')
367 {
368 // In the case of seconds, change the base of already calculated value.
369 if (factor == msFactor)
370 {
371 aInterval *= 10;
372 }
373
374 aInterval += static_cast<uint32_t>(*string - '0') * factor;
375
376 // In the case of milliseconds, change the multiplier factor.
377 if (factor != msFactor)
378 {
379 factor /= 10;
380 }
381 }
382 else if (*string == '.')
383 {
384 // Accept only one dot character.
385 VerifyOrExit(factor == msFactor, error = OT_ERROR_INVALID_ARGS);
386
387 // Start analyzing hundreds of milliseconds.
388 factor /= 10;
389 }
390 else
391 {
392 ExitNow(error = OT_ERROR_INVALID_ARGS);
393 }
394
395 string++;
396 }
397
398 exit:
399 return error;
400 }
401
402 #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
403
ParsePreference(const Arg & aArg,otRoutePreference & aPreference)404 otError Interpreter::ParsePreference(const Arg &aArg, otRoutePreference &aPreference)
405 {
406 otError error = OT_ERROR_NONE;
407
408 if (aArg == "high")
409 {
410 aPreference = OT_ROUTE_PREFERENCE_HIGH;
411 }
412 else if (aArg == "med")
413 {
414 aPreference = OT_ROUTE_PREFERENCE_MED;
415 }
416 else if (aArg == "low")
417 {
418 aPreference = OT_ROUTE_PREFERENCE_LOW;
419 }
420 else
421 {
422 error = OT_ERROR_INVALID_ARGS;
423 }
424
425 return error;
426 }
427
PreferenceToString(signed int aPreference)428 const char *Interpreter::PreferenceToString(signed int aPreference)
429 {
430 const char *str = "";
431
432 switch (aPreference)
433 {
434 case OT_ROUTE_PREFERENCE_LOW:
435 str = "low";
436 break;
437
438 case OT_ROUTE_PREFERENCE_MED:
439 str = "med";
440 break;
441
442 case OT_ROUTE_PREFERENCE_HIGH:
443 str = "high";
444 break;
445
446 default:
447 break;
448 }
449
450 return str;
451 }
452
453 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Process(Arg aArgs[])454 template <> otError Interpreter::Process<Cmd("history")>(Arg aArgs[])
455 {
456 return mHistory.Process(aArgs);
457 }
458 #endif
459
460 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
Process(Arg aArgs[])461 template <> otError Interpreter::Process<Cmd("ba")>(Arg aArgs[])
462 {
463 otError error = OT_ERROR_NONE;
464
465 /**
466 * @cli ba port
467 * @code
468 * ba port
469 * 49153
470 * Done
471 * @endcode
472 * @par api_copy
473 * #otBorderAgentGetUdpPort
474 */
475 if (aArgs[0] == "port")
476 {
477 OutputLine("%hu", otBorderAgentGetUdpPort(GetInstancePtr()));
478 }
479 /**
480 * @cli ba state
481 * @code
482 * ba state
483 * Started
484 * Done
485 * @endcode
486 * @par api_copy
487 * #otBorderAgentGetState
488 */
489 else if (aArgs[0] == "state")
490 {
491 static const char *const kStateStrings[] = {
492 "Stopped" // (0) OT_BORDER_AGENT_STATE_STOPPED
493 "Started", // (1) OT_BORDER_AGENT_STATE_STARTED
494 "Active", // (2) OT_BORDER_AGENT_STATE_ACTIVE
495 };
496
497 static_assert(0 == OT_BORDER_AGENT_STATE_STOPPED, "OT_BORDER_AGENT_STATE_STOPPED value is incorrect");
498 static_assert(1 == OT_BORDER_AGENT_STATE_STARTED, "OT_BORDER_AGENT_STATE_STARTED value is incorrect");
499 static_assert(2 == OT_BORDER_AGENT_STATE_ACTIVE, "OT_BORDER_AGENT_STATE_ACTIVE value is incorrect");
500
501 OutputLine("%s", Stringify(otBorderAgentGetState(GetInstancePtr()), kStateStrings));
502 }
503 else
504 {
505 error = OT_ERROR_INVALID_COMMAND;
506 }
507
508 return error;
509 }
510 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
511
512 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
Process(Arg aArgs[])513 template <> otError Interpreter::Process<Cmd("br")>(Arg aArgs[])
514 {
515 otError error = OT_ERROR_NONE;
516 bool enable;
517
518 /**
519 * @cli br (enable,disable)
520 * @code
521 * br enable
522 * Done
523 * @endcode
524 * @code
525 * br disable
526 * Done
527 * @endcode
528 * @par api_copy
529 * #otBorderRoutingSetEnabled
530 */
531 if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
532 {
533 SuccessOrExit(error = otBorderRoutingSetEnabled(GetInstancePtr(), enable));
534 }
535 /**
536 * @cli br omrprefix
537 * @code
538 * br omrprefix
539 * fdfc:1ff5:1512:5622::/64
540 * Done
541 * @endcode
542 * @par api_copy
543 * #otBorderRoutingGetOmrPrefix
544 */
545 else if (aArgs[0] == "omrprefix")
546 {
547 otIp6Prefix omrPrefix;
548
549 SuccessOrExit(error = otBorderRoutingGetOmrPrefix(GetInstancePtr(), &omrPrefix));
550 OutputIp6PrefixLine(omrPrefix);
551 }
552 /**
553 * @cli br favoredomrprefix
554 * @code
555 * br favoredomrprefix
556 * fdfc:1ff5:1512:5622::/64 prf:low
557 * Done
558 * @endcode
559 * @par api_copy
560 * #otBorderRoutingGetFavoredOmrPrefix
561 */
562 else if (aArgs[0] == "favoredomrprefix")
563 {
564 otIp6Prefix prefix;
565 otRoutePreference preference;
566
567 SuccessOrExit(error = otBorderRoutingGetFavoredOmrPrefix(GetInstancePtr(), &prefix, &preference));
568 OutputIp6Prefix(prefix);
569 OutputLine(" prf:%s", PreferenceToString(preference));
570 }
571 /**
572 * @cli br onlinkprefix
573 * @code
574 * br onlinkprefix
575 * fd41:2650:a6f5:0::/64
576 * Done
577 * @endcode
578 * @par api_copy
579 * #otBorderRoutingGetOnLinkPrefix
580 */
581 else if (aArgs[0] == "onlinkprefix")
582 {
583 otIp6Prefix onLinkPrefix;
584
585 SuccessOrExit(error = otBorderRoutingGetOnLinkPrefix(GetInstancePtr(), &onLinkPrefix));
586 OutputIp6PrefixLine(onLinkPrefix);
587 }
588 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
589 /**
590 * @cli br nat64prefix
591 * @code
592 * br nat64prefix
593 * fd14:1078:b3d5:b0b0:0:0::/96
594 * Done
595 * @endcode
596 * @par api_copy
597 * #otBorderRoutingGetNat64Prefix
598 */
599 else if (aArgs[0] == "nat64prefix")
600 {
601 otIp6Prefix nat64Prefix;
602
603 SuccessOrExit(error = otBorderRoutingGetNat64Prefix(GetInstancePtr(), &nat64Prefix));
604 OutputIp6PrefixLine(nat64Prefix);
605 }
606 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
607 /**
608 * @cli br rioprf (high,med,low)
609 * @code
610 * br rioprf
611 * med
612 * Done
613 * @endcode
614 * @code
615 * br rioprf low
616 * Done
617 * @endcode
618 * @cparam br rioprf [@ca{high}|@ca{med}|@ca{low}]
619 * @par api_copy
620 * #otBorderRoutingSetRouteInfoOptionPreference
621 *
622 */
623 else if (aArgs[0] == "rioprf")
624 {
625 if (aArgs[1].IsEmpty())
626 {
627 OutputLine("%s", PreferenceToString(otBorderRoutingGetRouteInfoOptionPreference(GetInstancePtr())));
628 }
629 else
630 {
631 otRoutePreference preference;
632
633 SuccessOrExit(error = ParsePreference(aArgs[1], preference));
634 otBorderRoutingSetRouteInfoOptionPreference(GetInstancePtr(), preference);
635 }
636 }
637 /**
638 * @cli br prefixtable
639 * @code
640 * br prefixtable
641 * prefix:fd00:1234:5678:0::/64, on-link:no, ms-since-rx:29526, lifetime:1800, route-prf:med,
642 * router:ff02:0:0:0:0:0:0:1
643 * prefix:1200:abba:baba:0::/64, on-link:yes, ms-since-rx:29527, lifetime:1800, preferred:1800,
644 * router:ff02:0:0:0:0:0:0:1
645 * Done
646 * @endcode
647 * @par api_copy
648 * #otBorderRoutingGetNextPrefixTableEntry
649 *
650 */
651 else if (aArgs[0] == "prefixtable")
652 {
653 otBorderRoutingPrefixTableIterator iterator;
654 otBorderRoutingPrefixTableEntry entry;
655
656 otBorderRoutingPrefixTableInitIterator(GetInstancePtr(), &iterator);
657
658 while (otBorderRoutingGetNextPrefixTableEntry(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
659 {
660 char string[OT_IP6_PREFIX_STRING_SIZE];
661
662 otIp6PrefixToString(&entry.mPrefix, string, sizeof(string));
663 OutputFormat("prefix:%s, on-link:%s, ms-since-rx:%u, lifetime:%u, ", string, entry.mIsOnLink ? "yes" : "no",
664 entry.mMsecSinceLastUpdate, entry.mValidLifetime);
665
666 if (entry.mIsOnLink)
667 {
668 OutputFormat("preferred:%u, ", entry.mPreferredLifetime);
669 }
670 else
671 {
672 OutputFormat("route-prf:%s, ", PreferenceToString(entry.mRoutePreference));
673 }
674
675 otIp6AddressToString(&entry.mRouterAddress, string, sizeof(string));
676 OutputLine("router:%s", string);
677 }
678 }
679 else
680 {
681 error = OT_ERROR_INVALID_COMMAND;
682 }
683
684 exit:
685 return error;
686 }
687 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
688
689 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
Process(Arg aArgs[])690 template <> otError Interpreter::Process<Cmd("bbr")>(Arg aArgs[])
691 {
692 otError error = OT_ERROR_INVALID_COMMAND;
693 otBackboneRouterConfig config;
694
695 if (aArgs[0].IsEmpty())
696 {
697 if (otBackboneRouterGetPrimary(GetInstancePtr(), &config) == OT_ERROR_NONE)
698 {
699 OutputLine("BBR Primary:");
700 OutputLine("server16: 0x%04X", config.mServer16);
701 OutputLine("seqno: %d", config.mSequenceNumber);
702 OutputLine("delay: %d secs", config.mReregistrationDelay);
703 OutputLine("timeout: %d secs", config.mMlrTimeout);
704 }
705 else
706 {
707 OutputLine("BBR Primary: None");
708 }
709
710 error = OT_ERROR_NONE;
711 }
712 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
713 else
714 {
715 if (aArgs[0] == "mgmt")
716 {
717 if (aArgs[1].IsEmpty())
718 {
719 ExitNow(error = OT_ERROR_INVALID_COMMAND);
720 }
721 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
722 else if (aArgs[1] == "dua")
723 {
724 uint8_t status;
725 otIp6InterfaceIdentifier *mlIid = nullptr;
726 otIp6InterfaceIdentifier iid;
727
728 SuccessOrExit(error = aArgs[2].ParseAsUint8(status));
729
730 if (!aArgs[3].IsEmpty())
731 {
732 SuccessOrExit(error = aArgs[3].ParseAsHexString(iid.mFields.m8));
733 mlIid = &iid;
734 VerifyOrExit(aArgs[4].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
735 }
736
737 otBackboneRouterConfigNextDuaRegistrationResponse(GetInstancePtr(), mlIid, status);
738 ExitNow();
739 }
740 #endif
741 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
742 else if (aArgs[1] == "mlr")
743 {
744 error = ProcessBackboneRouterMgmtMlr(aArgs + 2);
745 ExitNow();
746 }
747 #endif
748 }
749 SuccessOrExit(error = ProcessBackboneRouterLocal(aArgs));
750 }
751
752 exit:
753 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
754 return error;
755 }
756
757 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
758
759 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
ProcessBackboneRouterMgmtMlr(Arg aArgs[])760 otError Interpreter::ProcessBackboneRouterMgmtMlr(Arg aArgs[])
761 {
762 otError error = OT_ERROR_INVALID_COMMAND;
763
764 if (aArgs[0] == "listener")
765 {
766 if (aArgs[1].IsEmpty())
767 {
768 PrintMulticastListenersTable();
769 ExitNow(error = OT_ERROR_NONE);
770 }
771
772 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
773 if (aArgs[1] == "clear")
774 {
775 otBackboneRouterMulticastListenerClear(GetInstancePtr());
776 error = OT_ERROR_NONE;
777 }
778 else if (aArgs[1] == "add")
779 {
780 otIp6Address address;
781 uint32_t timeout = 0;
782
783 SuccessOrExit(error = aArgs[2].ParseAsIp6Address(address));
784
785 if (!aArgs[3].IsEmpty())
786 {
787 SuccessOrExit(error = aArgs[3].ParseAsUint32(timeout));
788 VerifyOrExit(aArgs[4].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
789 }
790
791 error = otBackboneRouterMulticastListenerAdd(GetInstancePtr(), &address, timeout);
792 }
793 }
794 else if (aArgs[0] == "response")
795 {
796 error = ProcessSet(aArgs + 1, otBackboneRouterConfigNextMulticastListenerRegistrationResponse);
797 #endif
798 }
799
800 exit:
801 return error;
802 }
803
PrintMulticastListenersTable(void)804 void Interpreter::PrintMulticastListenersTable(void)
805 {
806 otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
807 otBackboneRouterMulticastListenerInfo listenerInfo;
808
809 while (otBackboneRouterMulticastListenerGetNext(GetInstancePtr(), &iter, &listenerInfo) == OT_ERROR_NONE)
810 {
811 OutputIp6Address(listenerInfo.mAddress);
812 OutputLine(" %u", listenerInfo.mTimeout);
813 }
814 }
815
816 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
817
ProcessBackboneRouterLocal(Arg aArgs[])818 otError Interpreter::ProcessBackboneRouterLocal(Arg aArgs[])
819 {
820 otError error = OT_ERROR_NONE;
821 otBackboneRouterConfig config;
822 bool enable;
823
824 if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
825 {
826 otBackboneRouterSetEnabled(GetInstancePtr(), enable);
827 }
828 else if (aArgs[0] == "jitter")
829 {
830 error = ProcessGetSet(aArgs + 1, otBackboneRouterGetRegistrationJitter, otBackboneRouterSetRegistrationJitter);
831 }
832 else if (aArgs[0] == "register")
833 {
834 SuccessOrExit(error = otBackboneRouterRegister(GetInstancePtr()));
835 }
836 else if (aArgs[0] == "state")
837 {
838 static const char *const kStateStrings[] = {
839 "Disabled", // (0) OT_BACKBONE_ROUTER_STATE_DISABLED
840 "Secondary", // (1) OT_BACKBONE_ROUTER_STATE_SECONDARY
841 "Primary", // (2) OT_BACKBONE_ROUTER_STATE_PRIMARY
842 };
843
844 static_assert(0 == OT_BACKBONE_ROUTER_STATE_DISABLED, "OT_BACKBONE_ROUTER_STATE_DISABLED value is incorrect");
845 static_assert(1 == OT_BACKBONE_ROUTER_STATE_SECONDARY, "OT_BACKBONE_ROUTER_STATE_SECONDARY value is incorrect");
846 static_assert(2 == OT_BACKBONE_ROUTER_STATE_PRIMARY, "OT_BACKBONE_ROUTER_STATE_PRIMARY value is incorrect");
847
848 OutputLine("%s", Stringify(otBackboneRouterGetState(GetInstancePtr()), kStateStrings));
849 }
850 else if (aArgs[0] == "config")
851 {
852 otBackboneRouterGetConfig(GetInstancePtr(), &config);
853
854 if (aArgs[1].IsEmpty())
855 {
856 OutputLine("seqno: %d", config.mSequenceNumber);
857 OutputLine("delay: %d secs", config.mReregistrationDelay);
858 OutputLine("timeout: %d secs", config.mMlrTimeout);
859 }
860 else
861 {
862 // Set local Backbone Router configuration.
863 for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
864 {
865 if (*arg == "seqno")
866 {
867 arg++;
868 SuccessOrExit(error = arg->ParseAsUint8(config.mSequenceNumber));
869 }
870 else if (*arg == "delay")
871 {
872 arg++;
873 SuccessOrExit(error = arg->ParseAsUint16(config.mReregistrationDelay));
874 }
875 else if (*arg == "timeout")
876 {
877 arg++;
878 SuccessOrExit(error = arg->ParseAsUint32(config.mMlrTimeout));
879 }
880 else
881 {
882 ExitNow(error = OT_ERROR_INVALID_ARGS);
883 }
884 }
885
886 SuccessOrExit(error = otBackboneRouterSetConfig(GetInstancePtr(), &config));
887 }
888 }
889 else
890 {
891 error = OT_ERROR_INVALID_COMMAND;
892 }
893
894 exit:
895 return error;
896 }
897 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
898
Process(Arg aArgs[])899 template <> otError Interpreter::Process<Cmd("domainname")>(Arg aArgs[])
900 {
901 otError error = OT_ERROR_NONE;
902
903 if (aArgs[0].IsEmpty())
904 {
905 OutputLine("%s", otThreadGetDomainName(GetInstancePtr()));
906 }
907 else
908 {
909 SuccessOrExit(error = otThreadSetDomainName(GetInstancePtr(), aArgs[0].GetCString()));
910 }
911
912 exit:
913 return error;
914 }
915
916 #if OPENTHREAD_CONFIG_DUA_ENABLE
Process(Arg aArgs[])917 template <> otError Interpreter::Process<Cmd("dua")>(Arg aArgs[])
918 {
919 otError error = OT_ERROR_NONE;
920
921 if (aArgs[0] == "iid")
922 {
923 if (aArgs[1].IsEmpty())
924 {
925 const otIp6InterfaceIdentifier *iid = otThreadGetFixedDuaInterfaceIdentifier(GetInstancePtr());
926
927 if (iid != nullptr)
928 {
929 OutputBytesLine(iid->mFields.m8);
930 }
931 }
932 else if (aArgs[1] == "clear")
933 {
934 error = otThreadSetFixedDuaInterfaceIdentifier(GetInstancePtr(), nullptr);
935 }
936 else
937 {
938 otIp6InterfaceIdentifier iid;
939
940 SuccessOrExit(error = aArgs[1].ParseAsHexString(iid.mFields.m8));
941 error = otThreadSetFixedDuaInterfaceIdentifier(GetInstancePtr(), &iid);
942 }
943 }
944 else
945 {
946 error = OT_ERROR_INVALID_COMMAND;
947 }
948
949 exit:
950 return error;
951 }
952 #endif // OPENTHREAD_CONFIG_DUA_ENABLE
953
954 #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
955
Process(Arg aArgs[])956 template <> otError Interpreter::Process<Cmd("bufferinfo")>(Arg aArgs[])
957 {
958 OT_UNUSED_VARIABLE(aArgs);
959
960 struct BufferInfoName
961 {
962 const otMessageQueueInfo otBufferInfo::*mQueuePtr;
963 const char * mName;
964 };
965
966 static const BufferInfoName kBufferInfoNames[] = {
967 {&otBufferInfo::m6loSendQueue, "6lo send"},
968 {&otBufferInfo::m6loReassemblyQueue, "6lo reas"},
969 {&otBufferInfo::mIp6Queue, "ip6"},
970 {&otBufferInfo::mMplQueue, "mpl"},
971 {&otBufferInfo::mMleQueue, "mle"},
972 {&otBufferInfo::mCoapQueue, "coap"},
973 {&otBufferInfo::mCoapSecureQueue, "coap secure"},
974 {&otBufferInfo::mApplicationCoapQueue, "application coap"},
975 };
976
977 otBufferInfo bufferInfo;
978
979 otMessageGetBufferInfo(GetInstancePtr(), &bufferInfo);
980
981 OutputLine("total: %d", bufferInfo.mTotalBuffers);
982 OutputLine("free: %d", bufferInfo.mFreeBuffers);
983
984 for (const BufferInfoName &info : kBufferInfoNames)
985 {
986 OutputLine("%s: %u %u %u", info.mName, (bufferInfo.*info.mQueuePtr).mNumMessages,
987 (bufferInfo.*info.mQueuePtr).mNumBuffers, (bufferInfo.*info.mQueuePtr).mTotalBytes);
988 }
989
990 return OT_ERROR_NONE;
991 }
992
Process(Arg aArgs[])993 template <> otError Interpreter::Process<Cmd("ccathreshold")>(Arg aArgs[])
994 {
995 otError error = OT_ERROR_NONE;
996 int8_t cca;
997
998 if (aArgs[0].IsEmpty())
999 {
1000 SuccessOrExit(error = otPlatRadioGetCcaEnergyDetectThreshold(GetInstancePtr(), &cca));
1001 OutputLine("%d dBm", cca);
1002 }
1003 else
1004 {
1005 SuccessOrExit(error = aArgs[0].ParseAsInt8(cca));
1006 error = otPlatRadioSetCcaEnergyDetectThreshold(GetInstancePtr(), cca);
1007 }
1008
1009 exit:
1010 return error;
1011 }
1012
1013 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Process(Arg aArgs[])1014 template <> otError Interpreter::Process<Cmd("ccm")>(Arg aArgs[])
1015 {
1016 otError error = OT_ERROR_NONE;
1017 bool enable;
1018
1019 VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_COMMAND);
1020
1021 SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
1022 otThreadSetCcmEnabled(GetInstancePtr(), enable);
1023
1024 exit:
1025 return error;
1026 }
1027
Process(Arg aArgs[])1028 template <> otError Interpreter::Process<Cmd("tvcheck")>(Arg aArgs[])
1029 {
1030 otError error = OT_ERROR_NONE;
1031 bool enable;
1032
1033 VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_COMMAND);
1034
1035 SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
1036 otThreadSetThreadVersionCheckEnabled(GetInstancePtr(), enable);
1037
1038 exit:
1039 return error;
1040 }
1041
1042 #endif
1043
Process(Arg aArgs[])1044 template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
1045 {
1046 otError error = OT_ERROR_NONE;
1047
1048 if (aArgs[0] == "supported")
1049 {
1050 OutputLine("0x%x", otPlatRadioGetSupportedChannelMask(GetInstancePtr()));
1051 }
1052 else if (aArgs[0] == "preferred")
1053 {
1054 OutputLine("0x%x", otPlatRadioGetPreferredChannelMask(GetInstancePtr()));
1055 }
1056 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
1057 else if (aArgs[0] == "monitor")
1058 {
1059 if (aArgs[1].IsEmpty())
1060 {
1061 OutputLine("enabled: %d", otChannelMonitorIsEnabled(GetInstancePtr()));
1062 if (otChannelMonitorIsEnabled(GetInstancePtr()))
1063 {
1064 uint32_t channelMask = otLinkGetSupportedChannelMask(GetInstancePtr());
1065 uint8_t channelNum = sizeof(channelMask) * CHAR_BIT;
1066
1067 OutputLine("interval: %u", otChannelMonitorGetSampleInterval(GetInstancePtr()));
1068 OutputLine("threshold: %d", otChannelMonitorGetRssiThreshold(GetInstancePtr()));
1069 OutputLine("window: %u", otChannelMonitorGetSampleWindow(GetInstancePtr()));
1070 OutputLine("count: %u", otChannelMonitorGetSampleCount(GetInstancePtr()));
1071
1072 OutputLine("occupancies:");
1073 for (uint8_t channel = 0; channel < channelNum; channel++)
1074 {
1075 uint32_t occupancy = 0;
1076
1077 if (!((1UL << channel) & channelMask))
1078 {
1079 continue;
1080 }
1081
1082 occupancy = otChannelMonitorGetChannelOccupancy(GetInstancePtr(), channel);
1083
1084 OutputFormat("ch %d (0x%04x) ", channel, occupancy);
1085 occupancy = (occupancy * 10000) / 0xffff;
1086 OutputLine("%2d.%02d%% busy", occupancy / 100, occupancy % 100);
1087 }
1088 OutputLine("");
1089 }
1090 }
1091 else if (aArgs[1] == "start")
1092 {
1093 error = otChannelMonitorSetEnabled(GetInstancePtr(), true);
1094 }
1095 else if (aArgs[1] == "stop")
1096 {
1097 error = otChannelMonitorSetEnabled(GetInstancePtr(), false);
1098 }
1099 else
1100 {
1101 ExitNow(error = OT_ERROR_INVALID_ARGS);
1102 }
1103 }
1104 #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
1105 #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
1106 else if (aArgs[0] == "manager")
1107 {
1108 if (aArgs[1].IsEmpty())
1109 {
1110 OutputLine("channel: %d", otChannelManagerGetRequestedChannel(GetInstancePtr()));
1111 OutputLine("auto: %d", otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()));
1112
1113 if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()))
1114 {
1115 Mac::ChannelMask supportedMask(otChannelManagerGetSupportedChannels(GetInstancePtr()));
1116 Mac::ChannelMask favoredMask(otChannelManagerGetFavoredChannels(GetInstancePtr()));
1117
1118 OutputLine("delay: %d", otChannelManagerGetDelay(GetInstancePtr()));
1119 OutputLine("interval: %u", otChannelManagerGetAutoChannelSelectionInterval(GetInstancePtr()));
1120 OutputLine("cca threshold: 0x%04x", otChannelManagerGetCcaFailureRateThreshold(GetInstancePtr()));
1121 OutputLine("supported: %s", supportedMask.ToString().AsCString());
1122 OutputLine("favored: %s", supportedMask.ToString().AsCString());
1123 }
1124 }
1125 else if (aArgs[1] == "change")
1126 {
1127 error = ProcessSet(aArgs + 2, otChannelManagerRequestChannelChange);
1128 }
1129 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
1130 else if (aArgs[1] == "select")
1131 {
1132 bool enable;
1133
1134 SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
1135 error = otChannelManagerRequestChannelSelect(GetInstancePtr(), enable);
1136 }
1137 #endif
1138 else if (aArgs[1] == "auto")
1139 {
1140 bool enable;
1141
1142 SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
1143 otChannelManagerSetAutoChannelSelectionEnabled(GetInstancePtr(), enable);
1144 }
1145 else if (aArgs[1] == "delay")
1146 {
1147 error = ProcessSet(aArgs + 2, otChannelManagerSetDelay);
1148 }
1149 else if (aArgs[1] == "interval")
1150 {
1151 error = ProcessSet(aArgs + 2, otChannelManagerSetAutoChannelSelectionInterval);
1152 }
1153 else if (aArgs[1] == "supported")
1154 {
1155 error = ProcessSet(aArgs + 2, otChannelManagerSetSupportedChannels);
1156 }
1157 else if (aArgs[1] == "favored")
1158 {
1159 error = ProcessSet(aArgs + 2, otChannelManagerSetFavoredChannels);
1160 }
1161 else if (aArgs[1] == "threshold")
1162 {
1163 error = ProcessSet(aArgs + 2, otChannelManagerSetCcaFailureRateThreshold);
1164 }
1165 else
1166 {
1167 ExitNow(error = OT_ERROR_INVALID_ARGS);
1168 }
1169 }
1170 #endif // OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
1171 else
1172 {
1173 ExitNow(error = ProcessGetSet(aArgs, otLinkGetChannel, otLinkSetChannel));
1174 }
1175
1176 exit:
1177 return error;
1178 }
1179
1180 #if OPENTHREAD_FTD
Process(Arg aArgs[])1181 template <> otError Interpreter::Process<Cmd("child")>(Arg aArgs[])
1182 {
1183 otError error = OT_ERROR_NONE;
1184 otChildInfo childInfo;
1185 uint16_t childId;
1186 bool isTable;
1187 otLinkModeConfig linkMode;
1188 char linkModeString[kLinkModeStringSize];
1189
1190 isTable = (aArgs[0] == "table");
1191
1192 if (isTable || (aArgs[0] == "list"))
1193 {
1194 uint16_t maxChildren;
1195
1196 if (isTable)
1197 {
1198 static const char *const kChildTableTitles[] = {
1199 "ID", "RLOC16", "Timeout", "Age", "LQ In", "C_VN", "R",
1200 "D", "N", "Ver", "CSL", "QMsgCnt", "Extended MAC",
1201 };
1202
1203 static const uint8_t kChildTableColumnWidths[] = {
1204 5, 8, 12, 12, 7, 6, 1, 1, 1, 3, 3, 7, 18,
1205 };
1206
1207 OutputTableHeader(kChildTableTitles, kChildTableColumnWidths);
1208 }
1209
1210 maxChildren = otThreadGetMaxAllowedChildren(GetInstancePtr());
1211
1212 for (uint16_t i = 0; i < maxChildren; i++)
1213 {
1214 if ((otThreadGetChildInfoByIndex(GetInstancePtr(), i, &childInfo) != OT_ERROR_NONE) ||
1215 childInfo.mIsStateRestoring)
1216 {
1217 continue;
1218 }
1219
1220 if (isTable)
1221 {
1222 OutputFormat("| %3d ", childInfo.mChildId);
1223 OutputFormat("| 0x%04x ", childInfo.mRloc16);
1224 OutputFormat("| %10d ", childInfo.mTimeout);
1225 OutputFormat("| %10d ", childInfo.mAge);
1226 OutputFormat("| %5d ", childInfo.mLinkQualityIn);
1227 OutputFormat("| %4d ", childInfo.mNetworkDataVersion);
1228 OutputFormat("|%1d", childInfo.mRxOnWhenIdle);
1229 OutputFormat("|%1d", childInfo.mFullThreadDevice);
1230 OutputFormat("|%1d", childInfo.mFullNetworkData);
1231 OutputFormat("|%3d", childInfo.mVersion);
1232 OutputFormat("| %1d ", childInfo.mIsCslSynced);
1233 OutputFormat("| %5d ", childInfo.mQueuedMessageCnt);
1234 OutputFormat("| ");
1235 OutputExtAddress(childInfo.mExtAddress);
1236 OutputLine(" |");
1237 }
1238 else
1239 {
1240 OutputFormat("%d ", childInfo.mChildId);
1241 }
1242 }
1243
1244 OutputLine("");
1245 ExitNow();
1246 }
1247
1248 SuccessOrExit(error = aArgs[0].ParseAsUint16(childId));
1249 SuccessOrExit(error = otThreadGetChildInfoById(GetInstancePtr(), childId, &childInfo));
1250
1251 OutputLine("Child ID: %d", childInfo.mChildId);
1252 OutputLine("Rloc: %04x", childInfo.mRloc16);
1253 OutputFormat("Ext Addr: ");
1254 OutputExtAddressLine(childInfo.mExtAddress);
1255 linkMode.mRxOnWhenIdle = childInfo.mRxOnWhenIdle;
1256 linkMode.mDeviceType = childInfo.mFullThreadDevice;
1257 linkMode.mNetworkData = childInfo.mFullThreadDevice;
1258 OutputLine("Mode: %s", LinkModeToString(linkMode, linkModeString));
1259 OutputLine("Net Data: %d", childInfo.mNetworkDataVersion);
1260 OutputLine("Timeout: %d", childInfo.mTimeout);
1261 OutputLine("Age: %d", childInfo.mAge);
1262 OutputLine("Link Quality In: %d", childInfo.mLinkQualityIn);
1263 OutputLine("RSSI: %d", childInfo.mAverageRssi);
1264
1265 exit:
1266 return error;
1267 }
1268
Process(Arg aArgs[])1269 template <> otError Interpreter::Process<Cmd("childip")>(Arg aArgs[])
1270 {
1271 otError error = OT_ERROR_NONE;
1272
1273 if (aArgs[0].IsEmpty())
1274 {
1275 uint16_t maxChildren = otThreadGetMaxAllowedChildren(GetInstancePtr());
1276
1277 for (uint16_t childIndex = 0; childIndex < maxChildren; childIndex++)
1278 {
1279 otChildIp6AddressIterator iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT;
1280 otIp6Address ip6Address;
1281 otChildInfo childInfo;
1282
1283 if ((otThreadGetChildInfoByIndex(GetInstancePtr(), childIndex, &childInfo) != OT_ERROR_NONE) ||
1284 childInfo.mIsStateRestoring)
1285 {
1286 continue;
1287 }
1288
1289 iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT;
1290
1291 while (otThreadGetChildNextIp6Address(GetInstancePtr(), childIndex, &iterator, &ip6Address) ==
1292 OT_ERROR_NONE)
1293 {
1294 OutputFormat("%04x: ", childInfo.mRloc16);
1295 OutputIp6AddressLine(ip6Address);
1296 }
1297 }
1298 }
1299 else if (aArgs[0] == "max")
1300 {
1301 #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1302 error = ProcessGet(aArgs + 1, otThreadGetMaxChildIpAddresses);
1303 #else
1304 error = ProcessGetSet(aArgs + 1, otThreadGetMaxChildIpAddresses, otThreadSetMaxChildIpAddresses);
1305 #endif
1306 }
1307 else
1308 {
1309 error = OT_ERROR_INVALID_COMMAND;
1310 }
1311
1312 return error;
1313 }
1314
Process(Arg aArgs[])1315 template <> otError Interpreter::Process<Cmd("childmax")>(Arg aArgs[])
1316 {
1317 return ProcessGetSet(aArgs, otThreadGetMaxAllowedChildren, otThreadSetMaxAllowedChildren);
1318 }
1319 #endif // OPENTHREAD_FTD
1320
1321 #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
Process(Arg aArgs[])1322 template <> otError Interpreter::Process<Cmd("childsupervision")>(Arg aArgs[])
1323 {
1324 otError error = OT_ERROR_INVALID_ARGS;
1325
1326 if (aArgs[0] == "checktimeout")
1327 {
1328 error = ProcessGetSet(aArgs + 1, otChildSupervisionGetCheckTimeout, otChildSupervisionSetCheckTimeout);
1329 }
1330 #if OPENTHREAD_FTD
1331 else if (aArgs[0] == "interval")
1332 {
1333 error = ProcessGetSet(aArgs + 1, otChildSupervisionGetInterval, otChildSupervisionSetInterval);
1334 }
1335 #endif
1336
1337 return error;
1338 }
1339 #endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
1340
Process(Arg aArgs[])1341 template <> otError Interpreter::Process<Cmd("childtimeout")>(Arg aArgs[])
1342 {
1343 return ProcessGetSet(aArgs, otThreadGetChildTimeout, otThreadSetChildTimeout);
1344 }
1345
1346 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
Process(Arg aArgs[])1347 template <> otError Interpreter::Process<Cmd("coap")>(Arg aArgs[])
1348 {
1349 return mCoap.Process(aArgs);
1350 }
1351 #endif
1352
1353 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
Process(Arg aArgs[])1354 template <> otError Interpreter::Process<Cmd("coaps")>(Arg aArgs[])
1355 {
1356 return mCoapSecure.Process(aArgs);
1357 }
1358 #endif
1359
1360 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
Process(Arg aArgs[])1361 template <> otError Interpreter::Process<Cmd("coex")>(Arg aArgs[])
1362 {
1363 otError error = OT_ERROR_NONE;
1364 bool enable;
1365
1366 if (aArgs[0].IsEmpty())
1367 {
1368 OutputEnabledDisabledStatus(otPlatRadioIsCoexEnabled(GetInstancePtr()));
1369 }
1370 else if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
1371 {
1372 error = otPlatRadioSetCoexEnabled(GetInstancePtr(), enable);
1373 }
1374 else if (aArgs[0] == "metrics")
1375 {
1376 struct RadioCoexMetricName
1377 {
1378 const uint32_t otRadioCoexMetrics::*mValuePtr;
1379 const char * mName;
1380 };
1381
1382 static const RadioCoexMetricName kTxMetricNames[] = {
1383 {&otRadioCoexMetrics::mNumTxRequest, "Request"},
1384 {&otRadioCoexMetrics::mNumTxGrantImmediate, "Grant Immediate"},
1385 {&otRadioCoexMetrics::mNumTxGrantWait, "Grant Wait"},
1386 {&otRadioCoexMetrics::mNumTxGrantWaitActivated, "Grant Wait Activated"},
1387 {&otRadioCoexMetrics::mNumTxGrantWaitTimeout, "Grant Wait Timeout"},
1388 {&otRadioCoexMetrics::mNumTxGrantDeactivatedDuringRequest, "Grant Deactivated During Request"},
1389 {&otRadioCoexMetrics::mNumTxDelayedGrant, "Delayed Grant"},
1390 {&otRadioCoexMetrics::mAvgTxRequestToGrantTime, "Average Request To Grant Time"},
1391 };
1392
1393 static const RadioCoexMetricName kRxMetricNames[] = {
1394 {&otRadioCoexMetrics::mNumRxRequest, "Request"},
1395 {&otRadioCoexMetrics::mNumRxGrantImmediate, "Grant Immediate"},
1396 {&otRadioCoexMetrics::mNumRxGrantWait, "Grant Wait"},
1397 {&otRadioCoexMetrics::mNumRxGrantWaitActivated, "Grant Wait Activated"},
1398 {&otRadioCoexMetrics::mNumRxGrantWaitTimeout, "Grant Wait Timeout"},
1399 {&otRadioCoexMetrics::mNumRxGrantDeactivatedDuringRequest, "Grant Deactivated During Request"},
1400 {&otRadioCoexMetrics::mNumRxDelayedGrant, "Delayed Grant"},
1401 {&otRadioCoexMetrics::mAvgRxRequestToGrantTime, "Average Request To Grant Time"},
1402 {&otRadioCoexMetrics::mNumRxGrantNone, "Grant None"},
1403 };
1404
1405 otRadioCoexMetrics metrics;
1406
1407 SuccessOrExit(error = otPlatRadioGetCoexMetrics(GetInstancePtr(), &metrics));
1408
1409 OutputLine("Stopped: %s", metrics.mStopped ? "true" : "false");
1410 OutputLine("Grant Glitch: %u", metrics.mNumGrantGlitch);
1411 OutputLine("Transmit metrics");
1412
1413 for (const RadioCoexMetricName &metric : kTxMetricNames)
1414 {
1415 OutputLine(kIndentSize, "%s: %u", metric.mName, metrics.*metric.mValuePtr);
1416 }
1417
1418 OutputLine("Receive metrics");
1419
1420 for (const RadioCoexMetricName &metric : kRxMetricNames)
1421 {
1422 OutputLine(kIndentSize, "%s: %u", metric.mName, metrics.*metric.mValuePtr);
1423 }
1424 }
1425 else
1426 {
1427 error = OT_ERROR_INVALID_ARGS;
1428 }
1429
1430 exit:
1431 return error;
1432 }
1433 #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
1434
1435 #if OPENTHREAD_FTD
Process(Arg aArgs[])1436 template <> otError Interpreter::Process<Cmd("contextreusedelay")>(Arg aArgs[])
1437 {
1438 return ProcessGetSet(aArgs, otThreadGetContextIdReuseDelay, otThreadSetContextIdReuseDelay);
1439 }
1440 #endif
1441
Process(Arg aArgs[])1442 template <> otError Interpreter::Process<Cmd("counters")>(Arg aArgs[])
1443 {
1444 otError error = OT_ERROR_NONE;
1445
1446 if (aArgs[0].IsEmpty())
1447 {
1448 OutputLine("ip");
1449 OutputLine("mac");
1450 OutputLine("mle");
1451 }
1452 else if (aArgs[0] == "mac")
1453 {
1454 if (aArgs[1].IsEmpty())
1455 {
1456 struct MacCounterName
1457 {
1458 const uint32_t otMacCounters::*mValuePtr;
1459 const char * mName;
1460 };
1461
1462 static const MacCounterName kTxCounterNames[] = {
1463 {&otMacCounters::mTxUnicast, "TxUnicast"},
1464 {&otMacCounters::mTxBroadcast, "TxBroadcast"},
1465 {&otMacCounters::mTxAckRequested, "TxAckRequested"},
1466 {&otMacCounters::mTxAcked, "TxAcked"},
1467 {&otMacCounters::mTxNoAckRequested, "TxNoAckRequested"},
1468 {&otMacCounters::mTxData, "TxData"},
1469 {&otMacCounters::mTxDataPoll, "TxDataPoll"},
1470 {&otMacCounters::mTxBeacon, "TxBeacon"},
1471 {&otMacCounters::mTxBeaconRequest, "TxBeaconRequest"},
1472 {&otMacCounters::mTxOther, "TxOther"},
1473 {&otMacCounters::mTxRetry, "TxRetry"},
1474 {&otMacCounters::mTxErrCca, "TxErrCca"},
1475 {&otMacCounters::mTxErrBusyChannel, "TxErrBusyChannel"},
1476 };
1477
1478 static const MacCounterName kRxCounterNames[] = {
1479 {&otMacCounters::mRxUnicast, "RxUnicast"},
1480 {&otMacCounters::mRxBroadcast, "RxBroadcast"},
1481 {&otMacCounters::mRxData, "RxData"},
1482 {&otMacCounters::mRxDataPoll, "RxDataPoll"},
1483 {&otMacCounters::mRxBeacon, "RxBeacon"},
1484 {&otMacCounters::mRxBeaconRequest, "RxBeaconRequest"},
1485 {&otMacCounters::mRxOther, "RxOther"},
1486 {&otMacCounters::mRxAddressFiltered, "RxAddressFiltered"},
1487 {&otMacCounters::mRxDestAddrFiltered, "RxDestAddrFiltered"},
1488 {&otMacCounters::mRxDuplicated, "RxDuplicated"},
1489 {&otMacCounters::mRxErrNoFrame, "RxErrNoFrame"},
1490 {&otMacCounters::mRxErrUnknownNeighbor, "RxErrNoUnknownNeighbor"},
1491 {&otMacCounters::mRxErrInvalidSrcAddr, "RxErrInvalidSrcAddr"},
1492 {&otMacCounters::mRxErrSec, "RxErrSec"},
1493 {&otMacCounters::mRxErrFcs, "RxErrFcs"},
1494 {&otMacCounters::mRxErrOther, "RxErrOther"},
1495 };
1496
1497 const otMacCounters *macCounters = otLinkGetCounters(GetInstancePtr());
1498
1499 OutputLine("TxTotal: %d", macCounters->mTxTotal);
1500
1501 for (const MacCounterName &counter : kTxCounterNames)
1502 {
1503 OutputLine(kIndentSize, "%s: %u", counter.mName, macCounters->*counter.mValuePtr);
1504 }
1505
1506 OutputLine("RxTotal: %d", macCounters->mRxTotal);
1507
1508 for (const MacCounterName &counter : kRxCounterNames)
1509 {
1510 OutputLine(kIndentSize, "%s: %u", counter.mName, macCounters->*counter.mValuePtr);
1511 }
1512 }
1513 else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
1514 {
1515 otLinkResetCounters(GetInstancePtr());
1516 }
1517 else
1518 {
1519 error = OT_ERROR_INVALID_ARGS;
1520 }
1521 }
1522 else if (aArgs[0] == "mle")
1523 {
1524 if (aArgs[1].IsEmpty())
1525 {
1526 struct MleCounterName
1527 {
1528 const uint16_t otMleCounters::*mValuePtr;
1529 const char * mName;
1530 };
1531
1532 static const MleCounterName kCounterNames[] = {
1533 {&otMleCounters::mDisabledRole, "Role Disabled"},
1534 {&otMleCounters::mDetachedRole, "Role Detached"},
1535 {&otMleCounters::mChildRole, "Role Child"},
1536 {&otMleCounters::mRouterRole, "Role Router"},
1537 {&otMleCounters::mLeaderRole, "Role Leader"},
1538 {&otMleCounters::mAttachAttempts, "Attach Attempts"},
1539 {&otMleCounters::mPartitionIdChanges, "Partition Id Changes"},
1540 {&otMleCounters::mBetterPartitionAttachAttempts, "Better Partition Attach Attempts"},
1541 {&otMleCounters::mParentChanges, "Parent Changes"},
1542 };
1543
1544 const otMleCounters *mleCounters = otThreadGetMleCounters(GetInstancePtr());
1545
1546 for (const MleCounterName &counter : kCounterNames)
1547 {
1548 OutputLine("%s: %d", counter.mName, mleCounters->*counter.mValuePtr);
1549 }
1550 }
1551 else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
1552 {
1553 otThreadResetMleCounters(GetInstancePtr());
1554 }
1555 else
1556 {
1557 error = OT_ERROR_INVALID_ARGS;
1558 }
1559 }
1560 else if (aArgs[0] == "ip")
1561 {
1562 if (aArgs[1].IsEmpty())
1563 {
1564 struct IpCounterName
1565 {
1566 const uint32_t otIpCounters::*mValuePtr;
1567 const char * mName;
1568 };
1569
1570 static const IpCounterName kCounterNames[] = {
1571 {&otIpCounters::mTxSuccess, "TxSuccess"},
1572 {&otIpCounters::mTxFailure, "TxFailed"},
1573 {&otIpCounters::mRxSuccess, "RxSuccess"},
1574 {&otIpCounters::mRxFailure, "RxFailed"},
1575 };
1576
1577 const otIpCounters *ipCounters = otThreadGetIp6Counters(GetInstancePtr());
1578
1579 for (const IpCounterName &counter : kCounterNames)
1580 {
1581 OutputLine("%s: %d", counter.mName, ipCounters->*counter.mValuePtr);
1582 }
1583 }
1584 else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
1585 {
1586 otThreadResetIp6Counters(GetInstancePtr());
1587 }
1588 else
1589 {
1590 error = OT_ERROR_INVALID_ARGS;
1591 }
1592 }
1593 else
1594 {
1595 error = OT_ERROR_INVALID_ARGS;
1596 }
1597
1598 return error;
1599 }
1600
1601 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
Process(Arg aArgs[])1602 template <> otError Interpreter::Process<Cmd("csl")>(Arg aArgs[])
1603 {
1604 otError error = OT_ERROR_NONE;
1605
1606 if (aArgs[0].IsEmpty())
1607 {
1608 OutputLine("Channel: %u", otLinkCslGetChannel(GetInstancePtr()));
1609 OutputLine("Period: %u(in units of 10 symbols), %ums", otLinkCslGetPeriod(GetInstancePtr()),
1610 otLinkCslGetPeriod(GetInstancePtr()) * kUsPerTenSymbols / 1000);
1611 OutputLine("Timeout: %us", otLinkCslGetTimeout(GetInstancePtr()));
1612 }
1613 else if (aArgs[0] == "channel")
1614 {
1615 error = ProcessSet(aArgs + 1, otLinkCslSetChannel);
1616 }
1617 else if (aArgs[0] == "period")
1618 {
1619 error = ProcessSet(aArgs + 1, otLinkCslSetPeriod);
1620 }
1621 else if (aArgs[0] == "timeout")
1622 {
1623 error = ProcessSet(aArgs + 1, otLinkCslSetTimeout);
1624 }
1625 else
1626 {
1627 error = OT_ERROR_INVALID_ARGS;
1628 }
1629
1630 return error;
1631 }
1632 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1633
1634 #if OPENTHREAD_FTD
Process(Arg aArgs[])1635 template <> otError Interpreter::Process<Cmd("delaytimermin")>(Arg aArgs[])
1636 {
1637 otError error = OT_ERROR_NONE;
1638
1639 if (aArgs[0].IsEmpty())
1640 {
1641 OutputLine("%d", (otDatasetGetDelayTimerMinimal(GetInstancePtr()) / 1000));
1642 }
1643 else if (aArgs[1].IsEmpty())
1644 {
1645 uint32_t delay;
1646 SuccessOrExit(error = aArgs[0].ParseAsUint32(delay));
1647 SuccessOrExit(error = otDatasetSetDelayTimerMinimal(GetInstancePtr(), static_cast<uint32_t>(delay * 1000)));
1648 }
1649 else
1650 {
1651 error = OT_ERROR_INVALID_ARGS;
1652 }
1653
1654 exit:
1655 return error;
1656 }
1657 #endif
1658
Process(Arg aArgs[])1659 template <> otError Interpreter::Process<Cmd("detach")>(Arg aArgs[])
1660 {
1661 otError error = OT_ERROR_NONE;
1662
1663 if (aArgs[0] == "async")
1664 {
1665 SuccessOrExit(error = otThreadDetachGracefully(GetInstancePtr(), nullptr, nullptr));
1666 }
1667 else
1668 {
1669 SuccessOrExit(error =
1670 otThreadDetachGracefully(GetInstancePtr(), &Interpreter::HandleDetachGracefullyResult, this));
1671 error = OT_ERROR_PENDING;
1672 }
1673
1674 exit:
1675 return error;
1676 }
1677
Process(Arg aArgs[])1678 template <> otError Interpreter::Process<Cmd("discover")>(Arg aArgs[])
1679 {
1680 otError error = OT_ERROR_NONE;
1681 uint32_t scanChannels = 0;
1682
1683 if (!aArgs[0].IsEmpty())
1684 {
1685 uint8_t channel;
1686
1687 SuccessOrExit(error = aArgs[0].ParseAsUint8(channel));
1688 VerifyOrExit(channel < sizeof(scanChannels) * CHAR_BIT, error = OT_ERROR_INVALID_ARGS);
1689 scanChannels = 1 << channel;
1690 }
1691
1692 SuccessOrExit(error = otThreadDiscover(GetInstancePtr(), scanChannels, OT_PANID_BROADCAST, false, false,
1693 &Interpreter::HandleActiveScanResult, this));
1694
1695 static const char *const kScanTableTitles[] = {
1696 "Network Name", "Extended PAN", "PAN", "MAC Address", "Ch", "dBm", "LQI",
1697 };
1698
1699 static const uint8_t kScanTableColumnWidths[] = {
1700 18, 18, 6, 18, 4, 5, 5,
1701 };
1702
1703 OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
1704
1705 error = OT_ERROR_PENDING;
1706
1707 exit:
1708 return error;
1709 }
1710
Process(Arg aArgs[])1711 template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
1712 {
1713 OT_UNUSED_VARIABLE(aArgs);
1714
1715 otError error = OT_ERROR_NONE;
1716 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
1717 otDnsQueryConfig queryConfig;
1718 otDnsQueryConfig *config = &queryConfig;
1719 #endif
1720
1721 if (aArgs[0].IsEmpty())
1722 {
1723 error = OT_ERROR_INVALID_ARGS;
1724 }
1725 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1726 else if (aArgs[0] == "compression")
1727 {
1728 if (aArgs[1].IsEmpty())
1729 {
1730 OutputEnabledDisabledStatus(otDnsIsNameCompressionEnabled());
1731 }
1732 else
1733 {
1734 bool enable;
1735
1736 SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable));
1737 otDnsSetNameCompressionEnabled(enable);
1738 }
1739 }
1740 #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1741 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
1742 else if (aArgs[0] == "config")
1743 {
1744 if (aArgs[1].IsEmpty())
1745 {
1746 const otDnsQueryConfig *defaultConfig = otDnsClientGetDefaultConfig(GetInstancePtr());
1747
1748 OutputFormat("Server: ");
1749 OutputSockAddrLine(defaultConfig->mServerSockAddr);
1750 OutputLine("ResponseTimeout: %u ms", defaultConfig->mResponseTimeout);
1751 OutputLine("MaxTxAttempts: %u", defaultConfig->mMaxTxAttempts);
1752 OutputLine("RecursionDesired: %s",
1753 (defaultConfig->mRecursionFlag == OT_DNS_FLAG_RECURSION_DESIRED) ? "yes" : "no");
1754 }
1755 else
1756 {
1757 SuccessOrExit(error = GetDnsConfig(aArgs + 1, config));
1758 otDnsClientSetDefaultConfig(GetInstancePtr(), config);
1759 }
1760 }
1761 else if (aArgs[0] == "resolve")
1762 {
1763 VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
1764 SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
1765 SuccessOrExit(error = otDnsClientResolveAddress(GetInstancePtr(), aArgs[1].GetCString(),
1766 &Interpreter::HandleDnsAddressResponse, this, config));
1767 error = OT_ERROR_PENDING;
1768 }
1769 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
1770 else if (aArgs[0] == "resolve4")
1771 {
1772 VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
1773 SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
1774 SuccessOrExit(error = otDnsClientResolveIp4Address(GetInstancePtr(), aArgs[1].GetCString(),
1775 &Interpreter::HandleDnsAddressResponse, this, config));
1776 error = OT_ERROR_PENDING;
1777 }
1778 #endif
1779 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
1780 else if (aArgs[0] == "browse")
1781 {
1782 VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
1783 SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
1784 SuccessOrExit(error = otDnsClientBrowse(GetInstancePtr(), aArgs[1].GetCString(),
1785 &Interpreter::HandleDnsBrowseResponse, this, config));
1786 error = OT_ERROR_PENDING;
1787 }
1788 else if (aArgs[0] == "service")
1789 {
1790 VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
1791 SuccessOrExit(error = GetDnsConfig(aArgs + 3, config));
1792 SuccessOrExit(error = otDnsClientResolveService(GetInstancePtr(), aArgs[1].GetCString(), aArgs[2].GetCString(),
1793 &Interpreter::HandleDnsServiceResponse, this, config));
1794 error = OT_ERROR_PENDING;
1795 }
1796 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
1797 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
1798 else
1799 {
1800 ExitNow(error = OT_ERROR_INVALID_COMMAND);
1801 }
1802
1803 exit:
1804 return error;
1805 }
1806
1807 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
1808
GetDnsConfig(Arg aArgs[],otDnsQueryConfig * & aConfig)1809 otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig)
1810 {
1811 // This method gets the optional DNS config from `aArgs[]`.
1812 // The format: `[server IPv6 address] [server port] [timeout]
1813 // [max tx attempt] [recursion desired]`.
1814
1815 otError error = OT_ERROR_NONE;
1816 bool recursionDesired;
1817
1818 memset(aConfig, 0, sizeof(otDnsQueryConfig));
1819
1820 VerifyOrExit(!aArgs[0].IsEmpty(), aConfig = nullptr);
1821
1822 SuccessOrExit(error = aArgs[0].ParseAsIp6Address(aConfig->mServerSockAddr.mAddress));
1823
1824 VerifyOrExit(!aArgs[1].IsEmpty());
1825 SuccessOrExit(error = aArgs[1].ParseAsUint16(aConfig->mServerSockAddr.mPort));
1826
1827 VerifyOrExit(!aArgs[2].IsEmpty());
1828 SuccessOrExit(error = aArgs[2].ParseAsUint32(aConfig->mResponseTimeout));
1829
1830 VerifyOrExit(!aArgs[3].IsEmpty());
1831 SuccessOrExit(error = aArgs[3].ParseAsUint8(aConfig->mMaxTxAttempts));
1832
1833 VerifyOrExit(!aArgs[4].IsEmpty());
1834 SuccessOrExit(error = aArgs[4].ParseAsBool(recursionDesired));
1835 aConfig->mRecursionFlag = recursionDesired ? OT_DNS_FLAG_RECURSION_DESIRED : OT_DNS_FLAG_NO_RECURSION;
1836
1837 exit:
1838 return error;
1839 }
1840
HandleDnsAddressResponse(otError aError,const otDnsAddressResponse * aResponse,void * aContext)1841 void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext)
1842 {
1843 static_cast<Interpreter *>(aContext)->HandleDnsAddressResponse(aError, aResponse);
1844 }
1845
HandleDnsAddressResponse(otError aError,const otDnsAddressResponse * aResponse)1846 void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse)
1847 {
1848 char hostName[OT_DNS_MAX_NAME_SIZE];
1849 otIp6Address address;
1850 uint32_t ttl;
1851
1852 IgnoreError(otDnsAddressResponseGetHostName(aResponse, hostName, sizeof(hostName)));
1853
1854 OutputFormat("DNS response for %s - ", hostName);
1855
1856 if (aError == OT_ERROR_NONE)
1857 {
1858 uint16_t index = 0;
1859
1860 while (otDnsAddressResponseGetAddress(aResponse, index, &address, &ttl) == OT_ERROR_NONE)
1861 {
1862 OutputIp6Address(address);
1863 OutputFormat(" TTL:%u ", ttl);
1864 index++;
1865 }
1866 }
1867
1868 OutputLine("");
1869 OutputResult(aError);
1870 }
1871
1872 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
1873
OutputDnsServiceInfo(uint8_t aIndentSize,const otDnsServiceInfo & aServiceInfo)1874 void Interpreter::OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo)
1875 {
1876 OutputLine(aIndentSize, "Port:%d, Priority:%d, Weight:%d, TTL:%u", aServiceInfo.mPort, aServiceInfo.mPriority,
1877 aServiceInfo.mWeight, aServiceInfo.mTtl);
1878 OutputLine(aIndentSize, "Host:%s", aServiceInfo.mHostNameBuffer);
1879 OutputFormat(aIndentSize, "HostAddress:");
1880 OutputIp6Address(aServiceInfo.mHostAddress);
1881 OutputLine(" TTL:%u", aServiceInfo.mHostAddressTtl);
1882 OutputFormat(aIndentSize, "TXT:");
1883 OutputDnsTxtData(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
1884 OutputLine(" TTL:%u", aServiceInfo.mTxtDataTtl);
1885 }
1886
HandleDnsBrowseResponse(otError aError,const otDnsBrowseResponse * aResponse,void * aContext)1887 void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext)
1888 {
1889 static_cast<Interpreter *>(aContext)->HandleDnsBrowseResponse(aError, aResponse);
1890 }
1891
HandleDnsBrowseResponse(otError aError,const otDnsBrowseResponse * aResponse)1892 void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse)
1893 {
1894 char name[OT_DNS_MAX_NAME_SIZE];
1895 char label[OT_DNS_MAX_LABEL_SIZE];
1896 uint8_t txtBuffer[255];
1897 otDnsServiceInfo serviceInfo;
1898
1899 IgnoreError(otDnsBrowseResponseGetServiceName(aResponse, name, sizeof(name)));
1900
1901 OutputLine("DNS browse response for %s", name);
1902
1903 if (aError == OT_ERROR_NONE)
1904 {
1905 uint16_t index = 0;
1906
1907 while (otDnsBrowseResponseGetServiceInstance(aResponse, index, label, sizeof(label)) == OT_ERROR_NONE)
1908 {
1909 OutputLine("%s", label);
1910 index++;
1911
1912 serviceInfo.mHostNameBuffer = name;
1913 serviceInfo.mHostNameBufferSize = sizeof(name);
1914 serviceInfo.mTxtData = txtBuffer;
1915 serviceInfo.mTxtDataSize = sizeof(txtBuffer);
1916
1917 if (otDnsBrowseResponseGetServiceInfo(aResponse, label, &serviceInfo) == OT_ERROR_NONE)
1918 {
1919 OutputDnsServiceInfo(kIndentSize, serviceInfo);
1920 }
1921
1922 OutputLine("");
1923 }
1924 }
1925
1926 OutputResult(aError);
1927 }
1928
HandleDnsServiceResponse(otError aError,const otDnsServiceResponse * aResponse,void * aContext)1929 void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse, void *aContext)
1930 {
1931 static_cast<Interpreter *>(aContext)->HandleDnsServiceResponse(aError, aResponse);
1932 }
1933
HandleDnsServiceResponse(otError aError,const otDnsServiceResponse * aResponse)1934 void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse)
1935 {
1936 char name[OT_DNS_MAX_NAME_SIZE];
1937 char label[OT_DNS_MAX_LABEL_SIZE];
1938 uint8_t txtBuffer[255];
1939 otDnsServiceInfo serviceInfo;
1940
1941 IgnoreError(otDnsServiceResponseGetServiceName(aResponse, label, sizeof(label), name, sizeof(name)));
1942
1943 OutputLine("DNS service resolution response for %s for service %s", label, name);
1944
1945 if (aError == OT_ERROR_NONE)
1946 {
1947 serviceInfo.mHostNameBuffer = name;
1948 serviceInfo.mHostNameBufferSize = sizeof(name);
1949 serviceInfo.mTxtData = txtBuffer;
1950 serviceInfo.mTxtDataSize = sizeof(txtBuffer);
1951
1952 if (otDnsServiceResponseGetServiceInfo(aResponse, &serviceInfo) == OT_ERROR_NONE)
1953 {
1954 OutputDnsServiceInfo(/* aIndetSize */ 0, serviceInfo);
1955 OutputLine("");
1956 }
1957 }
1958
1959 OutputResult(aError);
1960 }
1961
1962 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
1963 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
1964
1965 #if OPENTHREAD_FTD
EidCacheStateToString(otCacheEntryState aState)1966 const char *EidCacheStateToString(otCacheEntryState aState)
1967 {
1968 static const char *const kStateStrings[4] = {
1969 "cache",
1970 "snoop",
1971 "query",
1972 "retry",
1973 };
1974
1975 return Interpreter::Stringify(aState, kStateStrings);
1976 }
1977
OutputEidCacheEntry(const otCacheEntryInfo & aEntry)1978 void Interpreter::OutputEidCacheEntry(const otCacheEntryInfo &aEntry)
1979 {
1980 OutputIp6Address(aEntry.mTarget);
1981 OutputFormat(" %04x", aEntry.mRloc16);
1982 OutputFormat(" %s", EidCacheStateToString(aEntry.mState));
1983 OutputFormat(" canEvict=%d", aEntry.mCanEvict);
1984
1985 if (aEntry.mState == OT_CACHE_ENTRY_STATE_CACHED)
1986 {
1987 if (aEntry.mValidLastTrans)
1988 {
1989 OutputFormat(" transTime=%u eid=", aEntry.mLastTransTime);
1990 OutputIp6Address(aEntry.mMeshLocalEid);
1991 }
1992 }
1993 else
1994 {
1995 OutputFormat(" timeout=%u", aEntry.mTimeout);
1996 }
1997
1998 if (aEntry.mState == OT_CACHE_ENTRY_STATE_RETRY_QUERY)
1999 {
2000 OutputFormat(" retryDelay=%u", aEntry.mRetryDelay);
2001 }
2002
2003 OutputLine("");
2004 }
2005
Process(Arg aArgs[])2006 template <> otError Interpreter::Process<Cmd("eidcache")>(Arg aArgs[])
2007 {
2008 OT_UNUSED_VARIABLE(aArgs);
2009
2010 otCacheEntryIterator iterator;
2011 otCacheEntryInfo entry;
2012
2013 memset(&iterator, 0, sizeof(iterator));
2014
2015 for (uint8_t i = 0;; i++)
2016 {
2017 SuccessOrExit(otThreadGetNextCacheEntry(GetInstancePtr(), &entry, &iterator));
2018 OutputEidCacheEntry(entry);
2019 }
2020
2021 exit:
2022 return OT_ERROR_NONE;
2023 }
2024 #endif
2025
2026 /**
2027 * @cli eui64
2028 * @code
2029 * eui64
2030 * 0615aae900124b00
2031 * Done
2032 * @endcode
2033 * @par api_copy
2034 * #otPlatRadioGetIeeeEui64
2035 */
Process(Arg aArgs[])2036 template <> otError Interpreter::Process<Cmd("eui64")>(Arg aArgs[])
2037 {
2038 OT_UNUSED_VARIABLE(aArgs);
2039
2040 otError error = OT_ERROR_NONE;
2041 otExtAddress extAddress;
2042
2043 VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
2044
2045 otLinkGetFactoryAssignedIeeeEui64(GetInstancePtr(), &extAddress);
2046 OutputExtAddressLine(extAddress);
2047
2048 exit:
2049 return error;
2050 }
2051
Process(Arg aArgs[])2052 template <> otError Interpreter::Process<Cmd("extaddr")>(Arg aArgs[])
2053 {
2054 otError error = OT_ERROR_NONE;
2055
2056 if (aArgs[0].IsEmpty())
2057 {
2058 OutputExtAddressLine(*otLinkGetExtendedAddress(GetInstancePtr()));
2059 }
2060 else
2061 {
2062 otExtAddress extAddress;
2063
2064 SuccessOrExit(error = aArgs[0].ParseAsHexString(extAddress.m8));
2065 error = otLinkSetExtendedAddress(GetInstancePtr(), &extAddress);
2066 }
2067
2068 exit:
2069 return error;
2070 }
2071
Process(Arg aArgs[])2072 template <> otError Interpreter::Process<Cmd("log")>(Arg aArgs[])
2073 {
2074 otError error = OT_ERROR_NONE;
2075
2076 if (aArgs[0] == "level")
2077 {
2078 if (aArgs[1].IsEmpty())
2079 {
2080 OutputLine("%d", otLoggingGetLevel());
2081 }
2082 else
2083 {
2084 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
2085 uint8_t level;
2086
2087 VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
2088 SuccessOrExit(error = aArgs[1].ParseAsUint8(level));
2089 error = otLoggingSetLevel(static_cast<otLogLevel>(level));
2090 #else
2091 error = OT_ERROR_INVALID_ARGS;
2092 #endif
2093 }
2094 }
2095 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX
2096 else if (aArgs[0] == "filename")
2097 {
2098 VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
2099 SuccessOrExit(error = otPlatDebugUart_logfile(aArgs[1].GetCString()));
2100 }
2101 #endif
2102 else
2103 {
2104 ExitNow(error = OT_ERROR_INVALID_ARGS);
2105 }
2106
2107 exit:
2108 return error;
2109 }
2110
Process(Arg aArgs[])2111 template <> otError Interpreter::Process<Cmd("extpanid")>(Arg aArgs[])
2112 {
2113 otError error = OT_ERROR_NONE;
2114
2115 if (aArgs[0].IsEmpty())
2116 {
2117 OutputBytesLine(otThreadGetExtendedPanId(GetInstancePtr())->m8);
2118 }
2119 else
2120 {
2121 otExtendedPanId extPanId;
2122
2123 SuccessOrExit(error = aArgs[0].ParseAsHexString(extPanId.m8));
2124 error = otThreadSetExtendedPanId(GetInstancePtr(), &extPanId);
2125 }
2126
2127 exit:
2128 return error;
2129 }
2130
Process(Arg aArgs[])2131 template <> otError Interpreter::Process<Cmd("factoryreset")>(Arg aArgs[])
2132 {
2133 OT_UNUSED_VARIABLE(aArgs);
2134
2135 otInstanceFactoryReset(GetInstancePtr());
2136
2137 return OT_ERROR_NONE;
2138 }
2139
2140 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Process(Arg aArgs[])2141 template <> otError Interpreter::Process<Cmd("fake")>(Arg aArgs[])
2142 {
2143 otError error = OT_ERROR_INVALID_COMMAND;
2144
2145 if (aArgs[0] == "/a/an")
2146 {
2147 otIp6Address destination, target;
2148 otIp6InterfaceIdentifier mlIid;
2149
2150 SuccessOrExit(error = aArgs[1].ParseAsIp6Address(destination));
2151 SuccessOrExit(error = aArgs[2].ParseAsIp6Address(target));
2152 SuccessOrExit(error = aArgs[3].ParseAsHexString(mlIid.mFields.m8));
2153 otThreadSendAddressNotification(GetInstancePtr(), &destination, &target, &mlIid);
2154 }
2155 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
2156 else if (aArgs[0] == "/b/ba")
2157 {
2158 otIp6Address target;
2159 otIp6InterfaceIdentifier mlIid;
2160 uint32_t timeSinceLastTransaction;
2161
2162 SuccessOrExit(error = aArgs[1].ParseAsIp6Address(target));
2163 SuccessOrExit(error = aArgs[2].ParseAsHexString(mlIid.mFields.m8));
2164 SuccessOrExit(error = aArgs[3].ParseAsUint32(timeSinceLastTransaction));
2165
2166 error = otThreadSendProactiveBackboneNotification(GetInstancePtr(), &target, &mlIid, timeSinceLastTransaction);
2167 }
2168 #endif
2169
2170 exit:
2171 return error;
2172 }
2173 #endif
2174
Process(Arg aArgs[])2175 template <> otError Interpreter::Process<Cmd("fem")>(Arg aArgs[])
2176 {
2177 otError error = OT_ERROR_NONE;
2178
2179 if (aArgs[0].IsEmpty())
2180 {
2181 int8_t lnaGain;
2182
2183 SuccessOrExit(error = otPlatRadioGetFemLnaGain(GetInstancePtr(), &lnaGain));
2184 OutputLine("LNA gain %d dBm", lnaGain);
2185 }
2186 else if (aArgs[0] == "lnagain")
2187 {
2188 if (aArgs[1].IsEmpty())
2189 {
2190 int8_t lnaGain;
2191
2192 SuccessOrExit(error = otPlatRadioGetFemLnaGain(GetInstancePtr(), &lnaGain));
2193 OutputLine("%d", lnaGain);
2194 }
2195 else
2196 {
2197 int8_t lnaGain;
2198
2199 SuccessOrExit(error = aArgs[1].ParseAsInt8(lnaGain));
2200 SuccessOrExit(error = otPlatRadioSetFemLnaGain(GetInstancePtr(), lnaGain));
2201 }
2202 }
2203 else
2204 {
2205 error = OT_ERROR_INVALID_ARGS;
2206 }
2207
2208 exit:
2209 return error;
2210 }
2211
Process(Arg aArgs[])2212 template <> otError Interpreter::Process<Cmd("ifconfig")>(Arg aArgs[])
2213 {
2214 otError error = OT_ERROR_NONE;
2215
2216 if (aArgs[0].IsEmpty())
2217 {
2218 if (otIp6IsEnabled(GetInstancePtr()))
2219 {
2220 OutputLine("up");
2221 }
2222 else
2223 {
2224 OutputLine("down");
2225 }
2226 }
2227 else if (aArgs[0] == "up")
2228 {
2229 SuccessOrExit(error = otIp6SetEnabled(GetInstancePtr(), true));
2230 }
2231 else if (aArgs[0] == "down")
2232 {
2233 SuccessOrExit(error = otIp6SetEnabled(GetInstancePtr(), false));
2234 }
2235 else
2236 {
2237 ExitNow(error = OT_ERROR_INVALID_ARGS);
2238 }
2239
2240 exit:
2241 return error;
2242 }
2243
AddressOriginToString(uint8_t aOrigin)2244 const char *Interpreter::AddressOriginToString(uint8_t aOrigin)
2245 {
2246 static const char *const kOriginStrings[4] = {
2247 "thread", // 0, OT_ADDRESS_ORIGIN_THREAD
2248 "slaac", // 1, OT_ADDRESS_ORIGIN_SLAAC
2249 "dhcp6", // 2, OT_ADDRESS_ORIGIN_DHCPV6
2250 "manual", // 3, OT_ADDRESS_ORIGIN_MANUAL
2251 };
2252
2253 static_assert(0 == OT_ADDRESS_ORIGIN_THREAD, "OT_ADDRESS_ORIGIN_THREAD value is incorrect");
2254 static_assert(1 == OT_ADDRESS_ORIGIN_SLAAC, "OT_ADDRESS_ORIGIN_SLAAC value is incorrect");
2255 static_assert(2 == OT_ADDRESS_ORIGIN_DHCPV6, "OT_ADDRESS_ORIGIN_DHCPV6 value is incorrect");
2256 static_assert(3 == OT_ADDRESS_ORIGIN_MANUAL, "OT_ADDRESS_ORIGIN_MANUAL value is incorrect");
2257
2258 return Stringify(aOrigin, kOriginStrings);
2259 }
2260
Process(Arg aArgs[])2261 template <> otError Interpreter::Process<Cmd("ipaddr")>(Arg aArgs[])
2262 {
2263 otError error = OT_ERROR_NONE;
2264 bool verbose = false;
2265
2266 if (aArgs[0] == "-v")
2267 {
2268 aArgs++;
2269 verbose = true;
2270 }
2271
2272 if (aArgs[0].IsEmpty())
2273 {
2274 const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(GetInstancePtr());
2275
2276 for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
2277 {
2278 OutputIp6Address(addr->mAddress);
2279
2280 if (verbose)
2281 {
2282 OutputFormat(" origin:%s", AddressOriginToString(addr->mAddressOrigin));
2283 }
2284
2285 OutputLine("");
2286 }
2287 }
2288 else if (aArgs[0] == "add")
2289 {
2290 otNetifAddress address;
2291
2292 SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address.mAddress));
2293 address.mPrefixLength = 64;
2294 address.mPreferred = true;
2295 address.mValid = true;
2296 address.mAddressOrigin = OT_ADDRESS_ORIGIN_MANUAL;
2297
2298 error = otIp6AddUnicastAddress(GetInstancePtr(), &address);
2299 }
2300 else if (aArgs[0] == "del")
2301 {
2302 otIp6Address address;
2303
2304 SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
2305 error = otIp6RemoveUnicastAddress(GetInstancePtr(), &address);
2306 }
2307 else if (aArgs[0] == "linklocal")
2308 {
2309 OutputIp6AddressLine(*otThreadGetLinkLocalIp6Address(GetInstancePtr()));
2310 }
2311 else if (aArgs[0] == "rloc")
2312 {
2313 OutputIp6AddressLine(*otThreadGetRloc(GetInstancePtr()));
2314 }
2315 else if (aArgs[0] == "mleid")
2316 {
2317 OutputIp6AddressLine(*otThreadGetMeshLocalEid(GetInstancePtr()));
2318 }
2319 else
2320 {
2321 error = OT_ERROR_INVALID_COMMAND;
2322 }
2323
2324 exit:
2325 return error;
2326 }
2327
Process(Arg aArgs[])2328 template <> otError Interpreter::Process<Cmd("ipmaddr")>(Arg aArgs[])
2329 {
2330 otError error = OT_ERROR_NONE;
2331
2332 if (aArgs[0].IsEmpty())
2333 {
2334 for (const otNetifMulticastAddress *addr = otIp6GetMulticastAddresses(GetInstancePtr()); addr;
2335 addr = addr->mNext)
2336 {
2337 OutputIp6AddressLine(addr->mAddress);
2338 }
2339 }
2340 else if (aArgs[0] == "add")
2341 {
2342 otIp6Address address;
2343
2344 aArgs++;
2345
2346 do
2347 {
2348 SuccessOrExit(error = aArgs->ParseAsIp6Address(address));
2349 SuccessOrExit(error = otIp6SubscribeMulticastAddress(GetInstancePtr(), &address));
2350 }
2351 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
2352 while (!(++aArgs)->IsEmpty());
2353 #else
2354 while (false);
2355 #endif
2356 }
2357 else if (aArgs[0] == "del")
2358 {
2359 otIp6Address address;
2360
2361 SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
2362 error = otIp6UnsubscribeMulticastAddress(GetInstancePtr(), &address);
2363 }
2364 else if (aArgs[0] == "promiscuous")
2365 {
2366 if (aArgs[1].IsEmpty())
2367 {
2368 OutputEnabledDisabledStatus(otIp6IsMulticastPromiscuousEnabled(GetInstancePtr()));
2369 }
2370 else
2371 {
2372 bool enable;
2373
2374 SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable));
2375 otIp6SetMulticastPromiscuousEnabled(GetInstancePtr(), enable);
2376 }
2377 }
2378 else if (aArgs[0] == "llatn")
2379 {
2380 OutputIp6AddressLine(*otThreadGetLinkLocalAllThreadNodesMulticastAddress(GetInstancePtr()));
2381 }
2382 else if (aArgs[0] == "rlatn")
2383 {
2384 OutputIp6AddressLine(*otThreadGetRealmLocalAllThreadNodesMulticastAddress(GetInstancePtr()));
2385 }
2386 else
2387 {
2388 error = OT_ERROR_INVALID_COMMAND;
2389 }
2390
2391 exit:
2392 return error;
2393 }
2394
Process(Arg aArgs[])2395 template <> otError Interpreter::Process<Cmd("keysequence")>(Arg aArgs[])
2396 {
2397 otError error = OT_ERROR_INVALID_ARGS;
2398
2399 if (aArgs[0] == "counter")
2400 {
2401 error = ProcessGetSet(aArgs + 1, otThreadGetKeySequenceCounter, otThreadSetKeySequenceCounter);
2402 }
2403 else if (aArgs[0] == "guardtime")
2404 {
2405 error = ProcessGetSet(aArgs + 1, otThreadGetKeySwitchGuardTime, otThreadSetKeySwitchGuardTime);
2406 }
2407
2408 return error;
2409 }
2410
Process(Arg aArgs[])2411 template <> otError Interpreter::Process<Cmd("leaderdata")>(Arg aArgs[])
2412 {
2413 OT_UNUSED_VARIABLE(aArgs);
2414
2415 otError error;
2416 otLeaderData leaderData;
2417
2418 SuccessOrExit(error = otThreadGetLeaderData(GetInstancePtr(), &leaderData));
2419
2420 OutputLine("Partition ID: %u", leaderData.mPartitionId);
2421 OutputLine("Weighting: %d", leaderData.mWeighting);
2422 OutputLine("Data Version: %d", leaderData.mDataVersion);
2423 OutputLine("Stable Data Version: %d", leaderData.mStableDataVersion);
2424 OutputLine("Leader Router ID: %d", leaderData.mLeaderRouterId);
2425
2426 exit:
2427 return error;
2428 }
2429
2430 #if OPENTHREAD_FTD
Process(Arg aArgs[])2431 template <> otError Interpreter::Process<Cmd("partitionid")>(Arg aArgs[])
2432 {
2433 otError error = OT_ERROR_INVALID_COMMAND;
2434
2435 if (aArgs[0].IsEmpty())
2436 {
2437 OutputLine("%u", otThreadGetPartitionId(GetInstancePtr()));
2438 error = OT_ERROR_NONE;
2439 }
2440 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
2441 else if (aArgs[0] == "preferred")
2442 {
2443 error = ProcessGetSet(aArgs + 1, otThreadGetPreferredLeaderPartitionId, otThreadSetPreferredLeaderPartitionId);
2444 }
2445 #endif
2446
2447 return error;
2448 }
2449
Process(Arg aArgs[])2450 template <> otError Interpreter::Process<Cmd("leaderweight")>(Arg aArgs[])
2451 {
2452 return ProcessGetSet(aArgs, otThreadGetLocalLeaderWeight, otThreadSetLocalLeaderWeight);
2453 }
2454 #endif // OPENTHREAD_FTD
2455
2456 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
HandleLinkMetricsReport(const otIp6Address * aAddress,const otLinkMetricsValues * aMetricsValues,uint8_t aStatus,void * aContext)2457 void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress,
2458 const otLinkMetricsValues *aMetricsValues,
2459 uint8_t aStatus,
2460 void * aContext)
2461 {
2462 static_cast<Interpreter *>(aContext)->HandleLinkMetricsReport(aAddress, aMetricsValues, aStatus);
2463 }
2464
PrintLinkMetricsValue(const otLinkMetricsValues * aMetricsValues)2465 void Interpreter::PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValues)
2466 {
2467 const char kLinkMetricsTypeCount[] = "(Count/Summation)";
2468 const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)";
2469
2470 if (aMetricsValues->mMetrics.mPduCount)
2471 {
2472 OutputLine(" - PDU Counter: %d %s", aMetricsValues->mPduCountValue, kLinkMetricsTypeCount);
2473 }
2474
2475 if (aMetricsValues->mMetrics.mLqi)
2476 {
2477 OutputLine(" - LQI: %d %s", aMetricsValues->mLqiValue, kLinkMetricsTypeAverage);
2478 }
2479
2480 if (aMetricsValues->mMetrics.mLinkMargin)
2481 {
2482 OutputLine(" - Margin: %d (dB) %s", aMetricsValues->mLinkMarginValue, kLinkMetricsTypeAverage);
2483 }
2484
2485 if (aMetricsValues->mMetrics.mRssi)
2486 {
2487 OutputLine(" - RSSI: %d (dBm) %s", aMetricsValues->mRssiValue, kLinkMetricsTypeAverage);
2488 }
2489 }
2490
HandleLinkMetricsReport(const otIp6Address * aAddress,const otLinkMetricsValues * aMetricsValues,uint8_t aStatus)2491 void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress,
2492 const otLinkMetricsValues *aMetricsValues,
2493 uint8_t aStatus)
2494 {
2495 OutputFormat("Received Link Metrics Report from: ");
2496 OutputIp6AddressLine(*aAddress);
2497
2498 if (aMetricsValues != nullptr)
2499 {
2500 PrintLinkMetricsValue(aMetricsValues);
2501 }
2502 else
2503 {
2504 OutputLine("Link Metrics Report, status: %s", LinkMetricsStatusToStr(aStatus));
2505 }
2506 }
2507
HandleLinkMetricsMgmtResponse(const otIp6Address * aAddress,uint8_t aStatus,void * aContext)2508 void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus, void *aContext)
2509 {
2510 static_cast<Interpreter *>(aContext)->HandleLinkMetricsMgmtResponse(aAddress, aStatus);
2511 }
2512
HandleLinkMetricsMgmtResponse(const otIp6Address * aAddress,uint8_t aStatus)2513 void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus)
2514 {
2515 OutputFormat("Received Link Metrics Management Response from: ");
2516 OutputIp6AddressLine(*aAddress);
2517
2518 OutputLine("Status: %s", LinkMetricsStatusToStr(aStatus));
2519 }
2520
HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,const otExtAddress * aExtAddress,const otLinkMetricsValues * aMetricsValues,void * aContext)2521 void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
2522 const otExtAddress * aExtAddress,
2523 const otLinkMetricsValues *aMetricsValues,
2524 void * aContext)
2525 {
2526 static_cast<Interpreter *>(aContext)->HandleLinkMetricsEnhAckProbingIe(aShortAddress, aExtAddress, aMetricsValues);
2527 }
2528
HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,const otExtAddress * aExtAddress,const otLinkMetricsValues * aMetricsValues)2529 void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
2530 const otExtAddress * aExtAddress,
2531 const otLinkMetricsValues *aMetricsValues)
2532 {
2533 OutputFormat("Received Link Metrics data in Enh Ack from neighbor, short address:0x%02x , extended address:",
2534 aShortAddress);
2535 OutputExtAddressLine(*aExtAddress);
2536
2537 if (aMetricsValues != nullptr)
2538 {
2539 PrintLinkMetricsValue(aMetricsValues);
2540 }
2541 }
2542
LinkMetricsStatusToStr(uint8_t aStatus)2543 const char *Interpreter::LinkMetricsStatusToStr(uint8_t aStatus)
2544 {
2545 static const char *const kStatusStrings[] = {
2546 "Success", // (0) OT_LINK_METRICS_STATUS_SUCCESS
2547 "Cannot support new series", // (1) OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES
2548 "Series ID already registered", // (2) OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED
2549 "Series ID not recognized", // (3) OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED
2550 "No matching series ID", // (4) OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED
2551 };
2552
2553 const char *str = "Unknown error";
2554
2555 static_assert(0 == OT_LINK_METRICS_STATUS_SUCCESS, "STATUS_SUCCESS is incorrect");
2556 static_assert(1 == OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES, "CANNOT_SUPPORT_NEW_SERIES is incorrect");
2557 static_assert(2 == OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED, "SERIESID_ALREADY_REGISTERED is incorrect");
2558 static_assert(3 == OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED, "SERIESID_NOT_RECOGNIZED is incorrect");
2559 static_assert(4 == OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED, "NO_MATCHING_FRAMES_RECEIVED is incorrect");
2560
2561 if (aStatus < OT_ARRAY_LENGTH(kStatusStrings))
2562 {
2563 str = kStatusStrings[aStatus];
2564 }
2565 else if (aStatus == OT_LINK_METRICS_STATUS_OTHER_ERROR)
2566 {
2567 str = "Other error";
2568 }
2569
2570 return str;
2571 }
2572
Process(Arg aArgs[])2573 template <> otError Interpreter::Process<Cmd("linkmetrics")>(Arg aArgs[])
2574 {
2575 otError error = OT_ERROR_INVALID_COMMAND;
2576
2577 if (aArgs[0] == "query")
2578 {
2579 otIp6Address address;
2580 otLinkMetrics linkMetrics;
2581
2582 SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
2583
2584 if (aArgs[2] == "single")
2585 {
2586 VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
2587 SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
2588 error = otLinkMetricsQuery(GetInstancePtr(), &address, /* aSeriesId */ 0, &linkMetrics,
2589 &Interpreter::HandleLinkMetricsReport, this);
2590 }
2591 else if (aArgs[2] == "forward")
2592 {
2593 uint8_t seriesId;
2594
2595 SuccessOrExit(error = aArgs[3].ParseAsUint8(seriesId));
2596 error = otLinkMetricsQuery(GetInstancePtr(), &address, seriesId, nullptr,
2597 &Interpreter::HandleLinkMetricsReport, this);
2598 }
2599 else
2600 {
2601 error = OT_ERROR_INVALID_ARGS;
2602 }
2603 }
2604 else if (aArgs[0] == "mgmt")
2605 {
2606 error = ProcessLinkMetricsMgmt(aArgs + 1);
2607 }
2608 else if (aArgs[0] == "probe")
2609 {
2610 otIp6Address address;
2611 uint8_t seriesId;
2612 uint8_t length;
2613
2614 SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
2615 SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
2616 SuccessOrExit(error = aArgs[3].ParseAsUint8(length));
2617
2618 error = otLinkMetricsSendLinkProbe(GetInstancePtr(), &address, seriesId, length);
2619 }
2620
2621 exit:
2622 return error;
2623 }
2624
ParseLinkMetricsFlags(otLinkMetrics & aLinkMetrics,const Arg & aFlags)2625 otError Interpreter::ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags)
2626 {
2627 otError error = OT_ERROR_NONE;
2628
2629 memset(&aLinkMetrics, 0, sizeof(aLinkMetrics));
2630
2631 for (const char *arg = aFlags.GetCString(); *arg != '\0'; arg++)
2632 {
2633 switch (*arg)
2634 {
2635 case 'p':
2636 aLinkMetrics.mPduCount = true;
2637 break;
2638
2639 case 'q':
2640 aLinkMetrics.mLqi = true;
2641 break;
2642
2643 case 'm':
2644 aLinkMetrics.mLinkMargin = true;
2645 break;
2646
2647 case 'r':
2648 aLinkMetrics.mRssi = true;
2649 break;
2650
2651 default:
2652 ExitNow(error = OT_ERROR_INVALID_ARGS);
2653 }
2654 }
2655
2656 exit:
2657 return error;
2658 }
2659
ProcessLinkMetricsMgmt(Arg aArgs[])2660 otError Interpreter::ProcessLinkMetricsMgmt(Arg aArgs[])
2661 {
2662 otError error;
2663 otIp6Address address;
2664 otLinkMetricsSeriesFlags seriesFlags;
2665 bool clear = false;
2666
2667 SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
2668
2669 memset(&seriesFlags, 0, sizeof(otLinkMetricsSeriesFlags));
2670
2671 if (aArgs[1] == "forward")
2672 {
2673 uint8_t seriesId;
2674 otLinkMetrics linkMetrics;
2675
2676 memset(&linkMetrics, 0, sizeof(otLinkMetrics));
2677 SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
2678 VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
2679
2680 for (const char *arg = aArgs[3].GetCString(); *arg != '\0'; arg++)
2681 {
2682 switch (*arg)
2683 {
2684 case 'l':
2685 seriesFlags.mLinkProbe = true;
2686 break;
2687
2688 case 'd':
2689 seriesFlags.mMacData = true;
2690 break;
2691
2692 case 'r':
2693 seriesFlags.mMacDataRequest = true;
2694 break;
2695
2696 case 'a':
2697 seriesFlags.mMacAck = true;
2698 break;
2699
2700 case 'X':
2701 VerifyOrExit(arg == aArgs[3].GetCString() && *(arg + 1) == '\0' && aArgs[4].IsEmpty(),
2702 error = OT_ERROR_INVALID_ARGS); // Ensure the flags only contain 'X'
2703 clear = true;
2704 break;
2705
2706 default:
2707 ExitNow(error = OT_ERROR_INVALID_ARGS);
2708 }
2709 }
2710
2711 if (!clear)
2712 {
2713 VerifyOrExit(!aArgs[4].IsEmpty() && aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
2714 SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[4]));
2715 }
2716
2717 error = otLinkMetricsConfigForwardTrackingSeries(GetInstancePtr(), &address, seriesId, seriesFlags,
2718 clear ? nullptr : &linkMetrics,
2719 &Interpreter::HandleLinkMetricsMgmtResponse, this);
2720 }
2721 else if (aArgs[1] == "enhanced-ack")
2722 {
2723 otLinkMetricsEnhAckFlags enhAckFlags;
2724 otLinkMetrics linkMetrics;
2725 otLinkMetrics * pLinkMetrics = &linkMetrics;
2726
2727 if (aArgs[2] == "clear")
2728 {
2729 enhAckFlags = OT_LINK_METRICS_ENH_ACK_CLEAR;
2730 pLinkMetrics = nullptr;
2731 }
2732 else if (aArgs[2] == "register")
2733 {
2734 enhAckFlags = OT_LINK_METRICS_ENH_ACK_REGISTER;
2735 VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
2736 SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
2737 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
2738 if (aArgs[4] == "r")
2739 {
2740 linkMetrics.mReserved = true;
2741 }
2742 #endif
2743 }
2744 else
2745 {
2746 ExitNow(error = OT_ERROR_INVALID_ARGS);
2747 }
2748
2749 error = otLinkMetricsConfigEnhAckProbing(GetInstancePtr(), &address, enhAckFlags, pLinkMetrics,
2750 &Interpreter::HandleLinkMetricsMgmtResponse, this,
2751 &Interpreter::HandleLinkMetricsEnhAckProbingIe, this);
2752 }
2753 else
2754 {
2755 error = OT_ERROR_INVALID_ARGS;
2756 }
2757
2758 exit:
2759 return error;
2760 }
2761
2762 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
2763
2764 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
2765
Process(Arg aArgs[])2766 template <> otError Interpreter::Process<Cmd("locate")>(Arg aArgs[])
2767 {
2768 otError error = OT_ERROR_INVALID_ARGS;
2769 otIp6Address anycastAddress;
2770
2771 if (aArgs[0].IsEmpty())
2772 {
2773 OutputLine(otThreadIsAnycastLocateInProgress(GetInstancePtr()) ? "In Progress" : "Idle");
2774 ExitNow(error = OT_ERROR_NONE);
2775 }
2776
2777 SuccessOrExit(error = aArgs[0].ParseAsIp6Address(anycastAddress));
2778 SuccessOrExit(error =
2779 otThreadLocateAnycastDestination(GetInstancePtr(), &anycastAddress, HandleLocateResult, this));
2780 SetCommandTimeout(kLocateTimeoutMsecs);
2781 mLocateInProgress = true;
2782 error = OT_ERROR_PENDING;
2783
2784 exit:
2785 return error;
2786 }
2787
HandleLocateResult(void * aContext,otError aError,const otIp6Address * aMeshLocalAddress,uint16_t aRloc16)2788 void Interpreter::HandleLocateResult(void * aContext,
2789 otError aError,
2790 const otIp6Address *aMeshLocalAddress,
2791 uint16_t aRloc16)
2792 {
2793 static_cast<Interpreter *>(aContext)->HandleLocateResult(aError, aMeshLocalAddress, aRloc16);
2794 }
2795
HandleLocateResult(otError aError,const otIp6Address * aMeshLocalAddress,uint16_t aRloc16)2796 void Interpreter::HandleLocateResult(otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16)
2797 {
2798 VerifyOrExit(mLocateInProgress);
2799
2800 mLocateInProgress = false;
2801
2802 if (aError == OT_ERROR_NONE)
2803 {
2804 OutputIp6Address(*aMeshLocalAddress);
2805 OutputLine(" 0x%04x", aRloc16);
2806 }
2807
2808 OutputResult(aError);
2809
2810 exit:
2811 return;
2812 }
2813
2814 #endif // OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
2815
2816 #if OPENTHREAD_FTD
Process(Arg aArgs[])2817 template <> otError Interpreter::Process<Cmd("pskc")>(Arg aArgs[])
2818 {
2819 otError error = OT_ERROR_NONE;
2820 otPskc pskc;
2821
2822 if (aArgs[0].IsEmpty())
2823 {
2824 otThreadGetPskc(GetInstancePtr(), &pskc);
2825 OutputBytesLine(pskc.m8);
2826 }
2827 else
2828 {
2829 if (aArgs[1].IsEmpty())
2830 {
2831 SuccessOrExit(error = aArgs[0].ParseAsHexString(pskc.m8));
2832 }
2833 else if (aArgs[0] == "-p")
2834 {
2835 SuccessOrExit(error = otDatasetGeneratePskc(
2836 aArgs[1].GetCString(),
2837 reinterpret_cast<const otNetworkName *>(otThreadGetNetworkName(GetInstancePtr())),
2838 otThreadGetExtendedPanId(GetInstancePtr()), &pskc));
2839 }
2840 else
2841 {
2842 ExitNow(error = OT_ERROR_INVALID_ARGS);
2843 }
2844
2845 error = otThreadSetPskc(GetInstancePtr(), &pskc);
2846 }
2847
2848 exit:
2849 return error;
2850 }
2851
2852 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
Process(Arg aArgs[])2853 template <> otError Interpreter::Process<Cmd("pskcref")>(Arg aArgs[])
2854 {
2855 otError error = OT_ERROR_NONE;
2856
2857 if (aArgs[0].IsEmpty())
2858 {
2859 OutputLine("0x%04x", otThreadGetPskcRef(GetInstancePtr()));
2860 }
2861 else
2862 {
2863 otPskcRef pskcRef;
2864
2865 if (aArgs[1].IsEmpty())
2866 {
2867 SuccessOrExit(error = aArgs[0].ParseAsUint32(pskcRef));
2868 }
2869 else
2870 {
2871 ExitNow(error = OT_ERROR_INVALID_ARGS);
2872 }
2873
2874 error = otThreadSetPskcRef(GetInstancePtr(), pskcRef);
2875 }
2876
2877 exit:
2878 return error;
2879 }
2880 #endif
2881 #endif // OPENTHREAD_FTD
2882
2883 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Process(Arg aArgs[])2884 template <> otError Interpreter::Process<Cmd("mliid")>(Arg aArgs[])
2885 {
2886 otError error = OT_ERROR_NONE;
2887 otIp6InterfaceIdentifier iid;
2888
2889 VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
2890
2891 SuccessOrExit(error = aArgs[0].ParseAsHexString(iid.mFields.m8));
2892 SuccessOrExit(error = otIp6SetMeshLocalIid(GetInstancePtr(), &iid));
2893
2894 exit:
2895 return error;
2896 }
2897 #endif
2898
2899 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
2900
Process(Arg aArgs[])2901 template <> otError Interpreter::Process<Cmd("mlr")>(Arg aArgs[])
2902 {
2903 otError error = OT_ERROR_INVALID_COMMAND;
2904
2905 if (aArgs[0] == "reg")
2906 {
2907 otIp6Address addresses[OT_IP6_MAX_MLR_ADDRESSES];
2908 uint32_t timeout;
2909 bool hasTimeout = false;
2910 uint8_t numAddresses = 0;
2911
2912 aArgs++;
2913
2914 while (aArgs->ParseAsIp6Address(addresses[numAddresses]) == OT_ERROR_NONE)
2915 {
2916 aArgs++;
2917 numAddresses++;
2918
2919 if (numAddresses == OT_ARRAY_LENGTH(addresses))
2920 {
2921 break;
2922 }
2923 }
2924
2925 if (aArgs->ParseAsUint32(timeout) == OT_ERROR_NONE)
2926 {
2927 aArgs++;
2928 hasTimeout = true;
2929 }
2930
2931 VerifyOrExit(aArgs->IsEmpty() && (numAddresses > 0), error = OT_ERROR_INVALID_ARGS);
2932
2933 SuccessOrExit(error = otIp6RegisterMulticastListeners(GetInstancePtr(), addresses, numAddresses,
2934 hasTimeout ? &timeout : nullptr,
2935 Interpreter::HandleMlrRegResult, this));
2936
2937 error = OT_ERROR_PENDING;
2938 }
2939
2940 exit:
2941 return error;
2942 }
2943
HandleMlrRegResult(void * aContext,otError aError,uint8_t aMlrStatus,const otIp6Address * aFailedAddresses,uint8_t aFailedAddressNum)2944 void Interpreter::HandleMlrRegResult(void * aContext,
2945 otError aError,
2946 uint8_t aMlrStatus,
2947 const otIp6Address *aFailedAddresses,
2948 uint8_t aFailedAddressNum)
2949 {
2950 static_cast<Interpreter *>(aContext)->HandleMlrRegResult(aError, aMlrStatus, aFailedAddresses, aFailedAddressNum);
2951 }
2952
HandleMlrRegResult(otError aError,uint8_t aMlrStatus,const otIp6Address * aFailedAddresses,uint8_t aFailedAddressNum)2953 void Interpreter::HandleMlrRegResult(otError aError,
2954 uint8_t aMlrStatus,
2955 const otIp6Address *aFailedAddresses,
2956 uint8_t aFailedAddressNum)
2957 {
2958 if (aError == OT_ERROR_NONE)
2959 {
2960 OutputLine("status %d, %d failed", aMlrStatus, aFailedAddressNum);
2961
2962 for (uint8_t i = 0; i < aFailedAddressNum; i++)
2963 {
2964 OutputIp6AddressLine(aFailedAddresses[i]);
2965 }
2966 }
2967
2968 OutputResult(aError);
2969 }
2970
2971 #endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
2972
Process(Arg aArgs[])2973 template <> otError Interpreter::Process<Cmd("mode")>(Arg aArgs[])
2974 {
2975 otError error = OT_ERROR_NONE;
2976 otLinkModeConfig linkMode;
2977
2978 memset(&linkMode, 0, sizeof(otLinkModeConfig));
2979
2980 if (aArgs[0].IsEmpty())
2981 {
2982 char linkModeString[kLinkModeStringSize];
2983
2984 OutputLine("%s", LinkModeToString(otThreadGetLinkMode(GetInstancePtr()), linkModeString));
2985 ExitNow();
2986 }
2987
2988 if (aArgs[0] != "-")
2989 {
2990 for (const char *arg = aArgs[0].GetCString(); *arg != '\0'; arg++)
2991 {
2992 switch (*arg)
2993 {
2994 case 'r':
2995 linkMode.mRxOnWhenIdle = true;
2996 break;
2997
2998 case 'd':
2999 linkMode.mDeviceType = true;
3000 break;
3001
3002 case 'n':
3003 linkMode.mNetworkData = true;
3004 break;
3005
3006 default:
3007 ExitNow(error = OT_ERROR_INVALID_ARGS);
3008 }
3009 }
3010 }
3011
3012 error = otThreadSetLinkMode(GetInstancePtr(), linkMode);
3013
3014 exit:
3015 return error;
3016 }
3017
Process(Arg aArgs[])3018 template <> otError Interpreter::Process<Cmd("multiradio")>(Arg aArgs[])
3019 {
3020 otError error = OT_ERROR_NONE;
3021
3022 OT_UNUSED_VARIABLE(aArgs);
3023
3024 if (aArgs[0].IsEmpty())
3025 {
3026 bool isFirst = true;
3027
3028 OutputFormat("[");
3029 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
3030 OutputFormat("15.4");
3031 isFirst = false;
3032 #endif
3033 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
3034 OutputFormat("%sTREL", isFirst ? "" : ", ");
3035 #endif
3036 OutputLine("]");
3037
3038 OT_UNUSED_VARIABLE(isFirst);
3039 }
3040 #if OPENTHREAD_CONFIG_MULTI_RADIO
3041 else if (aArgs[0] == "neighbor")
3042 {
3043 otMultiRadioNeighborInfo multiRadioInfo;
3044
3045 if (aArgs[1] == "list")
3046 {
3047 otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
3048 otNeighborInfo neighInfo;
3049
3050 while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighInfo) == OT_ERROR_NONE)
3051 {
3052 if (otMultiRadioGetNeighborInfo(GetInstancePtr(), &neighInfo.mExtAddress, &multiRadioInfo) !=
3053 OT_ERROR_NONE)
3054 {
3055 continue;
3056 }
3057
3058 OutputFormat("ExtAddr:");
3059 OutputExtAddress(neighInfo.mExtAddress);
3060 OutputFormat(", RLOC16:0x%04x, Radios:", neighInfo.mRloc16);
3061 OutputMultiRadioInfo(multiRadioInfo);
3062 }
3063 }
3064 else
3065 {
3066 otExtAddress extAddress;
3067
3068 SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddress.m8));
3069 SuccessOrExit(error = otMultiRadioGetNeighborInfo(GetInstancePtr(), &extAddress, &multiRadioInfo));
3070 OutputMultiRadioInfo(multiRadioInfo);
3071 }
3072 }
3073 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
3074 else
3075 {
3076 ExitNow(error = OT_ERROR_INVALID_COMMAND);
3077 }
3078
3079 exit:
3080 return error;
3081 }
3082
3083 #if OPENTHREAD_CONFIG_MULTI_RADIO
OutputMultiRadioInfo(const otMultiRadioNeighborInfo & aMultiRadioInfo)3084 void Interpreter::OutputMultiRadioInfo(const otMultiRadioNeighborInfo &aMultiRadioInfo)
3085 {
3086 bool isFirst = true;
3087
3088 OutputFormat("[");
3089
3090 if (aMultiRadioInfo.mSupportsIeee802154)
3091 {
3092 OutputFormat("15.4(%d)", aMultiRadioInfo.mIeee802154Info.mPreference);
3093 isFirst = false;
3094 }
3095
3096 if (aMultiRadioInfo.mSupportsTrelUdp6)
3097 {
3098 OutputFormat("%sTREL(%d)", isFirst ? "" : ", ", aMultiRadioInfo.mTrelUdp6Info.mPreference);
3099 }
3100
3101 OutputLine("]");
3102 }
3103 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
3104
3105 #if OPENTHREAD_FTD
Process(Arg aArgs[])3106 template <> otError Interpreter::Process<Cmd("neighbor")>(Arg aArgs[])
3107 {
3108 otError error = OT_ERROR_NONE;
3109 otNeighborInfo neighborInfo;
3110 bool isTable;
3111 otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
3112
3113 isTable = (aArgs[0] == "table");
3114
3115 if (isTable || (aArgs[0] == "list"))
3116 {
3117 if (isTable)
3118 {
3119 static const char *const kNeighborTableTitles[] = {
3120 "Role", "RLOC16", "Age", "Avg RSSI", "Last RSSI", "R", "D", "N", "Extended MAC",
3121 };
3122
3123 static const uint8_t kNeighborTableColumnWidths[] = {
3124 6, 8, 5, 10, 11, 1, 1, 1, 18,
3125 };
3126
3127 OutputTableHeader(kNeighborTableTitles, kNeighborTableColumnWidths);
3128 }
3129
3130 while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE)
3131 {
3132 if (isTable)
3133 {
3134 OutputFormat("| %3c ", neighborInfo.mIsChild ? 'C' : 'R');
3135 OutputFormat("| 0x%04x ", neighborInfo.mRloc16);
3136 OutputFormat("| %3d ", neighborInfo.mAge);
3137 OutputFormat("| %8d ", neighborInfo.mAverageRssi);
3138 OutputFormat("| %9d ", neighborInfo.mLastRssi);
3139 OutputFormat("|%1d", neighborInfo.mRxOnWhenIdle);
3140 OutputFormat("|%1d", neighborInfo.mFullThreadDevice);
3141 OutputFormat("|%1d", neighborInfo.mFullNetworkData);
3142 OutputFormat("| ");
3143 OutputExtAddress(neighborInfo.mExtAddress);
3144 OutputLine(" |");
3145 }
3146 else
3147 {
3148 OutputFormat("0x%04x ", neighborInfo.mRloc16);
3149 }
3150 }
3151
3152 OutputLine("");
3153 }
3154 else
3155 {
3156 error = OT_ERROR_INVALID_ARGS;
3157 }
3158
3159 return error;
3160 }
3161 #endif // OPENTHREAD_FTD
3162
Process(Arg aArgs[])3163 template <> otError Interpreter::Process<Cmd("netstat")>(Arg aArgs[])
3164 {
3165 OT_UNUSED_VARIABLE(aArgs);
3166
3167 static const char *const kNetstatTableTitles[] = {"Local Address", "Peer Address"};
3168 static const uint8_t kNetstatTableColumnWidths[] = {49, 49};
3169
3170 char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
3171
3172 OutputTableHeader(kNetstatTableTitles, kNetstatTableColumnWidths);
3173
3174 for (const otUdpSocket *socket = otUdpGetSockets(GetInstancePtr()); socket != nullptr; socket = socket->mNext)
3175 {
3176 otIp6SockAddrToString(&socket->mSockName, string, sizeof(string));
3177 OutputFormat("| %-47s ", string);
3178 otIp6SockAddrToString(&socket->mPeerName, string, sizeof(string));
3179 OutputLine("| %-47s |", string);
3180 }
3181
3182 return OT_ERROR_NONE;
3183 }
3184
3185 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
Process(Arg aArgs[])3186 template <> otError Interpreter::Process<Cmd("service")>(Arg aArgs[])
3187 {
3188 otError error = OT_ERROR_INVALID_COMMAND;
3189 otServiceConfig cfg;
3190
3191 if (aArgs[0].IsEmpty())
3192 {
3193 otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
3194 otServiceConfig config;
3195
3196 while (otServerGetNextService(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
3197 {
3198 mNetworkData.OutputService(config);
3199 }
3200
3201 error = OT_ERROR_NONE;
3202 }
3203 else
3204 {
3205 uint16_t length;
3206
3207 SuccessOrExit(error = aArgs[1].ParseAsUint32(cfg.mEnterpriseNumber));
3208
3209 length = sizeof(cfg.mServiceData);
3210 SuccessOrExit(error = aArgs[2].ParseAsHexString(length, cfg.mServiceData));
3211 VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS);
3212 cfg.mServiceDataLength = static_cast<uint8_t>(length);
3213
3214 if (aArgs[0] == "add")
3215 {
3216 length = sizeof(cfg.mServerConfig.mServerData);
3217 SuccessOrExit(error = aArgs[3].ParseAsHexString(length, cfg.mServerConfig.mServerData));
3218 VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS);
3219 cfg.mServerConfig.mServerDataLength = static_cast<uint8_t>(length);
3220
3221 cfg.mServerConfig.mStable = true;
3222
3223 error = otServerAddService(GetInstancePtr(), &cfg);
3224 }
3225 else if (aArgs[0] == "remove")
3226 {
3227 error = otServerRemoveService(GetInstancePtr(), cfg.mEnterpriseNumber, cfg.mServiceData,
3228 cfg.mServiceDataLength);
3229 }
3230 }
3231
3232 exit:
3233 return error;
3234 }
3235 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
3236
Process(Arg aArgs[])3237 template <> otError Interpreter::Process<Cmd("netdata")>(Arg aArgs[])
3238 {
3239 return mNetworkData.Process(aArgs);
3240 }
3241
3242 #if OPENTHREAD_FTD
Process(Arg aArgs[])3243 template <> otError Interpreter::Process<Cmd("networkidtimeout")>(Arg aArgs[])
3244 {
3245 return ProcessGetSet(aArgs, otThreadGetNetworkIdTimeout, otThreadSetNetworkIdTimeout);
3246 }
3247 #endif
3248
Process(Arg aArgs[])3249 template <> otError Interpreter::Process<Cmd("networkkey")>(Arg aArgs[])
3250 {
3251 otError error = OT_ERROR_NONE;
3252
3253 if (aArgs[0].IsEmpty())
3254 {
3255 otNetworkKey networkKey;
3256
3257 otThreadGetNetworkKey(GetInstancePtr(), &networkKey);
3258 OutputBytesLine(networkKey.m8);
3259 }
3260 else
3261 {
3262 otNetworkKey key;
3263
3264 SuccessOrExit(error = aArgs[0].ParseAsHexString(key.m8));
3265 SuccessOrExit(error = otThreadSetNetworkKey(GetInstancePtr(), &key));
3266 }
3267
3268 exit:
3269 return error;
3270 }
3271
3272 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
Process(Arg aArgs[])3273 template <> otError Interpreter::Process<Cmd("networkkeyref")>(Arg aArgs[])
3274 {
3275 otError error = OT_ERROR_NONE;
3276
3277 if (aArgs[0].IsEmpty())
3278 {
3279 OutputLine("0x%04x", otThreadGetNetworkKeyRef(GetInstancePtr()));
3280 }
3281 else
3282 {
3283 otNetworkKeyRef keyRef;
3284
3285 SuccessOrExit(error = aArgs[0].ParseAsUint32(keyRef));
3286 SuccessOrExit(error = otThreadSetNetworkKeyRef(GetInstancePtr(), keyRef));
3287 }
3288
3289 exit:
3290 return error;
3291 }
3292 #endif
3293
Process(Arg aArgs[])3294 template <> otError Interpreter::Process<Cmd("networkname")>(Arg aArgs[])
3295 {
3296 otError error = OT_ERROR_NONE;
3297
3298 if (aArgs[0].IsEmpty())
3299 {
3300 OutputLine("%s", otThreadGetNetworkName(GetInstancePtr()));
3301 }
3302 else
3303 {
3304 SuccessOrExit(error = otThreadSetNetworkName(GetInstancePtr(), aArgs[0].GetCString()));
3305 }
3306
3307 exit:
3308 return error;
3309 }
3310
3311 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
Process(Arg aArgs[])3312 template <> otError Interpreter::Process<Cmd("networktime")>(Arg aArgs[])
3313 {
3314 otError error = OT_ERROR_NONE;
3315
3316 if (aArgs[0].IsEmpty())
3317 {
3318 uint64_t time;
3319 otNetworkTimeStatus networkTimeStatus;
3320
3321 networkTimeStatus = otNetworkTimeGet(GetInstancePtr(), &time);
3322
3323 OutputFormat("Network Time: %luus", time);
3324
3325 switch (networkTimeStatus)
3326 {
3327 case OT_NETWORK_TIME_UNSYNCHRONIZED:
3328 OutputLine(" (unsynchronized)");
3329 break;
3330
3331 case OT_NETWORK_TIME_RESYNC_NEEDED:
3332 OutputLine(" (resync needed)");
3333 break;
3334
3335 case OT_NETWORK_TIME_SYNCHRONIZED:
3336 OutputLine(" (synchronized)");
3337 break;
3338
3339 default:
3340 break;
3341 }
3342
3343 OutputLine("Time Sync Period: %ds", otNetworkTimeGetSyncPeriod(GetInstancePtr()));
3344 OutputLine("XTAL Threshold: %dppm", otNetworkTimeGetXtalThreshold(GetInstancePtr()));
3345 }
3346 else
3347 {
3348 uint16_t period;
3349 uint16_t xtalThreshold;
3350
3351 SuccessOrExit(error = aArgs[0].ParseAsUint16(period));
3352 SuccessOrExit(error = aArgs[1].ParseAsUint16(xtalThreshold));
3353 SuccessOrExit(error = otNetworkTimeSetSyncPeriod(GetInstancePtr(), period));
3354 SuccessOrExit(error = otNetworkTimeSetXtalThreshold(GetInstancePtr(), xtalThreshold));
3355 }
3356
3357 exit:
3358 return error;
3359 }
3360 #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
3361
Process(Arg aArgs[])3362 template <> otError Interpreter::Process<Cmd("panid")>(Arg aArgs[])
3363 {
3364 otError error = OT_ERROR_NONE;
3365
3366 if (aArgs[0].IsEmpty())
3367 {
3368 OutputLine("0x%04x", otLinkGetPanId(GetInstancePtr()));
3369 }
3370 else
3371 {
3372 error = ProcessSet(aArgs, otLinkSetPanId);
3373 }
3374
3375 return error;
3376 }
3377
Process(Arg aArgs[])3378 template <> otError Interpreter::Process<Cmd("parent")>(Arg aArgs[])
3379 {
3380 OT_UNUSED_VARIABLE(aArgs);
3381
3382 otError error = OT_ERROR_NONE;
3383 otRouterInfo parentInfo;
3384
3385 SuccessOrExit(error = otThreadGetParentInfo(GetInstancePtr(), &parentInfo));
3386 OutputFormat("Ext Addr: ");
3387 OutputExtAddressLine(parentInfo.mExtAddress);
3388 OutputLine("Rloc: %x", parentInfo.mRloc16);
3389 OutputLine("Link Quality In: %d", parentInfo.mLinkQualityIn);
3390 OutputLine("Link Quality Out: %d", parentInfo.mLinkQualityOut);
3391 OutputLine("Age: %d", parentInfo.mAge);
3392
3393 exit:
3394 return error;
3395 }
3396
3397 #if OPENTHREAD_FTD
Process(Arg aArgs[])3398 template <> otError Interpreter::Process<Cmd("parentpriority")>(Arg aArgs[])
3399 {
3400 return ProcessGetSet(aArgs, otThreadGetParentPriority, otThreadSetParentPriority);
3401 }
3402 #endif
3403
3404 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Process(Arg * aArgs)3405 template <> otError Interpreter::Process<Cmd("routeridrange")>(Arg *aArgs)
3406 {
3407 uint8_t minRouterId;
3408 uint8_t maxRouterId;
3409 otError error = OT_ERROR_NONE;
3410
3411 if (aArgs[0].IsEmpty())
3412 {
3413 otThreadGetRouterIdRange(GetInstancePtr(), &minRouterId, &maxRouterId);
3414 OutputLine("%d %d", minRouterId, maxRouterId);
3415 }
3416 else
3417 {
3418 SuccessOrExit(error = aArgs[0].ParseAsUint8(minRouterId));
3419 SuccessOrExit(error = aArgs[1].ParseAsUint8(maxRouterId));
3420 VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3421 error = otThreadSetRouterIdRange(GetInstancePtr(), minRouterId, maxRouterId);
3422 }
3423
3424 exit:
3425 return error;
3426 }
3427 #endif
3428
3429 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
3430
HandlePingReply(const otPingSenderReply * aReply,void * aContext)3431 void Interpreter::HandlePingReply(const otPingSenderReply *aReply, void *aContext)
3432 {
3433 static_cast<Interpreter *>(aContext)->HandlePingReply(aReply);
3434 }
3435
HandlePingReply(const otPingSenderReply * aReply)3436 void Interpreter::HandlePingReply(const otPingSenderReply *aReply)
3437 {
3438 OutputFormat("%u bytes from ", static_cast<uint16_t>(aReply->mSize + sizeof(otIcmp6Header)));
3439 OutputIp6Address(aReply->mSenderAddress);
3440 OutputLine(": icmp_seq=%d hlim=%d time=%dms", aReply->mSequenceNumber, aReply->mHopLimit, aReply->mRoundTripTime);
3441 }
3442
HandlePingStatistics(const otPingSenderStatistics * aStatistics,void * aContext)3443 void Interpreter::HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext)
3444 {
3445 static_cast<Interpreter *>(aContext)->HandlePingStatistics(aStatistics);
3446 }
3447
HandlePingStatistics(const otPingSenderStatistics * aStatistics)3448 void Interpreter::HandlePingStatistics(const otPingSenderStatistics *aStatistics)
3449 {
3450 OutputFormat("%u packets transmitted, %u packets received.", aStatistics->mSentCount, aStatistics->mReceivedCount);
3451
3452 if ((aStatistics->mSentCount != 0) && !aStatistics->mIsMulticast &&
3453 aStatistics->mReceivedCount <= aStatistics->mSentCount)
3454 {
3455 uint32_t packetLossRate =
3456 1000 * (aStatistics->mSentCount - aStatistics->mReceivedCount) / aStatistics->mSentCount;
3457 OutputFormat(" Packet loss = %u.%u%%.", packetLossRate / 10, packetLossRate % 10);
3458 }
3459
3460 if (aStatistics->mReceivedCount != 0)
3461 {
3462 uint32_t avgRoundTripTime = 1000 * aStatistics->mTotalRoundTripTime / aStatistics->mReceivedCount;
3463 OutputFormat(" Round-trip min/avg/max = %u/%u.%u/%u ms.", aStatistics->mMinRoundTripTime,
3464 avgRoundTripTime / 1000, avgRoundTripTime % 1000, aStatistics->mMaxRoundTripTime);
3465 }
3466
3467 OutputLine("");
3468
3469 if (!mPingIsAsync)
3470 {
3471 OutputResult(OT_ERROR_NONE);
3472 }
3473 }
3474
Process(Arg aArgs[])3475 template <> otError Interpreter::Process<Cmd("ping")>(Arg aArgs[])
3476 {
3477 otError error = OT_ERROR_NONE;
3478 otPingSenderConfig config;
3479 bool async = false;
3480
3481 if (aArgs[0] == "stop")
3482 {
3483 otPingSenderStop(GetInstancePtr());
3484 ExitNow();
3485 }
3486 else if (aArgs[0] == "async")
3487 {
3488 async = true;
3489 aArgs++;
3490 }
3491
3492 memset(&config, 0, sizeof(config));
3493
3494 if (aArgs[0] == "-I")
3495 {
3496 SuccessOrExit(error = aArgs[1].ParseAsIp6Address(config.mSource));
3497
3498 #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
3499 {
3500 bool valid = false;
3501 const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(GetInstancePtr());
3502
3503 for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
3504 {
3505 if (otIp6IsAddressEqual(&addr->mAddress, &config.mSource))
3506 {
3507 valid = true;
3508 break;
3509 }
3510 }
3511
3512 VerifyOrExit(valid, error = OT_ERROR_INVALID_ARGS);
3513 }
3514 #endif
3515
3516 aArgs += 2;
3517 }
3518
3519 SuccessOrExit(error = aArgs[0].ParseAsIp6Address(config.mDestination));
3520
3521 if (!aArgs[1].IsEmpty())
3522 {
3523 SuccessOrExit(error = aArgs[1].ParseAsUint16(config.mSize));
3524 }
3525
3526 if (!aArgs[2].IsEmpty())
3527 {
3528 SuccessOrExit(error = aArgs[2].ParseAsUint16(config.mCount));
3529 }
3530
3531 if (!aArgs[3].IsEmpty())
3532 {
3533 SuccessOrExit(error = ParsePingInterval(aArgs[3], config.mInterval));
3534 }
3535
3536 if (!aArgs[4].IsEmpty())
3537 {
3538 SuccessOrExit(error = aArgs[4].ParseAsUint8(config.mHopLimit));
3539 config.mAllowZeroHopLimit = (config.mHopLimit == 0);
3540 }
3541
3542 if (!aArgs[5].IsEmpty())
3543 {
3544 uint32_t timeout;
3545
3546 SuccessOrExit(error = ParsePingInterval(aArgs[5], timeout));
3547 VerifyOrExit(timeout <= NumericLimits<uint16_t>::kMax, error = OT_ERROR_INVALID_ARGS);
3548 config.mTimeout = static_cast<uint16_t>(timeout);
3549 }
3550
3551 VerifyOrExit(aArgs[6].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3552
3553 config.mReplyCallback = Interpreter::HandlePingReply;
3554 config.mStatisticsCallback = Interpreter::HandlePingStatistics;
3555 config.mCallbackContext = this;
3556
3557 SuccessOrExit(error = otPingSenderPing(GetInstancePtr(), &config));
3558
3559 mPingIsAsync = async;
3560
3561 if (!async)
3562 {
3563 error = OT_ERROR_PENDING;
3564 }
3565
3566 exit:
3567 return error;
3568 }
3569
3570 #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
3571
Process(Arg aArgs[])3572 template <> otError Interpreter::Process<Cmd("pollperiod")>(Arg aArgs[])
3573 {
3574 return ProcessGetSet(aArgs, otLinkGetPollPeriod, otLinkSetPollPeriod);
3575 }
3576
Process(Arg aArgs[])3577 template <> otError Interpreter::Process<Cmd("promiscuous")>(Arg aArgs[])
3578 {
3579 otError error = OT_ERROR_NONE;
3580
3581 if (aArgs[0].IsEmpty())
3582 {
3583 OutputEnabledDisabledStatus(otLinkIsPromiscuous(GetInstancePtr()) &&
3584 otPlatRadioGetPromiscuous(GetInstancePtr()));
3585 }
3586 else
3587 {
3588 bool enable;
3589
3590 SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
3591
3592 if (!enable)
3593 {
3594 otLinkSetPcapCallback(GetInstancePtr(), nullptr, nullptr);
3595 }
3596
3597 SuccessOrExit(error = otLinkSetPromiscuous(GetInstancePtr(), enable));
3598
3599 if (enable)
3600 {
3601 otLinkSetPcapCallback(GetInstancePtr(), &HandleLinkPcapReceive, this);
3602 }
3603 }
3604
3605 exit:
3606 return error;
3607 }
3608
HandleLinkPcapReceive(const otRadioFrame * aFrame,bool aIsTx,void * aContext)3609 void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext)
3610 {
3611 static_cast<Interpreter *>(aContext)->HandleLinkPcapReceive(aFrame, aIsTx);
3612 }
3613
HandleLinkPcapReceive(const otRadioFrame * aFrame,bool aIsTx)3614 void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx)
3615 {
3616 OT_UNUSED_VARIABLE(aIsTx);
3617
3618 OutputLine("");
3619
3620 for (size_t i = 0; i < 44; i++)
3621 {
3622 OutputFormat("=");
3623 }
3624
3625 OutputFormat("[len = %3u]", aFrame->mLength);
3626
3627 for (size_t i = 0; i < 28; i++)
3628 {
3629 OutputFormat("=");
3630 }
3631
3632 OutputLine("");
3633
3634 for (size_t i = 0; i < aFrame->mLength; i += 16)
3635 {
3636 OutputFormat("|");
3637
3638 for (size_t j = 0; j < 16; j++)
3639 {
3640 if (i + j < aFrame->mLength)
3641 {
3642 OutputFormat(" %02X", aFrame->mPsdu[i + j]);
3643 }
3644 else
3645 {
3646 OutputFormat(" ..");
3647 }
3648 }
3649
3650 OutputFormat("|");
3651
3652 for (size_t j = 0; j < 16; j++)
3653 {
3654 if (i + j < aFrame->mLength)
3655 {
3656 if (31 < aFrame->mPsdu[i + j] && aFrame->mPsdu[i + j] < 127)
3657 {
3658 OutputFormat(" %c", aFrame->mPsdu[i + j]);
3659 }
3660 else
3661 {
3662 OutputFormat(" ?");
3663 }
3664 }
3665 else
3666 {
3667 OutputFormat(" .");
3668 }
3669 }
3670
3671 OutputLine("|");
3672 }
3673
3674 for (size_t i = 0; i < 83; i++)
3675 {
3676 OutputFormat("-");
3677 }
3678
3679 OutputLine("");
3680 }
3681
3682 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
ParsePrefix(Arg aArgs[],otBorderRouterConfig & aConfig)3683 otError Interpreter::ParsePrefix(Arg aArgs[], otBorderRouterConfig &aConfig)
3684 {
3685 otError error = OT_ERROR_NONE;
3686
3687 memset(&aConfig, 0, sizeof(otBorderRouterConfig));
3688
3689 SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
3690 aArgs++;
3691
3692 for (; !aArgs->IsEmpty(); aArgs++)
3693 {
3694 otRoutePreference preference;
3695
3696 if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
3697 {
3698 aConfig.mPreference = preference;
3699 }
3700 else
3701 {
3702 for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
3703 {
3704 switch (*arg)
3705 {
3706 case 'p':
3707 aConfig.mPreferred = true;
3708 break;
3709
3710 case 'a':
3711 aConfig.mSlaac = true;
3712 break;
3713
3714 case 'd':
3715 aConfig.mDhcp = true;
3716 break;
3717
3718 case 'c':
3719 aConfig.mConfigure = true;
3720 break;
3721
3722 case 'r':
3723 aConfig.mDefaultRoute = true;
3724 break;
3725
3726 case 'o':
3727 aConfig.mOnMesh = true;
3728 break;
3729
3730 case 's':
3731 aConfig.mStable = true;
3732 break;
3733
3734 case 'n':
3735 aConfig.mNdDns = true;
3736 break;
3737
3738 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
3739 case 'D':
3740 aConfig.mDp = true;
3741 break;
3742 #endif
3743 default:
3744 ExitNow(error = OT_ERROR_INVALID_ARGS);
3745 }
3746 }
3747 }
3748 }
3749
3750 exit:
3751 return error;
3752 }
3753
Process(Arg aArgs[])3754 template <> otError Interpreter::Process<Cmd("prefix")>(Arg aArgs[])
3755 {
3756 otError error = OT_ERROR_NONE;
3757
3758 /**
3759 * @cli prefix
3760 * @code
3761 * prefix
3762 * 2001:dead:beef:cafe::/64 paros med
3763 * - fd00:7d03:7d03:7d03::/64 prosD med
3764 * Done
3765 * @endcode
3766 * @par
3767 * Get the prefix list in the local Network Data.
3768 * @note For the Thread 1.2 border router with backbone capability, the local Domain Prefix
3769 * is listed as well and includes the `D` flag. If backbone functionality is disabled, a dash
3770 * `-` is printed before the local Domain Prefix.
3771 * @par
3772 * For more information about #otBorderRouterConfig flags, refer to @overview.
3773 * @sa otBorderRouterGetNextOnMeshPrefix
3774 */
3775 if (aArgs[0].IsEmpty())
3776 {
3777 otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
3778 otBorderRouterConfig config;
3779
3780 while (otBorderRouterGetNextOnMeshPrefix(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
3781 {
3782 mNetworkData.OutputPrefix(config);
3783 }
3784
3785 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
3786 if (otBackboneRouterGetState(GetInstancePtr()) == OT_BACKBONE_ROUTER_STATE_DISABLED)
3787 {
3788 SuccessOrExit(otBackboneRouterGetDomainPrefix(GetInstancePtr(), &config));
3789 OutputFormat("- ");
3790 mNetworkData.OutputPrefix(config);
3791 }
3792 #endif
3793 }
3794 /**
3795 * @cli prefix add
3796 * @code
3797 * prefix add 2001:dead:beef:cafe::/64 paros med
3798 * Done
3799 * @endcode
3800 * @code
3801 * prefix add fd00:7d03:7d03:7d03::/64 prosD low
3802 * Done
3803 * @endcode
3804 * @cparam prefix add @ca{prefix} [@ca{padcrosnD}] [@ca{high}|@ca{med}|@ca{low}]
3805 * OT CLI uses mapped arguments to configure #otBorderRouterConfig values. @moreinfo{the @overview}.
3806 * @par
3807 * Adds a valid prefix to the Network Data.
3808 * @sa otBorderRouterAddOnMeshPrefix
3809 */
3810 else if (aArgs[0] == "add")
3811 {
3812 otBorderRouterConfig config;
3813
3814 SuccessOrExit(error = ParsePrefix(aArgs + 1, config));
3815 error = otBorderRouterAddOnMeshPrefix(GetInstancePtr(), &config);
3816 }
3817 /**
3818 * @cli prefix remove
3819 * @code
3820 * prefix remove 2001:dead:beef:cafe::/64
3821 * Done
3822 * @endcode
3823 * @par api_copy
3824 * #otBorderRouterRemoveOnMeshPrefix
3825 */
3826 else if (aArgs[0] == "remove")
3827 {
3828 otIp6Prefix prefix;
3829
3830 SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
3831 error = otBorderRouterRemoveOnMeshPrefix(GetInstancePtr(), &prefix);
3832 }
3833 /**
3834 * @cli prefix meshlocal
3835 * @code
3836 * prefix meshlocal
3837 * fdde:ad00:beef:0::/64
3838 * Done
3839 * @endcode
3840 * @par
3841 * Get the mesh local prefix.
3842 */
3843 else if (aArgs[0] == "meshlocal")
3844 {
3845 if (aArgs[1].IsEmpty())
3846 {
3847 OutputIp6PrefixLine(*otThreadGetMeshLocalPrefix(GetInstancePtr()));
3848 }
3849 else
3850 {
3851 otIp6Prefix prefix;
3852
3853 SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
3854 VerifyOrExit(prefix.mLength == OT_IP6_PREFIX_BITSIZE, error = OT_ERROR_INVALID_ARGS);
3855 error =
3856 otThreadSetMeshLocalPrefix(GetInstancePtr(), reinterpret_cast<otMeshLocalPrefix *>(&prefix.mPrefix));
3857 }
3858 }
3859 else
3860 {
3861 error = OT_ERROR_INVALID_COMMAND;
3862 }
3863
3864 exit:
3865 return error;
3866 }
3867 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
3868
3869 #if OPENTHREAD_FTD
Process(Arg aArgs[])3870 template <> otError Interpreter::Process<Cmd("preferrouterid")>(Arg aArgs[])
3871 {
3872 return ProcessSet(aArgs, otThreadSetPreferredRouterId);
3873 }
3874 #endif
3875
3876 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
Process(Arg aArgs[])3877 template <> otError Interpreter::Process<Cmd("radiofilter")>(Arg aArgs[])
3878 {
3879 otError error = OT_ERROR_NONE;
3880
3881 if (aArgs[0].IsEmpty())
3882 {
3883 OutputEnabledDisabledStatus(otLinkIsRadioFilterEnabled(GetInstancePtr()));
3884 }
3885 else
3886 {
3887 bool enable;
3888
3889 SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
3890 otLinkSetRadioFilterEnabled(GetInstancePtr(), enable);
3891 }
3892
3893 exit:
3894 return error;
3895 }
3896 #endif
3897
Process(Arg aArgs[])3898 template <> otError Interpreter::Process<Cmd("rcp")>(Arg aArgs[])
3899 {
3900 otError error = OT_ERROR_NONE;
3901 const char *version = otPlatRadioGetVersionString(GetInstancePtr());
3902
3903 VerifyOrExit(version != otGetVersionString(), error = OT_ERROR_NOT_IMPLEMENTED);
3904
3905 if (aArgs[0] == "version")
3906 {
3907 OutputLine("%s", version);
3908 }
3909 else
3910 {
3911 error = OT_ERROR_INVALID_ARGS;
3912 }
3913
3914 exit:
3915 return error;
3916 }
3917
Process(Arg aArgs[])3918 template <> otError Interpreter::Process<Cmd("region")>(Arg aArgs[])
3919 {
3920 otError error = OT_ERROR_NONE;
3921 uint16_t regionCode;
3922
3923 if (aArgs[0].IsEmpty())
3924 {
3925 SuccessOrExit(error = otPlatRadioGetRegion(GetInstancePtr(), ®ionCode));
3926 OutputLine("%c%c", regionCode >> 8, regionCode & 0xff);
3927 }
3928 else
3929 {
3930 VerifyOrExit(aArgs[0].GetLength() == 2, error = OT_ERROR_INVALID_ARGS);
3931
3932 regionCode = static_cast<uint16_t>(static_cast<uint16_t>(aArgs[0].GetCString()[0]) << 8) +
3933 static_cast<uint16_t>(aArgs[0].GetCString()[1]);
3934 error = otPlatRadioSetRegion(GetInstancePtr(), regionCode);
3935 }
3936
3937 exit:
3938 return error;
3939 }
3940
3941 #if OPENTHREAD_FTD
Process(Arg aArgs[])3942 template <> otError Interpreter::Process<Cmd("releaserouterid")>(Arg aArgs[])
3943 {
3944 return ProcessSet(aArgs, otThreadReleaseRouterId);
3945 }
3946 #endif
3947
Process(Arg aArgs[])3948 template <> otError Interpreter::Process<Cmd("rloc16")>(Arg aArgs[])
3949 {
3950 OT_UNUSED_VARIABLE(aArgs);
3951
3952 OutputLine("%04x", otThreadGetRloc16(GetInstancePtr()));
3953
3954 return OT_ERROR_NONE;
3955 }
3956
3957 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
ParseRoute(Arg aArgs[],otExternalRouteConfig & aConfig)3958 otError Interpreter::ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig)
3959 {
3960 otError error = OT_ERROR_NONE;
3961
3962 memset(&aConfig, 0, sizeof(otExternalRouteConfig));
3963
3964 SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
3965 aArgs++;
3966
3967 for (; !aArgs->IsEmpty(); aArgs++)
3968 {
3969 otRoutePreference preference;
3970
3971 if (*aArgs == "s")
3972 {
3973 aConfig.mStable = true;
3974 }
3975 else if (*aArgs == "n")
3976 {
3977 aConfig.mNat64 = true;
3978 }
3979 else if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
3980 {
3981 aConfig.mPreference = preference;
3982 }
3983 else
3984 {
3985 ExitNow(error = OT_ERROR_INVALID_ARGS);
3986 }
3987 }
3988
3989 exit:
3990 return error;
3991 }
3992
Process(Arg aArgs[])3993 template <> otError Interpreter::Process<Cmd("route")>(Arg aArgs[])
3994 {
3995 otError error = OT_ERROR_NONE;
3996
3997 if (aArgs[0].IsEmpty())
3998 {
3999 otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
4000 otExternalRouteConfig config;
4001
4002 while (otBorderRouterGetNextRoute(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
4003 {
4004 mNetworkData.OutputRoute(config);
4005 }
4006 }
4007 else if (aArgs[0] == "add")
4008 {
4009 otExternalRouteConfig config;
4010
4011 SuccessOrExit(error = ParseRoute(aArgs + 1, config));
4012 error = otBorderRouterAddRoute(GetInstancePtr(), &config);
4013 }
4014 else if (aArgs[0] == "remove")
4015 {
4016 otIp6Prefix prefix;
4017
4018 SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
4019 error = otBorderRouterRemoveRoute(GetInstancePtr(), &prefix);
4020 }
4021 else
4022 {
4023 error = OT_ERROR_INVALID_COMMAND;
4024 }
4025
4026 exit:
4027 return error;
4028 }
4029 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
4030
4031 #if OPENTHREAD_FTD
Process(Arg aArgs[])4032 template <> otError Interpreter::Process<Cmd("router")>(Arg aArgs[])
4033 {
4034 otError error = OT_ERROR_NONE;
4035 otRouterInfo routerInfo;
4036 uint16_t routerId;
4037 bool isTable;
4038
4039 isTable = (aArgs[0] == "table");
4040
4041 if (isTable || (aArgs[0] == "list"))
4042 {
4043 uint8_t maxRouterId;
4044
4045 if (isTable)
4046 {
4047 static const char *const kRouterTableTitles[] = {
4048 "ID", "RLOC16", "Next Hop", "Path Cost", "LQ In", "LQ Out", "Age", "Extended MAC", "Link",
4049 };
4050
4051 static const uint8_t kRouterTableColumnWidths[] = {
4052 4, 8, 10, 11, 7, 8, 5, 18, 6,
4053 };
4054
4055 OutputTableHeader(kRouterTableTitles, kRouterTableColumnWidths);
4056 }
4057
4058 maxRouterId = otThreadGetMaxRouterId(GetInstancePtr());
4059
4060 for (uint8_t i = 0; i <= maxRouterId; i++)
4061 {
4062 if (otThreadGetRouterInfo(GetInstancePtr(), i, &routerInfo) != OT_ERROR_NONE)
4063 {
4064 continue;
4065 }
4066
4067 if (isTable)
4068 {
4069 OutputFormat("| %2d ", routerInfo.mRouterId);
4070 OutputFormat("| 0x%04x ", routerInfo.mRloc16);
4071 OutputFormat("| %8d ", routerInfo.mNextHop);
4072 OutputFormat("| %9d ", routerInfo.mPathCost);
4073 OutputFormat("| %5d ", routerInfo.mLinkQualityIn);
4074 OutputFormat("| %6d ", routerInfo.mLinkQualityOut);
4075 OutputFormat("| %3d ", routerInfo.mAge);
4076 OutputFormat("| ");
4077 OutputExtAddress(routerInfo.mExtAddress);
4078 OutputLine(" | %4d |", routerInfo.mLinkEstablished);
4079 }
4080 else
4081 {
4082 OutputFormat("%d ", i);
4083 }
4084 }
4085
4086 OutputLine("");
4087 ExitNow();
4088 }
4089
4090 SuccessOrExit(error = aArgs[0].ParseAsUint16(routerId));
4091 SuccessOrExit(error = otThreadGetRouterInfo(GetInstancePtr(), routerId, &routerInfo));
4092
4093 OutputLine("Alloc: %d", routerInfo.mAllocated);
4094
4095 if (routerInfo.mAllocated)
4096 {
4097 OutputLine("Router ID: %d", routerInfo.mRouterId);
4098 OutputLine("Rloc: %04x", routerInfo.mRloc16);
4099 OutputLine("Next Hop: %04x", static_cast<uint16_t>(routerInfo.mNextHop) << 10);
4100 OutputLine("Link: %d", routerInfo.mLinkEstablished);
4101
4102 if (routerInfo.mLinkEstablished)
4103 {
4104 OutputFormat("Ext Addr: ");
4105 OutputExtAddressLine(routerInfo.mExtAddress);
4106 OutputLine("Cost: %d", routerInfo.mPathCost);
4107 OutputLine("Link Quality In: %d", routerInfo.mLinkQualityIn);
4108 OutputLine("Link Quality Out: %d", routerInfo.mLinkQualityOut);
4109 OutputLine("Age: %d", routerInfo.mAge);
4110 }
4111 }
4112
4113 exit:
4114 return error;
4115 }
4116
Process(Arg aArgs[])4117 template <> otError Interpreter::Process<Cmd("routerdowngradethreshold")>(Arg aArgs[])
4118 {
4119 return ProcessGetSet(aArgs, otThreadGetRouterDowngradeThreshold, otThreadSetRouterDowngradeThreshold);
4120 }
4121
Process(Arg aArgs[])4122 template <> otError Interpreter::Process<Cmd("routereligible")>(Arg aArgs[])
4123 {
4124 otError error = OT_ERROR_NONE;
4125
4126 if (aArgs[0].IsEmpty())
4127 {
4128 OutputEnabledDisabledStatus(otThreadIsRouterEligible(GetInstancePtr()));
4129 }
4130 else
4131 {
4132 bool enable;
4133
4134 SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
4135 error = otThreadSetRouterEligible(GetInstancePtr(), enable);
4136 }
4137
4138 exit:
4139 return error;
4140 }
4141
Process(Arg aArgs[])4142 template <> otError Interpreter::Process<Cmd("routerselectionjitter")>(Arg aArgs[])
4143 {
4144 return ProcessGetSet(aArgs, otThreadGetRouterSelectionJitter, otThreadSetRouterSelectionJitter);
4145 }
4146
Process(Arg aArgs[])4147 template <> otError Interpreter::Process<Cmd("routerupgradethreshold")>(Arg aArgs[])
4148 {
4149 return ProcessGetSet(aArgs, otThreadGetRouterUpgradeThreshold, otThreadSetRouterUpgradeThreshold);
4150 }
4151 #endif // OPENTHREAD_FTD
4152
Process(Arg aArgs[])4153 template <> otError Interpreter::Process<Cmd("scan")>(Arg aArgs[])
4154 {
4155 otError error = OT_ERROR_NONE;
4156 uint32_t scanChannels = 0;
4157 uint16_t scanDuration = 0;
4158 bool energyScan = false;
4159
4160 if (aArgs[0] == "energy")
4161 {
4162 energyScan = true;
4163 aArgs++;
4164
4165 if (!aArgs->IsEmpty())
4166 {
4167 SuccessOrExit(error = aArgs->ParseAsUint16(scanDuration));
4168 aArgs++;
4169 }
4170 }
4171
4172 if (!aArgs->IsEmpty())
4173 {
4174 uint8_t channel;
4175
4176 SuccessOrExit(error = aArgs->ParseAsUint8(channel));
4177 VerifyOrExit(channel < sizeof(scanChannels) * CHAR_BIT, error = OT_ERROR_INVALID_ARGS);
4178 scanChannels = 1 << channel;
4179 }
4180
4181 if (energyScan)
4182 {
4183 static const char *const kEnergyScanTableTitles[] = {"Ch", "RSSI"};
4184 static const uint8_t kEnergyScanTableColumnWidths[] = {4, 6};
4185
4186 OutputTableHeader(kEnergyScanTableTitles, kEnergyScanTableColumnWidths);
4187 SuccessOrExit(error = otLinkEnergyScan(GetInstancePtr(), scanChannels, scanDuration,
4188 &Interpreter::HandleEnergyScanResult, this));
4189 }
4190 else
4191 {
4192 static const char *const kScanTableTitles[] = {"PAN", "MAC Address", "Ch", "dBm", "LQI"};
4193 static const uint8_t kScanTableColumnWidths[] = {6, 18, 4, 5, 5};
4194
4195 OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
4196
4197 SuccessOrExit(error = otLinkActiveScan(GetInstancePtr(), scanChannels, scanDuration,
4198 &Interpreter::HandleActiveScanResult, this));
4199 }
4200
4201 error = OT_ERROR_PENDING;
4202
4203 exit:
4204 return error;
4205 }
4206
HandleActiveScanResult(otActiveScanResult * aResult,void * aContext)4207 void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult, void *aContext)
4208 {
4209 static_cast<Interpreter *>(aContext)->HandleActiveScanResult(aResult);
4210 }
4211
HandleActiveScanResult(otActiveScanResult * aResult)4212 void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult)
4213 {
4214 if (aResult == nullptr)
4215 {
4216 OutputResult(OT_ERROR_NONE);
4217 ExitNow();
4218 }
4219
4220 if (aResult->mDiscover)
4221 {
4222 OutputFormat("| %-16s ", aResult->mNetworkName.m8);
4223
4224 OutputFormat("| ");
4225 OutputBytes(aResult->mExtendedPanId.m8);
4226 OutputFormat(" ");
4227 }
4228
4229 OutputFormat("| %04x | ", aResult->mPanId);
4230 OutputExtAddress(aResult->mExtAddress);
4231 OutputFormat(" | %2d ", aResult->mChannel);
4232 OutputFormat("| %3d ", aResult->mRssi);
4233 OutputLine("| %3d |", aResult->mLqi);
4234
4235 exit:
4236 return;
4237 }
4238
HandleEnergyScanResult(otEnergyScanResult * aResult,void * aContext)4239 void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext)
4240 {
4241 static_cast<Interpreter *>(aContext)->HandleEnergyScanResult(aResult);
4242 }
4243
HandleEnergyScanResult(otEnergyScanResult * aResult)4244 void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult)
4245 {
4246 if (aResult == nullptr)
4247 {
4248 OutputResult(OT_ERROR_NONE);
4249 ExitNow();
4250 }
4251
4252 OutputLine("| %2d | %4d |", aResult->mChannel, aResult->mMaxRssi);
4253
4254 exit:
4255 return;
4256 }
4257
Process(Arg aArgs[])4258 template <> otError Interpreter::Process<Cmd("singleton")>(Arg aArgs[])
4259 {
4260 OT_UNUSED_VARIABLE(aArgs);
4261
4262 OutputLine(otThreadIsSingleton(GetInstancePtr()) ? "true" : "false");
4263
4264 return OT_ERROR_NONE;
4265 }
4266
4267 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
Process(Arg aArgs[])4268 template <> otError Interpreter::Process<Cmd("sntp")>(Arg aArgs[])
4269 {
4270 otError error = OT_ERROR_NONE;
4271 uint16_t port = OT_SNTP_DEFAULT_SERVER_PORT;
4272 Ip6::MessageInfo messageInfo;
4273 otSntpQuery query;
4274
4275 if (aArgs[0] == "query")
4276 {
4277 VerifyOrExit(!mSntpQueryingInProgress, error = OT_ERROR_BUSY);
4278
4279 if (!aArgs[1].IsEmpty())
4280 {
4281 SuccessOrExit(error = aArgs[1].ParseAsIp6Address(messageInfo.GetPeerAddr()));
4282 }
4283 else
4284 {
4285 // Use IPv6 address of default SNTP server.
4286 SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_SNTP_DEFAULT_SERVER_IP));
4287 }
4288
4289 if (!aArgs[2].IsEmpty())
4290 {
4291 SuccessOrExit(error = aArgs[2].ParseAsUint16(port));
4292 }
4293
4294 messageInfo.SetPeerPort(port);
4295
4296 query.mMessageInfo = static_cast<const otMessageInfo *>(&messageInfo);
4297
4298 SuccessOrExit(error = otSntpClientQuery(GetInstancePtr(), &query, &Interpreter::HandleSntpResponse, this));
4299
4300 mSntpQueryingInProgress = true;
4301 error = OT_ERROR_PENDING;
4302 }
4303 else
4304 {
4305 error = OT_ERROR_INVALID_COMMAND;
4306 }
4307
4308 exit:
4309 return error;
4310 }
4311
HandleSntpResponse(void * aContext,uint64_t aTime,otError aResult)4312 void Interpreter::HandleSntpResponse(void *aContext, uint64_t aTime, otError aResult)
4313 {
4314 static_cast<Interpreter *>(aContext)->HandleSntpResponse(aTime, aResult);
4315 }
4316
HandleSntpResponse(uint64_t aTime,otError aResult)4317 void Interpreter::HandleSntpResponse(uint64_t aTime, otError aResult)
4318 {
4319 if (aResult == OT_ERROR_NONE)
4320 {
4321 // Some Embedded C libraries do not support printing of 64-bit unsigned integers.
4322 // To simplify, unix epoch time and era number are printed separately.
4323 OutputLine("SNTP response - Unix time: %u (era: %u)", static_cast<uint32_t>(aTime),
4324 static_cast<uint32_t>(aTime >> 32));
4325 }
4326 else
4327 {
4328 OutputLine("SNTP error - %s", otThreadErrorToString(aResult));
4329 }
4330
4331 mSntpQueryingInProgress = false;
4332
4333 OutputResult(OT_ERROR_NONE);
4334 }
4335 #endif // OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
4336
4337 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
Process(Arg aArgs[])4338 template <> otError Interpreter::Process<Cmd("srp")>(Arg aArgs[])
4339 {
4340 otError error = OT_ERROR_NONE;
4341
4342 if (aArgs[0].IsEmpty())
4343 {
4344 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
4345 OutputLine("client");
4346 #endif
4347 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
4348 OutputLine("server");
4349 #endif
4350 ExitNow();
4351 }
4352
4353 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
4354 if (aArgs[0] == "client")
4355 {
4356 ExitNow(error = mSrpClient.Process(aArgs + 1));
4357 }
4358 #endif
4359 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
4360 if (aArgs[0] == "server")
4361 {
4362 ExitNow(error = mSrpServer.Process(aArgs + 1));
4363 }
4364 #endif
4365
4366 error = OT_ERROR_INVALID_COMMAND;
4367
4368 exit:
4369 return error;
4370 }
4371 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
4372
Process(Arg aArgs[])4373 template <> otError Interpreter::Process<Cmd("state")>(Arg aArgs[])
4374 {
4375 otError error = OT_ERROR_NONE;
4376
4377 if (aArgs[0].IsEmpty())
4378 {
4379 OutputLine("%s", otThreadDeviceRoleToString(otThreadGetDeviceRole(GetInstancePtr())));
4380 }
4381 else if (aArgs[0] == "detached")
4382 {
4383 error = otThreadBecomeDetached(GetInstancePtr());
4384 }
4385 else if (aArgs[0] == "child")
4386 {
4387 error = otThreadBecomeChild(GetInstancePtr());
4388 }
4389 #if OPENTHREAD_FTD
4390 else if (aArgs[0] == "router")
4391 {
4392 error = otThreadBecomeRouter(GetInstancePtr());
4393 }
4394 else if (aArgs[0] == "leader")
4395 {
4396 error = otThreadBecomeLeader(GetInstancePtr());
4397 }
4398 #endif
4399 else
4400 {
4401 error = OT_ERROR_INVALID_ARGS;
4402 }
4403
4404 return error;
4405 }
4406
Process(Arg aArgs[])4407 template <> otError Interpreter::Process<Cmd("thread")>(Arg aArgs[])
4408 {
4409 otError error = OT_ERROR_NONE;
4410
4411 if (aArgs[0] == "start")
4412 {
4413 error = otThreadSetEnabled(GetInstancePtr(), true);
4414 }
4415 else if (aArgs[0] == "stop")
4416 {
4417 error = otThreadSetEnabled(GetInstancePtr(), false);
4418 }
4419 else if (aArgs[0] == "version")
4420 {
4421 OutputLine("%u", otThreadGetVersion());
4422 }
4423 else
4424 {
4425 error = OT_ERROR_INVALID_COMMAND;
4426 }
4427
4428 return error;
4429 }
4430
Process(Arg aArgs[])4431 template <> otError Interpreter::Process<Cmd("dataset")>(Arg aArgs[])
4432 {
4433 return mDataset.Process(aArgs);
4434 }
4435
Process(Arg aArgs[])4436 template <> otError Interpreter::Process<Cmd("txpower")>(Arg aArgs[])
4437 {
4438 otError error = OT_ERROR_NONE;
4439 int8_t power;
4440
4441 if (aArgs[0].IsEmpty())
4442 {
4443 SuccessOrExit(error = otPlatRadioGetTransmitPower(GetInstancePtr(), &power));
4444 OutputLine("%d dBm", power);
4445 }
4446 else
4447 {
4448 SuccessOrExit(error = aArgs[0].ParseAsInt8(power));
4449 error = otPlatRadioSetTransmitPower(GetInstancePtr(), power);
4450 }
4451
4452 exit:
4453 return error;
4454 }
4455
4456 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
Process(Arg aArgs[])4457 template <> otError Interpreter::Process<Cmd("tcp")>(Arg aArgs[])
4458 {
4459 return mTcp.Process(aArgs);
4460 }
4461 #endif
4462
Process(Arg aArgs[])4463 template <> otError Interpreter::Process<Cmd("udp")>(Arg aArgs[])
4464 {
4465 return mUdp.Process(aArgs);
4466 }
4467
Process(Arg aArgs[])4468 template <> otError Interpreter::Process<Cmd("unsecureport")>(Arg aArgs[])
4469 {
4470 otError error = OT_ERROR_NONE;
4471
4472 if (aArgs[0] == "add")
4473 {
4474 error = ProcessSet(aArgs + 1, otIp6AddUnsecurePort);
4475 }
4476 else if (aArgs[0] == "remove")
4477 {
4478 if (aArgs[1] == "all")
4479 {
4480 otIp6RemoveAllUnsecurePorts(GetInstancePtr());
4481 }
4482 else
4483 {
4484 error = ProcessSet(aArgs + 1, otIp6RemoveUnsecurePort);
4485 }
4486 }
4487 else if (aArgs[0] == "get")
4488 {
4489 const uint16_t *ports;
4490 uint8_t numPorts;
4491
4492 ports = otIp6GetUnsecurePorts(GetInstancePtr(), &numPorts);
4493
4494 if (ports != nullptr)
4495 {
4496 for (uint8_t i = 0; i < numPorts; i++)
4497 {
4498 OutputFormat("%d ", ports[i]);
4499 }
4500 }
4501
4502 OutputLine("");
4503 }
4504 else
4505 {
4506 error = OT_ERROR_INVALID_COMMAND;
4507 }
4508
4509 return error;
4510 }
4511
4512 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
Process(Arg aArgs[])4513 template <> otError Interpreter::Process<Cmd("uptime")>(Arg aArgs[])
4514 {
4515 otError error = OT_ERROR_NONE;
4516
4517 if (aArgs[0].IsEmpty())
4518 {
4519 char string[OT_UPTIME_STRING_SIZE];
4520
4521 otInstanceGetUptimeAsString(GetInstancePtr(), string, sizeof(string));
4522 OutputLine("%s", string);
4523 }
4524 else if (aArgs[0] == "ms")
4525 {
4526 OutputLine("%lu", otInstanceGetUptime(GetInstancePtr()));
4527 }
4528 else
4529 {
4530 error = OT_ERROR_INVALID_ARGS;
4531 }
4532
4533 return error;
4534 }
4535 #endif
4536
4537 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
Process(Arg aArgs[])4538 template <> otError Interpreter::Process<Cmd("commissioner")>(Arg aArgs[])
4539 {
4540 return mCommissioner.Process(aArgs);
4541 }
4542 #endif
4543
4544 #if OPENTHREAD_CONFIG_JOINER_ENABLE
Process(Arg aArgs[])4545 template <> otError Interpreter::Process<Cmd("joiner")>(Arg aArgs[])
4546 {
4547 return mJoiner.Process(aArgs);
4548 }
4549 #endif
4550
4551 #if OPENTHREAD_FTD
Process(Arg aArgs[])4552 template <> otError Interpreter::Process<Cmd("joinerport")>(Arg aArgs[])
4553 {
4554 return ProcessGetSet(aArgs, otThreadGetJoinerUdpPort, otThreadSetJoinerUdpPort);
4555 }
4556 #endif
4557
4558 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
Process(Arg aArgs[])4559 template <> otError Interpreter::Process<Cmd("macfilter")>(Arg aArgs[])
4560 {
4561 otError error = OT_ERROR_NONE;
4562
4563 if (aArgs[0].IsEmpty())
4564 {
4565 PrintMacFilter();
4566 }
4567 else if (aArgs[0] == "addr")
4568 {
4569 error = ProcessMacFilterAddress(aArgs + 1);
4570 }
4571 else if (aArgs[0] == "rss")
4572 {
4573 error = ProcessMacFilterRss(aArgs + 1);
4574 }
4575 else
4576 {
4577 error = OT_ERROR_INVALID_COMMAND;
4578 }
4579
4580 return error;
4581 }
4582
PrintMacFilter(void)4583 void Interpreter::PrintMacFilter(void)
4584 {
4585 otMacFilterEntry entry;
4586 otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
4587
4588 OutputLine("Address Mode: %s", MacFilterAddressModeToString(otLinkFilterGetAddressMode(GetInstancePtr())));
4589
4590 while (otLinkFilterGetNextAddress(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
4591 {
4592 OutputMacFilterEntry(entry);
4593 }
4594
4595 iterator = OT_MAC_FILTER_ITERATOR_INIT;
4596 OutputLine("RssIn List:");
4597
4598 while (otLinkFilterGetNextRssIn(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
4599 {
4600 uint8_t i = 0;
4601
4602 for (; i < OT_EXT_ADDRESS_SIZE; i++)
4603 {
4604 if (entry.mExtAddress.m8[i] != 0xff)
4605 {
4606 break;
4607 }
4608 }
4609
4610 if (i == OT_EXT_ADDRESS_SIZE)
4611 {
4612 OutputLine("Default rss : %d (lqi %d)", entry.mRssIn,
4613 otLinkConvertRssToLinkQuality(GetInstancePtr(), entry.mRssIn));
4614 }
4615 else
4616 {
4617 OutputMacFilterEntry(entry);
4618 }
4619 }
4620 }
4621
ProcessMacFilterAddress(Arg aArgs[])4622 otError Interpreter::ProcessMacFilterAddress(Arg aArgs[])
4623 {
4624 otError error = OT_ERROR_NONE;
4625 otExtAddress extAddr;
4626
4627 if (aArgs[0].IsEmpty())
4628 {
4629 otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
4630 otMacFilterEntry entry;
4631
4632 OutputLine("%s", MacFilterAddressModeToString(otLinkFilterGetAddressMode(GetInstancePtr())));
4633
4634 while (otLinkFilterGetNextAddress(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
4635 {
4636 OutputMacFilterEntry(entry);
4637 }
4638 }
4639 else if (aArgs[0] == "disable")
4640 {
4641 VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
4642 otLinkFilterSetAddressMode(GetInstancePtr(), OT_MAC_FILTER_ADDRESS_MODE_DISABLED);
4643 }
4644 else if (aArgs[0] == "allowlist")
4645 {
4646 VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
4647 otLinkFilterSetAddressMode(GetInstancePtr(), OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST);
4648 }
4649 else if (aArgs[0] == "denylist")
4650 {
4651 VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
4652 otLinkFilterSetAddressMode(GetInstancePtr(), OT_MAC_FILTER_ADDRESS_MODE_DENYLIST);
4653 }
4654 else if (aArgs[0] == "add")
4655 {
4656 SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
4657 error = otLinkFilterAddAddress(GetInstancePtr(), &extAddr);
4658
4659 VerifyOrExit(error == OT_ERROR_NONE || error == OT_ERROR_ALREADY);
4660
4661 if (!aArgs[2].IsEmpty())
4662 {
4663 int8_t rss;
4664
4665 SuccessOrExit(error = aArgs[2].ParseAsInt8(rss));
4666 SuccessOrExit(error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss));
4667 }
4668 }
4669 else if (aArgs[0] == "remove")
4670 {
4671 SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
4672 otLinkFilterRemoveAddress(GetInstancePtr(), &extAddr);
4673 }
4674 else if (aArgs[0] == "clear")
4675 {
4676 otLinkFilterClearAddresses(GetInstancePtr());
4677 }
4678 else
4679 {
4680 error = OT_ERROR_INVALID_COMMAND;
4681 }
4682
4683 exit:
4684 return error;
4685 }
4686
ProcessMacFilterRss(Arg aArgs[])4687 otError Interpreter::ProcessMacFilterRss(Arg aArgs[])
4688 {
4689 otError error = OT_ERROR_NONE;
4690 otMacFilterEntry entry;
4691 otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
4692 otExtAddress extAddr;
4693 int8_t rss;
4694
4695 if (aArgs[0].IsEmpty())
4696 {
4697 while (otLinkFilterGetNextRssIn(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
4698 {
4699 uint8_t i = 0;
4700
4701 for (; i < OT_EXT_ADDRESS_SIZE; i++)
4702 {
4703 if (entry.mExtAddress.m8[i] != 0xff)
4704 {
4705 break;
4706 }
4707 }
4708
4709 if (i == OT_EXT_ADDRESS_SIZE)
4710 {
4711 OutputLine("Default rss: %d (lqi %d)", entry.mRssIn,
4712 otLinkConvertRssToLinkQuality(GetInstancePtr(), entry.mRssIn));
4713 }
4714 else
4715 {
4716 OutputMacFilterEntry(entry);
4717 }
4718 }
4719 }
4720 else if (aArgs[0] == "add-lqi")
4721 {
4722 uint8_t linkQuality;
4723
4724 SuccessOrExit(error = aArgs[2].ParseAsUint8(linkQuality));
4725 VerifyOrExit(linkQuality <= 3, error = OT_ERROR_INVALID_ARGS);
4726 rss = otLinkConvertLinkQualityToRss(GetInstancePtr(), linkQuality);
4727
4728 if (aArgs[1] == "*")
4729 {
4730 otLinkFilterSetDefaultRssIn(GetInstancePtr(), rss);
4731 }
4732 else
4733 {
4734 SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
4735 error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss);
4736 }
4737 }
4738 else if (aArgs[0] == "add")
4739 {
4740 SuccessOrExit(error = aArgs[2].ParseAsInt8(rss));
4741
4742 if (aArgs[1] == "*")
4743 {
4744 otLinkFilterSetDefaultRssIn(GetInstancePtr(), rss);
4745 }
4746 else
4747 {
4748 SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
4749 error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss);
4750 }
4751 }
4752 else if (aArgs[0] == "remove")
4753 {
4754 if (aArgs[1] == "*")
4755 {
4756 otLinkFilterClearDefaultRssIn(GetInstancePtr());
4757 }
4758 else
4759 {
4760 SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
4761 otLinkFilterRemoveRssIn(GetInstancePtr(), &extAddr);
4762 }
4763 }
4764 else if (aArgs[0] == "clear")
4765 {
4766 otLinkFilterClearAllRssIn(GetInstancePtr());
4767 }
4768 else
4769 {
4770 error = OT_ERROR_INVALID_COMMAND;
4771 }
4772
4773 exit:
4774 return error;
4775 }
4776
OutputMacFilterEntry(const otMacFilterEntry & aEntry)4777 void Interpreter::OutputMacFilterEntry(const otMacFilterEntry &aEntry)
4778 {
4779 OutputExtAddress(aEntry.mExtAddress);
4780
4781 if (aEntry.mRssIn != OT_MAC_FILTER_FIXED_RSS_DISABLED)
4782 {
4783 OutputFormat(" : rss %d (lqi %d)", aEntry.mRssIn,
4784 otLinkConvertRssToLinkQuality(GetInstancePtr(), aEntry.mRssIn));
4785 }
4786
4787 OutputLine("");
4788 }
4789
MacFilterAddressModeToString(otMacFilterAddressMode aMode)4790 const char *Interpreter::MacFilterAddressModeToString(otMacFilterAddressMode aMode)
4791 {
4792 static const char *const kModeStrings[] = {
4793 "Disabled", // (0) OT_MAC_FILTER_ADDRESS_MODE_DISABLED
4794 "Allowlist", // (1) OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST
4795 "Denylist", // (2) OT_MAC_FILTER_ADDRESS_MODE_DENYLIST
4796 };
4797
4798 static_assert(0 == OT_MAC_FILTER_ADDRESS_MODE_DISABLED, "OT_MAC_FILTER_ADDRESS_MODE_DISABLED value is incorrect");
4799 static_assert(1 == OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST, "OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST value is incorrect");
4800 static_assert(2 == OT_MAC_FILTER_ADDRESS_MODE_DENYLIST, "OT_MAC_FILTER_ADDRESS_MODE_DENYLIST value is incorrect");
4801
4802 return Stringify(aMode, kModeStrings);
4803 }
4804
4805 #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
4806
Process(Arg aArgs[])4807 template <> otError Interpreter::Process<Cmd("mac")>(Arg aArgs[])
4808 {
4809 otError error = OT_ERROR_NONE;
4810
4811 if (aArgs[0] == "retries")
4812 {
4813 if (aArgs[1] == "direct")
4814 {
4815 error = ProcessGetSet(aArgs + 2, otLinkGetMaxFrameRetriesDirect, otLinkSetMaxFrameRetriesDirect);
4816 }
4817 #if OPENTHREAD_FTD
4818 else if (aArgs[1] == "indirect")
4819 {
4820 error = ProcessGetSet(aArgs + 2, otLinkGetMaxFrameRetriesIndirect, otLinkSetMaxFrameRetriesIndirect);
4821 }
4822 #endif
4823 else
4824 {
4825 error = OT_ERROR_INVALID_ARGS;
4826 }
4827 }
4828 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
4829 else if (aArgs[0] == "send")
4830 {
4831 VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
4832
4833 if (aArgs[1] == "datarequest")
4834 {
4835 error = otLinkSendDataRequest(GetInstancePtr());
4836 }
4837 else if (aArgs[1] == "emptydata")
4838 {
4839 error = otLinkSendEmptyData(GetInstancePtr());
4840 }
4841 else
4842 {
4843 error = OT_ERROR_INVALID_ARGS;
4844 }
4845 }
4846 #endif
4847 else
4848 {
4849 error = OT_ERROR_INVALID_COMMAND;
4850 ExitNow(); // To silence unused `exit` label warning when `REFERENCE_DEVICE_ENABLE` is not enabled.
4851 }
4852
4853 exit:
4854 return error;
4855 }
4856
4857 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
Process(Arg aArgs[])4858 template <> otError Interpreter::Process<Cmd("trel")>(Arg aArgs[])
4859 {
4860 otError error = OT_ERROR_NONE;
4861 bool enable;
4862
4863 if (aArgs[0].IsEmpty())
4864 {
4865 OutputEnabledDisabledStatus(otTrelIsEnabled(GetInstancePtr()));
4866 }
4867 else if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
4868 {
4869 if (enable)
4870 {
4871 otTrelEnable(GetInstancePtr());
4872 }
4873 else
4874 {
4875 otTrelDisable(GetInstancePtr());
4876 }
4877 }
4878 else if (aArgs[0] == "filter")
4879 {
4880 if (aArgs[1].IsEmpty())
4881 {
4882 OutputEnabledDisabledStatus(otTrelIsFilterEnabled(GetInstancePtr()));
4883 }
4884 else
4885 {
4886 SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable));
4887 otTrelSetFilterEnabled(GetInstancePtr(), enable);
4888 }
4889 }
4890 else if (aArgs[0] == "peers")
4891 {
4892 uint16_t index = 0;
4893 otTrelPeerIterator iterator;
4894 const otTrelPeer * peer;
4895 bool isTable = true;
4896
4897 if (aArgs[1] == "list")
4898 {
4899 isTable = false;
4900 }
4901 else
4902 {
4903 VerifyOrExit(aArgs[1].IsEmpty(), error = kErrorInvalidArgs);
4904 }
4905
4906 if (isTable)
4907 {
4908 static const char *const kTrelPeerTableTitles[] = {"No", "Ext MAC Address", "Ext PAN Id",
4909 "IPv6 Socket Address"};
4910
4911 static const uint8_t kTrelPeerTableColumnWidths[] = {5, 18, 18, 50};
4912
4913 OutputTableHeader(kTrelPeerTableTitles, kTrelPeerTableColumnWidths);
4914 }
4915
4916 otTrelInitPeerIterator(GetInstancePtr(), &iterator);
4917
4918 while ((peer = otTrelGetNextPeer(GetInstancePtr(), &iterator)) != nullptr)
4919 {
4920 if (!isTable)
4921 {
4922 OutputFormat("%03u ExtAddr:", ++index);
4923 OutputExtAddress(peer->mExtAddress);
4924 OutputFormat(" ExtPanId:");
4925 OutputBytes(peer->mExtPanId.m8);
4926 OutputFormat(" SockAddr:");
4927 OutputSockAddrLine(peer->mSockAddr);
4928 }
4929 else
4930 {
4931 char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
4932
4933 OutputFormat("| %3u | ", ++index);
4934 OutputExtAddress(peer->mExtAddress);
4935 OutputFormat(" | ");
4936 OutputBytes(peer->mExtPanId.m8);
4937 otIp6SockAddrToString(&peer->mSockAddr, string, sizeof(string));
4938 OutputLine(" | %-48s |", string);
4939 }
4940 }
4941 }
4942 else
4943 {
4944 error = OT_ERROR_INVALID_ARGS;
4945 }
4946
4947 exit:
4948 return error;
4949 }
4950 #endif
4951
4952 #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
Process(Arg aArgs[])4953 template <> otError Interpreter::Process<Cmd("networkdiagnostic")>(Arg aArgs[])
4954 {
4955 otError error = OT_ERROR_NONE;
4956 otIp6Address address;
4957 uint8_t tlvTypes[OT_NETWORK_DIAGNOSTIC_TYPELIST_MAX_ENTRIES];
4958 uint8_t count = 0;
4959
4960 SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
4961
4962 for (Arg *arg = &aArgs[2]; !arg->IsEmpty(); arg++)
4963 {
4964 VerifyOrExit(count < sizeof(tlvTypes), error = OT_ERROR_INVALID_ARGS);
4965 SuccessOrExit(error = arg->ParseAsUint8(tlvTypes[count++]));
4966 }
4967
4968 if (aArgs[0] == "get")
4969 {
4970 SuccessOrExit(error = otThreadSendDiagnosticGet(GetInstancePtr(), &address, tlvTypes, count,
4971 &Interpreter::HandleDiagnosticGetResponse, this));
4972 SetCommandTimeout(kNetworkDiagnosticTimeoutMsecs);
4973 error = OT_ERROR_PENDING;
4974 }
4975 else if (aArgs[0] == "reset")
4976 {
4977 IgnoreError(otThreadSendDiagnosticReset(GetInstancePtr(), &address, tlvTypes, count));
4978 }
4979 else
4980 {
4981 error = OT_ERROR_INVALID_COMMAND;
4982 }
4983
4984 exit:
4985 return error;
4986 }
4987
HandleDiagnosticGetResponse(otError aError,otMessage * aMessage,const otMessageInfo * aMessageInfo,void * aContext)4988 void Interpreter::HandleDiagnosticGetResponse(otError aError,
4989 otMessage * aMessage,
4990 const otMessageInfo *aMessageInfo,
4991 void * aContext)
4992 {
4993 static_cast<Interpreter *>(aContext)->HandleDiagnosticGetResponse(
4994 aError, aMessage, static_cast<const Ip6::MessageInfo *>(aMessageInfo));
4995 }
4996
HandleDiagnosticGetResponse(otError aError,const otMessage * aMessage,const Ip6::MessageInfo * aMessageInfo)4997 void Interpreter::HandleDiagnosticGetResponse(otError aError,
4998 const otMessage * aMessage,
4999 const Ip6::MessageInfo *aMessageInfo)
5000 {
5001 uint8_t buf[16];
5002 uint16_t bytesToPrint;
5003 uint16_t bytesPrinted = 0;
5004 uint16_t length;
5005 otNetworkDiagTlv diagTlv;
5006 otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT;
5007
5008 SuccessOrExit(aError);
5009
5010 OutputFormat("DIAG_GET.rsp/ans from ");
5011 OutputIp6Address(aMessageInfo->mPeerAddr);
5012 OutputFormat(": ");
5013
5014 length = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage);
5015
5016 while (length > 0)
5017 {
5018 bytesToPrint = (length < sizeof(buf)) ? length : sizeof(buf);
5019 otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint);
5020
5021 OutputBytes(buf, static_cast<uint8_t>(bytesToPrint));
5022
5023 length -= bytesToPrint;
5024 bytesPrinted += bytesToPrint;
5025 }
5026
5027 OutputLine("");
5028
5029 // Output Network Diagnostic TLV values in standard YAML format.
5030 while (otThreadGetNextDiagnosticTlv(aMessage, &iterator, &diagTlv) == OT_ERROR_NONE)
5031 {
5032 switch (diagTlv.mType)
5033 {
5034 case OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS:
5035 OutputFormat("Ext Address: '");
5036 OutputExtAddressLine(diagTlv.mData.mExtAddress);
5037 break;
5038 case OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS:
5039 OutputLine("Rloc16: 0x%04x", diagTlv.mData.mAddr16);
5040 break;
5041 case OT_NETWORK_DIAGNOSTIC_TLV_MODE:
5042 OutputLine("Mode:");
5043 OutputMode(kIndentSize, diagTlv.mData.mMode);
5044 break;
5045 case OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT:
5046 OutputLine("Timeout: %u", diagTlv.mData.mTimeout);
5047 break;
5048 case OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY:
5049 OutputLine("Connectivity:");
5050 OutputConnectivity(kIndentSize, diagTlv.mData.mConnectivity);
5051 break;
5052 case OT_NETWORK_DIAGNOSTIC_TLV_ROUTE:
5053 OutputLine("Route:");
5054 OutputRoute(kIndentSize, diagTlv.mData.mRoute);
5055 break;
5056 case OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA:
5057 OutputLine("Leader Data:");
5058 OutputLeaderData(kIndentSize, diagTlv.mData.mLeaderData);
5059 break;
5060 case OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA:
5061 OutputFormat("Network Data: '");
5062 OutputBytesLine(diagTlv.mData.mNetworkData.m8, diagTlv.mData.mNetworkData.mCount);
5063 break;
5064 case OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST:
5065 OutputLine("IP6 Address List:");
5066 for (uint16_t i = 0; i < diagTlv.mData.mIp6AddrList.mCount; ++i)
5067 {
5068 OutputFormat(kIndentSize, "- ");
5069 OutputIp6AddressLine(diagTlv.mData.mIp6AddrList.mList[i]);
5070 }
5071 break;
5072 case OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS:
5073 OutputLine("MAC Counters:");
5074 OutputNetworkDiagMacCounters(kIndentSize, diagTlv.mData.mMacCounters);
5075 break;
5076 case OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL:
5077 OutputLine("Battery Level: %u%%", diagTlv.mData.mBatteryLevel);
5078 break;
5079 case OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE:
5080 OutputLine("Supply Voltage: %umV", diagTlv.mData.mSupplyVoltage);
5081 break;
5082 case OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE:
5083 OutputLine("Child Table:");
5084 for (uint16_t i = 0; i < diagTlv.mData.mChildTable.mCount; ++i)
5085 {
5086 OutputFormat(kIndentSize, "- ");
5087 OutputChildTableEntry(kIndentSize + 2, diagTlv.mData.mChildTable.mTable[i]);
5088 }
5089 break;
5090 case OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES:
5091 OutputFormat("Channel Pages: '");
5092 OutputBytes(diagTlv.mData.mChannelPages.m8, diagTlv.mData.mChannelPages.mCount);
5093 OutputLine("'");
5094 break;
5095 case OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT:
5096 OutputLine("Max Child Timeout: %u", diagTlv.mData.mMaxChildTimeout);
5097 break;
5098 }
5099 }
5100
5101 exit:
5102 return;
5103 }
5104
OutputMode(uint8_t aIndentSize,const otLinkModeConfig & aMode)5105 void Interpreter::OutputMode(uint8_t aIndentSize, const otLinkModeConfig &aMode)
5106 {
5107 OutputLine(aIndentSize, "RxOnWhenIdle: %d", aMode.mRxOnWhenIdle);
5108 OutputLine(aIndentSize, "DeviceType: %d", aMode.mDeviceType);
5109 OutputLine(aIndentSize, "NetworkData: %d", aMode.mNetworkData);
5110 }
5111
OutputConnectivity(uint8_t aIndentSize,const otNetworkDiagConnectivity & aConnectivity)5112 void Interpreter::OutputConnectivity(uint8_t aIndentSize, const otNetworkDiagConnectivity &aConnectivity)
5113 {
5114 OutputLine(aIndentSize, "ParentPriority: %d", aConnectivity.mParentPriority);
5115 OutputLine(aIndentSize, "LinkQuality3: %u", aConnectivity.mLinkQuality3);
5116 OutputLine(aIndentSize, "LinkQuality2: %u", aConnectivity.mLinkQuality2);
5117 OutputLine(aIndentSize, "LinkQuality1: %u", aConnectivity.mLinkQuality1);
5118 OutputLine(aIndentSize, "LeaderCost: %u", aConnectivity.mLeaderCost);
5119 OutputLine(aIndentSize, "IdSequence: %u", aConnectivity.mIdSequence);
5120 OutputLine(aIndentSize, "ActiveRouters: %u", aConnectivity.mActiveRouters);
5121 OutputLine(aIndentSize, "SedBufferSize: %u", aConnectivity.mSedBufferSize);
5122 OutputLine(aIndentSize, "SedDatagramCount: %u", aConnectivity.mSedDatagramCount);
5123 }
OutputRoute(uint8_t aIndentSize,const otNetworkDiagRoute & aRoute)5124 void Interpreter::OutputRoute(uint8_t aIndentSize, const otNetworkDiagRoute &aRoute)
5125 {
5126 OutputLine(aIndentSize, "IdSequence: %u", aRoute.mIdSequence);
5127 OutputLine(aIndentSize, "RouteData:");
5128
5129 aIndentSize += kIndentSize;
5130 for (uint16_t i = 0; i < aRoute.mRouteCount; ++i)
5131 {
5132 OutputFormat(aIndentSize, "- ");
5133 OutputRouteData(aIndentSize + 2, aRoute.mRouteData[i]);
5134 }
5135 }
5136
OutputRouteData(uint8_t aIndentSize,const otNetworkDiagRouteData & aRouteData)5137 void Interpreter::OutputRouteData(uint8_t aIndentSize, const otNetworkDiagRouteData &aRouteData)
5138 {
5139 OutputLine("RouteId: 0x%02x", aRouteData.mRouterId);
5140
5141 OutputLine(aIndentSize, "LinkQualityOut: %u", aRouteData.mLinkQualityOut);
5142 OutputLine(aIndentSize, "LinkQualityIn: %u", aRouteData.mLinkQualityIn);
5143 OutputLine(aIndentSize, "RouteCost: %u", aRouteData.mRouteCost);
5144 }
5145
OutputLeaderData(uint8_t aIndentSize,const otLeaderData & aLeaderData)5146 void Interpreter::OutputLeaderData(uint8_t aIndentSize, const otLeaderData &aLeaderData)
5147 {
5148 OutputLine(aIndentSize, "PartitionId: 0x%08x", aLeaderData.mPartitionId);
5149 OutputLine(aIndentSize, "Weighting: %u", aLeaderData.mWeighting);
5150 OutputLine(aIndentSize, "DataVersion: %u", aLeaderData.mDataVersion);
5151 OutputLine(aIndentSize, "StableDataVersion: %u", aLeaderData.mStableDataVersion);
5152 OutputLine(aIndentSize, "LeaderRouterId: 0x%02x", aLeaderData.mLeaderRouterId);
5153 }
5154
OutputNetworkDiagMacCounters(uint8_t aIndentSize,const otNetworkDiagMacCounters & aMacCounters)5155 void Interpreter::OutputNetworkDiagMacCounters(uint8_t aIndentSize, const otNetworkDiagMacCounters &aMacCounters)
5156 {
5157 OutputLine(aIndentSize, "IfInUnknownProtos: %u", aMacCounters.mIfInUnknownProtos);
5158 OutputLine(aIndentSize, "IfInErrors: %u", aMacCounters.mIfInErrors);
5159 OutputLine(aIndentSize, "IfOutErrors: %u", aMacCounters.mIfOutErrors);
5160 OutputLine(aIndentSize, "IfInUcastPkts: %u", aMacCounters.mIfInUcastPkts);
5161 OutputLine(aIndentSize, "IfInBroadcastPkts: %u", aMacCounters.mIfInBroadcastPkts);
5162 OutputLine(aIndentSize, "IfInDiscards: %u", aMacCounters.mIfInDiscards);
5163 OutputLine(aIndentSize, "IfOutUcastPkts: %u", aMacCounters.mIfOutUcastPkts);
5164 OutputLine(aIndentSize, "IfOutBroadcastPkts: %u", aMacCounters.mIfOutBroadcastPkts);
5165 OutputLine(aIndentSize, "IfOutDiscards: %u", aMacCounters.mIfOutDiscards);
5166 }
5167
OutputChildTableEntry(uint8_t aIndentSize,const otNetworkDiagChildEntry & aChildEntry)5168 void Interpreter::OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiagChildEntry &aChildEntry)
5169 {
5170 OutputLine("ChildId: 0x%04x", aChildEntry.mChildId);
5171
5172 OutputLine(aIndentSize, "Timeout: %u", aChildEntry.mTimeout);
5173 OutputLine(aIndentSize, "Mode:");
5174 OutputMode(aIndentSize + kIndentSize, aChildEntry.mMode);
5175 }
5176 #endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
5177
HandleDetachGracefullyResult(void * aContext)5178 void Interpreter::HandleDetachGracefullyResult(void *aContext)
5179 {
5180 static_cast<Interpreter *>(aContext)->HandleDetachGracefullyResult();
5181 }
5182
HandleDetachGracefullyResult(void)5183 void Interpreter::HandleDetachGracefullyResult(void)
5184 {
5185 OutputLine("Finished detaching");
5186 OutputResult(OT_ERROR_NONE);
5187 }
5188
HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo & aInfo)5189 void Interpreter::HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo &aInfo)
5190 {
5191 OutputFormat("~ Discovery Request from ");
5192 OutputExtAddress(aInfo.mExtAddress);
5193 OutputLine(": version=%u,joiner=%d", aInfo.mVersion, aInfo.mIsJoiner);
5194 }
5195
5196 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
5197
Initialize(otInstance * aInstance,otCliOutputCallback aCallback,void * aContext)5198 void Interpreter::Initialize(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
5199 {
5200 Instance *instance = static_cast<Instance *>(aInstance);
5201
5202 Interpreter::sInterpreter = new (&sInterpreterRaw) Interpreter(instance, aCallback, aContext);
5203 }
5204
OutputPrompt(void)5205 void Interpreter::OutputPrompt(void)
5206 {
5207 #if OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE
5208 static const char sPrompt[] = "> ";
5209
5210 // The `OutputFormat()` below is adding the prompt which is not
5211 // part of any command output, so we set the `EmittingCommandOutput`
5212 // flag to false to avoid it being included in the command output
5213 // log (under `OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE`).
5214
5215 SetEmittingCommandOutput(false);
5216 OutputFormat("%s", sPrompt);
5217 SetEmittingCommandOutput(true);
5218 #endif // OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE
5219 }
5220
HandleTimer(Timer & aTimer)5221 void Interpreter::HandleTimer(Timer &aTimer)
5222 {
5223 static_cast<Interpreter *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleTimer();
5224 }
5225
HandleTimer(void)5226 void Interpreter::HandleTimer(void)
5227 {
5228 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
5229 if (mLocateInProgress)
5230 {
5231 mLocateInProgress = false;
5232 OutputResult(OT_ERROR_RESPONSE_TIMEOUT);
5233 }
5234 else
5235 #endif
5236 {
5237 OutputResult(OT_ERROR_NONE);
5238 }
5239 }
5240
SetCommandTimeout(uint32_t aTimeoutMilli)5241 void Interpreter::SetCommandTimeout(uint32_t aTimeoutMilli)
5242 {
5243 OT_ASSERT(mCommandIsPending);
5244 mTimer.Start(aTimeoutMilli);
5245 }
5246
ProcessCommand(Arg aArgs[])5247 otError Interpreter::ProcessCommand(Arg aArgs[])
5248 {
5249 #define CmdEntry(aCommandString) \
5250 { \
5251 aCommandString, &Interpreter::Process<Cmd(aCommandString)> \
5252 }
5253
5254 static constexpr Command kCommands[] = {
5255 #if OPENTHREAD_FTD || OPENTHREAD_MTD
5256 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
5257 CmdEntry("ba"),
5258 #endif
5259 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
5260 CmdEntry("bbr"),
5261 #endif
5262 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
5263 CmdEntry("br"),
5264 #endif
5265 CmdEntry("bufferinfo"),
5266 CmdEntry("ccathreshold"),
5267 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
5268 CmdEntry("ccm"),
5269 #endif
5270 CmdEntry("channel"),
5271 #if OPENTHREAD_FTD
5272 CmdEntry("child"),
5273 CmdEntry("childip"),
5274 CmdEntry("childmax"),
5275 #endif
5276 #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
5277 CmdEntry("childsupervision"),
5278 #endif
5279 CmdEntry("childtimeout"),
5280 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
5281 CmdEntry("coap"),
5282 #endif
5283 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
5284 CmdEntry("coaps"),
5285 #endif
5286 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
5287 CmdEntry("coex"),
5288 #endif
5289 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
5290 CmdEntry("commissioner"),
5291 #endif
5292 #if OPENTHREAD_FTD
5293 CmdEntry("contextreusedelay"),
5294 #endif
5295 CmdEntry("counters"),
5296 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
5297 CmdEntry("csl"),
5298 #endif
5299 CmdEntry("dataset"),
5300 #if OPENTHREAD_FTD
5301 CmdEntry("delaytimermin"),
5302 #endif
5303 CmdEntry("detach"),
5304 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
5305 #if OPENTHREAD_CONFIG_DIAG_ENABLE
5306 CmdEntry("diag"),
5307 #endif
5308 #if OPENTHREAD_FTD || OPENTHREAD_MTD
5309 CmdEntry("discover"),
5310 CmdEntry("dns"),
5311 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
5312 CmdEntry("domainname"),
5313 #endif
5314 #if OPENTHREAD_CONFIG_DUA_ENABLE
5315 CmdEntry("dua"),
5316 #endif
5317 #if OPENTHREAD_FTD
5318 CmdEntry("eidcache"),
5319 #endif
5320 CmdEntry("eui64"),
5321 CmdEntry("extaddr"),
5322 CmdEntry("extpanid"),
5323 CmdEntry("factoryreset"),
5324 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
5325 CmdEntry("fake"),
5326 #endif
5327 CmdEntry("fem"),
5328 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
5329 #if OPENTHREAD_FTD || OPENTHREAD_MTD
5330 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
5331 CmdEntry("history"),
5332 #endif
5333 CmdEntry("ifconfig"),
5334 CmdEntry("ipaddr"),
5335 CmdEntry("ipmaddr"),
5336 #if OPENTHREAD_CONFIG_JOINER_ENABLE
5337 CmdEntry("joiner"),
5338 #endif
5339 #if OPENTHREAD_FTD
5340 CmdEntry("joinerport"),
5341 #endif
5342 CmdEntry("keysequence"),
5343 CmdEntry("leaderdata"),
5344 #if OPENTHREAD_FTD
5345 CmdEntry("leaderweight"),
5346 #endif
5347 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
5348 CmdEntry("linkmetrics"),
5349 #endif
5350 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
5351 CmdEntry("locate"),
5352 #endif
5353 CmdEntry("log"),
5354 CmdEntry("mac"),
5355 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
5356 CmdEntry("macfilter"),
5357 #endif
5358 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
5359 CmdEntry("mliid"),
5360 #endif
5361 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
5362 CmdEntry("mlr"),
5363 #endif
5364 CmdEntry("mode"),
5365 CmdEntry("multiradio"),
5366 #if OPENTHREAD_FTD
5367 CmdEntry("neighbor"),
5368 #endif
5369 CmdEntry("netdata"),
5370 CmdEntry("netstat"),
5371 #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
5372 CmdEntry("networkdiagnostic"),
5373 #endif
5374 #if OPENTHREAD_FTD
5375 CmdEntry("networkidtimeout"),
5376 #endif
5377 CmdEntry("networkkey"),
5378 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
5379 CmdEntry("networkkeyref"),
5380 #endif
5381 CmdEntry("networkname"),
5382 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
5383 CmdEntry("networktime"),
5384 #endif
5385 CmdEntry("panid"),
5386 CmdEntry("parent"),
5387 #if OPENTHREAD_FTD
5388 CmdEntry("parentpriority"),
5389 CmdEntry("partitionid"),
5390 #endif
5391 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
5392 CmdEntry("ping"),
5393 #endif
5394 CmdEntry("pollperiod"),
5395 #if OPENTHREAD_FTD
5396 CmdEntry("preferrouterid"),
5397 #endif
5398 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
5399 CmdEntry("prefix"),
5400 #endif
5401 CmdEntry("promiscuous"),
5402 #if OPENTHREAD_FTD
5403 CmdEntry("pskc"),
5404 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
5405 CmdEntry("pskcref"),
5406 #endif
5407 #endif
5408 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
5409 CmdEntry("radiofilter"),
5410 #endif
5411 CmdEntry("rcp"),
5412 CmdEntry("region"),
5413 #if OPENTHREAD_FTD
5414 CmdEntry("releaserouterid"),
5415 #endif
5416 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
5417 CmdEntry("reset"),
5418 #if OPENTHREAD_FTD || OPENTHREAD_MTD
5419 CmdEntry("rloc16"),
5420 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
5421 CmdEntry("route"),
5422 #endif
5423 #if OPENTHREAD_FTD
5424 CmdEntry("router"),
5425 CmdEntry("routerdowngradethreshold"),
5426 CmdEntry("routereligible"),
5427 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
5428 CmdEntry("routeridrange"),
5429 #endif
5430 CmdEntry("routerselectionjitter"),
5431 CmdEntry("routerupgradethreshold"),
5432 #endif
5433 CmdEntry("scan"),
5434 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
5435 CmdEntry("service"),
5436 #endif
5437 CmdEntry("singleton"),
5438 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
5439 CmdEntry("sntp"),
5440 #endif
5441 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
5442 CmdEntry("srp"),
5443 #endif
5444 CmdEntry("state"),
5445 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
5446 CmdEntry("tcp"),
5447 #endif
5448 CmdEntry("thread"),
5449 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
5450 CmdEntry("trel"),
5451 #endif
5452 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
5453 CmdEntry("tvcheck"),
5454 #endif
5455 CmdEntry("txpower"),
5456 CmdEntry("udp"),
5457 CmdEntry("unsecureport"),
5458 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
5459 CmdEntry("uptime"),
5460 #endif
5461 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
5462 CmdEntry("version"),
5463 };
5464
5465 #undef CmdEntry
5466
5467 static_assert(BinarySearch::IsSorted(kCommands), "Command Table is not sorted");
5468
5469 otError error = OT_ERROR_NONE;
5470 const Command *command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
5471
5472 if (command != nullptr)
5473 {
5474 error = (this->*command->mHandler)(aArgs + 1);
5475 }
5476 else if (aArgs[0] == "help")
5477 {
5478 OutputCommandTable(kCommands);
5479
5480 for (uint8_t i = 0; i < mUserCommandsLength; i++)
5481 {
5482 OutputLine("%s", mUserCommands[i].mName);
5483 }
5484 }
5485 else
5486 {
5487 error = ProcessUserCommands(aArgs);
5488 }
5489
5490 return error;
5491 }
5492
otCliInit(otInstance * aInstance,otCliOutputCallback aCallback,void * aContext)5493 extern "C" void otCliInit(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
5494 {
5495 Interpreter::Initialize(aInstance, aCallback, aContext);
5496 }
5497
otCliInputLine(char * aBuf)5498 extern "C" void otCliInputLine(char *aBuf)
5499 {
5500 Interpreter::GetInterpreter().ProcessLine(aBuf);
5501 }
5502
otCliSetUserCommands(const otCliCommand * aUserCommands,uint8_t aLength,void * aContext)5503 extern "C" void otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext)
5504 {
5505 Interpreter::GetInterpreter().SetUserCommands(aUserCommands, aLength, aContext);
5506 }
5507
otCliOutputBytes(const uint8_t * aBytes,uint8_t aLength)5508 extern "C" void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength)
5509 {
5510 Interpreter::GetInterpreter().OutputBytes(aBytes, aLength);
5511 }
5512
otCliOutputFormat(const char * aFmt,...)5513 extern "C" void otCliOutputFormat(const char *aFmt, ...)
5514 {
5515 va_list aAp;
5516 va_start(aAp, aFmt);
5517 Interpreter::GetInterpreter().OutputFormatV(aFmt, aAp);
5518 va_end(aAp);
5519 }
5520
otCliAppendResult(otError aError)5521 extern "C" void otCliAppendResult(otError aError)
5522 {
5523 Interpreter::GetInterpreter().OutputResult(aError);
5524 }
5525
otCliPlatLogv(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aFormat,va_list aArgs)5526 extern "C" void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)
5527 {
5528 OT_UNUSED_VARIABLE(aLogLevel);
5529 OT_UNUSED_VARIABLE(aLogRegion);
5530
5531 VerifyOrExit(Interpreter::IsInitialized());
5532
5533 // CLI output is being used for logging, so we set the flag
5534 // `EmittingCommandOutput` to false indicate this.
5535 Interpreter::GetInterpreter().SetEmittingCommandOutput(false);
5536 Interpreter::GetInterpreter().OutputFormatV(aFormat, aArgs);
5537 Interpreter::GetInterpreter().OutputLine("");
5538 Interpreter::GetInterpreter().SetEmittingCommandOutput(true);
5539
5540 exit:
5541 return;
5542 }
5543
5544 } // namespace Cli
5545 } // namespace ot
5546
5547 #if OPENTHREAD_CONFIG_LEGACY_ENABLE
otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers * aHandlers)5548 OT_TOOL_WEAK void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers)
5549 {
5550 OT_UNUSED_VARIABLE(aHandlers);
5551 }
5552
otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t * aUlaPrefix)5553 OT_TOOL_WEAK void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix)
5554 {
5555 OT_UNUSED_VARIABLE(aUlaPrefix);
5556 }
5557
otNcpHandleLegacyNodeDidJoin(const otExtAddress * aExtAddr)5558 OT_TOOL_WEAK void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr)
5559 {
5560 OT_UNUSED_VARIABLE(aExtAddr);
5561 }
5562 #endif
5563