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