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/child_supervision.h>
41 #include <openthread/diag.h>
42 #include <openthread/dns.h>
43 #include <openthread/icmp6.h>
44 #include <openthread/link.h>
45 #include <openthread/logging.h>
46 #include <openthread/ncp.h>
47 #include <openthread/thread.h>
48 #include "common/num_utils.hpp"
49 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
50 #include <openthread/network_time.h>
51 #endif
52 #if OPENTHREAD_FTD
53 #include <openthread/dataset_ftd.h>
54 #include <openthread/thread_ftd.h>
55 #endif
56 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
57 #include <openthread/border_router.h>
58 #endif
59 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
60 #include <openthread/server.h>
61 #endif
62 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
63 #include <openthread/platform/misc.h>
64 #endif
65 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
66 #include <openthread/backbone_router.h>
67 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
68 #include <openthread/backbone_router_ftd.h>
69 #endif
70 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
71 #include <openthread/link_metrics.h>
72 #endif
73 #endif
74 #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
75 #include <openthread/channel_manager.h>
76 #endif
77 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
78 #include <openthread/channel_monitor.h>
79 #endif
80 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX
81 #include <openthread/platform/debug_uart.h>
82 #endif
83 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
84 #include <openthread/trel.h>
85 #endif
86 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
87 #include <openthread/nat64.h>
88 #endif
89 
90 #include "common/new.hpp"
91 #include "common/string.hpp"
92 #include "mac/channel_mask.hpp"
93 
94 namespace ot {
95 namespace Cli {
96 
97 Interpreter *Interpreter::sInterpreter = nullptr;
98 static OT_DEFINE_ALIGNED_VAR(sInterpreterRaw, sizeof(Interpreter), uint64_t);
99 
Interpreter(Instance * aInstance,otCliOutputCallback aCallback,void * aContext)100 Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, void *aContext)
101     : OutputImplementer(aCallback, aContext)
102     , Output(aInstance, *this)
103     , mCommandIsPending(false)
104     , mTimer(*aInstance, HandleTimer, this)
105 #if OPENTHREAD_FTD || OPENTHREAD_MTD
106 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
107     , mSntpQueryingInProgress(false)
108 #endif
109     , mDataset(aInstance, *this)
110     , mNetworkData(aInstance, *this)
111     , mUdp(aInstance, *this)
112 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
113     , mBr(aInstance, *this)
114 #endif
115 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
116     , mTcp(aInstance, *this)
117 #endif
118 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
119     , mCoap(aInstance, *this)
120 #endif
121 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
122     , mCoapSecure(aInstance, *this)
123 #endif
124 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
125     , mCommissioner(aInstance, *this)
126 #endif
127 #if OPENTHREAD_CONFIG_JOINER_ENABLE
128     , mJoiner(aInstance, *this)
129 #endif
130 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
131     , mSrpClient(aInstance, *this)
132 #endif
133 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
134     , mSrpServer(aInstance, *this)
135 #endif
136 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
137     , mHistory(aInstance, *this)
138 #endif
139 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
140     , mLocateInProgress(false)
141 #endif
142 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
143     , mLinkMetricsQueryInProgress(false)
144 #endif
145 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
146 {
147 #if (OPENTHREAD_FTD || OPENTHREAD_MTD) && OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK
148     otIp6SetReceiveCallback(GetInstancePtr(), &Interpreter::HandleIp6Receive, this);
149 #endif
150     memset(&mUserCommands, 0, sizeof(mUserCommands));
151 
152     OutputPrompt();
153 }
154 
OutputResult(otError aError)155 void Interpreter::OutputResult(otError aError)
156 {
157     OT_ASSERT(mCommandIsPending);
158 
159     VerifyOrExit(aError != OT_ERROR_PENDING);
160 
161     if (aError == OT_ERROR_NONE)
162     {
163         OutputLine("Done");
164     }
165     else
166     {
167         OutputLine("Error %u: %s", aError, otThreadErrorToString(aError));
168     }
169 
170     mCommandIsPending = false;
171     mTimer.Stop();
172     OutputPrompt();
173 
174 exit:
175     return;
176 }
177 
LinkModeToString(const otLinkModeConfig & aLinkMode,char (& aStringBuffer)[kLinkModeStringSize])178 const char *Interpreter::LinkModeToString(const otLinkModeConfig &aLinkMode, char (&aStringBuffer)[kLinkModeStringSize])
179 {
180     char *flagsPtr = &aStringBuffer[0];
181 
182     if (aLinkMode.mRxOnWhenIdle)
183     {
184         *flagsPtr++ = 'r';
185     }
186 
187     if (aLinkMode.mDeviceType)
188     {
189         *flagsPtr++ = 'd';
190     }
191 
192     if (aLinkMode.mNetworkData)
193     {
194         *flagsPtr++ = 'n';
195     }
196 
197     if (flagsPtr == &aStringBuffer[0])
198     {
199         *flagsPtr++ = '-';
200     }
201 
202     *flagsPtr = '\0';
203 
204     return aStringBuffer;
205 }
206 
207 #if OPENTHREAD_CONFIG_DIAG_ENABLE
Process(Arg aArgs[])208 template <> otError Interpreter::Process<Cmd("diag")>(Arg aArgs[])
209 {
210     otError error;
211     char   *args[kMaxArgs];
212     char    output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE];
213 
214     // all diagnostics related features are processed within diagnostics module
215     Arg::CopyArgsToStringArray(aArgs, args);
216 
217     error = otDiagProcessCmd(GetInstancePtr(), Arg::GetArgsLength(aArgs), args, output, sizeof(output));
218 
219     OutputFormat("%s", output);
220 
221     return error;
222 }
223 #endif
224 
Process(Arg aArgs[])225 template <> otError Interpreter::Process<Cmd("version")>(Arg aArgs[])
226 {
227     otError error = OT_ERROR_NONE;
228 
229     if (aArgs[0].IsEmpty())
230     {
231         OutputLine("%s", otGetVersionString());
232     }
233     else if (aArgs[0] == "api")
234     {
235         OutputLine("%u", OPENTHREAD_API_VERSION);
236     }
237     else
238     {
239         error = OT_ERROR_INVALID_COMMAND;
240     }
241 
242     return error;
243 }
244 
Process(Arg aArgs[])245 template <> otError Interpreter::Process<Cmd("reset")>(Arg aArgs[])
246 {
247     OT_UNUSED_VARIABLE(aArgs);
248 
249     otInstanceReset(GetInstancePtr());
250 
251     return OT_ERROR_NONE;
252 }
253 
ProcessLine(char * aBuf)254 void Interpreter::ProcessLine(char *aBuf)
255 {
256     Arg     args[kMaxArgs + 1];
257     otError error = OT_ERROR_NONE;
258 
259     OT_ASSERT(aBuf != nullptr);
260 
261     // Ignore the command if another command is pending.
262     VerifyOrExit(!mCommandIsPending, args[0].Clear());
263     mCommandIsPending = true;
264 
265     VerifyOrExit(StringLength(aBuf, kMaxLineLength) <= kMaxLineLength - 1, error = OT_ERROR_PARSE);
266 
267     SuccessOrExit(error = Utils::CmdLineParser::ParseCmd(aBuf, args, kMaxArgs));
268     VerifyOrExit(!args[0].IsEmpty(), mCommandIsPending = false);
269 
270     LogInput(args);
271 
272 #if OPENTHREAD_CONFIG_DIAG_ENABLE
273     if (otDiagIsEnabled(GetInstancePtr()) && (args[0] != "diag") && (args[0] != "factoryreset"))
274     {
275         OutputLine("under diagnostics mode, execute 'diag stop' before running any other commands.");
276         ExitNow(error = OT_ERROR_INVALID_STATE);
277     }
278 #endif
279 
280     error = ProcessCommand(args);
281 
282 exit:
283     if ((error != OT_ERROR_NONE) || !args[0].IsEmpty())
284     {
285         OutputResult(error);
286     }
287     else if (!mCommandIsPending)
288     {
289         OutputPrompt();
290     }
291 }
292 
ProcessUserCommands(Arg aArgs[])293 otError Interpreter::ProcessUserCommands(Arg aArgs[])
294 {
295     otError error = OT_ERROR_INVALID_COMMAND;
296 
297     for (const UserCommandsEntry &entry : mUserCommands)
298     {
299         for (uint8_t i = 0; i < entry.mLength; i++)
300         {
301             if (aArgs[0] == entry.mCommands[i].mName)
302             {
303                 char *args[kMaxArgs];
304 
305                 Arg::CopyArgsToStringArray(aArgs, args);
306                 error = entry.mCommands[i].mCommand(entry.mContext, Arg::GetArgsLength(aArgs) - 1, args + 1);
307                 break;
308             }
309         }
310     }
311 
312     return error;
313 }
314 
SetUserCommands(const otCliCommand * aCommands,uint8_t aLength,void * aContext)315 otError Interpreter::SetUserCommands(const otCliCommand *aCommands, uint8_t aLength, void *aContext)
316 {
317     otError error = OT_ERROR_FAILED;
318 
319     for (UserCommandsEntry &entry : mUserCommands)
320     {
321         if (entry.mCommands == nullptr)
322         {
323             entry.mCommands = aCommands;
324             entry.mLength   = aLength;
325             entry.mContext  = aContext;
326 
327             error = OT_ERROR_NONE;
328             break;
329         }
330     }
331 
332     return error;
333 }
334 
335 #if OPENTHREAD_FTD || OPENTHREAD_MTD
ParseEnableOrDisable(const Arg & aArg,bool & aEnable)336 otError Interpreter::ParseEnableOrDisable(const Arg &aArg, bool &aEnable)
337 {
338     otError error = OT_ERROR_NONE;
339 
340     if (aArg == "enable")
341     {
342         aEnable = true;
343     }
344     else if (aArg == "disable")
345     {
346         aEnable = false;
347     }
348     else
349     {
350         error = OT_ERROR_INVALID_COMMAND;
351     }
352 
353     return error;
354 }
355 
ParseJoinerDiscerner(Arg & aArg,otJoinerDiscerner & aDiscerner)356 otError Interpreter::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner)
357 {
358     otError error;
359     char   *separator;
360 
361     VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
362 
363     separator = strstr(aArg.GetCString(), "/");
364 
365     VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND);
366 
367     SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(separator + 1, aDiscerner.mLength));
368     VerifyOrExit(aDiscerner.mLength > 0 && aDiscerner.mLength <= 64, error = OT_ERROR_INVALID_ARGS);
369     *separator = '\0';
370     error      = aArg.ParseAsUint64(aDiscerner.mValue);
371 
372 exit:
373     return error;
374 }
375 
376 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
377 
ParsePingInterval(const Arg & aArg,uint32_t & aInterval)378 otError Interpreter::ParsePingInterval(const Arg &aArg, uint32_t &aInterval)
379 {
380     otError        error    = OT_ERROR_NONE;
381     const char    *string   = aArg.GetCString();
382     const uint32_t msFactor = 1000;
383     uint32_t       factor   = msFactor;
384 
385     aInterval = 0;
386 
387     while (*string)
388     {
389         if ('0' <= *string && *string <= '9')
390         {
391             // In the case of seconds, change the base of already calculated value.
392             if (factor == msFactor)
393             {
394                 aInterval *= 10;
395             }
396 
397             aInterval += static_cast<uint32_t>(*string - '0') * factor;
398 
399             // In the case of milliseconds, change the multiplier factor.
400             if (factor != msFactor)
401             {
402                 factor /= 10;
403             }
404         }
405         else if (*string == '.')
406         {
407             // Accept only one dot character.
408             VerifyOrExit(factor == msFactor, error = OT_ERROR_INVALID_ARGS);
409 
410             // Start analyzing hundreds of milliseconds.
411             factor /= 10;
412         }
413         else
414         {
415             ExitNow(error = OT_ERROR_INVALID_ARGS);
416         }
417 
418         string++;
419     }
420 
421 exit:
422     return error;
423 }
424 
425 #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
426 
ParsePreference(const Arg & aArg,otRoutePreference & aPreference)427 otError Interpreter::ParsePreference(const Arg &aArg, otRoutePreference &aPreference)
428 {
429     otError error = OT_ERROR_NONE;
430 
431     if (aArg == "high")
432     {
433         aPreference = OT_ROUTE_PREFERENCE_HIGH;
434     }
435     else if (aArg == "med")
436     {
437         aPreference = OT_ROUTE_PREFERENCE_MED;
438     }
439     else if (aArg == "low")
440     {
441         aPreference = OT_ROUTE_PREFERENCE_LOW;
442     }
443     else
444     {
445         error = OT_ERROR_INVALID_ARGS;
446     }
447 
448     return error;
449 }
450 
PreferenceToString(signed int aPreference)451 const char *Interpreter::PreferenceToString(signed int aPreference)
452 {
453     const char *str = "";
454 
455     switch (aPreference)
456     {
457     case OT_ROUTE_PREFERENCE_LOW:
458         str = "low";
459         break;
460 
461     case OT_ROUTE_PREFERENCE_MED:
462         str = "med";
463         break;
464 
465     case OT_ROUTE_PREFERENCE_HIGH:
466         str = "high";
467         break;
468 
469     default:
470         break;
471     }
472 
473     return str;
474 }
475 
ParseToIp6Address(otInstance * aInstance,const Arg & aArg,otIp6Address & aAddress,bool & aSynthesized)476 otError Interpreter::ParseToIp6Address(otInstance   *aInstance,
477                                        const Arg    &aArg,
478                                        otIp6Address &aAddress,
479                                        bool         &aSynthesized)
480 {
481     Error error = kErrorNone;
482 
483     VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
484     error        = aArg.ParseAsIp6Address(aAddress);
485     aSynthesized = false;
486     if (error != kErrorNone)
487     {
488         // It might be an IPv4 address, let's have a try.
489         otIp4Address ip4Address;
490 
491         // Do not touch the error value if we failed to parse it as an IPv4 address.
492         SuccessOrExit(aArg.ParseAsIp4Address(ip4Address));
493         SuccessOrExit(error = otNat64SynthesizeIp6Address(aInstance, &ip4Address, &aAddress));
494         aSynthesized = true;
495     }
496 
497 exit:
498     return error;
499 }
500 
501 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Process(Arg aArgs[])502 template <> otError Interpreter::Process<Cmd("history")>(Arg aArgs[]) { return mHistory.Process(aArgs); }
503 #endif
504 
505 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
Process(Arg aArgs[])506 template <> otError Interpreter::Process<Cmd("ba")>(Arg aArgs[])
507 {
508     otError error = OT_ERROR_NONE;
509 
510     /**
511      * @cli ba port
512      * @code
513      * ba port
514      * 49153
515      * Done
516      * @endcode
517      * @par api_copy
518      * #otBorderAgentGetUdpPort
519      */
520     if (aArgs[0] == "port")
521     {
522         OutputLine("%hu", otBorderAgentGetUdpPort(GetInstancePtr()));
523     }
524     /**
525      * @cli ba state
526      * @code
527      * ba state
528      * Started
529      * Done
530      * @endcode
531      * @par api_copy
532      * #otBorderAgentGetState
533      */
534     else if (aArgs[0] == "state")
535     {
536         static const char *const kStateStrings[] = {
537             "Stopped"  // (0) OT_BORDER_AGENT_STATE_STOPPED
538             "Started", // (1) OT_BORDER_AGENT_STATE_STARTED
539             "Active",  // (2) OT_BORDER_AGENT_STATE_ACTIVE
540         };
541 
542         static_assert(0 == OT_BORDER_AGENT_STATE_STOPPED, "OT_BORDER_AGENT_STATE_STOPPED value is incorrect");
543         static_assert(1 == OT_BORDER_AGENT_STATE_STARTED, "OT_BORDER_AGENT_STATE_STARTED value is incorrect");
544         static_assert(2 == OT_BORDER_AGENT_STATE_ACTIVE, "OT_BORDER_AGENT_STATE_ACTIVE value is incorrect");
545 
546         OutputLine("%s", Stringify(otBorderAgentGetState(GetInstancePtr()), kStateStrings));
547     }
548     else
549     {
550         error = OT_ERROR_INVALID_COMMAND;
551     }
552 
553     return error;
554 }
555 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
556 
557 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
Process(Arg aArgs[])558 template <> otError Interpreter::Process<Cmd("br")>(Arg aArgs[]) { return mBr.Process(aArgs); }
559 #endif
560 
561 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
Process(Arg aArgs[])562 template <> otError Interpreter::Process<Cmd("nat64")>(Arg aArgs[])
563 {
564     otError error = OT_ERROR_NONE;
565     bool    enable;
566 
567     if (aArgs[0].IsEmpty())
568     {
569         ExitNow(error = OT_ERROR_INVALID_COMMAND);
570     }
571     /**
572      * @cli nat64 (enable,disable)
573      * @code
574      * nat64 enable
575      * Done
576      * @endcode
577      * @code
578      * nat64 disable
579      * Done
580      * @endcode
581      * @cparam nat64 @ca{enable|disable}
582      * @par api_copy
583      * #otNat64SetEnabled
584      *
585      */
586     if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
587     {
588         otNat64SetEnabled(GetInstancePtr(), enable);
589     }
590     /**
591      * @cli nat64 state
592      * @code
593      * nat64 state
594      * PrefixManager: Active
595      * Translator: Active
596      * Done
597      * @endcode
598      * @par
599      * Gets the state of NAT64 functions.
600      * @par
601      * `PrefixManager` state is available when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled.
602      * `Translator` state is available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
603      * @par
604      * When `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled, `PrefixManager` returns one of the following
605      * states:
606      * - `Disabled`: NAT64 prefix manager is disabled.
607      * - `NotRunning`: NAT64 prefix manager is enabled, but is not running. This could mean that the routing manager is
608      *   disabled.
609      * - `Idle`: NAT64 prefix manager is enabled and is running, but is not publishing a NAT64 prefix. This can happen
610      *   when there is another border router publishing a NAT64 prefix with a higher priority.
611      * - `Active`: NAT64 prefix manager is enabled, running, and publishing a NAT64 prefix.
612      * @par
613      * When `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled, `Translator` returns one of the following states:
614      * - `Disabled`: NAT64 translator is disabled.
615      * - `NotRunning`: NAT64 translator is enabled, but is not translating packets. This could mean that the Translator
616      *   is not configured with a NAT64 prefix or a CIDR for NAT64.
617      * - `Active`: NAT64 translator is enabled and is translating packets.
618      * @sa otNat64GetPrefixManagerState
619      * @sa otNat64GetTranslatorState
620      *
621      */
622     else if (aArgs[0] == "state")
623     {
624         static const char *const kNat64State[] = {"Disabled", "NotRunning", "Idle", "Active"};
625 
626         static_assert(0 == OT_NAT64_STATE_DISABLED, "OT_NAT64_STATE_DISABLED value is incorrect");
627         static_assert(1 == OT_NAT64_STATE_NOT_RUNNING, "OT_NAT64_STATE_NOT_RUNNING value is incorrect");
628         static_assert(2 == OT_NAT64_STATE_IDLE, "OT_NAT64_STATE_IDLE value is incorrect");
629         static_assert(3 == OT_NAT64_STATE_ACTIVE, "OT_NAT64_STATE_ACTIVE value is incorrect");
630 
631 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
632         OutputLine("PrefixManager: %s", kNat64State[otNat64GetPrefixManagerState(GetInstancePtr())]);
633 #endif
634 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
635         OutputLine("Translator: %s", kNat64State[otNat64GetTranslatorState(GetInstancePtr())]);
636 #endif
637     }
638 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
639     /**
640      * @cli nat64 cidr
641      * @code
642      * nat64 cidr
643      * 192.168.255.0/24
644      * Done
645      * @endcode
646      * @par api_copy
647      * #otNat64GetCidr
648      *
649      */
650     else if (aArgs[0] == "cidr")
651     {
652         otIp4Cidr cidr;
653         char      cidrString[OT_IP4_CIDR_STRING_SIZE];
654 
655         SuccessOrExit(error = otNat64GetCidr(GetInstancePtr(), &cidr));
656         otIp4CidrToString(&cidr, cidrString, sizeof(cidrString));
657         OutputLine("%s", cidrString);
658     }
659     /**
660      * @cli nat64 mappings
661      * @code
662      * nat64 mappings
663      * |          | Address                   |        | 4 to 6       | 6 to 4       |
664      * +----------+---------------------------+--------+--------------+--------------+
665      * | ID       | IPv6       | IPv4         | Expiry | Pkts | Bytes | Pkts | Bytes |
666      * +----------+------------+--------------+--------+------+-------+------+-------+
667      * | 00021cb9 | fdc7::df79 | 192.168.64.2 |  7196s |    6 |   456 |   11 |  1928 |
668      * |          |                                TCP |    0 |     0 |    0 |     0 |
669      * |          |                                UDP |    1 |   136 |   16 |  1608 |
670      * |          |                               ICMP |    5 |   320 |    5 |   320 |
671      * @endcode
672      * @par api_copy
673      * #otNat64GetNextAddressMapping
674      *
675      */
676     else if (aArgs[0] == "mappings")
677     {
678         otNat64AddressMappingIterator iterator;
679         otNat64AddressMapping         mapping;
680 
681         static const char *const kNat64StatusLevel1Title[] = {"", "Address", "", "4 to 6", "6 to 4"};
682 
683         static const uint8_t kNat64StatusLevel1ColumnWidths[] = {
684             18, 61, 8, 25, 25,
685         };
686 
687         static const char *const kNat64StatusTableHeader[] = {
688             "ID", "IPv6", "IPv4", "Expiry", "Pkts", "Bytes", "Pkts", "Bytes",
689         };
690 
691         static const uint8_t kNat64StatusTableColumnWidths[] = {
692             18, 42, 18, 8, 10, 14, 10, 14,
693         };
694 
695         OutputTableHeader(kNat64StatusLevel1Title, kNat64StatusLevel1ColumnWidths);
696         OutputTableHeader(kNat64StatusTableHeader, kNat64StatusTableColumnWidths);
697 
698         otNat64InitAddressMappingIterator(GetInstancePtr(), &iterator);
699         while (otNat64GetNextAddressMapping(GetInstancePtr(), &iterator, &mapping) == OT_ERROR_NONE)
700         {
701             char               ip4AddressString[OT_IP4_ADDRESS_STRING_SIZE];
702             char               ip6AddressString[OT_IP6_PREFIX_STRING_SIZE];
703             Uint64StringBuffer u64StringBuffer;
704 
705             otIp6AddressToString(&mapping.mIp6, ip6AddressString, sizeof(ip6AddressString));
706             otIp4AddressToString(&mapping.mIp4, ip4AddressString, sizeof(ip4AddressString));
707 
708             OutputFormat("| %08lx%08lx ", ToUlong(static_cast<uint32_t>(mapping.mId >> 32)),
709                          ToUlong(static_cast<uint32_t>(mapping.mId & 0xffffffff)));
710             OutputFormat("| %40s ", ip6AddressString);
711             OutputFormat("| %16s ", ip4AddressString);
712             OutputFormat("| %5lus ", ToUlong(mapping.mRemainingTimeMs / 1000));
713             OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mTotal.m4To6Packets, u64StringBuffer));
714             OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mTotal.m4To6Bytes, u64StringBuffer));
715             OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mTotal.m6To4Packets, u64StringBuffer));
716             OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mTotal.m6To4Bytes, u64StringBuffer));
717 
718             OutputLine("|");
719 
720             OutputFormat("| %16s ", "");
721             OutputFormat("| %68s ", "TCP");
722             OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mTcp.m4To6Packets, u64StringBuffer));
723             OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mTcp.m4To6Bytes, u64StringBuffer));
724             OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mTcp.m6To4Packets, u64StringBuffer));
725             OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mTcp.m6To4Bytes, u64StringBuffer));
726             OutputLine("|");
727 
728             OutputFormat("| %16s ", "");
729             OutputFormat("| %68s ", "UDP");
730             OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mUdp.m4To6Packets, u64StringBuffer));
731             OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mUdp.m4To6Bytes, u64StringBuffer));
732             OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mUdp.m6To4Packets, u64StringBuffer));
733             OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mUdp.m6To4Bytes, u64StringBuffer));
734             OutputLine("|");
735 
736             OutputFormat("| %16s ", "");
737             OutputFormat("| %68s ", "ICMP");
738             OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mIcmp.m4To6Packets, u64StringBuffer));
739             OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mIcmp.m4To6Bytes, u64StringBuffer));
740             OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mIcmp.m6To4Packets, u64StringBuffer));
741             OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mIcmp.m6To4Bytes, u64StringBuffer));
742             OutputLine("|");
743         }
744     }
745     /**
746      * @cli nat64 counters
747      * @code
748      * nat64 counters
749      * |               | 4 to 6                  | 6 to 4                  |
750      * +---------------+-------------------------+-------------------------+
751      * | Protocol      | Pkts     | Bytes        | Pkts     | Bytes        |
752      * +---------------+----------+--------------+----------+--------------+
753      * |         Total |       11 |          704 |       11 |          704 |
754      * |           TCP |        0 |            0 |        0 |            0 |
755      * |           UDP |        0 |            0 |        0 |            0 |
756      * |          ICMP |       11 |          704 |       11 |          704 |
757      * | Errors        | Pkts                    | Pkts                    |
758      * +---------------+-------------------------+-------------------------+
759      * |         Total |                       8 |                       4 |
760      * |   Illegal Pkt |                       0 |                       0 |
761      * |   Unsup Proto |                       0 |                       0 |
762      * |    No Mapping |                       2 |                       0 |
763      * Done
764      * @endcode
765      * @par
766      * Gets the NAT64 translator packet and error counters.
767      * @par
768      * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
769      * @sa otNat64GetCounters
770      * @sa otNat64GetErrorCounters
771      *
772      */
773     else if (aArgs[0] == "counters")
774     {
775         static const char *const kNat64CounterTableHeader[] = {
776             "",
777             "4 to 6",
778             "6 to 4",
779         };
780         static const uint8_t     kNat64CounterTableHeaderColumns[] = {15, 25, 25};
781         static const char *const kNat64CounterTableSubHeader[]     = {
782                 "Protocol", "Pkts", "Bytes", "Pkts", "Bytes",
783         };
784         static const uint8_t kNat64CounterTableSubHeaderColumns[] = {
785             15, 10, 14, 10, 14,
786         };
787         static const char *const kNat64CounterTableErrorSubHeader[] = {
788             "Errors",
789             "Pkts",
790             "Pkts",
791         };
792         static const uint8_t kNat64CounterTableErrorSubHeaderColumns[] = {
793             15,
794             25,
795             25,
796         };
797         static const char *const kNat64CounterErrorType[] = {
798             "Unknown",
799             "Illegal Pkt",
800             "Unsup Proto",
801             "No Mapping",
802         };
803 
804         otNat64ProtocolCounters counters;
805         otNat64ErrorCounters    errorCounters;
806         Uint64StringBuffer      u64StringBuffer;
807 
808         OutputTableHeader(kNat64CounterTableHeader, kNat64CounterTableHeaderColumns);
809         OutputTableHeader(kNat64CounterTableSubHeader, kNat64CounterTableSubHeaderColumns);
810 
811         otNat64GetCounters(GetInstancePtr(), &counters);
812         otNat64GetErrorCounters(GetInstancePtr(), &errorCounters);
813 
814         OutputFormat("| %13s ", "Total");
815         OutputFormat("| %8s ", Uint64ToString(counters.mTotal.m4To6Packets, u64StringBuffer));
816         OutputFormat("| %12s ", Uint64ToString(counters.mTotal.m4To6Bytes, u64StringBuffer));
817         OutputFormat("| %8s ", Uint64ToString(counters.mTotal.m6To4Packets, u64StringBuffer));
818         OutputLine("| %12s |", Uint64ToString(counters.mTotal.m6To4Bytes, u64StringBuffer));
819 
820         OutputFormat("| %13s ", "TCP");
821         OutputFormat("| %8s ", Uint64ToString(counters.mTcp.m4To6Packets, u64StringBuffer));
822         OutputFormat("| %12s ", Uint64ToString(counters.mTcp.m4To6Bytes, u64StringBuffer));
823         OutputFormat("| %8s ", Uint64ToString(counters.mTcp.m6To4Packets, u64StringBuffer));
824         OutputLine("| %12s |", Uint64ToString(counters.mTcp.m6To4Bytes, u64StringBuffer));
825 
826         OutputFormat("| %13s ", "UDP");
827         OutputFormat("| %8s ", Uint64ToString(counters.mUdp.m4To6Packets, u64StringBuffer));
828         OutputFormat("| %12s ", Uint64ToString(counters.mUdp.m4To6Bytes, u64StringBuffer));
829         OutputFormat("| %8s ", Uint64ToString(counters.mUdp.m6To4Packets, u64StringBuffer));
830         OutputLine("| %12s |", Uint64ToString(counters.mUdp.m6To4Bytes, u64StringBuffer));
831 
832         OutputFormat("| %13s ", "ICMP");
833         OutputFormat("| %8s ", Uint64ToString(counters.mIcmp.m4To6Packets, u64StringBuffer));
834         OutputFormat("| %12s ", Uint64ToString(counters.mIcmp.m4To6Bytes, u64StringBuffer));
835         OutputFormat("| %8s ", Uint64ToString(counters.mIcmp.m6To4Packets, u64StringBuffer));
836         OutputLine("| %12s |", Uint64ToString(counters.mIcmp.m6To4Bytes, u64StringBuffer));
837 
838         OutputTableHeader(kNat64CounterTableErrorSubHeader, kNat64CounterTableErrorSubHeaderColumns);
839         for (uint8_t i = 0; i < OT_NAT64_DROP_REASON_COUNT; i++)
840         {
841             OutputFormat("| %13s | %23s ", kNat64CounterErrorType[i],
842                          Uint64ToString(errorCounters.mCount4To6[i], u64StringBuffer));
843             OutputLine("| %23s |", Uint64ToString(errorCounters.mCount6To4[i], u64StringBuffer));
844         }
845     }
846 #endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
847     else
848     {
849         ExitNow(error = OT_ERROR_INVALID_COMMAND);
850     }
851 
852 exit:
853     return error;
854 }
855 #endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
856 
857 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
Process(Arg aArgs[])858 template <> otError Interpreter::Process<Cmd("bbr")>(Arg aArgs[])
859 {
860     otError                error = OT_ERROR_INVALID_COMMAND;
861     otBackboneRouterConfig config;
862 
863     /**
864      * @cli bbr
865      * @code
866      * bbr
867      * BBR Primary:
868      * server16: 0xE400
869      * seqno:    10
870      * delay:    120 secs
871      * timeout:  300 secs
872      * Done
873      * @endcode
874      * @code
875      * bbr
876      * BBR Primary: None
877      * Done
878      * @endcode
879      * @par
880      * Returns the current Primary Backbone Router information for the Thread device.
881      */
882     if (aArgs[0].IsEmpty())
883     {
884         if (otBackboneRouterGetPrimary(GetInstancePtr(), &config) == OT_ERROR_NONE)
885         {
886             OutputLine("BBR Primary:");
887             OutputLine("server16: 0x%04X", config.mServer16);
888             OutputLine("seqno:    %u", config.mSequenceNumber);
889             OutputLine("delay:    %u secs", config.mReregistrationDelay);
890             OutputLine("timeout:  %lu secs", ToUlong(config.mMlrTimeout));
891         }
892         else
893         {
894             OutputLine("BBR Primary: None");
895         }
896 
897         error = OT_ERROR_NONE;
898     }
899 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
900     else
901     {
902         if (aArgs[0] == "mgmt")
903         {
904             if (aArgs[1].IsEmpty())
905             {
906                 ExitNow(error = OT_ERROR_INVALID_COMMAND);
907             }
908 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
909             /**
910              * @cli bbr mgmt dua
911              * @code
912              * bbr mgmt dua 1 2f7c235e5025a2fd
913              * Done
914              * @endcode
915              * @code
916              * bbr mgmt dua 160
917              * Done
918              * @endcode
919              * @cparam bbr mgmt dua @ca{status|coap-code} [@ca{meshLocalIid}]
920              * For `status` or `coap-code`, use:
921              * *    0: ST_DUA_SUCCESS
922              * *    1: ST_DUA_REREGISTER
923              * *    2: ST_DUA_INVALID
924              * *    3: ST_DUA_DUPLICATE
925              * *    4: ST_DUA_NO_RESOURCES
926              * *    5: ST_DUA_BBR_NOT_PRIMARY
927              * *    6: ST_DUA_GENERAL_FAILURE
928              * *    160: COAP code 5.00
929              * @par
930              * With the `meshLocalIid` included, this command configures the response status
931              * for the next DUA registration. Without `meshLocalIid`, respond to the next
932              * DUA.req with the specified `status` or `coap-code`.
933              * @par
934              * Available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
935              * @sa otBackboneRouterConfigNextDuaRegistrationResponse
936              */
937             else if (aArgs[1] == "dua")
938             {
939                 uint8_t                   status;
940                 otIp6InterfaceIdentifier *mlIid = nullptr;
941                 otIp6InterfaceIdentifier  iid;
942 
943                 SuccessOrExit(error = aArgs[2].ParseAsUint8(status));
944 
945                 if (!aArgs[3].IsEmpty())
946                 {
947                     SuccessOrExit(error = aArgs[3].ParseAsHexString(iid.mFields.m8));
948                     mlIid = &iid;
949                     VerifyOrExit(aArgs[4].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
950                 }
951 
952                 otBackboneRouterConfigNextDuaRegistrationResponse(GetInstancePtr(), mlIid, status);
953                 ExitNow();
954             }
955 #endif
956 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
957             else if (aArgs[1] == "mlr")
958             {
959                 error = ProcessBackboneRouterMgmtMlr(aArgs + 2);
960                 ExitNow();
961             }
962 #endif
963         }
964         SuccessOrExit(error = ProcessBackboneRouterLocal(aArgs));
965     }
966 
967 exit:
968 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
969     return error;
970 }
971 
972 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
973 
974 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
ProcessBackboneRouterMgmtMlr(Arg aArgs[])975 otError Interpreter::ProcessBackboneRouterMgmtMlr(Arg aArgs[])
976 {
977     otError error = OT_ERROR_INVALID_COMMAND;
978 
979     /**
980      * @cli bbr mgmt mlr listener
981      * @code
982      * bbr mgmt mlr listener
983      * ff04:0:0:0:0:0:0:abcd 3534000
984      * ff04:0:0:0:0:0:0:eeee 3537610
985      * Done
986      * @endcode
987      * @par
988      * Returns the Multicast Listeners with the #otBackboneRouterMulticastListenerInfo
989      * `mTimeout` in seconds.
990      * @par
991      * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` and
992      * `OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE` are enabled.
993      * @sa otBackboneRouterMulticastListenerGetNext
994      */
995     if (aArgs[0] == "listener")
996     {
997         if (aArgs[1].IsEmpty())
998         {
999             PrintMulticastListenersTable();
1000             ExitNow(error = OT_ERROR_NONE);
1001         }
1002 
1003 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1004         /**
1005          * @cli bbr mgmt mlr listener clear
1006          * @code
1007          * bbr mgmt mlr listener clear
1008          * Done
1009          * @endcode
1010          * @par api_copy
1011          * #otBackboneRouterMulticastListenerClear
1012          */
1013         if (aArgs[1] == "clear")
1014         {
1015             otBackboneRouterMulticastListenerClear(GetInstancePtr());
1016             error = OT_ERROR_NONE;
1017         }
1018         /**
1019          * @cli bbr mgmt mlr listener add
1020          * @code
1021          * bbr mgmt mlr listener add ff04::1
1022          * Done
1023          * @endcode
1024          * @code
1025          * bbr mgmt mlr listener add ff04::2 300
1026          * Done
1027          * @endcode
1028          * @cparam bbr mgmt mlr listener add @ca{ipaddress} [@ca{timeout-seconds}]
1029          * @par api_copy
1030          * #otBackboneRouterMulticastListenerAdd
1031          */
1032         else if (aArgs[1] == "add")
1033         {
1034             otIp6Address address;
1035             uint32_t     timeout = 0;
1036 
1037             SuccessOrExit(error = aArgs[2].ParseAsIp6Address(address));
1038 
1039             if (!aArgs[3].IsEmpty())
1040             {
1041                 SuccessOrExit(error = aArgs[3].ParseAsUint32(timeout));
1042                 VerifyOrExit(aArgs[4].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
1043             }
1044 
1045             error = otBackboneRouterMulticastListenerAdd(GetInstancePtr(), &address, timeout);
1046         }
1047     }
1048     /**
1049      * @cli bbr mgmt mlr response
1050      * @code
1051      * bbr mgmt mlr response 2
1052      * Done
1053      * @endcode
1054      * @cparam bbr mgmt mlr response @ca{status-code}
1055      * For `status-code`, use:
1056      * *    0: ST_MLR_SUCCESS
1057      * *    2: ST_MLR_INVALID
1058      * *    3: ST_MLR_NO_PERSISTENT
1059      * *    4: ST_MLR_NO_RESOURCES
1060      * *    5: ST_MLR_BBR_NOT_PRIMARY
1061      * *    6: ST_MLR_GENERAL_FAILURE
1062      * @par api_copy
1063      * #otBackboneRouterConfigNextMulticastListenerRegistrationResponse
1064      */
1065     else if (aArgs[0] == "response")
1066     {
1067         error = ProcessSet(aArgs + 1, otBackboneRouterConfigNextMulticastListenerRegistrationResponse);
1068 #endif
1069     }
1070 
1071 exit:
1072     return error;
1073 }
1074 
PrintMulticastListenersTable(void)1075 void Interpreter::PrintMulticastListenersTable(void)
1076 {
1077     otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
1078     otBackboneRouterMulticastListenerInfo     listenerInfo;
1079 
1080     while (otBackboneRouterMulticastListenerGetNext(GetInstancePtr(), &iter, &listenerInfo) == OT_ERROR_NONE)
1081     {
1082         OutputIp6Address(listenerInfo.mAddress);
1083         OutputLine(" %lu", ToUlong(listenerInfo.mTimeout));
1084     }
1085 }
1086 
1087 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
1088 
ProcessBackboneRouterLocal(Arg aArgs[])1089 otError Interpreter::ProcessBackboneRouterLocal(Arg aArgs[])
1090 {
1091     otError                error = OT_ERROR_NONE;
1092     otBackboneRouterConfig config;
1093     bool                   enable;
1094 
1095     /**
1096      * @cli bbr (enable,disable)
1097      * @code
1098      * bbr enable
1099      * Done
1100      * @endcode
1101      * @code
1102      * bbr disable
1103      * Done
1104      * @endcode
1105      * @par api_copy
1106      * #otBackboneRouterSetEnabled
1107      */
1108     if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
1109     {
1110         otBackboneRouterSetEnabled(GetInstancePtr(), enable);
1111     }
1112     /**
1113      * @cli bbr jitter (get,set)
1114      * @code
1115      * bbr jitter
1116      * 20
1117      * Done
1118      * @endcode
1119      * @code
1120      * bbr jitter 10
1121      * Done
1122      * @endcode
1123      * @cparam bbr jitter [@ca{jitter}]
1124      * @par
1125      * Gets or sets jitter (in seconds) for Backbone Router registration.
1126      * @par
1127      * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled.
1128      * @sa otBackboneRouterGetRegistrationJitter
1129      * @sa otBackboneRouterSetRegistrationJitter
1130      */
1131     else if (aArgs[0] == "jitter")
1132     {
1133         error = ProcessGetSet(aArgs + 1, otBackboneRouterGetRegistrationJitter, otBackboneRouterSetRegistrationJitter);
1134     }
1135     /**
1136      * @cli bbr register
1137      * @code
1138      * bbr register
1139      * Done
1140      * @endcode
1141      * @par api_copy
1142      * #otBackboneRouterRegister
1143      */
1144     else if (aArgs[0] == "register")
1145     {
1146         SuccessOrExit(error = otBackboneRouterRegister(GetInstancePtr()));
1147     }
1148     /**
1149      * @cli bbr state
1150      * @code
1151      * bbr state
1152      * Disabled
1153      * Done
1154      * @endcode
1155      * @code
1156      * bbr state
1157      * Primary
1158      * Done
1159      * @endcode
1160      * @code
1161      * bbr state
1162      * Secondary
1163      * Done
1164      * @endcode
1165      * @par
1166      * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled.
1167      * @par api_copy
1168      * #otBackboneRouterGetState
1169      */
1170     else if (aArgs[0] == "state")
1171     {
1172         static const char *const kStateStrings[] = {
1173             "Disabled",  // (0) OT_BACKBONE_ROUTER_STATE_DISABLED
1174             "Secondary", // (1) OT_BACKBONE_ROUTER_STATE_SECONDARY
1175             "Primary",   // (2) OT_BACKBONE_ROUTER_STATE_PRIMARY
1176         };
1177 
1178         static_assert(0 == OT_BACKBONE_ROUTER_STATE_DISABLED, "OT_BACKBONE_ROUTER_STATE_DISABLED value is incorrect");
1179         static_assert(1 == OT_BACKBONE_ROUTER_STATE_SECONDARY, "OT_BACKBONE_ROUTER_STATE_SECONDARY value is incorrect");
1180         static_assert(2 == OT_BACKBONE_ROUTER_STATE_PRIMARY, "OT_BACKBONE_ROUTER_STATE_PRIMARY value is incorrect");
1181 
1182         OutputLine("%s", Stringify(otBackboneRouterGetState(GetInstancePtr()), kStateStrings));
1183     }
1184     /**
1185      * @cli bbr config
1186      * @code
1187      * bbr config
1188      * seqno:    10
1189      * delay:    120 secs
1190      * timeout:  300 secs
1191      * Done
1192      * @endcode
1193      * @par api_copy
1194      * #otBackboneRouterGetConfig
1195      */
1196     else if (aArgs[0] == "config")
1197     {
1198         otBackboneRouterGetConfig(GetInstancePtr(), &config);
1199 
1200         if (aArgs[1].IsEmpty())
1201         {
1202             OutputLine("seqno:    %u", config.mSequenceNumber);
1203             OutputLine("delay:    %u secs", config.mReregistrationDelay);
1204             OutputLine("timeout:  %lu secs", ToUlong(config.mMlrTimeout));
1205         }
1206         else
1207         {
1208             // Set local Backbone Router configuration.
1209             /**
1210              * @cli bbr config (set)
1211              * @code
1212              * bbr config seqno 20 delay 30
1213              * Done
1214              * @endcode
1215              * @cparam bbr config [seqno @ca{seqno}] [delay @ca{delay}] [timeout @ca{timeout}]
1216              * @par
1217              * `bbr register` should be issued explicitly to register Backbone Router service to Leader
1218              * for Secondary Backbone Router.
1219              * @par api_copy
1220              * #otBackboneRouterSetConfig
1221              */
1222             for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
1223             {
1224                 if (*arg == "seqno")
1225                 {
1226                     arg++;
1227                     SuccessOrExit(error = arg->ParseAsUint8(config.mSequenceNumber));
1228                 }
1229                 else if (*arg == "delay")
1230                 {
1231                     arg++;
1232                     SuccessOrExit(error = arg->ParseAsUint16(config.mReregistrationDelay));
1233                 }
1234                 else if (*arg == "timeout")
1235                 {
1236                     arg++;
1237                     SuccessOrExit(error = arg->ParseAsUint32(config.mMlrTimeout));
1238                 }
1239                 else
1240                 {
1241                     ExitNow(error = OT_ERROR_INVALID_ARGS);
1242                 }
1243             }
1244 
1245             SuccessOrExit(error = otBackboneRouterSetConfig(GetInstancePtr(), &config));
1246         }
1247     }
1248     else
1249     {
1250         error = OT_ERROR_INVALID_COMMAND;
1251     }
1252 
1253 exit:
1254     return error;
1255 }
1256 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
1257 
1258 /**
1259  * @cli domainname
1260  * @code
1261  * domainname
1262  * Thread
1263  * Done
1264  * @endcode
1265  * @par api_copy
1266  * #otThreadGetDomainName
1267  */
Process(Arg aArgs[])1268 template <> otError Interpreter::Process<Cmd("domainname")>(Arg aArgs[])
1269 {
1270     /**
1271      * @cli domainname (set)
1272      * @code
1273      * domainname Test\ Thread
1274      * Done
1275      * @endcode
1276      * @cparam domainname @ca{name}
1277      * Use a `backslash` to escape spaces.
1278      * @par api_copy
1279      * #otThreadSetDomainName
1280      */
1281     return ProcessGetSet(aArgs, otThreadGetDomainName, otThreadSetDomainName);
1282 }
1283 
1284 #if OPENTHREAD_CONFIG_DUA_ENABLE
Process(Arg aArgs[])1285 template <> otError Interpreter::Process<Cmd("dua")>(Arg aArgs[])
1286 {
1287     otError error = OT_ERROR_NONE;
1288 
1289     /**
1290      * @cli dua iid
1291      * @code
1292      * dua iid
1293      * 0004000300020001
1294      * Done
1295      * @endcode
1296      * @par api_copy
1297      * #otThreadGetFixedDuaInterfaceIdentifier
1298      */
1299     if (aArgs[0] == "iid")
1300     {
1301         if (aArgs[1].IsEmpty())
1302         {
1303             const otIp6InterfaceIdentifier *iid = otThreadGetFixedDuaInterfaceIdentifier(GetInstancePtr());
1304 
1305             if (iid != nullptr)
1306             {
1307                 OutputBytesLine(iid->mFields.m8);
1308             }
1309         }
1310         /**
1311          * @cli dua iid (set,clear)
1312          * @code
1313          * dua iid 0004000300020001
1314          * Done
1315          * @endcode
1316          * @code
1317          * dua iid clear
1318          * Done
1319          * @endcode
1320          * @cparam dua iid @ca{iid|clear}
1321          * `dua iid clear` passes a `nullptr` to #otThreadSetFixedDuaInterfaceIdentifier.
1322          * Otherwise, you can pass the `iid`.
1323          * @par api_copy
1324          * #otThreadSetFixedDuaInterfaceIdentifier
1325          */
1326         else if (aArgs[1] == "clear")
1327         {
1328             error = otThreadSetFixedDuaInterfaceIdentifier(GetInstancePtr(), nullptr);
1329         }
1330         else
1331         {
1332             otIp6InterfaceIdentifier iid;
1333 
1334             SuccessOrExit(error = aArgs[1].ParseAsHexString(iid.mFields.m8));
1335             error = otThreadSetFixedDuaInterfaceIdentifier(GetInstancePtr(), &iid);
1336         }
1337     }
1338     else
1339     {
1340         error = OT_ERROR_INVALID_COMMAND;
1341     }
1342 
1343 exit:
1344     return error;
1345 }
1346 #endif // OPENTHREAD_CONFIG_DUA_ENABLE
1347 
1348 #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
1349 
1350 /**
1351  * @cli bufferinfo
1352  * @code
1353  * bufferinfo
1354  * total: 40
1355  * free: 40
1356  * max-used: 5
1357  * 6lo send: 0 0 0
1358  * 6lo reas: 0 0 0
1359  * ip6: 0 0 0
1360  * mpl: 0 0 0
1361  * mle: 0 0 0
1362  * coap: 0 0 0
1363  * coap secure: 0 0 0
1364  * application coap: 0 0 0
1365  * Done
1366  * @endcode
1367  * @par
1368  * Gets the current message buffer information.
1369  * *   `total` displays the total number of message buffers in pool.
1370  * *   `free` displays the number of free message buffers.
1371  * *   `max-used` displays max number of used buffers at the same time since OT stack
1372  *     initialization or last `bufferinfo reset`.
1373  * @par
1374  * Next, the CLI displays info about different queues used by the OpenThread stack,
1375  * for example `6lo send`. Each line after the queue represents info about a queue:
1376  * *   The first number shows number messages in the queue.
1377  * *   The second number shows number of buffers used by all messages in the queue.
1378  * *   The third number shows total number of bytes of all messages in the queue.
1379  * @sa otMessageGetBufferInfo
1380  */
Process(Arg aArgs[])1381 template <> otError Interpreter::Process<Cmd("bufferinfo")>(Arg aArgs[])
1382 {
1383     struct BufferInfoName
1384     {
1385         const otMessageQueueInfo otBufferInfo::*mQueuePtr;
1386         const char                             *mName;
1387     };
1388 
1389     static const BufferInfoName kBufferInfoNames[] = {
1390         {&otBufferInfo::m6loSendQueue, "6lo send"},
1391         {&otBufferInfo::m6loReassemblyQueue, "6lo reas"},
1392         {&otBufferInfo::mIp6Queue, "ip6"},
1393         {&otBufferInfo::mMplQueue, "mpl"},
1394         {&otBufferInfo::mMleQueue, "mle"},
1395         {&otBufferInfo::mCoapQueue, "coap"},
1396         {&otBufferInfo::mCoapSecureQueue, "coap secure"},
1397         {&otBufferInfo::mApplicationCoapQueue, "application coap"},
1398     };
1399 
1400     otError error = OT_ERROR_NONE;
1401 
1402     if (aArgs[0].IsEmpty())
1403     {
1404         otBufferInfo bufferInfo;
1405 
1406         otMessageGetBufferInfo(GetInstancePtr(), &bufferInfo);
1407 
1408         OutputLine("total: %u", bufferInfo.mTotalBuffers);
1409         OutputLine("free: %u", bufferInfo.mFreeBuffers);
1410         OutputLine("max-used: %u", bufferInfo.mMaxUsedBuffers);
1411 
1412         for (const BufferInfoName &info : kBufferInfoNames)
1413         {
1414             OutputLine("%s: %u %u %lu", info.mName, (bufferInfo.*info.mQueuePtr).mNumMessages,
1415                        (bufferInfo.*info.mQueuePtr).mNumBuffers, ToUlong((bufferInfo.*info.mQueuePtr).mTotalBytes));
1416         }
1417     }
1418     /**
1419      * @cli bufferinfo reset
1420      * @code
1421      * bufferinfo reset
1422      * Done
1423      * @endcode
1424      * @par api_copy
1425      * #otMessageResetBufferInfo
1426      */
1427     else if (aArgs[0] == "reset")
1428     {
1429         otMessageResetBufferInfo(GetInstancePtr());
1430     }
1431     else
1432     {
1433         error = OT_ERROR_INVALID_ARGS;
1434     }
1435 
1436     return error;
1437 }
1438 
1439 /**
1440  * @cli ccathreshold (get,set)
1441  * @code
1442  * ccathreshold
1443  * -75 dBm
1444  * Done
1445  * @endcode
1446  * @code
1447  * ccathreshold -62
1448  * Done
1449  * @endcode
1450  * @cparam ccathreshold [@ca{CCA-threshold-dBm}]
1451  * Use the optional `CCA-threshold-dBm` argument to set the CCA threshold.
1452  * @par
1453  * Gets or sets the CCA threshold in dBm measured at the antenna connector per
1454  * IEEE 802.15.4 - 2015 section 10.1.4.
1455  * @sa otPlatRadioGetCcaEnergyDetectThreshold
1456  * @sa otPlatRadioSetCcaEnergyDetectThreshold
1457  */
Process(Arg aArgs[])1458 template <> otError Interpreter::Process<Cmd("ccathreshold")>(Arg aArgs[])
1459 {
1460     otError error = OT_ERROR_NONE;
1461     int8_t  cca;
1462 
1463     if (aArgs[0].IsEmpty())
1464     {
1465         SuccessOrExit(error = otPlatRadioGetCcaEnergyDetectThreshold(GetInstancePtr(), &cca));
1466         OutputLine("%d dBm", cca);
1467     }
1468     else
1469     {
1470         SuccessOrExit(error = aArgs[0].ParseAsInt8(cca));
1471         error = otPlatRadioSetCcaEnergyDetectThreshold(GetInstancePtr(), cca);
1472     }
1473 
1474 exit:
1475     return error;
1476 }
1477 
1478 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Process(Arg aArgs[])1479 template <> otError Interpreter::Process<Cmd("ccm")>(Arg aArgs[])
1480 {
1481     otError error = OT_ERROR_NONE;
1482     bool    enable;
1483 
1484     VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_COMMAND);
1485 
1486     SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
1487     otThreadSetCcmEnabled(GetInstancePtr(), enable);
1488 
1489 exit:
1490     return error;
1491 }
1492 
Process(Arg aArgs[])1493 template <> otError Interpreter::Process<Cmd("tvcheck")>(Arg aArgs[])
1494 {
1495     otError error = OT_ERROR_NONE;
1496     bool    enable;
1497 
1498     VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_COMMAND);
1499 
1500     SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
1501     otThreadSetThreadVersionCheckEnabled(GetInstancePtr(), enable);
1502 
1503 exit:
1504     return error;
1505 }
1506 
1507 #endif
1508 
1509 /**
1510  * @cli channel (get,set)
1511  * @code
1512  * channel
1513  * 11
1514  * Done
1515  * @endcode
1516  * @code
1517  * channel 11
1518  * Done
1519  * @endcode
1520  * @cparam channel [@ca{channel-num}]
1521  * Use `channel-num` to set the channel.
1522  * @par
1523  * Gets or sets the IEEE 802.15.4 Channel value.
1524  */
Process(Arg aArgs[])1525 template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
1526 {
1527     otError error = OT_ERROR_NONE;
1528 
1529     /**
1530      * @cli channel supported
1531      * @code
1532      * channel supported
1533      * 0x7fff800
1534      * Done
1535      * @endcode
1536      * @par api_copy
1537      * #otPlatRadioGetSupportedChannelMask
1538      */
1539     if (aArgs[0] == "supported")
1540     {
1541         OutputLine("0x%lx", ToUlong(otPlatRadioGetSupportedChannelMask(GetInstancePtr())));
1542     }
1543     /**
1544      * @cli channel preferred
1545      * @code
1546      * channel preferred
1547      * 0x7fff800
1548      * Done
1549      * @endcode
1550      * @par api_copy
1551      * #otPlatRadioGetPreferredChannelMask
1552      */
1553     else if (aArgs[0] == "preferred")
1554     {
1555         OutputLine("0x%lx", ToUlong(otPlatRadioGetPreferredChannelMask(GetInstancePtr())));
1556     }
1557 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
1558     /**
1559      * @cli channel monitor
1560      * @code
1561      * channel monitor
1562      * enabled: 1
1563      * interval: 41000
1564      * threshold: -75
1565      * window: 960
1566      * count: 10552
1567      * occupancies:
1568      * ch 11 (0x0cb7)  4.96% busy
1569      * ch 12 (0x2e2b) 18.03% busy
1570      * ch 13 (0x2f54) 18.48% busy
1571      * ch 14 (0x0fef)  6.22% busy
1572      * ch 15 (0x1536)  8.28% busy
1573      * ch 16 (0x1746)  9.09% busy
1574      * ch 17 (0x0b8b)  4.50% busy
1575      * ch 18 (0x60a7) 37.75% busy
1576      * ch 19 (0x0810)  3.14% busy
1577      * ch 20 (0x0c2a)  4.75% busy
1578      * ch 21 (0x08dc)  3.46% busy
1579      * ch 22 (0x101d)  6.29% busy
1580      * ch 23 (0x0092)  0.22% busy
1581      * ch 24 (0x0028)  0.06% busy
1582      * ch 25 (0x0063)  0.15% busy
1583      * ch 26 (0x058c)  2.16% busy
1584      * Done
1585      * @endcode
1586      * @par
1587      * Get the current channel monitor state and channel occupancy.
1588      * `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` is required.
1589      */
1590     else if (aArgs[0] == "monitor")
1591     {
1592         if (aArgs[1].IsEmpty())
1593         {
1594             OutputLine("enabled: %d", otChannelMonitorIsEnabled(GetInstancePtr()));
1595             if (otChannelMonitorIsEnabled(GetInstancePtr()))
1596             {
1597                 uint32_t channelMask = otLinkGetSupportedChannelMask(GetInstancePtr());
1598                 uint8_t  channelNum  = sizeof(channelMask) * CHAR_BIT;
1599 
1600                 OutputLine("interval: %lu", ToUlong(otChannelMonitorGetSampleInterval(GetInstancePtr())));
1601                 OutputLine("threshold: %d", otChannelMonitorGetRssiThreshold(GetInstancePtr()));
1602                 OutputLine("window: %lu", ToUlong(otChannelMonitorGetSampleWindow(GetInstancePtr())));
1603                 OutputLine("count: %lu", ToUlong(otChannelMonitorGetSampleCount(GetInstancePtr())));
1604 
1605                 OutputLine("occupancies:");
1606 
1607                 for (uint8_t channel = 0; channel < channelNum; channel++)
1608                 {
1609                     uint16_t               occupancy;
1610                     PercentageStringBuffer stringBuffer;
1611 
1612                     if (!((1UL << channel) & channelMask))
1613                     {
1614                         continue;
1615                     }
1616 
1617                     occupancy = otChannelMonitorGetChannelOccupancy(GetInstancePtr(), channel);
1618 
1619                     OutputLine("ch %u (0x%04x) %6s%% busy", channel, occupancy,
1620                                PercentageToString(occupancy, stringBuffer));
1621                 }
1622 
1623                 OutputNewLine();
1624             }
1625         }
1626         /**
1627          * @cli channel monitor start
1628          * @code
1629          * channel monitor start
1630          * channel monitor start
1631          * Done
1632          * @endcode
1633          * @par
1634          * Start the channel monitor.
1635          * OT CLI sends a boolean value of `true` to #otChannelMonitorSetEnabled.
1636          * `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` is required.
1637          * @sa otChannelMonitorSetEnabled
1638          */
1639         else if (aArgs[1] == "start")
1640         {
1641             error = otChannelMonitorSetEnabled(GetInstancePtr(), true);
1642         }
1643         /**
1644          * @cli channel monitor stop
1645          * @code
1646          * channel monitor stop
1647          * channel monitor stop
1648          * Done
1649          * @endcode
1650          * @par
1651          * Stop the channel monitor.
1652          * OT CLI sends a boolean value of `false` to #otChannelMonitorSetEnabled.
1653          * `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` is required.
1654          * @sa otChannelMonitorSetEnabled
1655          */
1656         else if (aArgs[1] == "stop")
1657         {
1658             error = otChannelMonitorSetEnabled(GetInstancePtr(), false);
1659         }
1660         else
1661         {
1662             ExitNow(error = OT_ERROR_INVALID_ARGS);
1663         }
1664     }
1665 #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
1666 #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
1667     else if (aArgs[0] == "manager")
1668     {
1669         /**
1670          * @cli channel manager
1671          * @code
1672          * channel manager
1673          * channel: 11
1674          * auto: 1
1675          * delay: 120
1676          * interval: 10800
1677          * supported: { 11-26}
1678          * favored: { 11-26}
1679          * Done
1680          * @endcode
1681          * @par
1682          * Get the channel manager state.
1683          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` is required.
1684          * @sa otChannelManagerGetRequestedChannel
1685          */
1686         if (aArgs[1].IsEmpty())
1687         {
1688             OutputLine("channel: %u", otChannelManagerGetRequestedChannel(GetInstancePtr()));
1689             OutputLine("auto: %d", otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()));
1690 
1691             if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()))
1692             {
1693                 Mac::ChannelMask supportedMask(otChannelManagerGetSupportedChannels(GetInstancePtr()));
1694                 Mac::ChannelMask favoredMask(otChannelManagerGetFavoredChannels(GetInstancePtr()));
1695 
1696                 OutputLine("delay: %u", otChannelManagerGetDelay(GetInstancePtr()));
1697                 OutputLine("interval: %lu", ToUlong(otChannelManagerGetAutoChannelSelectionInterval(GetInstancePtr())));
1698                 OutputLine("cca threshold: 0x%04x", otChannelManagerGetCcaFailureRateThreshold(GetInstancePtr()));
1699                 OutputLine("supported: %s", supportedMask.ToString().AsCString());
1700                 OutputLine("favored: %s", favoredMask.ToString().AsCString());
1701             }
1702         }
1703         /**
1704          * @cli channel manager change
1705          * @code
1706          * channel manager change 11
1707          * channel manager change 11
1708          * Done
1709          * @endcode
1710          * @cparam channel manager change @ca{channel-num}
1711          * @par
1712          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` is required.
1713          * @par api_copy
1714          * #otChannelManagerRequestChannelChange
1715          */
1716         else if (aArgs[1] == "change")
1717         {
1718             error = ProcessSet(aArgs + 2, otChannelManagerRequestChannelChange);
1719         }
1720 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
1721         /**
1722          * @cli channel manager select
1723          * @code
1724          * channel manager select 1
1725          * channel manager select 1
1726          * Done
1727          * @endcode
1728          * @cparam channel manager select @ca{skip-quality-check}
1729          * Use a `1` or `0` for the boolean `skip-quality-check`.
1730          * @par
1731          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
1732          * @par api_copy
1733          * #otChannelManagerRequestChannelSelect
1734          */
1735         else if (aArgs[1] == "select")
1736         {
1737             bool enable;
1738 
1739             SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
1740             error = otChannelManagerRequestChannelSelect(GetInstancePtr(), enable);
1741         }
1742 #endif
1743         /**
1744          * @cli channel manager auto
1745          * @code
1746          * channel manager auto 1
1747          * channel manager auto 1
1748          * Done
1749          * @endcode
1750          * @cparam channel manager auto @ca{enable}
1751          * `1` is a boolean to `enable`.
1752          * @par
1753          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
1754          * @par api_copy
1755          * #otChannelManagerSetAutoChannelSelectionEnabled
1756          */
1757         else if (aArgs[1] == "auto")
1758         {
1759             bool enable;
1760 
1761             SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
1762             otChannelManagerSetAutoChannelSelectionEnabled(GetInstancePtr(), enable);
1763         }
1764         /**
1765          * @cli channel manager delay
1766          * @code
1767          * channel manager delay 120
1768          * channel manager delay 120
1769          * Done
1770          * @endcode
1771          * @cparam channel manager delay @ca{delay-seconds}
1772          * @par
1773          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
1774          * @par api_copy
1775          * #otChannelManagerSetDelay
1776          */
1777         else if (aArgs[1] == "delay")
1778         {
1779             error = ProcessGetSet(aArgs + 2, otChannelManagerGetDelay, otChannelManagerSetDelay);
1780         }
1781         /**
1782          * @cli channel manager interval
1783          * @code
1784          * channel manager interval 10800
1785          * channel manager interval 10800
1786          * Done
1787          * @endcode
1788          * @cparam channel manager interval @ca{interval-seconds}
1789          * @par
1790          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
1791          * @par api_copy
1792          * #otChannelManagerSetAutoChannelSelectionInterval
1793          */
1794         else if (aArgs[1] == "interval")
1795         {
1796             error = ProcessSet(aArgs + 2, otChannelManagerSetAutoChannelSelectionInterval);
1797         }
1798         /**
1799          * @cli channel manager supported
1800          * @code
1801          * channel manager supported 0x7fffc00
1802          * channel manager supported 0x7fffc00
1803          * Done
1804          * @endcode
1805          * @cparam channel manager supported @ca{mask}
1806          * @par
1807          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
1808          * @par api_copy
1809          * #otChannelManagerSetSupportedChannels
1810          */
1811         else if (aArgs[1] == "supported")
1812         {
1813             error = ProcessSet(aArgs + 2, otChannelManagerSetSupportedChannels);
1814         }
1815         /**
1816          * @cli channel manager favored
1817          * @code
1818          * channel manager favored 0x7fffc00
1819          * channel manager favored 0x7fffc00
1820          * Done
1821          * @endcode
1822          * @cparam channel manager favored @ca{mask}
1823          * @par
1824          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
1825          * @par api_copy
1826          * #otChannelManagerSetFavoredChannels
1827          */
1828         else if (aArgs[1] == "favored")
1829         {
1830             error = ProcessSet(aArgs + 2, otChannelManagerSetFavoredChannels);
1831         }
1832         /**
1833          * @cli channel manager threshold
1834          * @code
1835          * channel manager threshold 0xffff
1836          * channel manager threshold 0xffff
1837          * Done
1838          * @endcode
1839          * @cparam channel manager threshold @ca{threshold-percent}
1840          * Use a hex value for `threshold-percent`. `0` maps to 0% and `0xffff` maps to 100%.
1841          * @par
1842          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
1843          * @par api_copy
1844          * #otChannelManagerSetCcaFailureRateThreshold
1845          */
1846         else if (aArgs[1] == "threshold")
1847         {
1848             error = ProcessSet(aArgs + 2, otChannelManagerSetCcaFailureRateThreshold);
1849         }
1850         else
1851         {
1852             ExitNow(error = OT_ERROR_INVALID_ARGS);
1853         }
1854     }
1855 #endif // OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
1856     else
1857     {
1858         ExitNow(error = ProcessGetSet(aArgs, otLinkGetChannel, otLinkSetChannel));
1859     }
1860 
1861 exit:
1862     return error;
1863 }
1864 
1865 #if OPENTHREAD_FTD
Process(Arg aArgs[])1866 template <> otError Interpreter::Process<Cmd("child")>(Arg aArgs[])
1867 {
1868     otError          error = OT_ERROR_NONE;
1869     otChildInfo      childInfo;
1870     uint16_t         childId;
1871     bool             isTable;
1872     otLinkModeConfig linkMode;
1873     char             linkModeString[kLinkModeStringSize];
1874 
1875     isTable = (aArgs[0] == "table");
1876 
1877     if (isTable || (aArgs[0] == "list"))
1878     {
1879         uint16_t maxChildren;
1880 
1881         /**
1882          * @cli child table
1883          * @code
1884          * child table
1885          * | ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC     |
1886          * +-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+
1887          * |   1 | 0xc801 |        240 |         24 |     3 |  131 |1|0|0|  3| 0 |     0 | 4ecede68435358ac |
1888          * |   2 | 0xc802 |        240 |          2 |     3 |  131 |0|0|0|  3| 1 |     0 | a672a601d2ce37d8 |
1889          * Done
1890          * @endcode
1891          * @par
1892          * Prints a table of the attached children.
1893          * @sa otThreadGetChildInfoByIndex
1894          */
1895         if (isTable)
1896         {
1897             static const char *const kChildTableTitles[] = {
1898                 "ID", "RLOC16", "Timeout", "Age", "LQ In",   "C_VN",    "R",
1899                 "D",  "N",      "Ver",     "CSL", "QMsgCnt", "Suprvsn", "Extended MAC",
1900             };
1901 
1902             static const uint8_t kChildTableColumnWidths[] = {
1903                 5, 8, 12, 12, 7, 6, 1, 1, 1, 3, 3, 7, 7, 18,
1904             };
1905 
1906             OutputTableHeader(kChildTableTitles, kChildTableColumnWidths);
1907         }
1908 
1909         maxChildren = otThreadGetMaxAllowedChildren(GetInstancePtr());
1910 
1911         for (uint16_t i = 0; i < maxChildren; i++)
1912         {
1913             if ((otThreadGetChildInfoByIndex(GetInstancePtr(), i, &childInfo) != OT_ERROR_NONE) ||
1914                 childInfo.mIsStateRestoring)
1915             {
1916                 continue;
1917             }
1918 
1919             if (isTable)
1920             {
1921                 OutputFormat("| %3u ", childInfo.mChildId);
1922                 OutputFormat("| 0x%04x ", childInfo.mRloc16);
1923                 OutputFormat("| %10lu ", ToUlong(childInfo.mTimeout));
1924                 OutputFormat("| %10lu ", ToUlong(childInfo.mAge));
1925                 OutputFormat("| %5u ", childInfo.mLinkQualityIn);
1926                 OutputFormat("| %4u ", childInfo.mNetworkDataVersion);
1927                 OutputFormat("|%1d", childInfo.mRxOnWhenIdle);
1928                 OutputFormat("|%1d", childInfo.mFullThreadDevice);
1929                 OutputFormat("|%1d", childInfo.mFullNetworkData);
1930                 OutputFormat("|%3u", childInfo.mVersion);
1931                 OutputFormat("| %1d ", childInfo.mIsCslSynced);
1932                 OutputFormat("| %5u ", childInfo.mQueuedMessageCnt);
1933                 OutputFormat("| %5u ", childInfo.mSupervisionInterval);
1934                 OutputFormat("| ");
1935                 OutputExtAddress(childInfo.mExtAddress);
1936                 OutputLine(" |");
1937             }
1938             /**
1939              * @cli child list
1940              * @code
1941              * child list
1942              * 1 2 3 6 7 8
1943              * Done
1944              * @endcode
1945              * @par
1946              * Returns a list of attached Child IDs.
1947              * @sa otThreadGetChildInfoByIndex
1948              */
1949             else
1950             {
1951                 OutputFormat("%u ", childInfo.mChildId);
1952             }
1953         }
1954 
1955         OutputNewLine();
1956         ExitNow();
1957     }
1958 
1959     SuccessOrExit(error = aArgs[0].ParseAsUint16(childId));
1960     SuccessOrExit(error = otThreadGetChildInfoById(GetInstancePtr(), childId, &childInfo));
1961 
1962     /**
1963      * @cli child (id)
1964      * @code
1965      * child 1
1966      * Child ID: 1
1967      * Rloc: 9c01
1968      * Ext Addr: e2b3540590b0fd87
1969      * Mode: rn
1970      * CSL Synchronized: 1
1971      * Net Data: 184
1972      * Timeout: 100
1973      * Age: 0
1974      * Link Quality In: 3
1975      * RSSI: -20
1976      * Done
1977      * @endcode
1978      * @cparam child @ca{child-id}
1979      * @par api_copy
1980      * #otThreadGetChildInfoById
1981      */
1982     OutputLine("Child ID: %u", childInfo.mChildId);
1983     OutputLine("Rloc: %04x", childInfo.mRloc16);
1984     OutputFormat("Ext Addr: ");
1985     OutputExtAddressLine(childInfo.mExtAddress);
1986     linkMode.mRxOnWhenIdle = childInfo.mRxOnWhenIdle;
1987     linkMode.mDeviceType   = childInfo.mFullThreadDevice;
1988     linkMode.mNetworkData  = childInfo.mFullThreadDevice;
1989     OutputLine("Mode: %s", LinkModeToString(linkMode, linkModeString));
1990     OutputLine("CSL Synchronized: %d ", childInfo.mIsCslSynced);
1991     OutputLine("Net Data: %u", childInfo.mNetworkDataVersion);
1992     OutputLine("Timeout: %lu", ToUlong(childInfo.mTimeout));
1993     OutputLine("Age: %lu", ToUlong(childInfo.mAge));
1994     OutputLine("Link Quality In: %u", childInfo.mLinkQualityIn);
1995     OutputLine("RSSI: %d", childInfo.mAverageRssi);
1996     OutputLine("Supervision Interval: %d", childInfo.mSupervisionInterval);
1997 
1998 exit:
1999     return error;
2000 }
2001 
Process(Arg aArgs[])2002 template <> otError Interpreter::Process<Cmd("childip")>(Arg aArgs[])
2003 {
2004     otError error = OT_ERROR_NONE;
2005 
2006     /**
2007      * @cli childip
2008      * @code
2009      * childip
2010      * 3401: fdde:ad00:beef:0:3037:3e03:8c5f:bc0c
2011      * Done
2012      * @endcode
2013      * @par
2014      * Gets a list of IP addresses stored for MTD children.
2015      * @sa otThreadGetChildNextIp6Address
2016      */
2017     if (aArgs[0].IsEmpty())
2018     {
2019         uint16_t maxChildren = otThreadGetMaxAllowedChildren(GetInstancePtr());
2020 
2021         for (uint16_t childIndex = 0; childIndex < maxChildren; childIndex++)
2022         {
2023             otChildIp6AddressIterator iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT;
2024             otIp6Address              ip6Address;
2025             otChildInfo               childInfo;
2026 
2027             if ((otThreadGetChildInfoByIndex(GetInstancePtr(), childIndex, &childInfo) != OT_ERROR_NONE) ||
2028                 childInfo.mIsStateRestoring)
2029             {
2030                 continue;
2031             }
2032 
2033             iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT;
2034 
2035             while (otThreadGetChildNextIp6Address(GetInstancePtr(), childIndex, &iterator, &ip6Address) ==
2036                    OT_ERROR_NONE)
2037             {
2038                 OutputFormat("%04x: ", childInfo.mRloc16);
2039                 OutputIp6AddressLine(ip6Address);
2040             }
2041         }
2042     }
2043     /**
2044      * @cli childip max
2045      * @code
2046      * childip max
2047      * 4
2048      * Done
2049      * @endcode
2050      * @par api_copy
2051      * #otThreadGetMaxChildIpAddresses
2052      */
2053     else if (aArgs[0] == "max")
2054     {
2055 #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
2056         error = ProcessGet(aArgs + 1, otThreadGetMaxChildIpAddresses);
2057 #else
2058         /**
2059          * @cli childip max (set)
2060          * @code
2061          * childip max 2
2062          * Done
2063          * @endcode
2064          * @cparam childip max @ca{count}
2065          * @par api_copy
2066          * #otThreadSetMaxChildIpAddresses
2067          */
2068         error = ProcessGetSet(aArgs + 1, otThreadGetMaxChildIpAddresses, otThreadSetMaxChildIpAddresses);
2069 #endif
2070     }
2071     else
2072     {
2073         error = OT_ERROR_INVALID_COMMAND;
2074     }
2075 
2076     return error;
2077 }
2078 
2079 /**
2080  * @cli childmax
2081  * @code
2082  * childmax
2083  * 5
2084  * Done
2085  * @endcode
2086  * @par api_copy
2087  * #otThreadGetMaxAllowedChildren
2088  */
Process(Arg aArgs[])2089 template <> otError Interpreter::Process<Cmd("childmax")>(Arg aArgs[])
2090 {
2091     /**
2092      * @cli childmax (set)
2093      * @code
2094      * childmax 2
2095      * Done
2096      * @endcode
2097      * @cparam childmax @ca{count}
2098      * @par api_copy
2099      * #otThreadSetMaxAllowedChildren
2100      */
2101     return ProcessGetSet(aArgs, otThreadGetMaxAllowedChildren, otThreadSetMaxAllowedChildren);
2102 }
2103 #endif // OPENTHREAD_FTD
2104 
Process(Arg aArgs[])2105 template <> otError Interpreter::Process<Cmd("childsupervision")>(Arg aArgs[])
2106 {
2107     otError error = OT_ERROR_INVALID_ARGS;
2108 
2109     /**
2110      * @cli childsupervision checktimeout
2111      * @code
2112      * childsupervision checktimeout
2113      * 30
2114      * Done
2115      * @endcode
2116      * @par api_copy
2117      * #otChildSupervisionGetCheckTimeout
2118      */
2119     if (aArgs[0] == "checktimeout")
2120     {
2121         /** @cli childsupervision checktimeout (set)
2122          * @code
2123          * childsupervision checktimeout 30
2124          * Done
2125          * @endcode
2126          * @cparam childsupervision checktimeout @ca{timeout-seconds}
2127          * @par api_copy
2128          * #otChildSupervisionSetCheckTimeout
2129          */
2130         error = ProcessGetSet(aArgs + 1, otChildSupervisionGetCheckTimeout, otChildSupervisionSetCheckTimeout);
2131     }
2132     /**
2133      * @cli childsupervision interval
2134      * @code
2135      * childsupervision interval
2136      * 30
2137      * Done
2138      * @endcode
2139      * @par api_copy
2140      * #otChildSupervisionGetInterval
2141      */
2142     else if (aArgs[0] == "interval")
2143     {
2144         /**
2145          * @cli childsupervision interval (set)
2146          * @code
2147          * childsupervision interval 30
2148          * Done
2149          * @endcode
2150          * @cparam childsupervision interval @ca{interval-seconds}
2151          * @par api_copy
2152          * #otChildSupervisionSetInterval
2153          */
2154         error = ProcessGetSet(aArgs + 1, otChildSupervisionGetInterval, otChildSupervisionSetInterval);
2155     }
2156     else if (aArgs[0] == "failcounter")
2157     {
2158         if (aArgs[1].IsEmpty())
2159         {
2160             OutputLine("%u", otChildSupervisionGetCheckFailureCounter(GetInstancePtr()));
2161             error = OT_ERROR_NONE;
2162         }
2163         else if (aArgs[1] == "reset")
2164         {
2165             otChildSupervisionResetCheckFailureCounter(GetInstancePtr());
2166             error = OT_ERROR_NONE;
2167         }
2168     }
2169 
2170     return error;
2171 }
2172 
2173 /** @cli childtimeout
2174  * @code
2175  * childtimeout
2176  * 300
2177  * Done
2178  * @endcode
2179  * @par api_copy
2180  * #otThreadGetChildTimeout
2181  */
Process(Arg aArgs[])2182 template <> otError Interpreter::Process<Cmd("childtimeout")>(Arg aArgs[])
2183 {
2184     /** @cli childtimeout (set)
2185      * @code
2186      * childtimeout 300
2187      * Done
2188      * @endcode
2189      * @cparam childtimeout @ca{timeout-seconds}
2190      * @par api_copy
2191      * #otThreadSetChildTimeout
2192      */
2193     return ProcessGetSet(aArgs, otThreadGetChildTimeout, otThreadSetChildTimeout);
2194 }
2195 
2196 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
Process(Arg aArgs[])2197 template <> otError Interpreter::Process<Cmd("coap")>(Arg aArgs[]) { return mCoap.Process(aArgs); }
2198 #endif
2199 
2200 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
Process(Arg aArgs[])2201 template <> otError Interpreter::Process<Cmd("coaps")>(Arg aArgs[]) { return mCoapSecure.Process(aArgs); }
2202 #endif
2203 
2204 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
Process(Arg aArgs[])2205 template <> otError Interpreter::Process<Cmd("coex")>(Arg aArgs[])
2206 {
2207     otError error = OT_ERROR_NONE;
2208     bool    enable;
2209 
2210     if (aArgs[0].IsEmpty())
2211     {
2212         OutputEnabledDisabledStatus(otPlatRadioIsCoexEnabled(GetInstancePtr()));
2213     }
2214     else if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
2215     {
2216         error = otPlatRadioSetCoexEnabled(GetInstancePtr(), enable);
2217     }
2218     else if (aArgs[0] == "metrics")
2219     {
2220         struct RadioCoexMetricName
2221         {
2222             const uint32_t otRadioCoexMetrics::*mValuePtr;
2223             const char                         *mName;
2224         };
2225 
2226         static const RadioCoexMetricName kTxMetricNames[] = {
2227             {&otRadioCoexMetrics::mNumTxRequest, "Request"},
2228             {&otRadioCoexMetrics::mNumTxGrantImmediate, "Grant Immediate"},
2229             {&otRadioCoexMetrics::mNumTxGrantWait, "Grant Wait"},
2230             {&otRadioCoexMetrics::mNumTxGrantWaitActivated, "Grant Wait Activated"},
2231             {&otRadioCoexMetrics::mNumTxGrantWaitTimeout, "Grant Wait Timeout"},
2232             {&otRadioCoexMetrics::mNumTxGrantDeactivatedDuringRequest, "Grant Deactivated During Request"},
2233             {&otRadioCoexMetrics::mNumTxDelayedGrant, "Delayed Grant"},
2234             {&otRadioCoexMetrics::mAvgTxRequestToGrantTime, "Average Request To Grant Time"},
2235         };
2236 
2237         static const RadioCoexMetricName kRxMetricNames[] = {
2238             {&otRadioCoexMetrics::mNumRxRequest, "Request"},
2239             {&otRadioCoexMetrics::mNumRxGrantImmediate, "Grant Immediate"},
2240             {&otRadioCoexMetrics::mNumRxGrantWait, "Grant Wait"},
2241             {&otRadioCoexMetrics::mNumRxGrantWaitActivated, "Grant Wait Activated"},
2242             {&otRadioCoexMetrics::mNumRxGrantWaitTimeout, "Grant Wait Timeout"},
2243             {&otRadioCoexMetrics::mNumRxGrantDeactivatedDuringRequest, "Grant Deactivated During Request"},
2244             {&otRadioCoexMetrics::mNumRxDelayedGrant, "Delayed Grant"},
2245             {&otRadioCoexMetrics::mAvgRxRequestToGrantTime, "Average Request To Grant Time"},
2246             {&otRadioCoexMetrics::mNumRxGrantNone, "Grant None"},
2247         };
2248 
2249         otRadioCoexMetrics metrics;
2250 
2251         SuccessOrExit(error = otPlatRadioGetCoexMetrics(GetInstancePtr(), &metrics));
2252 
2253         OutputLine("Stopped: %s", metrics.mStopped ? "true" : "false");
2254         OutputLine("Grant Glitch: %lu", ToUlong(metrics.mNumGrantGlitch));
2255         OutputLine("Transmit metrics");
2256 
2257         for (const RadioCoexMetricName &metric : kTxMetricNames)
2258         {
2259             OutputLine(kIndentSize, "%s: %lu", metric.mName, ToUlong(metrics.*metric.mValuePtr));
2260         }
2261 
2262         OutputLine("Receive metrics");
2263 
2264         for (const RadioCoexMetricName &metric : kRxMetricNames)
2265         {
2266             OutputLine(kIndentSize, "%s: %lu", metric.mName, ToUlong(metrics.*metric.mValuePtr));
2267         }
2268     }
2269     else
2270     {
2271         error = OT_ERROR_INVALID_ARGS;
2272     }
2273 
2274 exit:
2275     return error;
2276 }
2277 #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
2278 
2279 #if OPENTHREAD_FTD
2280 /**
2281  * @cli contextreusedelay (get,set)
2282  * @code
2283  * contextreusedelay
2284  * 11
2285  * Done
2286  * @endcode
2287  * @code
2288  * contextreusedelay 11
2289  * Done
2290  * @endcode
2291  * @cparam contextreusedelay @ca{delay}
2292  * Use the optional `delay` argument to set the `CONTEXT_ID_REUSE_DELAY`.
2293  * @par
2294  * Gets or sets the `CONTEXT_ID_REUSE_DELAY` value.
2295  * @sa otThreadGetContextIdReuseDelay
2296  * @sa otThreadSetContextIdReuseDelay
2297  */
Process(Arg aArgs[])2298 template <> otError Interpreter::Process<Cmd("contextreusedelay")>(Arg aArgs[])
2299 {
2300     return ProcessGetSet(aArgs, otThreadGetContextIdReuseDelay, otThreadSetContextIdReuseDelay);
2301 }
2302 #endif
2303 
2304 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
OutputBorderRouterCounters(void)2305 void Interpreter::OutputBorderRouterCounters(void)
2306 {
2307     struct BrCounterName
2308     {
2309         const otPacketsAndBytes otBorderRoutingCounters::*mPacketsAndBytes;
2310         const char                                       *mName;
2311     };
2312 
2313     static const BrCounterName kCounterNames[] = {
2314         {&otBorderRoutingCounters::mInboundUnicast, "Inbound Unicast"},
2315         {&otBorderRoutingCounters::mInboundMulticast, "Inbound Multicast"},
2316         {&otBorderRoutingCounters::mOutboundUnicast, "Outbound Unicast"},
2317         {&otBorderRoutingCounters::mOutboundMulticast, "Outbound Multicast"},
2318     };
2319 
2320     const otBorderRoutingCounters *brCounters = otIp6GetBorderRoutingCounters(GetInstancePtr());
2321     Uint64StringBuffer             uint64StringBuffer;
2322 
2323     for (const BrCounterName &counter : kCounterNames)
2324     {
2325         OutputFormat("%s:", counter.mName);
2326         OutputFormat(" Packets %s",
2327                      Uint64ToString((brCounters->*counter.mPacketsAndBytes).mPackets, uint64StringBuffer));
2328         OutputLine(" Bytes %s", Uint64ToString((brCounters->*counter.mPacketsAndBytes).mBytes, uint64StringBuffer));
2329     }
2330 
2331     OutputLine("RA Rx: %lu", ToUlong(brCounters->mRaRx));
2332     OutputLine("RA TxSuccess: %lu", ToUlong(brCounters->mRaTxSuccess));
2333     OutputLine("RA TxFailed: %lu", ToUlong(brCounters->mRaTxFailure));
2334     OutputLine("RS Rx: %lu", ToUlong(brCounters->mRsRx));
2335     OutputLine("RS TxSuccess: %lu", ToUlong(brCounters->mRsTxSuccess));
2336     OutputLine("RS TxFailed: %lu", ToUlong(brCounters->mRsTxFailure));
2337 }
2338 #endif // OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
2339 
Process(Arg aArgs[])2340 template <> otError Interpreter::Process<Cmd("counters")>(Arg aArgs[])
2341 {
2342     otError error = OT_ERROR_NONE;
2343 
2344     /**
2345      * @cli counters
2346      * @code
2347      * counters
2348      * ip
2349      * mac
2350      * mle
2351      * Done
2352      * @endcode
2353      * @par
2354      * Gets the supported counter names.
2355      */
2356     if (aArgs[0].IsEmpty())
2357     {
2358 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
2359         OutputLine("br");
2360 #endif
2361         OutputLine("ip");
2362         OutputLine("mac");
2363         OutputLine("mle");
2364     }
2365 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
2366     /**
2367      * @cli counters br
2368      * @code
2369      * counters br
2370      * Inbound Unicast: Packets 4 Bytes 320
2371      * Inbound Multicast: Packets 0 Bytes 0
2372      * Outbound Unicast: Packets 2 Bytes 160
2373      * Outbound Multicast: Packets 0 Bytes 0
2374      * RA Rx: 4
2375      * RA TxSuccess: 2
2376      * RA TxFailed: 0
2377      * RS Rx: 0
2378      * RS TxSuccess: 2
2379      * RS TxFailed: 0
2380      * Done
2381      * @endcode
2382      * @par api_copy
2383      * #otIp6GetBorderRoutingCounters
2384      */
2385     else if (aArgs[0] == "br")
2386     {
2387         if (aArgs[1].IsEmpty())
2388         {
2389             OutputBorderRouterCounters();
2390         }
2391         /**
2392          * @cli counters br reset
2393          * @code
2394          * counters br reset
2395          * Done
2396          * @endcode
2397          * @par api_copy
2398          * #otIp6ResetBorderRoutingCounters
2399          */
2400         else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
2401         {
2402             otIp6ResetBorderRoutingCounters(GetInstancePtr());
2403         }
2404         else
2405         {
2406             error = OT_ERROR_INVALID_ARGS;
2407         }
2408     }
2409 #endif
2410     /**
2411      * @cli counters (mac)
2412      * @code
2413      * counters mac
2414      * TxTotal: 10
2415      *    TxUnicast: 3
2416      *    TxBroadcast: 7
2417      *    TxAckRequested: 3
2418      *    TxAcked: 3
2419      *    TxNoAckRequested: 7
2420      *    TxData: 10
2421      *    TxDataPoll: 0
2422      *    TxBeacon: 0
2423      *    TxBeaconRequest: 0
2424      *    TxOther: 0
2425      *    TxRetry: 0
2426      *    TxErrCca: 0
2427      *    TxErrBusyChannel: 0
2428      * RxTotal: 2
2429      *    RxUnicast: 1
2430      *    RxBroadcast: 1
2431      *    RxData: 2
2432      *    RxDataPoll: 0
2433      *    RxBeacon: 0
2434      *    RxBeaconRequest: 0
2435      *    RxOther: 0
2436      *    RxAddressFiltered: 0
2437      *    RxDestAddrFiltered: 0
2438      *    RxDuplicated: 0
2439      *    RxErrNoFrame: 0
2440      *    RxErrNoUnknownNeighbor: 0
2441      *    RxErrInvalidSrcAddr: 0
2442      *    RxErrSec: 0
2443      *    RxErrFcs: 0
2444      *    RxErrOther: 0
2445      * Done
2446      * @endcode
2447      * @cparam counters @ca{mac}
2448      * @par api_copy
2449      * #otLinkGetCounters
2450      */
2451     else if (aArgs[0] == "mac")
2452     {
2453         if (aArgs[1].IsEmpty())
2454         {
2455             struct MacCounterName
2456             {
2457                 const uint32_t otMacCounters::*mValuePtr;
2458                 const char                    *mName;
2459             };
2460 
2461             static const MacCounterName kTxCounterNames[] = {
2462                 {&otMacCounters::mTxUnicast, "TxUnicast"},
2463                 {&otMacCounters::mTxBroadcast, "TxBroadcast"},
2464                 {&otMacCounters::mTxAckRequested, "TxAckRequested"},
2465                 {&otMacCounters::mTxAcked, "TxAcked"},
2466                 {&otMacCounters::mTxNoAckRequested, "TxNoAckRequested"},
2467                 {&otMacCounters::mTxData, "TxData"},
2468                 {&otMacCounters::mTxDataPoll, "TxDataPoll"},
2469                 {&otMacCounters::mTxBeacon, "TxBeacon"},
2470                 {&otMacCounters::mTxBeaconRequest, "TxBeaconRequest"},
2471                 {&otMacCounters::mTxOther, "TxOther"},
2472                 {&otMacCounters::mTxRetry, "TxRetry"},
2473                 {&otMacCounters::mTxErrCca, "TxErrCca"},
2474                 {&otMacCounters::mTxErrBusyChannel, "TxErrBusyChannel"},
2475                 {&otMacCounters::mTxErrAbort, "TxErrAbort"},
2476                 {&otMacCounters::mTxDirectMaxRetryExpiry, "TxDirectMaxRetryExpiry"},
2477                 {&otMacCounters::mTxIndirectMaxRetryExpiry, "TxIndirectMaxRetryExpiry"},
2478             };
2479 
2480             static const MacCounterName kRxCounterNames[] = {
2481                 {&otMacCounters::mRxUnicast, "RxUnicast"},
2482                 {&otMacCounters::mRxBroadcast, "RxBroadcast"},
2483                 {&otMacCounters::mRxData, "RxData"},
2484                 {&otMacCounters::mRxDataPoll, "RxDataPoll"},
2485                 {&otMacCounters::mRxBeacon, "RxBeacon"},
2486                 {&otMacCounters::mRxBeaconRequest, "RxBeaconRequest"},
2487                 {&otMacCounters::mRxOther, "RxOther"},
2488                 {&otMacCounters::mRxAddressFiltered, "RxAddressFiltered"},
2489                 {&otMacCounters::mRxDestAddrFiltered, "RxDestAddrFiltered"},
2490                 {&otMacCounters::mRxDuplicated, "RxDuplicated"},
2491                 {&otMacCounters::mRxErrNoFrame, "RxErrNoFrame"},
2492                 {&otMacCounters::mRxErrUnknownNeighbor, "RxErrNoUnknownNeighbor"},
2493                 {&otMacCounters::mRxErrInvalidSrcAddr, "RxErrInvalidSrcAddr"},
2494                 {&otMacCounters::mRxErrSec, "RxErrSec"},
2495                 {&otMacCounters::mRxErrFcs, "RxErrFcs"},
2496                 {&otMacCounters::mRxErrOther, "RxErrOther"},
2497             };
2498 
2499             const otMacCounters *macCounters = otLinkGetCounters(GetInstancePtr());
2500 
2501             OutputLine("TxTotal: %lu", ToUlong(macCounters->mTxTotal));
2502 
2503             for (const MacCounterName &counter : kTxCounterNames)
2504             {
2505                 OutputLine(kIndentSize, "%s: %lu", counter.mName, ToUlong(macCounters->*counter.mValuePtr));
2506             }
2507 
2508             OutputLine("RxTotal: %lu", ToUlong(macCounters->mRxTotal));
2509 
2510             for (const MacCounterName &counter : kRxCounterNames)
2511             {
2512                 OutputLine(kIndentSize, "%s: %lu", counter.mName, ToUlong(macCounters->*counter.mValuePtr));
2513             }
2514         }
2515         /**
2516          * @cli counters mac reset
2517          * @code
2518          * counters mac reset
2519          * Done
2520          * @endcode
2521          * @cparam counters @ca{mac} reset
2522          * @par api_copy
2523          * #otLinkResetCounters
2524          */
2525         else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
2526         {
2527             otLinkResetCounters(GetInstancePtr());
2528         }
2529         else
2530         {
2531             error = OT_ERROR_INVALID_ARGS;
2532         }
2533     }
2534     /**
2535      * @cli counters (mle)
2536      * @code
2537      * counters mle
2538      * Role Disabled: 0
2539      * Role Detached: 1
2540      * Role Child: 0
2541      * Role Router: 0
2542      * Role Leader: 1
2543      * Attach Attempts: 1
2544      * Partition Id Changes: 1
2545      * Better Partition Attach Attempts: 0
2546      * Parent Changes: 0
2547      * Done
2548      * @endcode
2549      * @cparam counters @ca{mle}
2550      * @par api_copy
2551      * #otThreadGetMleCounters
2552      */
2553     else if (aArgs[0] == "mle")
2554     {
2555         if (aArgs[1].IsEmpty())
2556         {
2557             struct MleCounterName
2558             {
2559                 const uint16_t otMleCounters::*mValuePtr;
2560                 const char                    *mName;
2561             };
2562 
2563             static const MleCounterName kCounterNames[] = {
2564                 {&otMleCounters::mDisabledRole, "Role Disabled"},
2565                 {&otMleCounters::mDetachedRole, "Role Detached"},
2566                 {&otMleCounters::mChildRole, "Role Child"},
2567                 {&otMleCounters::mRouterRole, "Role Router"},
2568                 {&otMleCounters::mLeaderRole, "Role Leader"},
2569                 {&otMleCounters::mAttachAttempts, "Attach Attempts"},
2570                 {&otMleCounters::mPartitionIdChanges, "Partition Id Changes"},
2571                 {&otMleCounters::mBetterPartitionAttachAttempts, "Better Partition Attach Attempts"},
2572                 {&otMleCounters::mParentChanges, "Parent Changes"},
2573             };
2574 
2575             const otMleCounters *mleCounters = otThreadGetMleCounters(GetInstancePtr());
2576 
2577             for (const MleCounterName &counter : kCounterNames)
2578             {
2579                 OutputLine("%s: %u", counter.mName, mleCounters->*counter.mValuePtr);
2580             }
2581 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
2582             {
2583                 struct MleTimeCounterName
2584                 {
2585                     const uint64_t otMleCounters::*mValuePtr;
2586                     const char                    *mName;
2587                 };
2588 
2589                 static const MleTimeCounterName kTimeCounterNames[] = {
2590                     {&otMleCounters::mDisabledTime, "Disabled"}, {&otMleCounters::mDetachedTime, "Detached"},
2591                     {&otMleCounters::mChildTime, "Child"},       {&otMleCounters::mRouterTime, "Router"},
2592                     {&otMleCounters::mLeaderTime, "Leader"},
2593                 };
2594 
2595                 for (const MleTimeCounterName &counter : kTimeCounterNames)
2596                 {
2597                     OutputFormat("Time %s Milli: ", counter.mName);
2598                     OutputUint64Line(mleCounters->*counter.mValuePtr);
2599                 }
2600 
2601                 OutputFormat("Time Tracked Milli: ");
2602                 OutputUint64Line(mleCounters->mTrackedTime);
2603             }
2604 #endif
2605         }
2606         /**
2607          * @cli counters mle reset
2608          * @code
2609          * counters mle reset
2610          * Done
2611          * @endcode
2612          * @cparam counters @ca{mle} reset
2613          * @par api_copy
2614          * #otThreadResetMleCounters
2615          */
2616         else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
2617         {
2618             otThreadResetMleCounters(GetInstancePtr());
2619         }
2620         else
2621         {
2622             error = OT_ERROR_INVALID_ARGS;
2623         }
2624     }
2625     /**
2626      * @cli counters ip
2627      * @code
2628      * counters ip
2629      * TxSuccess: 10
2630      * TxFailed: 0
2631      * RxSuccess: 5
2632      * RxFailed: 0
2633      * Done
2634      * @endcode
2635      * @cparam counters @ca{ip}
2636      * @par api_copy
2637      * #otThreadGetIp6Counters
2638      */
2639     else if (aArgs[0] == "ip")
2640     {
2641         if (aArgs[1].IsEmpty())
2642         {
2643             struct IpCounterName
2644             {
2645                 const uint32_t otIpCounters::*mValuePtr;
2646                 const char                   *mName;
2647             };
2648 
2649             static const IpCounterName kCounterNames[] = {
2650                 {&otIpCounters::mTxSuccess, "TxSuccess"},
2651                 {&otIpCounters::mTxFailure, "TxFailed"},
2652                 {&otIpCounters::mRxSuccess, "RxSuccess"},
2653                 {&otIpCounters::mRxFailure, "RxFailed"},
2654             };
2655 
2656             const otIpCounters *ipCounters = otThreadGetIp6Counters(GetInstancePtr());
2657 
2658             for (const IpCounterName &counter : kCounterNames)
2659             {
2660                 OutputLine("%s: %lu", counter.mName, ToUlong(ipCounters->*counter.mValuePtr));
2661             }
2662         }
2663         /**
2664          * @cli counters ip reset
2665          * @code
2666          * counters ip reset
2667          * Done
2668          * @endcode
2669          * @cparam counters @ca{ip} reset
2670          * @par api_copy
2671          * #otThreadResetIp6Counters
2672          */
2673         else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
2674         {
2675             otThreadResetIp6Counters(GetInstancePtr());
2676         }
2677         else
2678         {
2679             error = OT_ERROR_INVALID_ARGS;
2680         }
2681     }
2682     else
2683     {
2684         error = OT_ERROR_INVALID_ARGS;
2685     }
2686 
2687     return error;
2688 }
2689 
2690 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
Process(Arg aArgs[])2691 template <> otError Interpreter::Process<Cmd("csl")>(Arg aArgs[])
2692 {
2693     otError error = OT_ERROR_NONE;
2694 
2695     /**
2696      * @cli csl
2697      * @code
2698      * csl
2699      * Channel: 11
2700      * Period: 1000 (in units of 10 symbols), 160ms
2701      * Timeout: 1000s
2702      * Done
2703      * @endcode
2704      * @par
2705      * Gets the CSL configuration.
2706      * @sa otLinkCslGetChannel
2707      * @sa otLinkCslGetPeriod
2708      * @sa otLinkCslGetPeriod
2709      * @sa otLinkCslGetTimeout
2710      */
2711     if (aArgs[0].IsEmpty())
2712     {
2713         OutputLine("Channel: %u", otLinkCslGetChannel(GetInstancePtr()));
2714         OutputLine("Period: %u(in units of 10 symbols), %lums", otLinkCslGetPeriod(GetInstancePtr()),
2715                    ToUlong(otLinkCslGetPeriod(GetInstancePtr()) * kUsPerTenSymbols / 1000));
2716         OutputLine("Timeout: %lus", ToUlong(otLinkCslGetTimeout(GetInstancePtr())));
2717     }
2718     /**
2719      * @cli csl channel
2720      * @code
2721      * csl channel 20
2722      * Done
2723      * @endcode
2724      * @cparam csl channel @ca{channel}
2725      * @par api_copy
2726      * #otLinkCslSetChannel
2727      */
2728     else if (aArgs[0] == "channel")
2729     {
2730         error = ProcessSet(aArgs + 1, otLinkCslSetChannel);
2731     }
2732     /**
2733      * @cli csl period
2734      * @code
2735      * csl period 3000
2736      * Done
2737      * @endcode
2738      * @cparam csl period @ca{period}
2739      * @par api_copy
2740      * #otLinkCslSetPeriod
2741      */
2742     else if (aArgs[0] == "period")
2743     {
2744         error = ProcessSet(aArgs + 1, otLinkCslSetPeriod);
2745     }
2746     /**
2747      * @cli csl timeout
2748      * @code
2749      * cls timeout 10
2750      * Done
2751      * @endcode
2752      * @cparam csl timeout @ca{timeout}
2753      * @par api_copy
2754      * #otLinkCslSetTimeout
2755      */
2756     else if (aArgs[0] == "timeout")
2757     {
2758         error = ProcessSet(aArgs + 1, otLinkCslSetTimeout);
2759     }
2760     else
2761     {
2762         error = OT_ERROR_INVALID_ARGS;
2763     }
2764 
2765     return error;
2766 }
2767 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
2768 
2769 #if OPENTHREAD_FTD
Process(Arg aArgs[])2770 template <> otError Interpreter::Process<Cmd("delaytimermin")>(Arg aArgs[])
2771 {
2772     otError error = OT_ERROR_NONE;
2773 
2774     /**
2775      * @cli delaytimermin
2776      * @code
2777      * delaytimermin
2778      * 30
2779      * Done
2780      * @endcode
2781      * @par
2782      * Get the minimal delay timer (in seconds).
2783      * @sa otDatasetGetDelayTimerMinimal
2784      */
2785     if (aArgs[0].IsEmpty())
2786     {
2787         OutputLine("%lu", ToUlong((otDatasetGetDelayTimerMinimal(GetInstancePtr()) / 1000)));
2788     }
2789     /**
2790      * @cli delaytimermin (set)
2791      * @code
2792      * delaytimermin 60
2793      * Done
2794      * @endcode
2795      * @cparam delaytimermin @ca{delaytimermin}
2796      * @par
2797      * Sets the minimal delay timer (in seconds).
2798      * @sa otDatasetSetDelayTimerMinimal
2799      */
2800     else if (aArgs[1].IsEmpty())
2801     {
2802         uint32_t delay;
2803         SuccessOrExit(error = aArgs[0].ParseAsUint32(delay));
2804         SuccessOrExit(error = otDatasetSetDelayTimerMinimal(GetInstancePtr(), static_cast<uint32_t>(delay * 1000)));
2805     }
2806     else
2807     {
2808         error = OT_ERROR_INVALID_ARGS;
2809     }
2810 
2811 exit:
2812     return error;
2813 }
2814 #endif
2815 
Process(Arg aArgs[])2816 template <> otError Interpreter::Process<Cmd("detach")>(Arg aArgs[])
2817 {
2818     otError error = OT_ERROR_NONE;
2819 
2820     if (aArgs[0] == "async")
2821     {
2822         SuccessOrExit(error = otThreadDetachGracefully(GetInstancePtr(), nullptr, nullptr));
2823     }
2824     else
2825     {
2826         SuccessOrExit(error =
2827                           otThreadDetachGracefully(GetInstancePtr(), &Interpreter::HandleDetachGracefullyResult, this));
2828         error = OT_ERROR_PENDING;
2829     }
2830 
2831 exit:
2832     return error;
2833 }
2834 
2835 /**
2836  * @cli discover
2837  * @code
2838  * discover
2839  * | J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |
2840  * +---+------------------+------------------+------+------------------+----+-----+-----+
2841  * | 0 | OpenThread       | dead00beef00cafe | ffff | f1d92a82c8d8fe43 | 11 | -20 |   0 |
2842  * Done
2843  * @endcode
2844  * @cparam discover [@ca{channel}]
2845  * `channel`: The channel to discover on. If no channel is provided, the discovery will cover all
2846  * valid channels.
2847  * @par
2848  * Perform an MLE Discovery operation.
2849  * @sa otThreadDiscover
2850  */
Process(Arg aArgs[])2851 template <> otError Interpreter::Process<Cmd("discover")>(Arg aArgs[])
2852 {
2853     otError  error        = OT_ERROR_NONE;
2854     uint32_t scanChannels = 0;
2855 
2856 #if OPENTHREAD_FTD
2857     /**
2858      * @cli discover reqcallback (enable,disable)
2859      * @code
2860      * discover reqcallback enable
2861      * Done
2862      * @endcode
2863      * @cparam discover reqcallback @ca{enable|disable}
2864      * @par api_copy
2865      * #otThreadSetDiscoveryRequestCallback
2866      */
2867     if (aArgs[0] == "reqcallback")
2868     {
2869         bool                             enable;
2870         otThreadDiscoveryRequestCallback callback = nullptr;
2871         void                            *context  = nullptr;
2872 
2873         SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable));
2874 
2875         if (enable)
2876         {
2877             callback = &Interpreter::HandleDiscoveryRequest;
2878             context  = this;
2879         }
2880 
2881         otThreadSetDiscoveryRequestCallback(GetInstancePtr(), callback, context);
2882         ExitNow();
2883     }
2884 #endif // OPENTHREAD_FTD
2885 
2886     if (!aArgs[0].IsEmpty())
2887     {
2888         uint8_t channel;
2889 
2890         SuccessOrExit(error = aArgs[0].ParseAsUint8(channel));
2891         VerifyOrExit(channel < sizeof(scanChannels) * CHAR_BIT, error = OT_ERROR_INVALID_ARGS);
2892         scanChannels = 1 << channel;
2893     }
2894 
2895     SuccessOrExit(error = otThreadDiscover(GetInstancePtr(), scanChannels, OT_PANID_BROADCAST, false, false,
2896                                            &Interpreter::HandleActiveScanResult, this));
2897 
2898     static const char *const kScanTableTitles[] = {
2899         "Network Name", "Extended PAN", "PAN", "MAC Address", "Ch", "dBm", "LQI",
2900     };
2901 
2902     static const uint8_t kScanTableColumnWidths[] = {
2903         18, 18, 6, 18, 4, 5, 5,
2904     };
2905 
2906     OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
2907 
2908     error = OT_ERROR_PENDING;
2909 
2910 exit:
2911     return error;
2912 }
2913 
Process(Arg aArgs[])2914 template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
2915 {
2916     OT_UNUSED_VARIABLE(aArgs);
2917 
2918     otError error = OT_ERROR_NONE;
2919 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
2920     otDnsQueryConfig  queryConfig;
2921     otDnsQueryConfig *config = &queryConfig;
2922 #endif
2923 
2924     if (aArgs[0].IsEmpty())
2925     {
2926         error = OT_ERROR_INVALID_ARGS;
2927     }
2928     /**
2929      * @cli dns compression
2930      * @code
2931      * dns compression
2932      * Enabled
2933      * @endcode
2934      * @cparam dns compression [@ca{enable|disable}]
2935      * @par api_copy
2936      * #otDnsIsNameCompressionEnabled
2937      * @par
2938      * By default DNS name compression is enabled. When disabled,
2939      * DNS names are appended as full and never compressed. This
2940      * is applicable to OpenThread's DNS and SRP client/server
2941      * modules."
2942      * 'OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE' is required.
2943      */
2944 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
2945     else if (aArgs[0] == "compression")
2946     {
2947         /**
2948          * @cli dns compression (enable,disable)
2949          * @code
2950          * dns compression enable
2951          * Enabled
2952          * @endcode
2953          * @code
2954          * dns compression disable
2955          * Done
2956          * dns compression
2957          * Disabled
2958          * Done
2959          * @endcode
2960          * @cparam dns compression [@ca{enable|disable}]
2961          * @par
2962          * Set the "DNS name compression" mode.
2963          * @par
2964          * By default DNS name compression is enabled. When disabled,
2965          * DNS names are appended as full and never compressed. This
2966          * is applicable to OpenThread's DNS and SRP client/server
2967          * modules."
2968          * 'OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE' is required.
2969          * @sa otDnsSetNameCompressionEnabled
2970          */
2971         if (aArgs[1].IsEmpty())
2972         {
2973             OutputEnabledDisabledStatus(otDnsIsNameCompressionEnabled());
2974         }
2975         else
2976         {
2977             bool enable;
2978 
2979             SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable));
2980             otDnsSetNameCompressionEnabled(enable);
2981         }
2982     }
2983 #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
2984 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
2985 
2986     else if (aArgs[0] == "config")
2987     {
2988         /**
2989          * @cli dns config
2990          * @code
2991          * dns config
2992          * Server: [fd00:0:0:0:0:0:0:1]:1234
2993          * ResponseTimeout: 5000 ms
2994          * MaxTxAttempts: 2
2995          * RecursionDesired: no
2996          * ServiceMode: srv
2997          * Nat64Mode: allow
2998          * Done
2999          * @endcode
3000          * @par api_copy
3001          * #otDnsClientGetDefaultConfig
3002          * @par
3003          * The config includes the server IPv6 address and port, response
3004          * timeout in msec (wait time to rx response), maximum tx attempts
3005          * before reporting failure, boolean flag to indicate whether the server
3006          * can resolve the query recursively or not.
3007          * 'OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE' is required.
3008          */
3009         if (aArgs[1].IsEmpty())
3010         {
3011             const otDnsQueryConfig *defaultConfig = otDnsClientGetDefaultConfig(GetInstancePtr());
3012 
3013             OutputFormat("Server: ");
3014             OutputSockAddrLine(defaultConfig->mServerSockAddr);
3015             OutputLine("ResponseTimeout: %lu ms", ToUlong(defaultConfig->mResponseTimeout));
3016             OutputLine("MaxTxAttempts: %u", defaultConfig->mMaxTxAttempts);
3017             OutputLine("RecursionDesired: %s",
3018                        (defaultConfig->mRecursionFlag == OT_DNS_FLAG_RECURSION_DESIRED) ? "yes" : "no");
3019             OutputLine("ServiceMode: %s", DnsConfigServiceModeToString(defaultConfig->mServiceMode));
3020 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
3021             OutputLine("Nat64Mode: %s", (defaultConfig->mNat64Mode == OT_DNS_NAT64_ALLOW) ? "allow" : "disallow");
3022 #endif
3023 #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE
3024             OutputLine("TransportProtocol: %s",
3025                        (defaultConfig->mTransportProto == OT_DNS_TRANSPORT_UDP) ? "udp" : "tcp");
3026 #endif
3027         }
3028         /**
3029          * @cli dns config (set)
3030          * @code
3031          * dns config fd00::1 1234 5000 2 0
3032          * Done
3033          * @endcode
3034          * @code
3035          * dns config
3036          * Server: [fd00:0:0:0:0:0:0:1]:1234
3037          * ResponseTimeout: 5000 ms
3038          * MaxTxAttempts: 2
3039          * RecursionDesired: no
3040          * Done
3041          * @endcode
3042          * @code
3043          * dns config fd00::2
3044          * Done
3045          * @endcode
3046          * @code
3047          * dns config
3048          * Server: [fd00:0:0:0:0:0:0:2]:53
3049          * ResponseTimeout: 3000 ms
3050          * MaxTxAttempts: 3
3051          * RecursionDesired: yes
3052          * Done
3053          * @endcode
3054          * @par api_copy
3055          * #otDnsClientSetDefaultConfig
3056          * @cparam dns config [@ca{dns-server-IP}] [@ca{dns-server-port}] <!--
3057          * -->                [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
3058          * -->                [@ca{recursion-desired-boolean}] [@ca{service-mode}]
3059          * @par
3060          * We can leave some of the fields as unspecified (or use value zero). The
3061          * unspecified fields are replaced by the corresponding OT config option
3062          * definitions OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT to form the default
3063          * query config.
3064          * 'OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE' is required.
3065          */
3066         else
3067         {
3068             SuccessOrExit(error = GetDnsConfig(aArgs + 1, config));
3069             otDnsClientSetDefaultConfig(GetInstancePtr(), config);
3070         }
3071     }
3072     /**
3073      * @cli dns resolve
3074      * @code
3075      * dns resolve ipv6.google.com
3076      * DNS response for ipv6.google.com - 2a00:1450:401b:801:0:0:0:200e TTL: 300
3077      * @endcode
3078      * @code
3079      * dns resolve example.com 8.8.8.8
3080      * Synthesized IPv6 DNS server address: fdde:ad00:beef:2:0:0:808:808
3081      * DNS response for example.com. - fd4c:9574:3720:2:0:0:5db8:d822 TTL:20456
3082      * Done
3083      * @endcode
3084      * @cparam dns resolve @ca{hostname} [@ca{dns-server-IP}] <!--
3085      * -->                 [@ca{dns-server-port}] [@ca{response-timeout-ms}] <!--
3086      * -->                 [@ca{max-tx-attempts}] [@ca{recursion-desired-boolean}]
3087      * @par api_copy
3088      * #otDnsClientResolveAddress
3089      * @par
3090      * Send DNS Query to obtain IPv6 address for given hostname.
3091      * @par
3092      * The parameters after hostname are optional. Any unspecified (or zero) value
3093      * for these optional parameters is replaced by the value from the current default
3094      * config (dns config).
3095      * @par
3096      * The DNS server IP can be an IPv4 address, which will be synthesized to an
3097      * IPv6 address using the preferred NAT64 prefix from the network data.
3098      * @par
3099      * Note: The command will return InvalidState when the DNS server IP is an IPv4
3100      * address but the preferred NAT64 prefix is unavailable.
3101      * 'OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE' is required.
3102      */
3103     else if (aArgs[0] == "resolve")
3104     {
3105         VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3106         SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
3107         SuccessOrExit(error = otDnsClientResolveAddress(GetInstancePtr(), aArgs[1].GetCString(),
3108                                                         &Interpreter::HandleDnsAddressResponse, this, config));
3109         error = OT_ERROR_PENDING;
3110     }
3111 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
3112     else if (aArgs[0] == "resolve4")
3113     {
3114         VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3115         SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
3116         SuccessOrExit(error = otDnsClientResolveIp4Address(GetInstancePtr(), aArgs[1].GetCString(),
3117                                                            &Interpreter::HandleDnsAddressResponse, this, config));
3118         error = OT_ERROR_PENDING;
3119     }
3120 #endif
3121 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
3122     /**
3123      * @cli dns browse
3124      * @code
3125      * dns browse _service._udp.example.com
3126      * DNS browse response for _service._udp.example.com.
3127      * inst1
3128      *     Port:1234, Priority:1, Weight:2, TTL:7200
3129      *     Host:host.example.com.
3130      *     HostAddress:fd00:0:0:0:0:0:0:abcd TTL:7200
3131      *     TXT:[a=6531, b=6c12] TTL:7300
3132      * instance2
3133      *     Port:1234, Priority:1, Weight:2, TTL:7200
3134      *     Host:host.example.com.
3135      *     HostAddress:fd00:0:0:0:0:0:0:abcd TTL:7200
3136      *     TXT:[a=1234] TTL:7300
3137      * Done
3138      * @endcode
3139      * @code
3140      * dns browse _airplay._tcp.default.service.arpa
3141      * DNS browse response for _airplay._tcp.default.service.arpa.
3142      * Mac mini
3143      *     Port:7000, Priority:0, Weight:0, TTL:10
3144      *     Host:Mac-mini.default.service.arpa.
3145      *     HostAddress:fd97:739d:386a:1:1c2e:d83c:fcbe:9cf4 TTL:10
3146      * Done
3147      * @endcode
3148      * @cparam dns browse @ca{service-name} [@ca{dns-server-IP}] [@ca{dns-server-port}] <!--
3149      * -->                [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
3150      * -->                [@ca{recursion-desired-boolean}]
3151      * @sa otDnsClientBrowse
3152      * @par
3153      * Send a browse (service instance enumeration) DNS query to get the list of services for
3154      * given service-name
3155      * @par
3156      * The parameters after `service-name` are optional. Any unspecified (or zero) value
3157      * for these optional parameters is replaced by the value from the current default
3158      * config (`dns config`).
3159      * @par
3160      * Note: The DNS server IP can be an IPv4 address, which will be synthesized to an IPv6
3161      * address using the preferred NAT64 prefix from the network data. The command will return
3162      * `InvalidState` when the DNS server IP is an IPv4 address but the preferred NAT64 prefix
3163      * is unavailable. When testing DNS-SD discovery proxy, the zone is not `local` and
3164      * instead should be `default.service.arpa`.
3165      * `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
3166      */
3167     else if (aArgs[0] == "browse")
3168     {
3169         VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3170         SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
3171         SuccessOrExit(error = otDnsClientBrowse(GetInstancePtr(), aArgs[1].GetCString(),
3172                                                 &Interpreter::HandleDnsBrowseResponse, this, config));
3173         error = OT_ERROR_PENDING;
3174     }
3175     /**
3176      * @cli dns service
3177      * @cparam dns service @ca{service-instance-label} @ca{service-name} <!--
3178      * -->                 [@ca{DNS-server-IP}] [@ca{DNS-server-port}] <!--
3179      * -->                 [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
3180      * -->                 [@ca{recursion-desired-boolean}]
3181      * @par api_copy
3182      * #otDnsClientResolveService
3183      * @par
3184      * Send a service instance resolution DNS query for a given service instance.
3185      * Service instance label is provided first, followed by the service name
3186      * (note that service instance label can contain dot '.' character).
3187      * @par
3188      * The parameters after `service-name` are optional. Any unspecified (or zero)
3189      * value for these optional parameters is replaced by the value from the
3190      * current default config (`dns config`).
3191      * @par
3192      * Note: The DNS server IP can be an IPv4 address, which will be synthesized
3193      * to an IPv6 address using the preferred NAT64 prefix from the network data.
3194      * The command will return `InvalidState` when the DNS server IP is an IPv4
3195      * address but the preferred NAT64 prefix is unavailable.
3196      * `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
3197      */
3198     else if (aArgs[0] == "service")
3199     {
3200         VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3201         SuccessOrExit(error = GetDnsConfig(aArgs + 3, config));
3202         SuccessOrExit(error = otDnsClientResolveService(GetInstancePtr(), aArgs[1].GetCString(), aArgs[2].GetCString(),
3203                                                         &Interpreter::HandleDnsServiceResponse, this, config));
3204         error = OT_ERROR_PENDING;
3205     }
3206     /**
3207      * @cli dns servicehost
3208      * @cparam dns servicehost @ca{service-instance-label} @ca{service-name} <!--
3209      * -->                 [@ca{DNS-server-IP}] [@ca{DNS-server-port}] <!--
3210      * -->                 [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] <!--
3211      * -->                 [@ca{recursion-desired-boolean}]
3212      * @par api_copy
3213      * #otDnsClientResolveServiceAndHostAddress
3214      * @par
3215      * Send a service instance resolution DNS query for a given service instance
3216      * with potential follow-up host name resolution.
3217      * Service instance label is provided first, followed by the service name
3218      * (note that service instance label can contain dot '.' character).
3219      * @par
3220      * The parameters after `service-name` are optional. Any unspecified (or zero)
3221      * value for these optional parameters is replaced by the value from the
3222      * current default config (`dns config`).
3223      * @par
3224      * Note: The DNS server IP can be an IPv4 address, which will be synthesized
3225      * to an IPv6 address using the preferred NAT64 prefix from the network data.
3226      * The command will return `InvalidState` when the DNS server IP is an IPv4
3227      * address but the preferred NAT64 prefix is unavailable.
3228      * `OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE` is required.
3229      */
3230     else if (aArgs[0] == "servicehost")
3231     {
3232         VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3233         SuccessOrExit(error = GetDnsConfig(aArgs + 3, config));
3234         SuccessOrExit(error = otDnsClientResolveServiceAndHostAddress(
3235                           GetInstancePtr(), aArgs[1].GetCString(), aArgs[2].GetCString(),
3236                           &Interpreter::HandleDnsServiceResponse, this, config));
3237         error = OT_ERROR_PENDING;
3238     }
3239 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
3240 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
3241 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
3242     else if (aArgs[0] == "server")
3243     {
3244         if (aArgs[1].IsEmpty())
3245         {
3246             error = OT_ERROR_INVALID_ARGS;
3247         }
3248 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
3249         else if (aArgs[1] == "upstream")
3250         {
3251             /**
3252              * @cli dns server upstream
3253              * @code
3254              * dns server upstream
3255              * Enabled
3256              * Done
3257              * @endcode
3258              * @par api_copy
3259              * #otDnssdUpstreamQueryIsEnabled
3260              */
3261             if (aArgs[2].IsEmpty())
3262             {
3263                 OutputEnabledDisabledStatus(otDnssdUpstreamQueryIsEnabled(GetInstancePtr()));
3264             }
3265             /**
3266              * @cli dns server upstream {enable|disable}
3267              * @code
3268              * dns server upstream enable
3269              * Done
3270              * @endcode
3271              * @cparam dns server upstream @ca{enable|disable}
3272              * @par api_copy
3273              * #otDnssdUpstreamQuerySetEnabled
3274              */
3275             else
3276             {
3277                 bool enable;
3278 
3279                 SuccessOrExit(error = ParseEnableOrDisable(aArgs[2], enable));
3280                 otDnssdUpstreamQuerySetEnabled(GetInstancePtr(), enable);
3281             }
3282         }
3283 #endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
3284         else
3285         {
3286             ExitNow(error = OT_ERROR_INVALID_COMMAND);
3287         }
3288     }
3289 #endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
3290     else
3291     {
3292         ExitNow(error = OT_ERROR_INVALID_COMMAND);
3293     }
3294 
3295 exit:
3296     return error;
3297 }
3298 
3299 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
3300 
DnsConfigServiceModeToString(otDnsServiceMode aMode) const3301 const char *Interpreter::DnsConfigServiceModeToString(otDnsServiceMode aMode) const
3302 {
3303     static const char *const kServiceModeStrings[] = {
3304         "unspec",      // OT_DNS_SERVICE_MODE_UNSPECIFIED      (0)
3305         "srv",         // OT_DNS_SERVICE_MODE_SRV              (1)
3306         "txt",         // OT_DNS_SERVICE_MODE_TXT              (2)
3307         "srv_txt",     // OT_DNS_SERVICE_MODE_SRV_TXT          (3)
3308         "srv_txt_sep", // OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE (4)
3309         "srv_txt_opt", // OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE (5)
3310     };
3311 
3312     static_assert(OT_DNS_SERVICE_MODE_UNSPECIFIED == 0, "OT_DNS_SERVICE_MODE_UNSPECIFIED value is incorrect");
3313     static_assert(OT_DNS_SERVICE_MODE_SRV == 1, "OT_DNS_SERVICE_MODE_SRV value is incorrect");
3314     static_assert(OT_DNS_SERVICE_MODE_TXT == 2, "OT_DNS_SERVICE_MODE_TXT value is incorrect");
3315     static_assert(OT_DNS_SERVICE_MODE_SRV_TXT == 3, "OT_DNS_SERVICE_MODE_SRV_TXT value is incorrect");
3316     static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE == 4, "OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE value is incorrect");
3317     static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE == 5, "OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE value is incorrect");
3318 
3319     return Stringify(aMode, kServiceModeStrings);
3320 }
3321 
ParseDnsServiceMode(const Arg & aArg,otDnsServiceMode & aMode) const3322 otError Interpreter::ParseDnsServiceMode(const Arg &aArg, otDnsServiceMode &aMode) const
3323 {
3324     otError error = OT_ERROR_NONE;
3325 
3326     if (aArg == "def")
3327     {
3328         aMode = OT_DNS_SERVICE_MODE_UNSPECIFIED;
3329     }
3330     else if (aArg == "srv")
3331     {
3332         aMode = OT_DNS_SERVICE_MODE_SRV;
3333     }
3334     else if (aArg == "txt")
3335     {
3336         aMode = OT_DNS_SERVICE_MODE_TXT;
3337     }
3338     else if (aArg == "srv_txt")
3339     {
3340         aMode = OT_DNS_SERVICE_MODE_SRV_TXT;
3341     }
3342     else if (aArg == "srv_txt_sep")
3343     {
3344         aMode = OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE;
3345     }
3346     else if (aArg == "srv_txt_opt")
3347     {
3348         aMode = OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE;
3349     }
3350     else
3351     {
3352         error = OT_ERROR_INVALID_ARGS;
3353     }
3354 
3355     return error;
3356 }
3357 
GetDnsConfig(Arg aArgs[],otDnsQueryConfig * & aConfig)3358 otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig)
3359 {
3360     // This method gets the optional DNS config from `aArgs[]`.
3361     // The format: `[server IP address] [server port] [timeout]
3362     // [max tx attempt] [recursion desired] [service mode]
3363     // [transport]`
3364 
3365     otError error = OT_ERROR_NONE;
3366     bool    recursionDesired;
3367     bool    nat64SynthesizedAddress;
3368 
3369     memset(aConfig, 0, sizeof(otDnsQueryConfig));
3370 
3371     VerifyOrExit(!aArgs[0].IsEmpty(), aConfig = nullptr);
3372 
3373     SuccessOrExit(error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], aConfig->mServerSockAddr.mAddress,
3374                                                          nat64SynthesizedAddress));
3375     if (nat64SynthesizedAddress)
3376     {
3377         OutputFormat("Synthesized IPv6 DNS server address: ");
3378         OutputIp6AddressLine(aConfig->mServerSockAddr.mAddress);
3379     }
3380 
3381     VerifyOrExit(!aArgs[1].IsEmpty());
3382     SuccessOrExit(error = aArgs[1].ParseAsUint16(aConfig->mServerSockAddr.mPort));
3383 
3384     VerifyOrExit(!aArgs[2].IsEmpty());
3385     SuccessOrExit(error = aArgs[2].ParseAsUint32(aConfig->mResponseTimeout));
3386 
3387     VerifyOrExit(!aArgs[3].IsEmpty());
3388     SuccessOrExit(error = aArgs[3].ParseAsUint8(aConfig->mMaxTxAttempts));
3389 
3390     VerifyOrExit(!aArgs[4].IsEmpty());
3391     SuccessOrExit(error = aArgs[4].ParseAsBool(recursionDesired));
3392     aConfig->mRecursionFlag = recursionDesired ? OT_DNS_FLAG_RECURSION_DESIRED : OT_DNS_FLAG_NO_RECURSION;
3393 
3394     VerifyOrExit(!aArgs[5].IsEmpty());
3395     SuccessOrExit(error = ParseDnsServiceMode(aArgs[5], aConfig->mServiceMode));
3396 
3397     VerifyOrExit(!aArgs[6].IsEmpty());
3398 
3399     if (aArgs[6] == "tcp")
3400     {
3401         aConfig->mTransportProto = OT_DNS_TRANSPORT_TCP;
3402     }
3403     else if (aArgs[6] == "udp")
3404     {
3405         aConfig->mTransportProto = OT_DNS_TRANSPORT_UDP;
3406     }
3407     else
3408     {
3409         error = OT_ERROR_INVALID_ARGS;
3410     }
3411 
3412 exit:
3413     return error;
3414 }
3415 
HandleDnsAddressResponse(otError aError,const otDnsAddressResponse * aResponse,void * aContext)3416 void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext)
3417 {
3418     static_cast<Interpreter *>(aContext)->HandleDnsAddressResponse(aError, aResponse);
3419 }
3420 
HandleDnsAddressResponse(otError aError,const otDnsAddressResponse * aResponse)3421 void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse)
3422 {
3423     char         hostName[OT_DNS_MAX_NAME_SIZE];
3424     otIp6Address address;
3425     uint32_t     ttl;
3426 
3427     IgnoreError(otDnsAddressResponseGetHostName(aResponse, hostName, sizeof(hostName)));
3428 
3429     OutputFormat("DNS response for %s - ", hostName);
3430 
3431     if (aError == OT_ERROR_NONE)
3432     {
3433         uint16_t index = 0;
3434 
3435         while (otDnsAddressResponseGetAddress(aResponse, index, &address, &ttl) == OT_ERROR_NONE)
3436         {
3437             OutputIp6Address(address);
3438             OutputFormat(" TTL:%lu ", ToUlong(ttl));
3439             index++;
3440         }
3441     }
3442 
3443     OutputNewLine();
3444     OutputResult(aError);
3445 }
3446 
3447 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
3448 
OutputDnsServiceInfo(uint8_t aIndentSize,const otDnsServiceInfo & aServiceInfo)3449 void Interpreter::OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo)
3450 {
3451     OutputLine(aIndentSize, "Port:%d, Priority:%d, Weight:%d, TTL:%lu", aServiceInfo.mPort, aServiceInfo.mPriority,
3452                aServiceInfo.mWeight, ToUlong(aServiceInfo.mTtl));
3453     OutputLine(aIndentSize, "Host:%s", aServiceInfo.mHostNameBuffer);
3454     OutputFormat(aIndentSize, "HostAddress:");
3455     OutputIp6Address(aServiceInfo.mHostAddress);
3456     OutputLine(" TTL:%lu", ToUlong(aServiceInfo.mHostAddressTtl));
3457     OutputFormat(aIndentSize, "TXT:");
3458 
3459     if (!aServiceInfo.mTxtDataTruncated)
3460     {
3461         OutputDnsTxtData(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
3462     }
3463     else
3464     {
3465         OutputFormat("[");
3466         OutputBytes(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
3467         OutputFormat("...]");
3468     }
3469 
3470     OutputLine(" TTL:%lu", ToUlong(aServiceInfo.mTxtDataTtl));
3471 }
3472 
HandleDnsBrowseResponse(otError aError,const otDnsBrowseResponse * aResponse,void * aContext)3473 void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext)
3474 {
3475     static_cast<Interpreter *>(aContext)->HandleDnsBrowseResponse(aError, aResponse);
3476 }
3477 
HandleDnsBrowseResponse(otError aError,const otDnsBrowseResponse * aResponse)3478 void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse)
3479 {
3480     char             name[OT_DNS_MAX_NAME_SIZE];
3481     char             label[OT_DNS_MAX_LABEL_SIZE];
3482     uint8_t          txtBuffer[kMaxTxtDataSize];
3483     otDnsServiceInfo serviceInfo;
3484 
3485     IgnoreError(otDnsBrowseResponseGetServiceName(aResponse, name, sizeof(name)));
3486 
3487     OutputLine("DNS browse response for %s", name);
3488 
3489     if (aError == OT_ERROR_NONE)
3490     {
3491         uint16_t index = 0;
3492 
3493         while (otDnsBrowseResponseGetServiceInstance(aResponse, index, label, sizeof(label)) == OT_ERROR_NONE)
3494         {
3495             OutputLine("%s", label);
3496             index++;
3497 
3498             serviceInfo.mHostNameBuffer     = name;
3499             serviceInfo.mHostNameBufferSize = sizeof(name);
3500             serviceInfo.mTxtData            = txtBuffer;
3501             serviceInfo.mTxtDataSize        = sizeof(txtBuffer);
3502 
3503             if (otDnsBrowseResponseGetServiceInfo(aResponse, label, &serviceInfo) == OT_ERROR_NONE)
3504             {
3505                 OutputDnsServiceInfo(kIndentSize, serviceInfo);
3506             }
3507 
3508             OutputNewLine();
3509         }
3510     }
3511 
3512     OutputResult(aError);
3513 }
3514 
HandleDnsServiceResponse(otError aError,const otDnsServiceResponse * aResponse,void * aContext)3515 void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse, void *aContext)
3516 {
3517     static_cast<Interpreter *>(aContext)->HandleDnsServiceResponse(aError, aResponse);
3518 }
3519 
HandleDnsServiceResponse(otError aError,const otDnsServiceResponse * aResponse)3520 void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse)
3521 {
3522     char             name[OT_DNS_MAX_NAME_SIZE];
3523     char             label[OT_DNS_MAX_LABEL_SIZE];
3524     uint8_t          txtBuffer[kMaxTxtDataSize];
3525     otDnsServiceInfo serviceInfo;
3526 
3527     IgnoreError(otDnsServiceResponseGetServiceName(aResponse, label, sizeof(label), name, sizeof(name)));
3528 
3529     OutputLine("DNS service resolution response for %s for service %s", label, name);
3530 
3531     if (aError == OT_ERROR_NONE)
3532     {
3533         serviceInfo.mHostNameBuffer     = name;
3534         serviceInfo.mHostNameBufferSize = sizeof(name);
3535         serviceInfo.mTxtData            = txtBuffer;
3536         serviceInfo.mTxtDataSize        = sizeof(txtBuffer);
3537 
3538         if (otDnsServiceResponseGetServiceInfo(aResponse, &serviceInfo) == OT_ERROR_NONE)
3539         {
3540             OutputDnsServiceInfo(/* aIndentSize */ 0, serviceInfo);
3541             OutputNewLine();
3542         }
3543     }
3544 
3545     OutputResult(aError);
3546 }
3547 
3548 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
3549 #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
3550 
3551 #if OPENTHREAD_FTD
EidCacheStateToString(otCacheEntryState aState)3552 const char *EidCacheStateToString(otCacheEntryState aState)
3553 {
3554     static const char *const kStateStrings[4] = {
3555         "cache",
3556         "snoop",
3557         "query",
3558         "retry",
3559     };
3560 
3561     return Interpreter::Stringify(aState, kStateStrings);
3562 }
3563 
OutputEidCacheEntry(const otCacheEntryInfo & aEntry)3564 void Interpreter::OutputEidCacheEntry(const otCacheEntryInfo &aEntry)
3565 {
3566     OutputIp6Address(aEntry.mTarget);
3567     OutputFormat(" %04x", aEntry.mRloc16);
3568     OutputFormat(" %s", EidCacheStateToString(aEntry.mState));
3569     OutputFormat(" canEvict=%d", aEntry.mCanEvict);
3570 
3571     if (aEntry.mState == OT_CACHE_ENTRY_STATE_CACHED)
3572     {
3573         if (aEntry.mValidLastTrans)
3574         {
3575             OutputFormat(" transTime=%lu eid=", ToUlong(aEntry.mLastTransTime));
3576             OutputIp6Address(aEntry.mMeshLocalEid);
3577         }
3578     }
3579     else
3580     {
3581         OutputFormat(" timeout=%u", aEntry.mTimeout);
3582     }
3583 
3584     if (aEntry.mState == OT_CACHE_ENTRY_STATE_RETRY_QUERY)
3585     {
3586         OutputFormat(" retryDelay=%u", aEntry.mRetryDelay);
3587     }
3588 
3589     OutputNewLine();
3590 }
3591 
3592 /**
3593  * @cli eidcache
3594  * @code
3595  * eidcache
3596  * fd49:caf4:a29f:dc0e:97fc:69dd:3c16:df7d 2000 cache canEvict=1 transTime=0 eid=fd49:caf4:a29f:dc0e:97fc:69dd:3c16:df7d
3597  * fd49:caf4:a29f:dc0e:97fc:69dd:3c16:df7f fffe retry canEvict=1 timeout=10 retryDelay=30
3598  * Done
3599  * @endcode
3600  * @par
3601  * Returns the EID-to-RLOC cache entries.
3602  * @sa otThreadGetNextCacheEntry
3603  */
Process(Arg aArgs[])3604 template <> otError Interpreter::Process<Cmd("eidcache")>(Arg aArgs[])
3605 {
3606     OT_UNUSED_VARIABLE(aArgs);
3607 
3608     otCacheEntryIterator iterator;
3609     otCacheEntryInfo     entry;
3610 
3611     memset(&iterator, 0, sizeof(iterator));
3612 
3613     while (true)
3614     {
3615         SuccessOrExit(otThreadGetNextCacheEntry(GetInstancePtr(), &entry, &iterator));
3616         OutputEidCacheEntry(entry);
3617     }
3618 
3619 exit:
3620     return OT_ERROR_NONE;
3621 }
3622 #endif
3623 
3624 /**
3625  * @cli eui64
3626  * @code
3627  * eui64
3628  * 0615aae900124b00
3629  * Done
3630  * @endcode
3631  * @par api_copy
3632  * #otPlatRadioGetIeeeEui64
3633  */
Process(Arg aArgs[])3634 template <> otError Interpreter::Process<Cmd("eui64")>(Arg aArgs[])
3635 {
3636     OT_UNUSED_VARIABLE(aArgs);
3637 
3638     otError      error = OT_ERROR_NONE;
3639     otExtAddress extAddress;
3640 
3641     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3642 
3643     otLinkGetFactoryAssignedIeeeEui64(GetInstancePtr(), &extAddress);
3644     OutputExtAddressLine(extAddress);
3645 
3646 exit:
3647     return error;
3648 }
3649 
Process(Arg aArgs[])3650 template <> otError Interpreter::Process<Cmd("extaddr")>(Arg aArgs[])
3651 {
3652     otError error = OT_ERROR_NONE;
3653 
3654     /**
3655      * @cli extaddr
3656      * @code
3657      * extaddr
3658      * dead00beef00cafe
3659      * Done
3660      * @endcode
3661      * @par api_copy
3662      * #otLinkGetExtendedAddress
3663      */
3664     if (aArgs[0].IsEmpty())
3665     {
3666         OutputExtAddressLine(*otLinkGetExtendedAddress(GetInstancePtr()));
3667     }
3668     /**
3669      * @cli extaddr (set)
3670      * @code
3671      * extaddr dead00beef00cafe
3672      * dead00beef00cafe
3673      * Done
3674      * @endcode
3675      * @cparam extaddr @ca{extaddr}
3676      * @par api_copy
3677      * #otLinkSetExtendedAddress
3678      */
3679     else
3680     {
3681         otExtAddress extAddress;
3682 
3683         SuccessOrExit(error = aArgs[0].ParseAsHexString(extAddress.m8));
3684         error = otLinkSetExtendedAddress(GetInstancePtr(), &extAddress);
3685     }
3686 
3687 exit:
3688     return error;
3689 }
3690 
Process(Arg aArgs[])3691 template <> otError Interpreter::Process<Cmd("log")>(Arg aArgs[])
3692 {
3693     otError error = OT_ERROR_NONE;
3694 
3695     if (aArgs[0] == "level")
3696     {
3697         if (aArgs[1].IsEmpty())
3698         {
3699             OutputLine("%d", otLoggingGetLevel());
3700         }
3701         else
3702         {
3703 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
3704             uint8_t level;
3705 
3706             VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3707             SuccessOrExit(error = aArgs[1].ParseAsUint8(level));
3708             error = otLoggingSetLevel(static_cast<otLogLevel>(level));
3709 #else
3710             error = OT_ERROR_INVALID_ARGS;
3711 #endif
3712         }
3713     }
3714 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX
3715     else if (aArgs[0] == "filename")
3716     {
3717         VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3718         SuccessOrExit(error = otPlatDebugUart_logfile(aArgs[1].GetCString()));
3719     }
3720 #endif
3721     else
3722     {
3723         ExitNow(error = OT_ERROR_INVALID_ARGS);
3724     }
3725 
3726 exit:
3727     return error;
3728 }
3729 
Process(Arg aArgs[])3730 template <> otError Interpreter::Process<Cmd("extpanid")>(Arg aArgs[])
3731 {
3732     otError error = OT_ERROR_NONE;
3733 
3734     /**
3735      * @cli extpanid
3736      * @code
3737      * extpanid
3738      * dead00beef00cafe
3739      * Done
3740      * @endcode
3741      * @par api_copy
3742      * #otThreadGetExtendedPanId
3743      */
3744     if (aArgs[0].IsEmpty())
3745     {
3746         OutputBytesLine(otThreadGetExtendedPanId(GetInstancePtr())->m8);
3747     }
3748     /**
3749      * @cli extpanid (set)
3750      * @code
3751      * extpanid dead00beef00cafe
3752      * Done
3753      * @endcode
3754      * @cparam extpanid @ca{extpanid}
3755      * @par
3756      * @note The current commissioning credential becomes stale after changing this value.
3757      * Use `pskc` to reset.
3758      * @par api_copy
3759      * #otThreadSetExtendedPanId
3760      */
3761     else
3762     {
3763         otExtendedPanId extPanId;
3764 
3765         SuccessOrExit(error = aArgs[0].ParseAsHexString(extPanId.m8));
3766         error = otThreadSetExtendedPanId(GetInstancePtr(), &extPanId);
3767     }
3768 
3769 exit:
3770     return error;
3771 }
3772 
3773 /**
3774  * @cli factoryreset
3775  * @code
3776  * factoryreset
3777  * @endcode
3778  * @par api_copy
3779  * #otInstanceFactoryReset
3780  */
Process(Arg aArgs[])3781 template <> otError Interpreter::Process<Cmd("factoryreset")>(Arg aArgs[])
3782 {
3783     OT_UNUSED_VARIABLE(aArgs);
3784 
3785     otInstanceFactoryReset(GetInstancePtr());
3786 
3787     return OT_ERROR_NONE;
3788 }
3789 
3790 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Process(Arg aArgs[])3791 template <> otError Interpreter::Process<Cmd("fake")>(Arg aArgs[])
3792 {
3793     otError error = OT_ERROR_INVALID_COMMAND;
3794 
3795     /**
3796      * @cli fake (a,an)
3797      * @code
3798      * fake /a/an fdde:ad00:beef:0:0:ff:fe00:a800 fd00:7d03:7d03:7d03:55f2:bb6a:7a43:a03b 1111222233334444
3799      * Done
3800      * @endcode
3801      * @cparam fake /a/an @ca{dst-ipaddr} @ca{target} @ca{meshLocalIid}
3802      * @par
3803      * Sends fake Thread messages.
3804      * @par
3805      * Available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
3806      * @sa otThreadSendAddressNotification
3807      */
3808     if (aArgs[0] == "/a/an")
3809     {
3810         otIp6Address             destination, target;
3811         otIp6InterfaceIdentifier mlIid;
3812 
3813         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(destination));
3814         SuccessOrExit(error = aArgs[2].ParseAsIp6Address(target));
3815         SuccessOrExit(error = aArgs[3].ParseAsHexString(mlIid.mFields.m8));
3816         otThreadSendAddressNotification(GetInstancePtr(), &destination, &target, &mlIid);
3817     }
3818 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
3819     else if (aArgs[0] == "/b/ba")
3820     {
3821         otIp6Address             target;
3822         otIp6InterfaceIdentifier mlIid;
3823         uint32_t                 timeSinceLastTransaction;
3824 
3825         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(target));
3826         SuccessOrExit(error = aArgs[2].ParseAsHexString(mlIid.mFields.m8));
3827         SuccessOrExit(error = aArgs[3].ParseAsUint32(timeSinceLastTransaction));
3828 
3829         error = otThreadSendProactiveBackboneNotification(GetInstancePtr(), &target, &mlIid, timeSinceLastTransaction);
3830     }
3831 #endif
3832 
3833 exit:
3834     return error;
3835 }
3836 #endif
3837 
Process(Arg aArgs[])3838 template <> otError Interpreter::Process<Cmd("fem")>(Arg aArgs[])
3839 {
3840     otError error = OT_ERROR_NONE;
3841 
3842     /**
3843      * @cli fem
3844      * @code
3845      * fem
3846      * LNA gain 11 dBm
3847      * Done
3848      * @endcode
3849      * @par
3850      * Gets external FEM parameters.
3851      * @sa otPlatRadioGetFemLnaGain
3852      */
3853     if (aArgs[0].IsEmpty())
3854     {
3855         int8_t lnaGain;
3856 
3857         SuccessOrExit(error = otPlatRadioGetFemLnaGain(GetInstancePtr(), &lnaGain));
3858         OutputLine("LNA gain %d dBm", lnaGain);
3859     }
3860     /**
3861      * @cli fem lnagain (get)
3862      * @code
3863      * fem lnagain
3864      * 11
3865      * Done
3866      * @endcode
3867      * @par api_copy
3868      * #otPlatRadioGetFemLnaGain
3869      */
3870     else if (aArgs[0] == "lnagain")
3871     {
3872         if (aArgs[1].IsEmpty())
3873         {
3874             int8_t lnaGain;
3875 
3876             SuccessOrExit(error = otPlatRadioGetFemLnaGain(GetInstancePtr(), &lnaGain));
3877             OutputLine("%d", lnaGain);
3878         }
3879         /**
3880          * @cli fem lnagain (set)
3881          * @code
3882          * fem lnagain 8
3883          * Done
3884          * @endcode
3885          * @par api_copy
3886          * #otPlatRadioSetFemLnaGain
3887          */
3888         else
3889         {
3890             int8_t lnaGain;
3891 
3892             SuccessOrExit(error = aArgs[1].ParseAsInt8(lnaGain));
3893             SuccessOrExit(error = otPlatRadioSetFemLnaGain(GetInstancePtr(), lnaGain));
3894         }
3895     }
3896     else
3897     {
3898         error = OT_ERROR_INVALID_ARGS;
3899     }
3900 
3901 exit:
3902     return error;
3903 }
3904 
Process(Arg aArgs[])3905 template <> otError Interpreter::Process<Cmd("ifconfig")>(Arg aArgs[])
3906 {
3907     otError error = OT_ERROR_NONE;
3908 
3909     /**
3910      * @cli ifconfig
3911      * @code
3912      * ifconfig
3913      * down
3914      * Done
3915      * @endcode
3916      * @code
3917      * ifconfig
3918      * up
3919      * Done
3920      * @endcode
3921      * @par api_copy
3922      * #otIp6IsEnabled
3923      */
3924     if (aArgs[0].IsEmpty())
3925     {
3926         if (otIp6IsEnabled(GetInstancePtr()))
3927         {
3928             OutputLine("up");
3929         }
3930         else
3931         {
3932             OutputLine("down");
3933         }
3934     }
3935     /**
3936      * @cli ifconfig (up,down)
3937      * @code
3938      * ifconfig up
3939      * Done
3940      * @endcode
3941      * @code
3942      * ifconfig down
3943      * Done
3944      * @endcode
3945      * @cparam ifconfig @ca{up|down}
3946      * @par api_copy
3947      * #otIp6SetEnabled
3948      */
3949     else if (aArgs[0] == "up")
3950     {
3951         SuccessOrExit(error = otIp6SetEnabled(GetInstancePtr(), true));
3952     }
3953     else if (aArgs[0] == "down")
3954     {
3955         SuccessOrExit(error = otIp6SetEnabled(GetInstancePtr(), false));
3956     }
3957     else
3958     {
3959         ExitNow(error = OT_ERROR_INVALID_ARGS);
3960     }
3961 
3962 exit:
3963     return error;
3964 }
3965 
AddressOriginToString(uint8_t aOrigin)3966 const char *Interpreter::AddressOriginToString(uint8_t aOrigin)
3967 {
3968     static const char *const kOriginStrings[4] = {
3969         "thread", // 0, OT_ADDRESS_ORIGIN_THREAD
3970         "slaac",  // 1, OT_ADDRESS_ORIGIN_SLAAC
3971         "dhcp6",  // 2, OT_ADDRESS_ORIGIN_DHCPV6
3972         "manual", // 3, OT_ADDRESS_ORIGIN_MANUAL
3973     };
3974 
3975     static_assert(0 == OT_ADDRESS_ORIGIN_THREAD, "OT_ADDRESS_ORIGIN_THREAD value is incorrect");
3976     static_assert(1 == OT_ADDRESS_ORIGIN_SLAAC, "OT_ADDRESS_ORIGIN_SLAAC value is incorrect");
3977     static_assert(2 == OT_ADDRESS_ORIGIN_DHCPV6, "OT_ADDRESS_ORIGIN_DHCPV6 value is incorrect");
3978     static_assert(3 == OT_ADDRESS_ORIGIN_MANUAL, "OT_ADDRESS_ORIGIN_MANUAL value is incorrect");
3979 
3980     return Stringify(aOrigin, kOriginStrings);
3981 }
3982 
Process(Arg aArgs[])3983 template <> otError Interpreter::Process<Cmd("ipaddr")>(Arg aArgs[])
3984 {
3985     otError error   = OT_ERROR_NONE;
3986     bool    verbose = false;
3987 
3988     if (aArgs[0] == "-v")
3989     {
3990         aArgs++;
3991         verbose = true;
3992     }
3993 
3994     /**
3995      * @cli ipaddr
3996      * @code
3997      * ipaddr
3998      * fdde:ad00:beef:0:0:ff:fe00:0
3999      * fdde:ad00:beef:0:558:f56b:d688:799
4000      * fe80:0:0:0:f3d9:2a82:c8d8:fe43
4001      * Done
4002      * @endcode
4003      * @code
4004      * ipaddr -v
4005      * fdde:ad00:beef:0:0:ff:fe00:0 origin:thread
4006      * fdde:ad00:beef:0:558:f56b:d688:799 origin:thread
4007      * fe80:0:0:0:f3d9:2a82:c8d8:fe43 origin:thread
4008      * Done
4009      * @endcode
4010      * @cparam ipaddr [@ca{-v}]
4011      * Use `-v` to get verbose IP Address information.
4012      * @par api_copy
4013      * #otIp6GetUnicastAddresses
4014      */
4015     if (aArgs[0].IsEmpty())
4016     {
4017         const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(GetInstancePtr());
4018 
4019         for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
4020         {
4021             OutputIp6Address(addr->mAddress);
4022 
4023             if (verbose)
4024             {
4025                 OutputFormat(" origin:%s", AddressOriginToString(addr->mAddressOrigin));
4026             }
4027 
4028             OutputNewLine();
4029         }
4030     }
4031     /**
4032      * @cli ipaddr add
4033      * @code
4034      * ipaddr add 2001::dead:beef:cafe
4035      * Done
4036      * @endcode
4037      * @cparam ipaddr add @ca{aAddress}
4038      * @par api_copy
4039      * #otIp6AddUnicastAddress
4040      */
4041     else if (aArgs[0] == "add")
4042     {
4043         otNetifAddress address;
4044 
4045         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address.mAddress));
4046         address.mPrefixLength  = 64;
4047         address.mPreferred     = true;
4048         address.mValid         = true;
4049         address.mAddressOrigin = OT_ADDRESS_ORIGIN_MANUAL;
4050 
4051         error = otIp6AddUnicastAddress(GetInstancePtr(), &address);
4052     }
4053     /**
4054      * @cli ipaddr del
4055      * @code
4056      * ipaddr del 2001::dead:beef:cafe
4057      * Done
4058      * @endcode
4059      * @cparam ipaddr del @ca{aAddress}
4060      * @par api_copy
4061      * #otIp6RemoveUnicastAddress
4062      */
4063     else if (aArgs[0] == "del")
4064     {
4065         otIp6Address address;
4066 
4067         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
4068         error = otIp6RemoveUnicastAddress(GetInstancePtr(), &address);
4069     }
4070     /**
4071      * @cli ipaddr linklocal
4072      * @code
4073      * ipaddr linklocal
4074      * fe80:0:0:0:f3d9:2a82:c8d8:fe43
4075      * Done
4076      * @endcode
4077      * @par api_copy
4078      * #otThreadGetLinkLocalIp6Address
4079      */
4080     else if (aArgs[0] == "linklocal")
4081     {
4082         OutputIp6AddressLine(*otThreadGetLinkLocalIp6Address(GetInstancePtr()));
4083     }
4084     /**
4085      * @cli ipaddr rloc
4086      * @code
4087      * ipaddr rloc
4088      * fdde:ad00:beef:0:0:ff:fe00:0
4089      * Done
4090      * @endcode
4091      * @par api_copy
4092      * #otThreadGetRloc
4093      */
4094     else if (aArgs[0] == "rloc")
4095     {
4096         OutputIp6AddressLine(*otThreadGetRloc(GetInstancePtr()));
4097     }
4098     /**
4099      * @cli ipaddr mleid
4100      * @code
4101      * ipaddr mleid
4102      * fdde:ad00:beef:0:558:f56b:d688:799
4103      * Done
4104      * @endcode
4105      * @par api_copy
4106      * #otThreadGetMeshLocalEid
4107      */
4108     else if (aArgs[0] == "mleid")
4109     {
4110         OutputIp6AddressLine(*otThreadGetMeshLocalEid(GetInstancePtr()));
4111     }
4112     else
4113     {
4114         error = OT_ERROR_INVALID_COMMAND;
4115     }
4116 
4117 exit:
4118     return error;
4119 }
4120 
Process(Arg aArgs[])4121 template <> otError Interpreter::Process<Cmd("ipmaddr")>(Arg aArgs[])
4122 {
4123     otError error = OT_ERROR_NONE;
4124 
4125     /**
4126      * @cli ipmaddr
4127      * @code
4128      * ipmaddr
4129      * ff05:0:0:0:0:0:0:1
4130      * ff33:40:fdde:ad00:beef:0:0:1
4131      * ff32:40:fdde:ad00:beef:0:0:1
4132      * Done
4133      * @endcode
4134      * @par api_copy
4135      * #otIp6GetMulticastAddresses
4136      */
4137     if (aArgs[0].IsEmpty())
4138     {
4139         for (const otNetifMulticastAddress *addr = otIp6GetMulticastAddresses(GetInstancePtr()); addr;
4140              addr                                = addr->mNext)
4141         {
4142             OutputIp6AddressLine(addr->mAddress);
4143         }
4144     }
4145     /**
4146      * @cli ipmaddr add
4147      * @code
4148      * ipmaddr add ff05::1
4149      * Done
4150      * @endcode
4151      * @cparam ipmaddr add @ca{aAddress}
4152      * @par api_copy
4153      * #otIp6SubscribeMulticastAddress
4154      */
4155     else if (aArgs[0] == "add")
4156     {
4157         otIp6Address address;
4158 
4159         aArgs++;
4160 
4161         do
4162         {
4163             SuccessOrExit(error = aArgs->ParseAsIp6Address(address));
4164             SuccessOrExit(error = otIp6SubscribeMulticastAddress(GetInstancePtr(), &address));
4165         }
4166 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
4167         while (!(++aArgs)->IsEmpty());
4168 #else
4169         while (false);
4170 #endif
4171     }
4172     /**
4173      * @cli ipmaddr del
4174      * @code
4175      * ipmaddr del ff05::1
4176      * Done
4177      * @endcode
4178      * @cparam ipmaddr del @ca{aAddress}
4179      * @par api_copy
4180      * #otIp6UnsubscribeMulticastAddress
4181      */
4182     else if (aArgs[0] == "del")
4183     {
4184         otIp6Address address;
4185 
4186         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
4187         error = otIp6UnsubscribeMulticastAddress(GetInstancePtr(), &address);
4188     }
4189     /**
4190      * @cli ipmaddr promiscuous
4191      * @code
4192      * ipmaddr promiscuous
4193      * Disabled
4194      * Done
4195      * @endcode
4196      * @par api_copy
4197      * #otIp6IsMulticastPromiscuousEnabled
4198      */
4199     else if (aArgs[0] == "promiscuous")
4200     {
4201         if (aArgs[1].IsEmpty())
4202         {
4203             OutputEnabledDisabledStatus(otIp6IsMulticastPromiscuousEnabled(GetInstancePtr()));
4204         }
4205         /**
4206          * @cli ipmaddr promiscuous (enable,disable)
4207          * @code
4208          * ipmaddr promiscuous enable
4209          * Done
4210          * @endcode
4211          * @code
4212          * ipmaddr promiscuous disable
4213          * Done
4214          * @endcode
4215          * @cparam ipmaddr promiscuous @ca{enable|disable}
4216          * @par api_copy
4217          * #otIp6SetMulticastPromiscuousEnabled
4218          */
4219         else
4220         {
4221             bool enable;
4222 
4223             SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable));
4224             otIp6SetMulticastPromiscuousEnabled(GetInstancePtr(), enable);
4225         }
4226     }
4227     /**
4228      * @cli ipmaddr llatn
4229      * @code
4230      * ipmaddr llatn
4231      * ff32:40:fdde:ad00:beef:0:0:1
4232      * Done
4233      * @endcode
4234      * @par api_copy
4235      * #otThreadGetLinkLocalAllThreadNodesMulticastAddress
4236      */
4237     else if (aArgs[0] == "llatn")
4238     {
4239         OutputIp6AddressLine(*otThreadGetLinkLocalAllThreadNodesMulticastAddress(GetInstancePtr()));
4240     }
4241     /**
4242      * @cli ipmaddr rlatn
4243      * @code
4244      * ipmaddr rlatn
4245      * ff33:40:fdde:ad00:beef:0:0:1
4246      * Done
4247      * @endcode
4248      * @par api_copy
4249      * #otThreadGetRealmLocalAllThreadNodesMulticastAddress
4250      */
4251     else if (aArgs[0] == "rlatn")
4252     {
4253         OutputIp6AddressLine(*otThreadGetRealmLocalAllThreadNodesMulticastAddress(GetInstancePtr()));
4254     }
4255     else
4256     {
4257         error = OT_ERROR_INVALID_COMMAND;
4258     }
4259 
4260 exit:
4261     return error;
4262 }
4263 
Process(Arg aArgs[])4264 template <> otError Interpreter::Process<Cmd("keysequence")>(Arg aArgs[])
4265 {
4266     otError error = OT_ERROR_INVALID_ARGS;
4267 
4268     /**
4269      * @cli keysequence counter
4270      * @code
4271      * keysequence counter
4272      * 10
4273      * Done
4274      * @endcode
4275      * @par api_copy
4276      * #otThreadGetKeySequenceCounter
4277      */
4278     if (aArgs[0] == "counter")
4279     {
4280         /**
4281          * @cli keysequence counter (set)
4282          * @code
4283          * keysequence counter 10
4284          * Done
4285          * @endcode
4286          * @cparam keysequence counter @ca{counter}
4287          * @par api_copy
4288          * #otThreadSetKeySequenceCounter
4289          */
4290         error = ProcessGetSet(aArgs + 1, otThreadGetKeySequenceCounter, otThreadSetKeySequenceCounter);
4291     }
4292     /**
4293      * @cli keysequence guardtime
4294      * @code
4295      * keysequence guardtime
4296      * 0
4297      * Done
4298      * @endcode
4299      * @par api_copy
4300      * #otThreadGetKeySwitchGuardTime
4301      */
4302     else if (aArgs[0] == "guardtime")
4303     {
4304         /**
4305          * @cli keysequence guardtime (set)
4306          * @code
4307          * keysequence guardtime 0
4308          * Done
4309          * @endcode
4310          * @cparam keysequence guardtime @ca{guardtime-hours}
4311          * Use `0` to `Thread Key Switch` immediately if there's a key index match.
4312          * @par api_copy
4313          * #otThreadSetKeySwitchGuardTime
4314          */
4315         error = ProcessGetSet(aArgs + 1, otThreadGetKeySwitchGuardTime, otThreadSetKeySwitchGuardTime);
4316     }
4317 
4318     return error;
4319 }
4320 
4321 /**
4322  * @cli leaderdata
4323  * @code
4324  * leaderdata
4325  * Partition ID: 1077744240
4326  * Weighting: 64
4327  * Data Version: 109
4328  * Stable Data Version: 211
4329  * Leader Router ID: 60
4330  * Done
4331  * @endcode
4332  * @par
4333  * Gets the Thread Leader Data.
4334  * @sa otThreadGetLeaderData
4335  */
Process(Arg aArgs[])4336 template <> otError Interpreter::Process<Cmd("leaderdata")>(Arg aArgs[])
4337 {
4338     OT_UNUSED_VARIABLE(aArgs);
4339 
4340     otError      error;
4341     otLeaderData leaderData;
4342 
4343     SuccessOrExit(error = otThreadGetLeaderData(GetInstancePtr(), &leaderData));
4344 
4345     OutputLine("Partition ID: %lu", ToUlong(leaderData.mPartitionId));
4346     OutputLine("Weighting: %u", leaderData.mWeighting);
4347     OutputLine("Data Version: %u", leaderData.mDataVersion);
4348     OutputLine("Stable Data Version: %u", leaderData.mStableDataVersion);
4349     OutputLine("Leader Router ID: %u", leaderData.mLeaderRouterId);
4350 
4351 exit:
4352     return error;
4353 }
4354 
4355 #if OPENTHREAD_FTD
Process(Arg aArgs[])4356 template <> otError Interpreter::Process<Cmd("partitionid")>(Arg aArgs[])
4357 {
4358     otError error = OT_ERROR_INVALID_COMMAND;
4359 
4360     /**
4361      * @cli partitionid
4362      * @code
4363      * partitionid
4364      * 4294967295
4365      * Done
4366      * @endcode
4367      * @par
4368      * Get the Thread Network Partition ID.
4369      * @sa otThreadGetPartitionId
4370      */
4371     if (aArgs[0].IsEmpty())
4372     {
4373         OutputLine("%lu", ToUlong(otThreadGetPartitionId(GetInstancePtr())));
4374         error = OT_ERROR_NONE;
4375     }
4376 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
4377     /**
4378      * @cli partitionid preferred (get,set)
4379      * @code
4380      * partitionid preferred
4381      * 4294967295
4382      * Done
4383      * @endcode
4384      * @code
4385      * partitionid preferred 0xffffffff
4386      * Done
4387      * @endcode
4388      * @cparam partitionid preferred @ca{partitionid}
4389      * @sa otThreadGetPreferredLeaderPartitionId
4390      * @sa otThreadSetPreferredLeaderPartitionId
4391      * @par
4392      * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is required.
4393      */
4394     else if (aArgs[0] == "preferred")
4395     {
4396         error = ProcessGetSet(aArgs + 1, otThreadGetPreferredLeaderPartitionId, otThreadSetPreferredLeaderPartitionId);
4397     }
4398 #endif
4399 
4400     return error;
4401 }
4402 
4403 /**
4404  * @cli leaderweight
4405  * @code
4406  * leaderweight
4407  * 128
4408  * Done
4409  * @endcode
4410  * @par api_copy
4411  * #otThreadGetLocalLeaderWeight
4412  */
Process(Arg aArgs[])4413 template <> otError Interpreter::Process<Cmd("leaderweight")>(Arg aArgs[])
4414 {
4415     /**
4416      * @cli leaderweight (set)
4417      * @code
4418      * leaderweight 128
4419      * Done
4420      * @endcode
4421      * @cparam leaderweight @ca{weight}
4422      * @par api_copy
4423      * #otThreadSetLocalLeaderWeight
4424      */
4425     return ProcessGetSet(aArgs, otThreadGetLocalLeaderWeight, otThreadSetLocalLeaderWeight);
4426 }
4427 
Process(Arg aArgs[])4428 template <> otError Interpreter::Process<Cmd("deviceprops")>(Arg aArgs[])
4429 {
4430     static const char *const kPowerSupplyStrings[4] = {
4431         "battery",           // (0) OT_POWER_SUPPLY_BATTERY
4432         "external",          // (1) OT_POWER_SUPPLY_EXTERNAL
4433         "external-stable",   // (2) OT_POWER_SUPPLY_EXTERNAL_STABLE
4434         "external-unstable", // (3) OT_POWER_SUPPLY_EXTERNAL_UNSTABLE
4435     };
4436 
4437     static_assert(0 == OT_POWER_SUPPLY_BATTERY, "OT_POWER_SUPPLY_BATTERY value is incorrect");
4438     static_assert(1 == OT_POWER_SUPPLY_EXTERNAL, "OT_POWER_SUPPLY_EXTERNAL value is incorrect");
4439     static_assert(2 == OT_POWER_SUPPLY_EXTERNAL_STABLE, "OT_POWER_SUPPLY_EXTERNAL_STABLE value is incorrect");
4440     static_assert(3 == OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, "OT_POWER_SUPPLY_EXTERNAL_UNSTABLE value is incorrect");
4441 
4442     otError error = OT_ERROR_NONE;
4443 
4444     /**
4445      * @cli deviceprops
4446      * @code
4447      * deviceprops
4448      * PowerSupply      : external
4449      * IsBorderRouter   : yes
4450      * SupportsCcm      : no
4451      * IsUnstable       : no
4452      * WeightAdjustment : 0
4453      * Done
4454      * @endcode
4455      * @par api_copy
4456      * #otThreadGetDeviceProperties
4457      */
4458     if (aArgs[0].IsEmpty())
4459     {
4460         const otDeviceProperties *props = otThreadGetDeviceProperties(GetInstancePtr());
4461 
4462         OutputLine("PowerSupply      : %s", Stringify(props->mPowerSupply, kPowerSupplyStrings));
4463         OutputLine("IsBorderRouter   : %s", props->mIsBorderRouter ? "yes" : "no");
4464         OutputLine("SupportsCcm      : %s", props->mSupportsCcm ? "yes" : "no");
4465         OutputLine("IsUnstable       : %s", props->mIsUnstable ? "yes" : "no");
4466         OutputLine("WeightAdjustment : %d", props->mLeaderWeightAdjustment);
4467     }
4468     /**
4469      * @cli deviceprops (set)
4470      * @code
4471      * deviceprops battery 0 0 0 -5
4472      * Done
4473      * @endcode
4474      * @code
4475      * deviceprops
4476      * PowerSupply      : battery
4477      * IsBorderRouter   : no
4478      * SupportsCcm      : no
4479      * IsUnstable       : no
4480      * WeightAdjustment : -5
4481      * Done
4482      * @endcode
4483      * @cparam deviceprops @ca{powerSupply} @ca{isBr} @ca{supportsCcm} @ca{isUnstable} @ca{weightAdjustment}
4484      * `powerSupply`: should be 'battery', 'external', 'external-stable', 'external-unstable'.
4485      * @par
4486      * Sets the device properties.
4487      * @csa{leaderweight}
4488      * @csa{leaderweight (set)}
4489      * @sa #otThreadSetDeviceProperties
4490      */
4491     else
4492     {
4493         otDeviceProperties props;
4494         bool               value;
4495         uint8_t            index;
4496 
4497         for (index = 0; index < OT_ARRAY_LENGTH(kPowerSupplyStrings); index++)
4498         {
4499             if (aArgs[0] == kPowerSupplyStrings[index])
4500             {
4501                 props.mPowerSupply = static_cast<otPowerSupply>(index);
4502                 break;
4503             }
4504         }
4505 
4506         VerifyOrExit(index < OT_ARRAY_LENGTH(kPowerSupplyStrings), error = OT_ERROR_INVALID_ARGS);
4507 
4508         SuccessOrExit(error = aArgs[1].ParseAsBool(value));
4509         props.mIsBorderRouter = value;
4510 
4511         SuccessOrExit(error = aArgs[2].ParseAsBool(value));
4512         props.mSupportsCcm = value;
4513 
4514         SuccessOrExit(error = aArgs[3].ParseAsBool(value));
4515         props.mIsUnstable = value;
4516 
4517         SuccessOrExit(error = aArgs[4].ParseAsInt8(props.mLeaderWeightAdjustment));
4518 
4519         VerifyOrExit(aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
4520         otThreadSetDeviceProperties(GetInstancePtr(), &props);
4521     }
4522 
4523 exit:
4524     return error;
4525 }
4526 
4527 #endif // OPENTHREAD_FTD
4528 
4529 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
HandleLinkMetricsReport(const otIp6Address * aAddress,const otLinkMetricsValues * aMetricsValues,uint8_t aStatus,void * aContext)4530 void Interpreter::HandleLinkMetricsReport(const otIp6Address        *aAddress,
4531                                           const otLinkMetricsValues *aMetricsValues,
4532                                           uint8_t                    aStatus,
4533                                           void                      *aContext)
4534 {
4535     static_cast<Interpreter *>(aContext)->HandleLinkMetricsReport(aAddress, aMetricsValues, aStatus);
4536 }
4537 
PrintLinkMetricsValue(const otLinkMetricsValues * aMetricsValues)4538 void Interpreter::PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValues)
4539 {
4540     static const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)";
4541 
4542     if (aMetricsValues->mMetrics.mPduCount)
4543     {
4544         OutputLine(" - PDU Counter: %lu (Count/Summation)", ToUlong(aMetricsValues->mPduCountValue));
4545     }
4546 
4547     if (aMetricsValues->mMetrics.mLqi)
4548     {
4549         OutputLine(" - LQI: %u %s", aMetricsValues->mLqiValue, kLinkMetricsTypeAverage);
4550     }
4551 
4552     if (aMetricsValues->mMetrics.mLinkMargin)
4553     {
4554         OutputLine(" - Margin: %u (dB) %s", aMetricsValues->mLinkMarginValue, kLinkMetricsTypeAverage);
4555     }
4556 
4557     if (aMetricsValues->mMetrics.mRssi)
4558     {
4559         OutputLine(" - RSSI: %d (dBm) %s", aMetricsValues->mRssiValue, kLinkMetricsTypeAverage);
4560     }
4561 }
4562 
HandleLinkMetricsReport(const otIp6Address * aAddress,const otLinkMetricsValues * aMetricsValues,uint8_t aStatus)4563 void Interpreter::HandleLinkMetricsReport(const otIp6Address        *aAddress,
4564                                           const otLinkMetricsValues *aMetricsValues,
4565                                           uint8_t                    aStatus)
4566 {
4567     OutputFormat("Received Link Metrics Report from: ");
4568     OutputIp6AddressLine(*aAddress);
4569 
4570     if (aMetricsValues != nullptr)
4571     {
4572         PrintLinkMetricsValue(aMetricsValues);
4573     }
4574     else
4575     {
4576         OutputLine("Link Metrics Report, status: %s", LinkMetricsStatusToStr(aStatus));
4577     }
4578 
4579     if (mLinkMetricsQueryInProgress)
4580     {
4581         mLinkMetricsQueryInProgress = false;
4582         OutputResult(OT_ERROR_NONE);
4583     }
4584 }
4585 
HandleLinkMetricsMgmtResponse(const otIp6Address * aAddress,uint8_t aStatus,void * aContext)4586 void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus, void *aContext)
4587 {
4588     static_cast<Interpreter *>(aContext)->HandleLinkMetricsMgmtResponse(aAddress, aStatus);
4589 }
4590 
HandleLinkMetricsMgmtResponse(const otIp6Address * aAddress,uint8_t aStatus)4591 void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus)
4592 {
4593     OutputFormat("Received Link Metrics Management Response from: ");
4594     OutputIp6AddressLine(*aAddress);
4595 
4596     OutputLine("Status: %s", LinkMetricsStatusToStr(aStatus));
4597 }
4598 
HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,const otExtAddress * aExtAddress,const otLinkMetricsValues * aMetricsValues,void * aContext)4599 void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress             aShortAddress,
4600                                                    const otExtAddress        *aExtAddress,
4601                                                    const otLinkMetricsValues *aMetricsValues,
4602                                                    void                      *aContext)
4603 {
4604     static_cast<Interpreter *>(aContext)->HandleLinkMetricsEnhAckProbingIe(aShortAddress, aExtAddress, aMetricsValues);
4605 }
4606 
HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,const otExtAddress * aExtAddress,const otLinkMetricsValues * aMetricsValues)4607 void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress             aShortAddress,
4608                                                    const otExtAddress        *aExtAddress,
4609                                                    const otLinkMetricsValues *aMetricsValues)
4610 {
4611     OutputFormat("Received Link Metrics data in Enh Ack from neighbor, short address:0x%02x , extended address:",
4612                  aShortAddress);
4613     OutputExtAddressLine(*aExtAddress);
4614 
4615     if (aMetricsValues != nullptr)
4616     {
4617         PrintLinkMetricsValue(aMetricsValues);
4618     }
4619 }
4620 
LinkMetricsStatusToStr(uint8_t aStatus)4621 const char *Interpreter::LinkMetricsStatusToStr(uint8_t aStatus)
4622 {
4623     static const char *const kStatusStrings[] = {
4624         "Success",                      // (0) OT_LINK_METRICS_STATUS_SUCCESS
4625         "Cannot support new series",    // (1) OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES
4626         "Series ID already registered", // (2) OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED
4627         "Series ID not recognized",     // (3) OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED
4628         "No matching series ID",        // (4) OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED
4629     };
4630 
4631     const char *str = "Unknown error";
4632 
4633     static_assert(0 == OT_LINK_METRICS_STATUS_SUCCESS, "STATUS_SUCCESS is incorrect");
4634     static_assert(1 == OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES, "CANNOT_SUPPORT_NEW_SERIES is incorrect");
4635     static_assert(2 == OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED, "SERIESID_ALREADY_REGISTERED is incorrect");
4636     static_assert(3 == OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED, "SERIESID_NOT_RECOGNIZED is incorrect");
4637     static_assert(4 == OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED, "NO_MATCHING_FRAMES_RECEIVED is incorrect");
4638 
4639     if (aStatus < OT_ARRAY_LENGTH(kStatusStrings))
4640     {
4641         str = kStatusStrings[aStatus];
4642     }
4643     else if (aStatus == OT_LINK_METRICS_STATUS_OTHER_ERROR)
4644     {
4645         str = "Other error";
4646     }
4647 
4648     return str;
4649 }
4650 
Process(Arg aArgs[])4651 template <> otError Interpreter::Process<Cmd("linkmetrics")>(Arg aArgs[])
4652 {
4653     otError error = OT_ERROR_INVALID_COMMAND;
4654 
4655     if (aArgs[0] == "query")
4656     {
4657         otIp6Address  address;
4658         bool          isSingle;
4659         bool          blocking;
4660         uint8_t       seriesId;
4661         otLinkMetrics linkMetrics;
4662 
4663         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
4664 
4665         /**
4666          * @cli linkmetrics query single
4667          * @code
4668          * linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 single qmr
4669          * Done
4670          * > Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2
4671          * - LQI: 76 (Exponential Moving Average)
4672          * - Margin: 82 (dB) (Exponential Moving Average)
4673          * - RSSI: -18 (dBm) (Exponential Moving Average)
4674          * @endcode
4675          * @cparam linkmetrics query @ca{peer-ipaddr} single [@ca{pqmr}]
4676          * - `peer-ipaddr`: Peer address.
4677          * - [`p`, `q`, `m`, and `r`] map to #otLinkMetrics.
4678          *   - `p`: Layer 2 Number of PDUs received.
4679          *   - `q`: Layer 2 LQI.
4680          *   - `m`: Link Margin.
4681          *   - `r`: RSSI.
4682          * @par
4683          * Perform a Link Metrics query (Single Probe).
4684          * @sa otLinkMetricsQuery
4685          */
4686         if (aArgs[2] == "single")
4687         {
4688             isSingle = true;
4689             SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
4690         }
4691         /**
4692          * @cli linkmetrics query forward
4693          * @code
4694          * linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 forward 1
4695          * Done
4696          * > Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2
4697          * - PDU Counter: 2 (Count/Summation)
4698          * - LQI: 76 (Exponential Moving Average)
4699          * - Margin: 82 (dB) (Exponential Moving Average)
4700          * - RSSI: -18 (dBm) (Exponential Moving Average)
4701          * @endcode
4702          * @cparam linkmetrics query @ca{peer-ipaddr} forward @ca{series-id}
4703          * - `peer-ipaddr`: Peer address.
4704          * - `series-id`: The Series ID.
4705          * @par
4706          * Perform a Link Metrics query (Forward Tracking Series).
4707          * @sa otLinkMetricsQuery
4708          */
4709         else if (aArgs[2] == "forward")
4710         {
4711             isSingle = false;
4712             SuccessOrExit(error = aArgs[3].ParseAsUint8(seriesId));
4713         }
4714         else
4715         {
4716             ExitNow(error = OT_ERROR_INVALID_ARGS);
4717         }
4718 
4719         blocking = (aArgs[4] == "block");
4720 
4721         SuccessOrExit(error = otLinkMetricsQuery(GetInstancePtr(), &address, isSingle ? 0 : seriesId,
4722                                                  isSingle ? &linkMetrics : nullptr, HandleLinkMetricsReport, this));
4723 
4724         if (blocking)
4725         {
4726             mLinkMetricsQueryInProgress = true;
4727             error                       = OT_ERROR_PENDING;
4728         }
4729     }
4730     else if (aArgs[0] == "mgmt")
4731     {
4732         error = ProcessLinkMetricsMgmt(aArgs + 1);
4733     }
4734     /**
4735      * @cli linkmetrics probe
4736      * @code
4737      * linkmetrics probe fe80:0:0:0:3092:f334:1455:1ad2 1 10
4738      * Done
4739      * @endcode
4740      * @cparam linkmetrics probe @ca{peer-ipaddr} @ca{series-id} @ca{length}
4741      * - `peer-ipaddr`: Peer address.
4742      * - `series-id`: The Series ID for which this Probe message targets.
4743      * - `length`: The length of the Probe message. A valid range is [0, 64].
4744      * @par api_copy
4745      * #otLinkMetricsSendLinkProbe
4746      */
4747     else if (aArgs[0] == "probe")
4748     {
4749         otIp6Address address;
4750         uint8_t      seriesId;
4751         uint8_t      length;
4752 
4753         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
4754         SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
4755         SuccessOrExit(error = aArgs[3].ParseAsUint8(length));
4756 
4757         error = otLinkMetricsSendLinkProbe(GetInstancePtr(), &address, seriesId, length);
4758     }
4759 
4760 exit:
4761     return error;
4762 }
4763 
ParseLinkMetricsFlags(otLinkMetrics & aLinkMetrics,const Arg & aFlags)4764 otError Interpreter::ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags)
4765 {
4766     otError error = OT_ERROR_NONE;
4767 
4768     VerifyOrExit(!aFlags.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
4769 
4770     memset(&aLinkMetrics, 0, sizeof(aLinkMetrics));
4771 
4772     for (const char *arg = aFlags.GetCString(); *arg != '\0'; arg++)
4773     {
4774         switch (*arg)
4775         {
4776         case 'p':
4777             aLinkMetrics.mPduCount = true;
4778             break;
4779 
4780         case 'q':
4781             aLinkMetrics.mLqi = true;
4782             break;
4783 
4784         case 'm':
4785             aLinkMetrics.mLinkMargin = true;
4786             break;
4787 
4788         case 'r':
4789             aLinkMetrics.mRssi = true;
4790             break;
4791 
4792         default:
4793             ExitNow(error = OT_ERROR_INVALID_ARGS);
4794         }
4795     }
4796 
4797 exit:
4798     return error;
4799 }
4800 
ProcessLinkMetricsMgmt(Arg aArgs[])4801 otError Interpreter::ProcessLinkMetricsMgmt(Arg aArgs[])
4802 {
4803     otError                  error;
4804     otIp6Address             address;
4805     otLinkMetricsSeriesFlags seriesFlags;
4806     bool                     clear = false;
4807 
4808     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
4809 
4810     memset(&seriesFlags, 0, sizeof(otLinkMetricsSeriesFlags));
4811 
4812     /**
4813      * @cli linkmetrics mgmt forward
4814      * @code
4815      * linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 forward 1 dra pqmr
4816      * Done
4817      * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
4818      * Status: SUCCESS
4819      * @endcode
4820      * @cparam linkmetrics mgmt @ca{peer-ipaddr} forward @ca{series-id} [@ca{ldraX}][@ca{pqmr}]
4821      * - `peer-ipaddr`: Peer address.
4822      * - `series-id`: The Series ID.
4823      * - [`l`, `d`, `r`, and `a`] map to #otLinkMetricsSeriesFlags. `X` represents none of the
4824      *   `otLinkMetricsSeriesFlags`, and stops the accounting and removes the series.
4825      *   - `l`: MLE Link Probe.
4826      *   - `d`: MAC Data.
4827      *   - `r`: MAC Data Request.
4828      *   - `a`: MAC Ack.
4829      *   - `X`: Can only be used without any other flags.
4830      * - [`p`, `q`, `m`, and `r`] map to #otLinkMetricsValues.
4831      *   - `p`: Layer 2 Number of PDUs received.
4832      *   - `q`: Layer 2 LQI.
4833      *   - `m`: Link Margin.
4834      *   - `r`: RSSI.
4835      * @par api_copy
4836      * #otLinkMetricsConfigForwardTrackingSeries
4837      */
4838     if (aArgs[1] == "forward")
4839     {
4840         uint8_t       seriesId;
4841         otLinkMetrics linkMetrics;
4842 
4843         memset(&linkMetrics, 0, sizeof(otLinkMetrics));
4844         SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
4845         VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
4846 
4847         for (const char *arg = aArgs[3].GetCString(); *arg != '\0'; arg++)
4848         {
4849             switch (*arg)
4850             {
4851             case 'l':
4852                 seriesFlags.mLinkProbe = true;
4853                 break;
4854 
4855             case 'd':
4856                 seriesFlags.mMacData = true;
4857                 break;
4858 
4859             case 'r':
4860                 seriesFlags.mMacDataRequest = true;
4861                 break;
4862 
4863             case 'a':
4864                 seriesFlags.mMacAck = true;
4865                 break;
4866 
4867             case 'X':
4868                 VerifyOrExit(arg == aArgs[3].GetCString() && *(arg + 1) == '\0' && aArgs[4].IsEmpty(),
4869                              error = OT_ERROR_INVALID_ARGS); // Ensure the flags only contain 'X'
4870                 clear = true;
4871                 break;
4872 
4873             default:
4874                 ExitNow(error = OT_ERROR_INVALID_ARGS);
4875             }
4876         }
4877 
4878         if (!clear)
4879         {
4880             SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[4]));
4881             VerifyOrExit(aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
4882         }
4883 
4884         error = otLinkMetricsConfigForwardTrackingSeries(GetInstancePtr(), &address, seriesId, seriesFlags,
4885                                                          clear ? nullptr : &linkMetrics,
4886                                                          &Interpreter::HandleLinkMetricsMgmtResponse, this);
4887     }
4888     else if (aArgs[1] == "enhanced-ack")
4889     {
4890         otLinkMetricsEnhAckFlags enhAckFlags;
4891         otLinkMetrics            linkMetrics;
4892         otLinkMetrics           *pLinkMetrics = &linkMetrics;
4893 
4894         /**
4895          * @cli linkmetrics mgmt enhanced-ack clear
4896          * @code
4897          * linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack clear
4898          * Done
4899          * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
4900          * Status: Success
4901          * @endcode
4902          * @cparam linkmetrics mgmt @ca{peer-ipaddr} enhanced-ack clear
4903          * `peer-ipaddr` should be the Link Local address of the neighboring device.
4904          * @par
4905          * Sends a Link Metrics Management Request to clear an Enhanced-ACK Based Probing.
4906          * @sa otLinkMetricsConfigEnhAckProbing
4907          */
4908         if (aArgs[2] == "clear")
4909         {
4910             enhAckFlags  = OT_LINK_METRICS_ENH_ACK_CLEAR;
4911             pLinkMetrics = nullptr;
4912         }
4913         /**
4914          * @cli linkmetrics mgmt enhanced-ack register
4915          * @code
4916          * linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm
4917          * Done
4918          * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
4919          * Status: Success
4920          * @endcode
4921          * @code
4922          * > linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm r
4923          * Done
4924          * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
4925          * Status: Cannot support new series
4926          * @endcode
4927          * @cparam linkmetrics mgmt @ca{peer-ipaddr} enhanced-ack register [@ca{qmr}][@ca{r}]
4928          * [`q`, `m`, and `r`] map to #otLinkMetricsValues. Per spec 4.11.3.4.4.6, you can
4929          * only use a maximum of two options at once, for example `q`, or `qm`.
4930          * - `q`: Layer 2 LQI.
4931          * - `m`: Link Margin.
4932          * - `r`: RSSI.
4933          * .
4934          * The additional `r` is optional and only used for reference devices. When this option
4935          * is specified, Type/Average Enum of each Type Id Flags is set to reserved. This is
4936          * used to verify that the Probing Subject correctly handles invalid Type Id Flags, and
4937          * only available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
4938          * @par
4939          * Sends a Link Metrics Management Request to register an Enhanced-ACK Based Probing.
4940          * @sa otLinkMetricsConfigEnhAckProbing
4941          */
4942         else if (aArgs[2] == "register")
4943         {
4944             enhAckFlags = OT_LINK_METRICS_ENH_ACK_REGISTER;
4945             SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
4946 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
4947             if (aArgs[4] == "r")
4948             {
4949                 linkMetrics.mReserved = true;
4950             }
4951 #endif
4952         }
4953         else
4954         {
4955             ExitNow(error = OT_ERROR_INVALID_ARGS);
4956         }
4957 
4958         error = otLinkMetricsConfigEnhAckProbing(GetInstancePtr(), &address, enhAckFlags, pLinkMetrics,
4959                                                  &Interpreter::HandleLinkMetricsMgmtResponse, this,
4960                                                  &Interpreter::HandleLinkMetricsEnhAckProbingIe, this);
4961     }
4962     else
4963     {
4964         error = OT_ERROR_INVALID_ARGS;
4965     }
4966 
4967 exit:
4968     return error;
4969 }
4970 
4971 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
4972 
4973 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
4974 
Process(Arg aArgs[])4975 template <> otError Interpreter::Process<Cmd("locate")>(Arg aArgs[])
4976 {
4977     otError      error = OT_ERROR_INVALID_ARGS;
4978     otIp6Address anycastAddress;
4979 
4980     if (aArgs[0].IsEmpty())
4981     {
4982         OutputLine(otThreadIsAnycastLocateInProgress(GetInstancePtr()) ? "In Progress" : "Idle");
4983         ExitNow(error = OT_ERROR_NONE);
4984     }
4985 
4986     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(anycastAddress));
4987     SuccessOrExit(error =
4988                       otThreadLocateAnycastDestination(GetInstancePtr(), &anycastAddress, HandleLocateResult, this));
4989     SetCommandTimeout(kLocateTimeoutMsecs);
4990     mLocateInProgress = true;
4991     error             = OT_ERROR_PENDING;
4992 
4993 exit:
4994     return error;
4995 }
4996 
HandleLocateResult(void * aContext,otError aError,const otIp6Address * aMeshLocalAddress,uint16_t aRloc16)4997 void Interpreter::HandleLocateResult(void               *aContext,
4998                                      otError             aError,
4999                                      const otIp6Address *aMeshLocalAddress,
5000                                      uint16_t            aRloc16)
5001 {
5002     static_cast<Interpreter *>(aContext)->HandleLocateResult(aError, aMeshLocalAddress, aRloc16);
5003 }
5004 
HandleLocateResult(otError aError,const otIp6Address * aMeshLocalAddress,uint16_t aRloc16)5005 void Interpreter::HandleLocateResult(otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16)
5006 {
5007     VerifyOrExit(mLocateInProgress);
5008 
5009     mLocateInProgress = false;
5010 
5011     if (aError == OT_ERROR_NONE)
5012     {
5013         OutputIp6Address(*aMeshLocalAddress);
5014         OutputLine(" 0x%04x", aRloc16);
5015     }
5016 
5017     OutputResult(aError);
5018 
5019 exit:
5020     return;
5021 }
5022 
5023 #endif //  OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
5024 
5025 #if OPENTHREAD_FTD
Process(Arg aArgs[])5026 template <> otError Interpreter::Process<Cmd("pskc")>(Arg aArgs[])
5027 {
5028     otError error = OT_ERROR_NONE;
5029     otPskc  pskc;
5030 
5031     if (aArgs[0].IsEmpty())
5032     {
5033         otThreadGetPskc(GetInstancePtr(), &pskc);
5034         OutputBytesLine(pskc.m8);
5035     }
5036     else
5037     {
5038         if (aArgs[1].IsEmpty())
5039         {
5040             SuccessOrExit(error = aArgs[0].ParseAsHexString(pskc.m8));
5041         }
5042         else if (aArgs[0] == "-p")
5043         {
5044             SuccessOrExit(error = otDatasetGeneratePskc(
5045                               aArgs[1].GetCString(),
5046                               reinterpret_cast<const otNetworkName *>(otThreadGetNetworkName(GetInstancePtr())),
5047                               otThreadGetExtendedPanId(GetInstancePtr()), &pskc));
5048         }
5049         else
5050         {
5051             ExitNow(error = OT_ERROR_INVALID_ARGS);
5052         }
5053 
5054         error = otThreadSetPskc(GetInstancePtr(), &pskc);
5055     }
5056 
5057 exit:
5058     return error;
5059 }
5060 
5061 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
Process(Arg aArgs[])5062 template <> otError Interpreter::Process<Cmd("pskcref")>(Arg aArgs[])
5063 {
5064     otError error = OT_ERROR_NONE;
5065 
5066     if (aArgs[0].IsEmpty())
5067     {
5068         OutputLine("0x%08lx", ToUlong(otThreadGetPskcRef(GetInstancePtr())));
5069     }
5070     else
5071     {
5072         otPskcRef pskcRef;
5073 
5074         if (aArgs[1].IsEmpty())
5075         {
5076             SuccessOrExit(error = aArgs[0].ParseAsUint32(pskcRef));
5077         }
5078         else
5079         {
5080             ExitNow(error = OT_ERROR_INVALID_ARGS);
5081         }
5082 
5083         error = otThreadSetPskcRef(GetInstancePtr(), pskcRef);
5084     }
5085 
5086 exit:
5087     return error;
5088 }
5089 #endif
5090 #endif // OPENTHREAD_FTD
5091 
5092 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Process(Arg aArgs[])5093 template <> otError Interpreter::Process<Cmd("mliid")>(Arg aArgs[])
5094 {
5095     otError                  error = OT_ERROR_NONE;
5096     otIp6InterfaceIdentifier iid;
5097 
5098     VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
5099 
5100     SuccessOrExit(error = aArgs[0].ParseAsHexString(iid.mFields.m8));
5101     SuccessOrExit(error = otIp6SetMeshLocalIid(GetInstancePtr(), &iid));
5102 
5103 exit:
5104     return error;
5105 }
5106 #endif
5107 
5108 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
5109 
Process(Arg aArgs[])5110 template <> otError Interpreter::Process<Cmd("mlr")>(Arg aArgs[])
5111 {
5112     otError error = OT_ERROR_INVALID_COMMAND;
5113 
5114     if (aArgs[0] == "reg")
5115     {
5116         otIp6Address addresses[OT_IP6_MAX_MLR_ADDRESSES];
5117         uint32_t     timeout;
5118         bool         hasTimeout   = false;
5119         uint8_t      numAddresses = 0;
5120 
5121         aArgs++;
5122 
5123         while (aArgs->ParseAsIp6Address(addresses[numAddresses]) == OT_ERROR_NONE)
5124         {
5125             aArgs++;
5126             numAddresses++;
5127 
5128             if (numAddresses == OT_ARRAY_LENGTH(addresses))
5129             {
5130                 break;
5131             }
5132         }
5133 
5134         if (aArgs->ParseAsUint32(timeout) == OT_ERROR_NONE)
5135         {
5136             aArgs++;
5137             hasTimeout = true;
5138         }
5139 
5140         VerifyOrExit(aArgs->IsEmpty() && (numAddresses > 0), error = OT_ERROR_INVALID_ARGS);
5141 
5142         SuccessOrExit(error = otIp6RegisterMulticastListeners(GetInstancePtr(), addresses, numAddresses,
5143                                                               hasTimeout ? &timeout : nullptr,
5144                                                               Interpreter::HandleMlrRegResult, this));
5145 
5146         error = OT_ERROR_PENDING;
5147     }
5148 
5149 exit:
5150     return error;
5151 }
5152 
HandleMlrRegResult(void * aContext,otError aError,uint8_t aMlrStatus,const otIp6Address * aFailedAddresses,uint8_t aFailedAddressNum)5153 void Interpreter::HandleMlrRegResult(void               *aContext,
5154                                      otError             aError,
5155                                      uint8_t             aMlrStatus,
5156                                      const otIp6Address *aFailedAddresses,
5157                                      uint8_t             aFailedAddressNum)
5158 {
5159     static_cast<Interpreter *>(aContext)->HandleMlrRegResult(aError, aMlrStatus, aFailedAddresses, aFailedAddressNum);
5160 }
5161 
HandleMlrRegResult(otError aError,uint8_t aMlrStatus,const otIp6Address * aFailedAddresses,uint8_t aFailedAddressNum)5162 void Interpreter::HandleMlrRegResult(otError             aError,
5163                                      uint8_t             aMlrStatus,
5164                                      const otIp6Address *aFailedAddresses,
5165                                      uint8_t             aFailedAddressNum)
5166 {
5167     if (aError == OT_ERROR_NONE)
5168     {
5169         OutputLine("status %d, %d failed", aMlrStatus, aFailedAddressNum);
5170 
5171         for (uint8_t i = 0; i < aFailedAddressNum; i++)
5172         {
5173             OutputIp6AddressLine(aFailedAddresses[i]);
5174         }
5175     }
5176 
5177     OutputResult(aError);
5178 }
5179 
5180 #endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
5181 
Process(Arg aArgs[])5182 template <> otError Interpreter::Process<Cmd("mode")>(Arg aArgs[])
5183 {
5184     otError          error = OT_ERROR_NONE;
5185     otLinkModeConfig linkMode;
5186 
5187     memset(&linkMode, 0, sizeof(otLinkModeConfig));
5188 
5189     if (aArgs[0].IsEmpty())
5190     {
5191         char linkModeString[kLinkModeStringSize];
5192 
5193         OutputLine("%s", LinkModeToString(otThreadGetLinkMode(GetInstancePtr()), linkModeString));
5194         ExitNow();
5195     }
5196 
5197     if (aArgs[0] != "-")
5198     {
5199         for (const char *arg = aArgs[0].GetCString(); *arg != '\0'; arg++)
5200         {
5201             switch (*arg)
5202             {
5203             case 'r':
5204                 linkMode.mRxOnWhenIdle = true;
5205                 break;
5206 
5207             case 'd':
5208                 linkMode.mDeviceType = true;
5209                 break;
5210 
5211             case 'n':
5212                 linkMode.mNetworkData = true;
5213                 break;
5214 
5215             default:
5216                 ExitNow(error = OT_ERROR_INVALID_ARGS);
5217             }
5218         }
5219     }
5220 
5221     error = otThreadSetLinkMode(GetInstancePtr(), linkMode);
5222 
5223 exit:
5224     return error;
5225 }
5226 
Process(Arg aArgs[])5227 template <> otError Interpreter::Process<Cmd("multiradio")>(Arg aArgs[])
5228 {
5229     otError error = OT_ERROR_NONE;
5230 
5231     OT_UNUSED_VARIABLE(aArgs);
5232 
5233     if (aArgs[0].IsEmpty())
5234     {
5235         bool isFirst = true;
5236 
5237         OutputFormat("[");
5238 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
5239         OutputFormat("15.4");
5240         isFirst = false;
5241 #endif
5242 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
5243         OutputFormat("%sTREL", isFirst ? "" : ", ");
5244 #endif
5245         OutputLine("]");
5246 
5247         OT_UNUSED_VARIABLE(isFirst);
5248     }
5249 #if OPENTHREAD_CONFIG_MULTI_RADIO
5250     else if (aArgs[0] == "neighbor")
5251     {
5252         otMultiRadioNeighborInfo multiRadioInfo;
5253 
5254         if (aArgs[1] == "list")
5255         {
5256             otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
5257             otNeighborInfo         neighInfo;
5258 
5259             while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighInfo) == OT_ERROR_NONE)
5260             {
5261                 if (otMultiRadioGetNeighborInfo(GetInstancePtr(), &neighInfo.mExtAddress, &multiRadioInfo) !=
5262                     OT_ERROR_NONE)
5263                 {
5264                     continue;
5265                 }
5266 
5267                 OutputFormat("ExtAddr:");
5268                 OutputExtAddress(neighInfo.mExtAddress);
5269                 OutputFormat(", RLOC16:0x%04x, Radios:", neighInfo.mRloc16);
5270                 OutputMultiRadioInfo(multiRadioInfo);
5271             }
5272         }
5273         else
5274         {
5275             otExtAddress extAddress;
5276 
5277             SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddress.m8));
5278             SuccessOrExit(error = otMultiRadioGetNeighborInfo(GetInstancePtr(), &extAddress, &multiRadioInfo));
5279             OutputMultiRadioInfo(multiRadioInfo);
5280         }
5281     }
5282 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
5283     else
5284     {
5285         ExitNow(error = OT_ERROR_INVALID_COMMAND);
5286     }
5287 
5288 exit:
5289     return error;
5290 }
5291 
5292 #if OPENTHREAD_CONFIG_MULTI_RADIO
OutputMultiRadioInfo(const otMultiRadioNeighborInfo & aMultiRadioInfo)5293 void Interpreter::OutputMultiRadioInfo(const otMultiRadioNeighborInfo &aMultiRadioInfo)
5294 {
5295     bool isFirst = true;
5296 
5297     OutputFormat("[");
5298 
5299     if (aMultiRadioInfo.mSupportsIeee802154)
5300     {
5301         OutputFormat("15.4(%u)", aMultiRadioInfo.mIeee802154Info.mPreference);
5302         isFirst = false;
5303     }
5304 
5305     if (aMultiRadioInfo.mSupportsTrelUdp6)
5306     {
5307         OutputFormat("%sTREL(%u)", isFirst ? "" : ", ", aMultiRadioInfo.mTrelUdp6Info.mPreference);
5308     }
5309 
5310     OutputLine("]");
5311 }
5312 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
5313 
5314 #if OPENTHREAD_FTD
Process(Arg aArgs[])5315 template <> otError Interpreter::Process<Cmd("neighbor")>(Arg aArgs[])
5316 {
5317     otError                error = OT_ERROR_NONE;
5318     otNeighborInfo         neighborInfo;
5319     bool                   isTable;
5320     otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
5321 
5322     isTable = (aArgs[0] == "table");
5323 
5324     if (isTable || (aArgs[0] == "list"))
5325     {
5326         if (isTable)
5327         {
5328             static const char *const kNeighborTableTitles[] = {
5329                 "Role", "RLOC16", "Age", "Avg RSSI", "Last RSSI", "R", "D", "N", "Extended MAC", "Version",
5330             };
5331 
5332             static const uint8_t kNeighborTableColumnWidths[] = {
5333                 6, 8, 5, 10, 11, 1, 1, 1, 18, 9,
5334             };
5335 
5336             OutputTableHeader(kNeighborTableTitles, kNeighborTableColumnWidths);
5337         }
5338 
5339         while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE)
5340         {
5341             if (isTable)
5342             {
5343                 OutputFormat("| %3c  ", neighborInfo.mIsChild ? 'C' : 'R');
5344                 OutputFormat("| 0x%04x ", neighborInfo.mRloc16);
5345                 OutputFormat("| %3lu ", ToUlong(neighborInfo.mAge));
5346                 OutputFormat("| %8d ", neighborInfo.mAverageRssi);
5347                 OutputFormat("| %9d ", neighborInfo.mLastRssi);
5348                 OutputFormat("|%1d", neighborInfo.mRxOnWhenIdle);
5349                 OutputFormat("|%1d", neighborInfo.mFullThreadDevice);
5350                 OutputFormat("|%1d", neighborInfo.mFullNetworkData);
5351                 OutputFormat("| ");
5352                 OutputExtAddress(neighborInfo.mExtAddress);
5353                 OutputLine(" | %7d |", neighborInfo.mVersion);
5354             }
5355             else
5356             {
5357                 OutputFormat("0x%04x ", neighborInfo.mRloc16);
5358             }
5359         }
5360 
5361         OutputNewLine();
5362     }
5363     else if (aArgs[0] == "linkquality")
5364     {
5365         static const char *const kLinkQualityTableTitles[] = {
5366             "RLOC16", "Extended MAC", "Frame Error", "Msg Error", "Avg RSS", "Last RSS", "Age",
5367         };
5368 
5369         static const uint8_t kLinkQualityTableColumnWidths[] = {
5370             8, 18, 13, 11, 9, 10, 7,
5371         };
5372 
5373         OutputTableHeader(kLinkQualityTableTitles, kLinkQualityTableColumnWidths);
5374 
5375         while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE)
5376         {
5377             PercentageStringBuffer stringBuffer;
5378 
5379             OutputFormat("| 0x%04x | ", neighborInfo.mRloc16);
5380             OutputExtAddress(neighborInfo.mExtAddress);
5381             OutputFormat(" | %9s %% ", PercentageToString(neighborInfo.mFrameErrorRate, stringBuffer));
5382             OutputFormat("| %7s %% ", PercentageToString(neighborInfo.mMessageErrorRate, stringBuffer));
5383             OutputFormat("| %7d ", neighborInfo.mAverageRssi);
5384             OutputFormat("| %8d ", neighborInfo.mLastRssi);
5385             OutputLine("| %5lu |", ToUlong(neighborInfo.mAge));
5386         }
5387     }
5388 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
5389     /**
5390      * @cli neighbor conntime
5391      * @code
5392      * neighbor conntime
5393      * | RLOC16 | Extended MAC     | Last Heard (Age) | Connection Time  |
5394      * +--------+------------------+------------------+------------------+
5395      * | 0x8401 | 1a28be396a14a318 |         00:00:13 |         00:07:59 |
5396      * | 0x5c00 | 723ebf0d9eba3264 |         00:00:03 |         00:11:27 |
5397      * | 0xe800 | ce53628a1e3f5b3c |         00:00:02 |         00:00:15 |
5398      * Done
5399      * @endcode
5400      * @par
5401      * Print the connection time and age of neighbors. Info per neighbor:
5402      * - RLOC16
5403      * - Extended MAC address
5404      * - Last Heard (seconds since last heard from neighbor)
5405      * - Connection time (seconds since link establishment with neighbor)
5406      * Duration intervals are formatted as `{hh}:{mm}:{ss}` for hours, minutes, and seconds if the duration is less
5407      * than one day. If the duration is longer than one day, the format is `{dd}d.{hh}:{mm}:{ss}`.
5408      */
5409     else if (aArgs[0] == "conntime")
5410     {
5411         /**
5412          * @cli neighbor conntime list
5413          * @code
5414          * neighbor conntime list
5415          * 0x8401 1a28be396a14a318 age:63 conn-time:644
5416          * 0x5c00 723ebf0d9eba3264 age:23 conn-time:852
5417          * 0xe800 ce53628a1e3f5b3c age:23 conn-time:180
5418          * Done
5419          * @endcode
5420          * @par
5421          * Print connection time and age of neighbors.
5422          * This command is similar to `neighbor conntime`, but it displays the information in a list format. The age
5423          * and connection time are both displayed in seconds.
5424          */
5425         if (aArgs[1] == "list")
5426         {
5427             isTable = false;
5428         }
5429         else
5430         {
5431             static const char *const kConnTimeTableTitles[] = {
5432                 "RLOC16",
5433                 "Extended MAC",
5434                 "Last Heard (Age)",
5435                 "Connection Time",
5436             };
5437 
5438             static const uint8_t kConnTimeTableColumnWidths[] = {8, 18, 18, 18};
5439 
5440             isTable = true;
5441             OutputTableHeader(kConnTimeTableTitles, kConnTimeTableColumnWidths);
5442         }
5443 
5444         while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE)
5445         {
5446             if (isTable)
5447             {
5448                 char string[OT_DURATION_STRING_SIZE];
5449 
5450                 OutputFormat("| 0x%04x | ", neighborInfo.mRloc16);
5451                 OutputExtAddress(neighborInfo.mExtAddress);
5452                 otConvertDurationInSecondsToString(neighborInfo.mAge, string, sizeof(string));
5453                 OutputFormat(" | %16s", string);
5454                 otConvertDurationInSecondsToString(neighborInfo.mConnectionTime, string, sizeof(string));
5455                 OutputLine(" | %16s |", string);
5456             }
5457             else
5458             {
5459                 OutputFormat("0x%04x ", neighborInfo.mRloc16);
5460                 OutputExtAddress(neighborInfo.mExtAddress);
5461                 OutputLine(" age:%lu conn-time:%lu", ToUlong(neighborInfo.mAge), ToUlong(neighborInfo.mConnectionTime));
5462             }
5463         }
5464     }
5465 #endif
5466     else
5467     {
5468         error = OT_ERROR_INVALID_ARGS;
5469     }
5470 
5471     return error;
5472 }
5473 #endif // OPENTHREAD_FTD
5474 
Process(Arg aArgs[])5475 template <> otError Interpreter::Process<Cmd("netstat")>(Arg aArgs[])
5476 {
5477     OT_UNUSED_VARIABLE(aArgs);
5478 
5479     static const char *const kNetstatTableTitles[]       = {"Local Address", "Peer Address"};
5480     static const uint8_t     kNetstatTableColumnWidths[] = {49, 49};
5481 
5482     char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
5483 
5484     OutputTableHeader(kNetstatTableTitles, kNetstatTableColumnWidths);
5485 
5486     for (const otUdpSocket *socket = otUdpGetSockets(GetInstancePtr()); socket != nullptr; socket = socket->mNext)
5487     {
5488         otIp6SockAddrToString(&socket->mSockName, string, sizeof(string));
5489         OutputFormat("| %-47s ", string);
5490         otIp6SockAddrToString(&socket->mPeerName, string, sizeof(string));
5491         OutputLine("| %-47s |", string);
5492     }
5493 
5494     return OT_ERROR_NONE;
5495 }
5496 
5497 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
Process(Arg aArgs[])5498 template <> otError Interpreter::Process<Cmd("service")>(Arg aArgs[])
5499 {
5500     otError         error = OT_ERROR_INVALID_COMMAND;
5501     otServiceConfig cfg;
5502 
5503     if (aArgs[0].IsEmpty())
5504     {
5505         otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
5506         otServiceConfig       config;
5507 
5508         while (otServerGetNextService(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
5509         {
5510             mNetworkData.OutputService(config);
5511         }
5512 
5513         error = OT_ERROR_NONE;
5514     }
5515     else
5516     {
5517         uint16_t length;
5518 
5519         SuccessOrExit(error = aArgs[1].ParseAsUint32(cfg.mEnterpriseNumber));
5520 
5521         length = sizeof(cfg.mServiceData);
5522         SuccessOrExit(error = aArgs[2].ParseAsHexString(length, cfg.mServiceData));
5523         VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS);
5524         cfg.mServiceDataLength = static_cast<uint8_t>(length);
5525 
5526         if (aArgs[0] == "add")
5527         {
5528             length = sizeof(cfg.mServerConfig.mServerData);
5529             SuccessOrExit(error = aArgs[3].ParseAsHexString(length, cfg.mServerConfig.mServerData));
5530             VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS);
5531             cfg.mServerConfig.mServerDataLength = static_cast<uint8_t>(length);
5532 
5533             cfg.mServerConfig.mStable = true;
5534 
5535             error = otServerAddService(GetInstancePtr(), &cfg);
5536         }
5537         else if (aArgs[0] == "remove")
5538         {
5539             error = otServerRemoveService(GetInstancePtr(), cfg.mEnterpriseNumber, cfg.mServiceData,
5540                                           cfg.mServiceDataLength);
5541         }
5542     }
5543 
5544 exit:
5545     return error;
5546 }
5547 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
5548 
Process(Arg aArgs[])5549 template <> otError Interpreter::Process<Cmd("netdata")>(Arg aArgs[]) { return mNetworkData.Process(aArgs); }
5550 
5551 #if OPENTHREAD_FTD
Process(Arg aArgs[])5552 template <> otError Interpreter::Process<Cmd("networkidtimeout")>(Arg aArgs[])
5553 {
5554     return ProcessGetSet(aArgs, otThreadGetNetworkIdTimeout, otThreadSetNetworkIdTimeout);
5555 }
5556 #endif
5557 
Process(Arg aArgs[])5558 template <> otError Interpreter::Process<Cmd("networkkey")>(Arg aArgs[])
5559 {
5560     otError error = OT_ERROR_NONE;
5561 
5562     /**
5563      * @cli networkkey
5564      * @code
5565      * networkkey
5566      * 00112233445566778899aabbccddeeff
5567      * Done
5568      * @endcode
5569      * @par api_copy
5570      * #otThreadGetNetworkKey
5571      */
5572     if (aArgs[0].IsEmpty())
5573     {
5574         otNetworkKey networkKey;
5575 
5576         otThreadGetNetworkKey(GetInstancePtr(), &networkKey);
5577         OutputBytesLine(networkKey.m8);
5578     }
5579     /**
5580      * @cli networkkey (key)
5581      * @code
5582      * networkkey 00112233445566778899aabbccddeeff
5583      * Done
5584      * @endcode
5585      * @par api_copy
5586      * #otThreadSetNetworkKey
5587      * @cparam networkkey @ca{key}
5588      */
5589     else
5590     {
5591         otNetworkKey key;
5592 
5593         SuccessOrExit(error = aArgs[0].ParseAsHexString(key.m8));
5594         SuccessOrExit(error = otThreadSetNetworkKey(GetInstancePtr(), &key));
5595     }
5596 
5597 exit:
5598     return error;
5599 }
5600 
5601 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
Process(Arg aArgs[])5602 template <> otError Interpreter::Process<Cmd("networkkeyref")>(Arg aArgs[])
5603 {
5604     otError error = OT_ERROR_NONE;
5605 
5606     if (aArgs[0].IsEmpty())
5607     {
5608         OutputLine("0x%08lx", ToUlong(otThreadGetNetworkKeyRef(GetInstancePtr())));
5609     }
5610     else
5611     {
5612         otNetworkKeyRef keyRef;
5613 
5614         SuccessOrExit(error = aArgs[0].ParseAsUint32(keyRef));
5615         SuccessOrExit(error = otThreadSetNetworkKeyRef(GetInstancePtr(), keyRef));
5616     }
5617 
5618 exit:
5619     return error;
5620 }
5621 #endif
5622 
5623 /**
5624  * @cli networkname
5625  * @code
5626  * networkname
5627  * OpenThread
5628  * Done
5629  * @endcode
5630  * @par api_copy
5631  * #otThreadGetNetworkName
5632  */
Process(Arg aArgs[])5633 template <> otError Interpreter::Process<Cmd("networkname")>(Arg aArgs[])
5634 {
5635     /**
5636      * @cli networkname (name)
5637      * @code
5638      * networkname OpenThread
5639      * Done
5640      * @endcode
5641      * @par api_copy
5642      * #otThreadSetNetworkName
5643      * @cparam networkname @ca{name}
5644      * @par
5645      * Note: The current commissioning credential becomes stale after changing this value. Use `pskc` to reset.
5646      */
5647     return ProcessGetSet(aArgs, otThreadGetNetworkName, otThreadSetNetworkName);
5648 }
5649 
5650 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
Process(Arg aArgs[])5651 template <> otError Interpreter::Process<Cmd("networktime")>(Arg aArgs[])
5652 {
5653     otError error = OT_ERROR_NONE;
5654 
5655     /**
5656      * @cli networktime
5657      * @code
5658      * networktime
5659      * Network Time:     21084154us (synchronized)
5660      * Time Sync Period: 100s
5661      * XTAL Threshold:   300ppm
5662      * Done
5663      * @endcode
5664      * @par
5665      * Gets the Thread network time and the time sync parameters.
5666      * @sa otNetworkTimeGet
5667      * @sa otNetworkTimeGetSyncPeriod
5668      * @sa otNetworkTimeGetXtalThreshold
5669      */
5670     if (aArgs[0].IsEmpty())
5671     {
5672         uint64_t            time;
5673         otNetworkTimeStatus networkTimeStatus;
5674 
5675         networkTimeStatus = otNetworkTimeGet(GetInstancePtr(), &time);
5676 
5677         OutputFormat("Network Time:     ");
5678         OutputUint64(time);
5679         OutputFormat("us");
5680 
5681         switch (networkTimeStatus)
5682         {
5683         case OT_NETWORK_TIME_UNSYNCHRONIZED:
5684             OutputLine(" (unsynchronized)");
5685             break;
5686 
5687         case OT_NETWORK_TIME_RESYNC_NEEDED:
5688             OutputLine(" (resync needed)");
5689             break;
5690 
5691         case OT_NETWORK_TIME_SYNCHRONIZED:
5692             OutputLine(" (synchronized)");
5693             break;
5694 
5695         default:
5696             break;
5697         }
5698 
5699         OutputLine("Time Sync Period: %us", otNetworkTimeGetSyncPeriod(GetInstancePtr()));
5700         OutputLine("XTAL Threshold:   %uppm", otNetworkTimeGetXtalThreshold(GetInstancePtr()));
5701     }
5702     /**
5703      * @cli networktime (set)
5704      * @code
5705      * networktime 100 300
5706      * Done
5707      * @endcode
5708      * @cparam networktime @ca{timesyncperiod} @ca{xtalthreshold}
5709      * @par
5710      * Sets the time sync parameters.
5711      * *   `timesyncperiod`: The time synchronization period, in seconds.
5712      * *   `xtalthreshold`: The XTAL accuracy threshold for a device to become Router-Capable device, in PPM.
5713      * @sa otNetworkTimeSetSyncPeriod
5714      * @sa otNetworkTimeSetXtalThreshold
5715      */
5716     else
5717     {
5718         uint16_t period;
5719         uint16_t xtalThreshold;
5720 
5721         SuccessOrExit(error = aArgs[0].ParseAsUint16(period));
5722         SuccessOrExit(error = aArgs[1].ParseAsUint16(xtalThreshold));
5723         SuccessOrExit(error = otNetworkTimeSetSyncPeriod(GetInstancePtr(), period));
5724         SuccessOrExit(error = otNetworkTimeSetXtalThreshold(GetInstancePtr(), xtalThreshold));
5725     }
5726 
5727 exit:
5728     return error;
5729 }
5730 #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
5731 
5732 #if OPENTHREAD_FTD
Process(Arg aArgs[])5733 template <> otError Interpreter::Process<Cmd("nexthop")>(Arg aArgs[])
5734 {
5735     constexpr uint8_t  kRouterIdOffset = 10; // Bit offset of Router ID in RLOC16
5736     constexpr uint16_t kInvalidRloc16  = 0xfffe;
5737 
5738     otError  error = OT_ERROR_NONE;
5739     uint16_t destRloc16;
5740     uint16_t nextHopRloc16;
5741     uint8_t  pathCost;
5742 
5743     /**
5744      * @cli nexthop
5745      * @code
5746      * nexthop
5747      * | ID   |NxtHop| Cost |
5748      * +------+------+------+
5749      * |    9 |    9 |    1 |
5750      * |   25 |   25 |    0 |
5751      * |   30 |   30 |    1 |
5752      * |   46 |    - |    - |
5753      * |   50 |   30 |    3 |
5754      * |   60 |   30 |    2 |
5755      * Done
5756      * @endcode
5757      * @par
5758      * Output table of allocated Router IDs and current next hop and path
5759      * cost for each router.
5760      * @sa otThreadGetNextHopAndPathCost
5761      * @sa otThreadIsRouterIdAllocated
5762      */
5763     if (aArgs[0].IsEmpty())
5764     {
5765         static const char *const kNextHopTableTitles[] = {
5766             "ID",
5767             "NxtHop",
5768             "Cost",
5769         };
5770 
5771         static const uint8_t kNextHopTableColumnWidths[] = {
5772             6,
5773             6,
5774             6,
5775         };
5776 
5777         OutputTableHeader(kNextHopTableTitles, kNextHopTableColumnWidths);
5778 
5779         for (uint8_t routerId = 0; routerId <= OT_NETWORK_MAX_ROUTER_ID; routerId++)
5780         {
5781             if (!otThreadIsRouterIdAllocated(GetInstancePtr(), routerId))
5782             {
5783                 continue;
5784             }
5785 
5786             destRloc16 = routerId;
5787             destRloc16 <<= kRouterIdOffset;
5788 
5789             otThreadGetNextHopAndPathCost(GetInstancePtr(), destRloc16, &nextHopRloc16, &pathCost);
5790 
5791             OutputFormat("| %4u | ", routerId);
5792 
5793             if (nextHopRloc16 != kInvalidRloc16)
5794             {
5795                 OutputLine("%4u | %4u |", nextHopRloc16 >> kRouterIdOffset, pathCost);
5796             }
5797             else
5798             {
5799                 OutputLine("%4s | %4s |", "-", "-");
5800             }
5801         }
5802     }
5803     /**
5804      * @cli nexthop (get)
5805      * @code
5806      * nexthop 0xc000
5807      * 0xc000 cost:0
5808      * Done
5809      * @endcode
5810      * @code
5811      * nexthop 0x8001
5812      * 0x2000 cost:3
5813      * Done
5814      * @endcode
5815      * @cparam nexthop @ca{rloc16}
5816      * @par api_copy
5817      * #otThreadGetNextHopAndPathCost
5818      */
5819     else
5820     {
5821         SuccessOrExit(error = aArgs[0].ParseAsUint16(destRloc16));
5822         otThreadGetNextHopAndPathCost(GetInstancePtr(), destRloc16, &nextHopRloc16, &pathCost);
5823         OutputLine("0x%04x cost:%u", nextHopRloc16, pathCost);
5824     }
5825 
5826 exit:
5827     return error;
5828 }
5829 
5830 #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE
5831 
Process(Arg aArgs[])5832 template <> otError Interpreter::Process<Cmd("meshdiag")>(Arg aArgs[])
5833 {
5834     otError error = OT_ERROR_NONE;
5835 
5836     /**
5837      * @cli meshdiag topology
5838      * @code
5839      * meshdiag topology
5840      * id:02 rloc16:0x0800 ext-addr:8aa57d2c603fe16c ver:4 - me - leader
5841      *    3-links:{ 46 }
5842      * id:46 rloc16:0xb800 ext-addr:fe109d277e0175cc ver:4
5843      *    3-links:{ 02 51 57 }
5844      * id:33 rloc16:0x8400 ext-addr:d2e511a146b9e54d ver:4
5845      *    3-links:{ 51 57 }
5846      * id:51 rloc16:0xcc00 ext-addr:9aab43ababf05352 ver:4
5847      *    3-links:{ 33 57 }
5848      *    2-links:{ 46 }
5849      * id:57 rloc16:0xe400 ext-addr:dae9c4c0e9da55ff ver:4
5850      *    3-links:{ 46 51 }
5851      *    1-links:{ 33 }
5852      * Done
5853      * @endcode
5854      * @par
5855      * Discover network topology (list of routers and their connections).
5856      * Parameters are optional and indicate additional items to discover. Can be added in any order.
5857      * * `ip6-addrs` to discover the list of IPv6 addresses of every router.
5858      * * `children` to discover the child table of every router.
5859      * @par
5860      * Information per router:
5861      * * Router ID
5862      * * RLOC16
5863      * * Extended MAC address
5864      * * Thread Version (if known)
5865      * * Whether the router is this device is itself (`me`)
5866      * * Whether the router is the parent of this device when device is a child (`parent`)
5867      * * Whether the router is `leader`
5868      * * Whether the router acts as a border router providing external connectivity (`br`)
5869      * * List of routers to which this router has a link:
5870      *   * `3-links`: Router IDs to which this router has a incoming link with link quality 3
5871      *   * `2-links`: Router IDs to which this router has a incoming link with link quality 2
5872      *   * `1-links`: Router IDs to which this router has a incoming link with link quality 1
5873      *   * If a list if empty, it is omitted in the out.
5874      * * If `ip6-addrs`, list of IPv6 addresses of the router
5875      * * If `children`, list of all children of the router. Information per child:
5876      *   * RLOC16
5877      *   * Incoming Link Quality from perspective of parent to child (zero indicates unknown)
5878      *   * Child Device mode (`r` rx-on-when-idle, `d` Full Thread Device, `n` Full Network Data, `-` no flags set)
5879      *   * Whether the child is this device itself (`me`)
5880      *   * Whether the child acts as a border router providing external connectivity (`br`)
5881      * @cparam meshdiag topology [@ca{ip6-addrs}] [@ca{children}]
5882      * @sa otMeshDiagDiscoverTopology
5883      */
5884     if (aArgs[0] == "topology")
5885     {
5886         otMeshDiagDiscoverConfig config;
5887 
5888         config.mDiscoverIp6Addresses = false;
5889         config.mDiscoverChildTable   = false;
5890 
5891         aArgs++;
5892 
5893         for (; !aArgs->IsEmpty(); aArgs++)
5894         {
5895             if (*aArgs == "ip6-addrs")
5896             {
5897                 config.mDiscoverIp6Addresses = true;
5898             }
5899             else if (*aArgs == "children")
5900             {
5901                 config.mDiscoverChildTable = true;
5902             }
5903             else
5904             {
5905                 ExitNow(error = OT_ERROR_INVALID_ARGS);
5906             }
5907         }
5908 
5909         SuccessOrExit(error = otMeshDiagDiscoverTopology(GetInstancePtr(), &config, HandleMeshDiagDiscoverDone, this));
5910         error = OT_ERROR_PENDING;
5911     }
5912     else
5913     {
5914         error = OT_ERROR_INVALID_COMMAND;
5915     }
5916 
5917 exit:
5918     return error;
5919 }
5920 
HandleMeshDiagDiscoverDone(otError aError,otMeshDiagRouterInfo * aRouterInfo,void * aContext)5921 void Interpreter::HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo, void *aContext)
5922 {
5923     reinterpret_cast<Interpreter *>(aContext)->HandleMeshDiagDiscoverDone(aError, aRouterInfo);
5924 }
5925 
HandleMeshDiagDiscoverDone(otError aError,otMeshDiagRouterInfo * aRouterInfo)5926 void Interpreter::HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo)
5927 {
5928     VerifyOrExit(aRouterInfo != nullptr);
5929 
5930     OutputFormat("id:%02u rloc16:0x%04x ext-addr:", aRouterInfo->mRouterId, aRouterInfo->mRloc16);
5931     OutputExtAddress(aRouterInfo->mExtAddress);
5932 
5933     if (aRouterInfo->mVersion != OT_MESH_DIAG_VERSION_UNKNOWN)
5934     {
5935         OutputFormat(" ver:%u", aRouterInfo->mVersion);
5936     }
5937 
5938     if (aRouterInfo->mIsThisDevice)
5939     {
5940         OutputFormat(" - me");
5941     }
5942 
5943     if (aRouterInfo->mIsThisDeviceParent)
5944     {
5945         OutputFormat(" - parent");
5946     }
5947 
5948     if (aRouterInfo->mIsLeader)
5949     {
5950         OutputFormat(" - leader");
5951     }
5952 
5953     if (aRouterInfo->mIsBorderRouter)
5954     {
5955         OutputFormat(" - br");
5956     }
5957 
5958     OutputNewLine();
5959 
5960     for (uint8_t linkQuality = 3; linkQuality > 0; linkQuality--)
5961     {
5962         bool hasLinkQuality = false;
5963 
5964         for (uint8_t entryQuality : aRouterInfo->mLinkQualities)
5965         {
5966             if (entryQuality == linkQuality)
5967             {
5968                 hasLinkQuality = true;
5969                 break;
5970             }
5971         }
5972 
5973         if (hasLinkQuality)
5974         {
5975             OutputFormat(kIndentSize, "%u-links:{ ", linkQuality);
5976 
5977             for (uint8_t id = 0; id < OT_ARRAY_LENGTH(aRouterInfo->mLinkQualities); id++)
5978             {
5979                 if (aRouterInfo->mLinkQualities[id] == linkQuality)
5980                 {
5981                     OutputFormat("%02u ", id);
5982                 }
5983             }
5984 
5985             OutputLine("}");
5986         }
5987     }
5988 
5989     if (aRouterInfo->mIp6AddrIterator != nullptr)
5990     {
5991         otIp6Address ip6Address;
5992 
5993         OutputLine(kIndentSize, "ip6-addrs:");
5994 
5995         while (otMeshDiagGetNextIp6Address(aRouterInfo->mIp6AddrIterator, &ip6Address) == OT_ERROR_NONE)
5996         {
5997             OutputSpaces(kIndentSize * 2);
5998             OutputIp6AddressLine(ip6Address);
5999         }
6000     }
6001 
6002     if (aRouterInfo->mChildIterator != nullptr)
6003     {
6004         otMeshDiagChildInfo childInfo;
6005         char                linkModeString[kLinkModeStringSize];
6006         bool                isFirst = true;
6007 
6008         while (otMeshDiagGetNextChildInfo(aRouterInfo->mChildIterator, &childInfo) == OT_ERROR_NONE)
6009         {
6010             if (isFirst)
6011             {
6012                 OutputLine(kIndentSize, "children:");
6013                 isFirst = false;
6014             }
6015 
6016             OutputFormat(kIndentSize * 2, "rloc16:0x%04x lq:%u, mode:%s", childInfo.mRloc16, childInfo.mLinkQuality,
6017                          LinkModeToString(childInfo.mMode, linkModeString));
6018 
6019             if (childInfo.mIsThisDevice)
6020             {
6021                 OutputFormat(" - me");
6022             }
6023 
6024             if (childInfo.mIsBorderRouter)
6025             {
6026                 OutputFormat(" - br");
6027             }
6028 
6029             OutputNewLine();
6030         }
6031 
6032         if (isFirst)
6033         {
6034             OutputLine(kIndentSize, "children: none");
6035         }
6036     }
6037 
6038 exit:
6039     OutputResult(aError);
6040 }
6041 
6042 #endif // OPENTHREAD_CONFIG_MESH_DIAG_ENABLE
6043 
6044 #endif // OPENTHREAD_FTD
6045 
Process(Arg aArgs[])6046 template <> otError Interpreter::Process<Cmd("panid")>(Arg aArgs[])
6047 {
6048     otError error = OT_ERROR_NONE;
6049     /**
6050      * @cli panid
6051      * @code
6052      * panid
6053      * 0xdead
6054      * Done
6055      * @endcode
6056      * @par api_copy
6057      * #otLinkGetPanId
6058      */
6059     if (aArgs[0].IsEmpty())
6060     {
6061         OutputLine("0x%04x", otLinkGetPanId(GetInstancePtr()));
6062     }
6063     /**
6064      * @cli panid (panid)
6065      * @code
6066      * panid 0xdead
6067      * Done
6068      * @endcode
6069      * @par api_copy
6070      * #otLinkSetPanId
6071      * @cparam panid @ca{panid}
6072      */
6073     else
6074     {
6075         error = ProcessSet(aArgs, otLinkSetPanId);
6076     }
6077 
6078     return error;
6079 }
6080 
Process(Arg aArgs[])6081 template <> otError Interpreter::Process<Cmd("parent")>(Arg aArgs[])
6082 {
6083     otError error = OT_ERROR_NONE;
6084     /**
6085      * @cli parent
6086      * @code
6087      * parent
6088      * Ext Addr: be1857c6c21dce55
6089      * Rloc: 5c00
6090      * Link Quality In: 3
6091      * Link Quality Out: 3
6092      * Age: 20
6093      * Version: 4
6094      * Done
6095      * @endcode
6096      * @sa otThreadGetParentInfo
6097      * @par
6098      * Get the diagnostic information for a Thread Router as parent.
6099      * @par
6100      * When operating as a Thread Router when OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE is enabled, this command
6101      * will return the cached information from when the device was previously attached as a Thread Child. Returning
6102      * cached information is necessary to support the Thread Test Harness - Test Scenario 8.2.x requests the former
6103      * parent (i.e. %Joiner Router's) MAC address even if the device has already promoted to a router.
6104      * @par
6105      * Note: When OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE is enabled, this command will return two extra lines with
6106      * information relevant for CSL Receiver operation.
6107      */
6108     if (aArgs[0].IsEmpty())
6109     {
6110         otRouterInfo parentInfo;
6111 
6112         SuccessOrExit(error = otThreadGetParentInfo(GetInstancePtr(), &parentInfo));
6113         OutputFormat("Ext Addr: ");
6114         OutputExtAddressLine(parentInfo.mExtAddress);
6115         OutputLine("Rloc: %x", parentInfo.mRloc16);
6116         OutputLine("Link Quality In: %u", parentInfo.mLinkQualityIn);
6117         OutputLine("Link Quality Out: %u", parentInfo.mLinkQualityOut);
6118         OutputLine("Age: %lu", ToUlong(parentInfo.mAge));
6119         OutputLine("Version: %u", parentInfo.mVersion);
6120 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
6121         OutputLine("CSL clock accuracy: %u", parentInfo.mCslClockAccuracy);
6122         OutputLine("CSL uncertainty: %u", parentInfo.mCslUncertainty);
6123 #endif
6124     }
6125     /**
6126      * @cli parent search
6127      * @code
6128      * parent search
6129      * Done
6130      * @endcode
6131      * @par api_copy
6132      * #otThreadSearchForBetterParent
6133      */
6134     else if (aArgs[0] == "search")
6135     {
6136         error = otThreadSearchForBetterParent(GetInstancePtr());
6137     }
6138     else
6139     {
6140         error = OT_ERROR_INVALID_ARGS;
6141     }
6142 
6143 exit:
6144     return error;
6145 }
6146 
6147 #if OPENTHREAD_FTD
6148 /**
6149  * @cli parentpriority (get,set)
6150  * @code
6151  * parentpriority
6152  * 1
6153  * Done
6154  * @endcode
6155  * @code
6156  * parentpriority 1
6157  * Done
6158  * @endcode
6159  * @cparam parentpriority [@ca{parentpriority}]
6160  * @par
6161  * Gets or sets the assigned parent priority value: 1, 0, -1 or -2. -2 means not assigned.
6162  * @sa otThreadGetParentPriority
6163  * @sa otThreadSetParentPriority
6164  */
Process(Arg aArgs[])6165 template <> otError Interpreter::Process<Cmd("parentpriority")>(Arg aArgs[])
6166 {
6167     return ProcessGetSet(aArgs, otThreadGetParentPriority, otThreadSetParentPriority);
6168 }
6169 #endif
6170 
6171 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Process(Arg * aArgs)6172 template <> otError Interpreter::Process<Cmd("routeridrange")>(Arg *aArgs)
6173 {
6174     uint8_t minRouterId;
6175     uint8_t maxRouterId;
6176     otError error = OT_ERROR_NONE;
6177 
6178     if (aArgs[0].IsEmpty())
6179     {
6180         otThreadGetRouterIdRange(GetInstancePtr(), &minRouterId, &maxRouterId);
6181         OutputLine("%u %u", minRouterId, maxRouterId);
6182     }
6183     else
6184     {
6185         SuccessOrExit(error = aArgs[0].ParseAsUint8(minRouterId));
6186         SuccessOrExit(error = aArgs[1].ParseAsUint8(maxRouterId));
6187         VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
6188         error = otThreadSetRouterIdRange(GetInstancePtr(), minRouterId, maxRouterId);
6189     }
6190 
6191 exit:
6192     return error;
6193 }
6194 #endif
6195 
6196 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
6197 
HandlePingReply(const otPingSenderReply * aReply,void * aContext)6198 void Interpreter::HandlePingReply(const otPingSenderReply *aReply, void *aContext)
6199 {
6200     static_cast<Interpreter *>(aContext)->HandlePingReply(aReply);
6201 }
6202 
HandlePingReply(const otPingSenderReply * aReply)6203 void Interpreter::HandlePingReply(const otPingSenderReply *aReply)
6204 {
6205     OutputFormat("%u bytes from ", static_cast<uint16_t>(aReply->mSize + sizeof(otIcmp6Header)));
6206     OutputIp6Address(aReply->mSenderAddress);
6207     OutputLine(": icmp_seq=%u hlim=%u time=%ums", aReply->mSequenceNumber, aReply->mHopLimit, aReply->mRoundTripTime);
6208 }
6209 
HandlePingStatistics(const otPingSenderStatistics * aStatistics,void * aContext)6210 void Interpreter::HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext)
6211 {
6212     static_cast<Interpreter *>(aContext)->HandlePingStatistics(aStatistics);
6213 }
6214 
HandlePingStatistics(const otPingSenderStatistics * aStatistics)6215 void Interpreter::HandlePingStatistics(const otPingSenderStatistics *aStatistics)
6216 {
6217     OutputFormat("%u packets transmitted, %u packets received.", aStatistics->mSentCount, aStatistics->mReceivedCount);
6218 
6219     if ((aStatistics->mSentCount != 0) && !aStatistics->mIsMulticast &&
6220         aStatistics->mReceivedCount <= aStatistics->mSentCount)
6221     {
6222         uint32_t packetLossRate =
6223             1000 * (aStatistics->mSentCount - aStatistics->mReceivedCount) / aStatistics->mSentCount;
6224 
6225         OutputFormat(" Packet loss = %lu.%u%%.", ToUlong(packetLossRate / 10),
6226                      static_cast<uint16_t>(packetLossRate % 10));
6227     }
6228 
6229     if (aStatistics->mReceivedCount != 0)
6230     {
6231         uint32_t avgRoundTripTime = 1000 * aStatistics->mTotalRoundTripTime / aStatistics->mReceivedCount;
6232 
6233         OutputFormat(" Round-trip min/avg/max = %u/%u.%u/%u ms.", aStatistics->mMinRoundTripTime,
6234                      static_cast<uint16_t>(avgRoundTripTime / 1000), static_cast<uint16_t>(avgRoundTripTime % 1000),
6235                      aStatistics->mMaxRoundTripTime);
6236     }
6237 
6238     OutputNewLine();
6239 
6240     if (!mPingIsAsync)
6241     {
6242         OutputResult(OT_ERROR_NONE);
6243     }
6244 }
6245 
6246 /**
6247  * @cli ping
6248  * @code
6249  * ping fd00:db8:0:0:76b:6a05:3ae9:a61a
6250  * 16 bytes from fd00:db8:0:0:76b:6a05:3ae9:a61a: icmp_seq=5 hlim=64 time=0ms
6251  * 1 packets transmitted, 1 packets received. Packet loss = 0.0%. Round-trip min/avg/max = 0/0.0/0 ms.
6252  * Done
6253  * @endcode
6254  * @code
6255  * ping -I fd00:db8:0:0:76b:6a05:3ae9:a61a ff02::1 100 1 1 1
6256  * 108 bytes from fd00:db8:0:0:f605:fb4b:d429:d59a: icmp_seq=4 hlim=64 time=7ms
6257  * 1 packets transmitted, 1 packets received. Round-trip min/avg/max = 7/7.0/7 ms.
6258  * Done
6259  * @endcode
6260  * @code
6261  * ping 172.17.0.1
6262  * Pinging synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1
6263  * 16 bytes from fdde:ad00:beef:2:0:0:ac11:1: icmp_seq=5 hlim=64 time=0ms
6264  * 1 packets transmitted, 1 packets received. Packet loss = 0.0%. Round-trip min/avg/max = 0/0.0/0 ms.
6265  * Done
6266  * @endcode
6267  * @cparam ping [@ca{async}] [@ca{-I source}] @ca{ipaddrc} [@ca{size}] [@ca{count}] <!--
6268  * -->          [@ca{interval}] [@ca{hoplimit}] [@ca{timeout}]
6269  * @par
6270  * Send an ICMPv6 Echo Request.
6271  * @par
6272  * The address can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix
6273  * from the network data.
6274  * @par
6275  * Note: The command will return InvalidState when the preferred NAT64 prefix is unavailable.
6276  * @sa otPingSenderPing
6277  */
Process(Arg aArgs[])6278 template <> otError Interpreter::Process<Cmd("ping")>(Arg aArgs[])
6279 {
6280     otError            error = OT_ERROR_NONE;
6281     otPingSenderConfig config;
6282     bool               async = false;
6283     bool               nat64SynthesizedAddress;
6284 
6285     /**
6286      * @cli ping stop
6287      * @code
6288      * ping stop
6289      * Done
6290      * @endcode
6291      * @par
6292      * Stop sending ICMPv6 Echo Requests.
6293      * @sa otPingSenderStop
6294      */
6295     if (aArgs[0] == "stop")
6296     {
6297         otPingSenderStop(GetInstancePtr());
6298         ExitNow();
6299     }
6300     else if (aArgs[0] == "async")
6301     {
6302         async = true;
6303         aArgs++;
6304     }
6305 
6306     memset(&config, 0, sizeof(config));
6307 
6308     if (aArgs[0] == "-I")
6309     {
6310         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(config.mSource));
6311 
6312 #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
6313         {
6314             bool                  valid        = false;
6315             const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(GetInstancePtr());
6316 
6317             for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
6318             {
6319                 if (otIp6IsAddressEqual(&addr->mAddress, &config.mSource))
6320                 {
6321                     valid = true;
6322                     break;
6323                 }
6324             }
6325 
6326             VerifyOrExit(valid, error = OT_ERROR_INVALID_ARGS);
6327         }
6328 #endif
6329 
6330         aArgs += 2;
6331     }
6332 
6333     SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], config.mDestination, nat64SynthesizedAddress));
6334     if (nat64SynthesizedAddress)
6335     {
6336         OutputFormat("Pinging synthesized IPv6 address: ");
6337         OutputIp6AddressLine(config.mDestination);
6338     }
6339 
6340     if (!aArgs[1].IsEmpty())
6341     {
6342         SuccessOrExit(error = aArgs[1].ParseAsUint16(config.mSize));
6343     }
6344 
6345     if (!aArgs[2].IsEmpty())
6346     {
6347         SuccessOrExit(error = aArgs[2].ParseAsUint16(config.mCount));
6348     }
6349 
6350     if (!aArgs[3].IsEmpty())
6351     {
6352         SuccessOrExit(error = ParsePingInterval(aArgs[3], config.mInterval));
6353     }
6354 
6355     if (!aArgs[4].IsEmpty())
6356     {
6357         SuccessOrExit(error = aArgs[4].ParseAsUint8(config.mHopLimit));
6358         config.mAllowZeroHopLimit = (config.mHopLimit == 0);
6359     }
6360 
6361     if (!aArgs[5].IsEmpty())
6362     {
6363         uint32_t timeout;
6364 
6365         SuccessOrExit(error = ParsePingInterval(aArgs[5], timeout));
6366         VerifyOrExit(timeout <= NumericLimits<uint16_t>::kMax, error = OT_ERROR_INVALID_ARGS);
6367         config.mTimeout = static_cast<uint16_t>(timeout);
6368     }
6369 
6370     VerifyOrExit(aArgs[6].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
6371 
6372     config.mReplyCallback      = Interpreter::HandlePingReply;
6373     config.mStatisticsCallback = Interpreter::HandlePingStatistics;
6374     config.mCallbackContext    = this;
6375 
6376     SuccessOrExit(error = otPingSenderPing(GetInstancePtr(), &config));
6377 
6378     mPingIsAsync = async;
6379 
6380     if (!async)
6381     {
6382         error = OT_ERROR_PENDING;
6383     }
6384 
6385 exit:
6386     return error;
6387 }
6388 
6389 #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
6390 
6391 /**
6392  * @cli platform
6393  * @code
6394  * platform
6395  * NRF52840
6396  * Done
6397  * @endcode
6398  * @par
6399  * Print the current platform
6400  */
Process(Arg aArgs[])6401 template <> otError Interpreter::Process<Cmd("platform")>(Arg aArgs[])
6402 {
6403     OT_UNUSED_VARIABLE(aArgs);
6404     OutputLine("%s", OPENTHREAD_CONFIG_PLATFORM_INFO);
6405     return OT_ERROR_NONE;
6406 }
6407 
6408 /**
6409  * @cli pollperiod (get,set)
6410  * @code
6411  * pollperiod
6412  * 0
6413  * Done
6414  * @endcode
6415  * @code
6416  * pollperiod 10
6417  * Done
6418  * @endcode
6419  * @sa otLinkGetPollPeriod
6420  * @sa otLinkSetPollPeriod
6421  * @par
6422  * Get or set the customized data poll period of sleepy end device (milliseconds). Only for certification test.
6423  */
Process(Arg aArgs[])6424 template <> otError Interpreter::Process<Cmd("pollperiod")>(Arg aArgs[])
6425 {
6426     return ProcessGetSet(aArgs, otLinkGetPollPeriod, otLinkSetPollPeriod);
6427 }
6428 
Process(Arg aArgs[])6429 template <> otError Interpreter::Process<Cmd("promiscuous")>(Arg aArgs[])
6430 {
6431     otError error = OT_ERROR_NONE;
6432 
6433     if (aArgs[0].IsEmpty())
6434     {
6435         OutputEnabledDisabledStatus(otLinkIsPromiscuous(GetInstancePtr()) &&
6436                                     otPlatRadioGetPromiscuous(GetInstancePtr()));
6437     }
6438     else
6439     {
6440         bool enable;
6441 
6442         SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
6443 
6444         if (!enable)
6445         {
6446             otLinkSetPcapCallback(GetInstancePtr(), nullptr, nullptr);
6447         }
6448 
6449         SuccessOrExit(error = otLinkSetPromiscuous(GetInstancePtr(), enable));
6450 
6451         if (enable)
6452         {
6453             otLinkSetPcapCallback(GetInstancePtr(), &HandleLinkPcapReceive, this);
6454         }
6455     }
6456 
6457 exit:
6458     return error;
6459 }
6460 
HandleLinkPcapReceive(const otRadioFrame * aFrame,bool aIsTx,void * aContext)6461 void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext)
6462 {
6463     static_cast<Interpreter *>(aContext)->HandleLinkPcapReceive(aFrame, aIsTx);
6464 }
6465 
HandleLinkPcapReceive(const otRadioFrame * aFrame,bool aIsTx)6466 void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx)
6467 {
6468     OT_UNUSED_VARIABLE(aIsTx);
6469 
6470     OutputNewLine();
6471 
6472     for (size_t i = 0; i < 44; i++)
6473     {
6474         OutputFormat("=");
6475     }
6476 
6477     OutputFormat("[len = %3u]", aFrame->mLength);
6478 
6479     for (size_t i = 0; i < 28; i++)
6480     {
6481         OutputFormat("=");
6482     }
6483 
6484     OutputNewLine();
6485 
6486     for (size_t i = 0; i < aFrame->mLength; i += 16)
6487     {
6488         OutputFormat("|");
6489 
6490         for (size_t j = 0; j < 16; j++)
6491         {
6492             if (i + j < aFrame->mLength)
6493             {
6494                 OutputFormat(" %02X", aFrame->mPsdu[i + j]);
6495             }
6496             else
6497             {
6498                 OutputFormat(" ..");
6499             }
6500         }
6501 
6502         OutputFormat("|");
6503 
6504         for (size_t j = 0; j < 16; j++)
6505         {
6506             if (i + j < aFrame->mLength)
6507             {
6508                 if (31 < aFrame->mPsdu[i + j] && aFrame->mPsdu[i + j] < 127)
6509                 {
6510                     OutputFormat(" %c", aFrame->mPsdu[i + j]);
6511                 }
6512                 else
6513                 {
6514                     OutputFormat(" ?");
6515                 }
6516             }
6517             else
6518             {
6519                 OutputFormat(" .");
6520             }
6521         }
6522 
6523         OutputLine("|");
6524     }
6525 
6526     for (size_t i = 0; i < 83; i++)
6527     {
6528         OutputFormat("-");
6529     }
6530 
6531     OutputNewLine();
6532 }
6533 
6534 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
ParsePrefix(Arg aArgs[],otBorderRouterConfig & aConfig)6535 otError Interpreter::ParsePrefix(Arg aArgs[], otBorderRouterConfig &aConfig)
6536 {
6537     otError error = OT_ERROR_NONE;
6538 
6539     memset(&aConfig, 0, sizeof(otBorderRouterConfig));
6540 
6541     SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
6542     aArgs++;
6543 
6544     for (; !aArgs->IsEmpty(); aArgs++)
6545     {
6546         otRoutePreference preference;
6547 
6548         if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
6549         {
6550             aConfig.mPreference = preference;
6551         }
6552         else
6553         {
6554             for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
6555             {
6556                 switch (*arg)
6557                 {
6558                 case 'p':
6559                     aConfig.mPreferred = true;
6560                     break;
6561 
6562                 case 'a':
6563                     aConfig.mSlaac = true;
6564                     break;
6565 
6566                 case 'd':
6567                     aConfig.mDhcp = true;
6568                     break;
6569 
6570                 case 'c':
6571                     aConfig.mConfigure = true;
6572                     break;
6573 
6574                 case 'r':
6575                     aConfig.mDefaultRoute = true;
6576                     break;
6577 
6578                 case 'o':
6579                     aConfig.mOnMesh = true;
6580                     break;
6581 
6582                 case 's':
6583                     aConfig.mStable = true;
6584                     break;
6585 
6586                 case 'n':
6587                     aConfig.mNdDns = true;
6588                     break;
6589 
6590 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
6591                 case 'D':
6592                     aConfig.mDp = true;
6593                     break;
6594 #endif
6595                 case '-':
6596                     break;
6597 
6598                 default:
6599                     ExitNow(error = OT_ERROR_INVALID_ARGS);
6600                 }
6601             }
6602         }
6603     }
6604 
6605 exit:
6606     return error;
6607 }
6608 
Process(Arg aArgs[])6609 template <> otError Interpreter::Process<Cmd("prefix")>(Arg aArgs[])
6610 {
6611     otError error = OT_ERROR_NONE;
6612 
6613     /**
6614      * @cli prefix
6615      * @code
6616      * prefix
6617      * 2001:dead:beef:cafe::/64 paros med
6618      * - fd00:7d03:7d03:7d03::/64 prosD med
6619      * Done
6620      * @endcode
6621      * @par
6622      * Get the prefix list in the local Network Data.
6623      * @note For the Thread 1.2 border router with backbone capability, the local Domain Prefix
6624      * is listed as well and includes the `D` flag. If backbone functionality is disabled, a dash
6625      * `-` is printed before the local Domain Prefix.
6626      * @par
6627      * For more information about #otBorderRouterConfig flags, refer to @overview.
6628      * @sa otBorderRouterGetNextOnMeshPrefix
6629      */
6630     if (aArgs[0].IsEmpty())
6631     {
6632         otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
6633         otBorderRouterConfig  config;
6634 
6635         while (otBorderRouterGetNextOnMeshPrefix(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
6636         {
6637             mNetworkData.OutputPrefix(config);
6638         }
6639 
6640 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
6641         if (otBackboneRouterGetState(GetInstancePtr()) == OT_BACKBONE_ROUTER_STATE_DISABLED)
6642         {
6643             SuccessOrExit(otBackboneRouterGetDomainPrefix(GetInstancePtr(), &config));
6644             OutputFormat("- ");
6645             mNetworkData.OutputPrefix(config);
6646         }
6647 #endif
6648     }
6649     /**
6650      * @cli prefix add
6651      * @code
6652      * prefix add 2001:dead:beef:cafe::/64 paros med
6653      * Done
6654      * @endcode
6655      * @code
6656      * prefix add fd00:7d03:7d03:7d03::/64 prosD low
6657      * Done
6658      * @endcode
6659      * @cparam prefix add @ca{prefix} [@ca{padcrosnD}] [@ca{high}|@ca{med}|@ca{low}]
6660      * OT CLI uses mapped arguments to configure #otBorderRouterConfig values. @moreinfo{the @overview}.
6661      * @par
6662      * Adds a valid prefix to the Network Data.
6663      * @sa otBorderRouterAddOnMeshPrefix
6664      */
6665     else if (aArgs[0] == "add")
6666     {
6667         otBorderRouterConfig config;
6668 
6669         SuccessOrExit(error = ParsePrefix(aArgs + 1, config));
6670         error = otBorderRouterAddOnMeshPrefix(GetInstancePtr(), &config);
6671     }
6672     /**
6673      * @cli prefix remove
6674      * @code
6675      * prefix remove 2001:dead:beef:cafe::/64
6676      * Done
6677      * @endcode
6678      * @par api_copy
6679      * #otBorderRouterRemoveOnMeshPrefix
6680      */
6681     else if (aArgs[0] == "remove")
6682     {
6683         otIp6Prefix prefix;
6684 
6685         SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
6686         error = otBorderRouterRemoveOnMeshPrefix(GetInstancePtr(), &prefix);
6687     }
6688     /**
6689      * @cli prefix meshlocal
6690      * @code
6691      * prefix meshlocal
6692      * fdde:ad00:beef:0::/64
6693      * Done
6694      * @endcode
6695      * @par
6696      * Get the mesh local prefix.
6697      */
6698     else if (aArgs[0] == "meshlocal")
6699     {
6700         if (aArgs[1].IsEmpty())
6701         {
6702             OutputIp6PrefixLine(*otThreadGetMeshLocalPrefix(GetInstancePtr()));
6703         }
6704         else
6705         {
6706             otIp6Prefix prefix;
6707 
6708             SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
6709             VerifyOrExit(prefix.mLength == OT_IP6_PREFIX_BITSIZE, error = OT_ERROR_INVALID_ARGS);
6710             error =
6711                 otThreadSetMeshLocalPrefix(GetInstancePtr(), reinterpret_cast<otMeshLocalPrefix *>(&prefix.mPrefix));
6712         }
6713     }
6714     else
6715     {
6716         error = OT_ERROR_INVALID_COMMAND;
6717     }
6718 
6719 exit:
6720     return error;
6721 }
6722 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
6723 
6724 #if OPENTHREAD_FTD
Process(Arg aArgs[])6725 template <> otError Interpreter::Process<Cmd("preferrouterid")>(Arg aArgs[])
6726 {
6727     return ProcessSet(aArgs, otThreadSetPreferredRouterId);
6728 }
6729 #endif
6730 
6731 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
Process(Arg aArgs[])6732 template <> otError Interpreter::Process<Cmd("radiofilter")>(Arg aArgs[])
6733 {
6734     otError error = OT_ERROR_NONE;
6735 
6736     if (aArgs[0].IsEmpty())
6737     {
6738         OutputEnabledDisabledStatus(otLinkIsRadioFilterEnabled(GetInstancePtr()));
6739     }
6740     else
6741     {
6742         bool enable;
6743 
6744         SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
6745         otLinkSetRadioFilterEnabled(GetInstancePtr(), enable);
6746     }
6747 
6748 exit:
6749     return error;
6750 }
6751 #endif
6752 
Process(Arg aArgs[])6753 template <> otError Interpreter::Process<Cmd("rcp")>(Arg aArgs[])
6754 {
6755     otError     error   = OT_ERROR_NONE;
6756     const char *version = otPlatRadioGetVersionString(GetInstancePtr());
6757 
6758     VerifyOrExit(version != otGetVersionString(), error = OT_ERROR_NOT_IMPLEMENTED);
6759     /**
6760      * @cli rcp version
6761      * @code
6762      * rcp version
6763      * OPENTHREAD/20191113-00825-g82053cc9d-dirty; SIMULATION; Jun  4 2020 17:53:16
6764      * Done
6765      * @endcode
6766      * @par api_copy
6767      * #otPlatRadioGetVersionString
6768      */
6769     if (aArgs[0] == "version")
6770     {
6771         OutputLine("%s", version);
6772     }
6773 
6774     else
6775     {
6776         error = OT_ERROR_INVALID_ARGS;
6777     }
6778 
6779 exit:
6780     return error;
6781 }
6782 /**
6783  * @cli region
6784  * @code
6785  * region
6786  * US
6787  * Done
6788  * @endcode
6789  * @par api_copy
6790  * #otPlatRadioGetRegion
6791  */
Process(Arg aArgs[])6792 template <> otError Interpreter::Process<Cmd("region")>(Arg aArgs[])
6793 {
6794     otError  error = OT_ERROR_NONE;
6795     uint16_t regionCode;
6796 
6797     if (aArgs[0].IsEmpty())
6798     {
6799         SuccessOrExit(error = otPlatRadioGetRegion(GetInstancePtr(), &regionCode));
6800         OutputLine("%c%c", regionCode >> 8, regionCode & 0xff);
6801     }
6802     /**
6803      * @cli region (set)
6804      * @code
6805      * region US
6806      * Done
6807      * @endcode
6808      * @par api_copy
6809      * #otPlatRadioSetRegion
6810      * @par
6811      * Changing this can affect the transmit power limit.
6812      */
6813     else
6814 
6815     {
6816         VerifyOrExit(aArgs[0].GetLength() == 2, error = OT_ERROR_INVALID_ARGS);
6817 
6818         regionCode = static_cast<uint16_t>(static_cast<uint16_t>(aArgs[0].GetCString()[0]) << 8) +
6819                      static_cast<uint16_t>(aArgs[0].GetCString()[1]);
6820         error = otPlatRadioSetRegion(GetInstancePtr(), regionCode);
6821     }
6822 
6823 exit:
6824     return error;
6825 }
6826 
6827 #if OPENTHREAD_FTD
6828 /**
6829  * @cli releaserouterid (routerid)
6830  * @code
6831  * releaserouterid 16
6832  * Done
6833  * @endcode
6834  * @cparam releaserouterid [@ca{routerid}]
6835  * @par api_copy
6836  * #otThreadReleaseRouterId
6837  */
Process(Arg aArgs[])6838 template <> otError Interpreter::Process<Cmd("releaserouterid")>(Arg aArgs[])
6839 
6840 {
6841     return ProcessSet(aArgs, otThreadReleaseRouterId);
6842 }
6843 #endif
6844 /**
6845  * @cli rloc16
6846  * @code
6847  * rloc16
6848  * 0xdead
6849  * Done
6850  * @endcode
6851  * @par api_copy
6852  * #otThreadGetRloc16
6853  */
Process(Arg aArgs[])6854 template <> otError Interpreter::Process<Cmd("rloc16")>(Arg aArgs[])
6855 
6856 {
6857     OT_UNUSED_VARIABLE(aArgs);
6858 
6859     OutputLine("%04x", otThreadGetRloc16(GetInstancePtr()));
6860 
6861     return OT_ERROR_NONE;
6862 }
6863 
6864 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
ParseRoute(Arg aArgs[],otExternalRouteConfig & aConfig)6865 otError Interpreter::ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig)
6866 {
6867     otError error = OT_ERROR_NONE;
6868 
6869     memset(&aConfig, 0, sizeof(otExternalRouteConfig));
6870 
6871     SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
6872     aArgs++;
6873 
6874     for (; !aArgs->IsEmpty(); aArgs++)
6875     {
6876         otRoutePreference preference;
6877 
6878         if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
6879         {
6880             aConfig.mPreference = preference;
6881         }
6882         else
6883         {
6884             for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
6885             {
6886                 switch (*arg)
6887                 {
6888                 case 's':
6889                     aConfig.mStable = true;
6890                     break;
6891 
6892                 case 'n':
6893                     aConfig.mNat64 = true;
6894                     break;
6895 
6896                 case '-':
6897                     break;
6898 
6899                 default:
6900                     ExitNow(error = OT_ERROR_INVALID_ARGS);
6901                 }
6902             }
6903         }
6904     }
6905 
6906 exit:
6907     return error;
6908 }
6909 
Process(Arg aArgs[])6910 template <> otError Interpreter::Process<Cmd("route")>(Arg aArgs[])
6911 {
6912     otError error = OT_ERROR_NONE;
6913     /**
6914      * @cli route
6915      * @code
6916      * route
6917      * 2001:dead:beef:cafe::/64 s med
6918      * Done
6919      * @endcode
6920      * @sa otBorderRouterGetNextRoute
6921      * @par
6922      * Get the external route list in the local Network Data.
6923      */
6924     if (aArgs[0].IsEmpty())
6925     {
6926         otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
6927         otExternalRouteConfig config;
6928 
6929         while (otBorderRouterGetNextRoute(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
6930         {
6931             mNetworkData.OutputRoute(config);
6932         }
6933     }
6934     /**
6935      * @cli route add
6936      * @code
6937      * route add 2001:dead:beef:cafe::/64 s med
6938      * Done
6939      * @endcode
6940      * @par
6941      * For parameters, use:
6942      * *    s: Stable flag
6943      * *    n: NAT64 flag
6944      * *    prf: Default Router Preference, [high, med, low].
6945      * @cparam route add @ca{prefix} [@ca{sn}] [@ca{high}|@ca{med}|@ca{low}]
6946      * @par api_copy
6947      * #otExternalRouteConfig
6948      * @par
6949      * Add a valid external route to the Network Data.
6950      */
6951     else if (aArgs[0] == "add")
6952     {
6953         otExternalRouteConfig config;
6954 
6955         SuccessOrExit(error = ParseRoute(aArgs + 1, config));
6956         error = otBorderRouterAddRoute(GetInstancePtr(), &config);
6957     }
6958     /**
6959      * @cli route remove
6960      * @code
6961      * route remove 2001:dead:beef:cafe::/64
6962      * Done
6963      * @endcode
6964      * @cparam route remove [@ca{prefix}]
6965      * @par api_copy
6966      * #otBorderRouterRemoveRoute
6967      */
6968     else if (aArgs[0] == "remove")
6969     {
6970         otIp6Prefix prefix;
6971 
6972         SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
6973         error = otBorderRouterRemoveRoute(GetInstancePtr(), &prefix);
6974     }
6975     else
6976     {
6977         error = OT_ERROR_INVALID_COMMAND;
6978     }
6979 
6980 exit:
6981     return error;
6982 }
6983 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
6984 
6985 #if OPENTHREAD_FTD
Process(Arg aArgs[])6986 template <> otError Interpreter::Process<Cmd("router")>(Arg aArgs[])
6987 {
6988     otError      error = OT_ERROR_NONE;
6989     otRouterInfo routerInfo;
6990     uint16_t     routerId;
6991     bool         isTable;
6992     /**
6993      * @cli router table
6994      * @code
6995      * router table
6996      * | ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC     | Link |
6997      * +----+--------+----------+-----------+-------+--------+-----+------------------+------+
6998      * | 22 | 0x5800 |       63 |         0 |     0 |      0 |   0 | 0aeb8196c9f61658 |    0 |
6999      * | 49 | 0xc400 |       63 |         0 |     3 |      3 |   0 | faa1c03908e2dbf2 |    1 |
7000      * Done
7001      * @endcode
7002      * @sa otThreadGetRouterInfo
7003      * @par
7004      * Prints a list of routers in a table format.
7005      */
7006     isTable = (aArgs[0] == "table");
7007     /**
7008      * @cli router list
7009      * @code
7010      * router list
7011      * 8 24 50
7012      * Done
7013      * @endcode
7014      * @sa otThreadGetRouterInfo
7015      * @par
7016      * List allocated Router IDs.
7017      */
7018     if (isTable || (aArgs[0] == "list"))
7019     {
7020         uint8_t maxRouterId;
7021 
7022         if (isTable)
7023         {
7024             static const char *const kRouterTableTitles[] = {
7025                 "ID", "RLOC16", "Next Hop", "Path Cost", "LQ In", "LQ Out", "Age", "Extended MAC", "Link",
7026             };
7027 
7028             static const uint8_t kRouterTableColumnWidths[] = {
7029                 4, 8, 10, 11, 7, 8, 5, 18, 6,
7030             };
7031 
7032             OutputTableHeader(kRouterTableTitles, kRouterTableColumnWidths);
7033         }
7034 
7035         maxRouterId = otThreadGetMaxRouterId(GetInstancePtr());
7036 
7037         for (uint8_t i = 0; i <= maxRouterId; i++)
7038         {
7039             if (otThreadGetRouterInfo(GetInstancePtr(), i, &routerInfo) != OT_ERROR_NONE)
7040             {
7041                 continue;
7042             }
7043 
7044             if (isTable)
7045             {
7046                 OutputFormat("| %2u ", routerInfo.mRouterId);
7047                 OutputFormat("| 0x%04x ", routerInfo.mRloc16);
7048                 OutputFormat("| %8u ", routerInfo.mNextHop);
7049                 OutputFormat("| %9u ", routerInfo.mPathCost);
7050                 OutputFormat("| %5u ", routerInfo.mLinkQualityIn);
7051                 OutputFormat("| %6u ", routerInfo.mLinkQualityOut);
7052                 OutputFormat("| %3u ", routerInfo.mAge);
7053                 OutputFormat("| ");
7054                 OutputExtAddress(routerInfo.mExtAddress);
7055                 OutputLine(" | %4d |", routerInfo.mLinkEstablished);
7056             }
7057             else
7058             {
7059                 OutputFormat("%u ", i);
7060             }
7061         }
7062 
7063         OutputNewLine();
7064         ExitNow();
7065     }
7066     /**
7067      * @cli router (id)
7068      * @code
7069      * router 50
7070      * Alloc: 1
7071      * Router ID: 50
7072      * Rloc: c800
7073      * Next Hop: c800
7074      * Link: 1
7075      * Ext Addr: e2b3540590b0fd87
7076      * Cost: 0
7077      * Link Quality In: 3
7078      * Link Quality Out: 3
7079      * Age: 3
7080      * Done
7081      * @endcode
7082      * @code
7083      * router 0xc800
7084      * Alloc: 1
7085      * Router ID: 50
7086      * Rloc: c800
7087      * Next Hop: c800
7088      * Link: 1
7089      * Ext Addr: e2b3540590b0fd87
7090      * Cost: 0
7091      * Link Quality In: 3
7092      * Link Quality Out: 3
7093      * Age: 7
7094      * Done
7095      * @endcode
7096      * @cparam router [@ca{id}]
7097      * @par api_copy
7098      * #otThreadGetRouterInfo
7099      * @par
7100      * Print diagnostic information for a Thread Router. The id may be a Router ID or
7101      * an RLOC16.
7102      */
7103     SuccessOrExit(error = aArgs[0].ParseAsUint16(routerId));
7104     SuccessOrExit(error = otThreadGetRouterInfo(GetInstancePtr(), routerId, &routerInfo));
7105 
7106     OutputLine("Alloc: %d", routerInfo.mAllocated);
7107 
7108     if (routerInfo.mAllocated)
7109     {
7110         OutputLine("Router ID: %u", routerInfo.mRouterId);
7111         OutputLine("Rloc: %04x", routerInfo.mRloc16);
7112         OutputLine("Next Hop: %04x", static_cast<uint16_t>(routerInfo.mNextHop) << 10);
7113         OutputLine("Link: %d", routerInfo.mLinkEstablished);
7114 
7115         if (routerInfo.mLinkEstablished)
7116         {
7117             OutputFormat("Ext Addr: ");
7118             OutputExtAddressLine(routerInfo.mExtAddress);
7119             OutputLine("Cost: %u", routerInfo.mPathCost);
7120             OutputLine("Link Quality In: %u", routerInfo.mLinkQualityIn);
7121             OutputLine("Link Quality Out: %u", routerInfo.mLinkQualityOut);
7122             OutputLine("Age: %u", routerInfo.mAge);
7123         }
7124     }
7125 
7126 exit:
7127     return error;
7128 }
7129 /**
7130  * @cli routerdowngradethreshold (get,set)
7131  * @code routerdowngradethreshold
7132  * 23
7133  * Done
7134  * @endcode
7135  * @code routerdowngradethreshold 23
7136  * Done
7137  * @endcode
7138  * @cparam routerdowngradethreshold [@ca{threshold}]
7139  * @par
7140  * Gets or sets the ROUTER_DOWNGRADE_THRESHOLD value.
7141  * @sa otThreadGetRouterDowngradeThreshold
7142  * @sa otThreadSetRouterDowngradeThreshold
7143  */
Process(Arg aArgs[])7144 template <> otError Interpreter::Process<Cmd("routerdowngradethreshold")>(Arg aArgs[])
7145 {
7146     return ProcessGetSet(aArgs, otThreadGetRouterDowngradeThreshold, otThreadSetRouterDowngradeThreshold);
7147 }
7148 
Process(Arg aArgs[])7149 template <> otError Interpreter::Process<Cmd("routereligible")>(Arg aArgs[])
7150 {
7151     otError error = OT_ERROR_NONE;
7152     /**
7153      * @cli routereligible
7154      * @code
7155      * routereligible
7156      * Enabled
7157      * Done
7158      * @endcode
7159      * @sa otThreadIsRouterEligible
7160      * @par
7161      * Indicates whether the router role is enabled or disabled.
7162      */
7163     if (aArgs[0].IsEmpty())
7164     {
7165         OutputEnabledDisabledStatus(otThreadIsRouterEligible(GetInstancePtr()));
7166     }
7167     /**
7168      * @cli routereligible (enable,disable)
7169      * @code
7170      * routereligible enable
7171      * Done
7172      * @endcode
7173      * @code
7174      * routereligible disable
7175      * Done
7176      * @endcode
7177      * @cparam routereligible [@ca{enable|disable}]
7178      * @sa otThreadSetRouterEligible
7179      * @par
7180      * Enables or disables the router role.
7181      */
7182     else
7183     {
7184         bool enable;
7185 
7186         SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
7187         error = otThreadSetRouterEligible(GetInstancePtr(), enable);
7188     }
7189 
7190 exit:
7191     return error;
7192 }
7193 /**
7194  * @cli routerselectionjitter
7195  * @code
7196  * routerselectionjitter
7197  * 120
7198  * Done
7199  * @endcode
7200  * @code
7201  * routerselectionjitter 120
7202  * Done
7203  * @endcode
7204  * @cparam routerselectionjitter [@ca{jitter}]
7205  * @par
7206  * Gets or sets the ROUTER_SELECTION_JITTER value.
7207  * @sa otThreadGetRouterSelectionJitter
7208  * @sa otThreadSetRouterSelectionJitter
7209  */
Process(Arg aArgs[])7210 template <> otError Interpreter::Process<Cmd("routerselectionjitter")>(Arg aArgs[])
7211 {
7212     return ProcessGetSet(aArgs, otThreadGetRouterSelectionJitter, otThreadSetRouterSelectionJitter);
7213 }
7214 /**
7215  * @cli routerupgradethreshold (get,set)
7216  * @code
7217  * routerupgradethreshold
7218  * 16
7219  * Done
7220  * @endcode
7221  * @code
7222  * routerupgradethreshold 16
7223  * Done
7224  * @endcode
7225  * @cparam routerupgradethreshold [@ca{threshold}]
7226  * @par
7227  * Gets or sets the ROUTER_UPGRADE_THRESHOLD value.
7228  * @sa otThreadGetRouterUpgradeThreshold
7229  * @sa otThreadSetRouterUpgradeThreshold
7230  */
Process(Arg aArgs[])7231 template <> otError Interpreter::Process<Cmd("routerupgradethreshold")>(Arg aArgs[])
7232 {
7233     return ProcessGetSet(aArgs, otThreadGetRouterUpgradeThreshold, otThreadSetRouterUpgradeThreshold);
7234 }
7235 /**
7236  * @cli childrouterlinks (get,set)
7237  * @code
7238  * childrouterlinks
7239  * 16
7240  * Done
7241  * @endcode
7242  * @code
7243  * childrouterlinks 16
7244  * Done
7245  * @endcode
7246  * @cparam childrouterlinks [@ca{links}]
7247  * @par
7248  * Gets or sets the MLE_CHILD_ROUTER_LINKS value.
7249  * @sa otThreadGetChildRouterLinks
7250  * @sa otThreadSetChildRouterLinks
7251  */
Process(Arg aArgs[])7252 template <> otError Interpreter::Process<Cmd("childrouterlinks")>(Arg aArgs[])
7253 {
7254     return ProcessGetSet(aArgs, otThreadGetChildRouterLinks, otThreadSetChildRouterLinks);
7255 }
7256 #endif // OPENTHREAD_FTD
7257 
Process(Arg aArgs[])7258 template <> otError Interpreter::Process<Cmd("scan")>(Arg aArgs[])
7259 {
7260     otError  error        = OT_ERROR_NONE;
7261     uint32_t scanChannels = 0;
7262     uint16_t scanDuration = 0;
7263     bool     energyScan   = false;
7264 
7265     if (aArgs[0] == "energy")
7266     {
7267         energyScan = true;
7268         aArgs++;
7269 
7270         if (!aArgs->IsEmpty())
7271         {
7272             SuccessOrExit(error = aArgs->ParseAsUint16(scanDuration));
7273             aArgs++;
7274         }
7275     }
7276 
7277     if (!aArgs->IsEmpty())
7278     {
7279         uint8_t channel;
7280 
7281         SuccessOrExit(error = aArgs->ParseAsUint8(channel));
7282         VerifyOrExit(channel < sizeof(scanChannels) * CHAR_BIT, error = OT_ERROR_INVALID_ARGS);
7283         scanChannels = 1 << channel;
7284     }
7285 
7286     if (energyScan)
7287     {
7288         static const char *const kEnergyScanTableTitles[]       = {"Ch", "RSSI"};
7289         static const uint8_t     kEnergyScanTableColumnWidths[] = {4, 6};
7290 
7291         OutputTableHeader(kEnergyScanTableTitles, kEnergyScanTableColumnWidths);
7292         SuccessOrExit(error = otLinkEnergyScan(GetInstancePtr(), scanChannels, scanDuration,
7293                                                &Interpreter::HandleEnergyScanResult, this));
7294     }
7295     else
7296     {
7297         static const char *const kScanTableTitles[]       = {"PAN", "MAC Address", "Ch", "dBm", "LQI"};
7298         static const uint8_t     kScanTableColumnWidths[] = {6, 18, 4, 5, 5};
7299 
7300         OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
7301 
7302         SuccessOrExit(error = otLinkActiveScan(GetInstancePtr(), scanChannels, scanDuration,
7303                                                &Interpreter::HandleActiveScanResult, this));
7304     }
7305 
7306     error = OT_ERROR_PENDING;
7307 
7308 exit:
7309     return error;
7310 }
7311 
HandleActiveScanResult(otActiveScanResult * aResult,void * aContext)7312 void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult, void *aContext)
7313 {
7314     static_cast<Interpreter *>(aContext)->HandleActiveScanResult(aResult);
7315 }
7316 
HandleActiveScanResult(otActiveScanResult * aResult)7317 void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult)
7318 {
7319     if (aResult == nullptr)
7320     {
7321         OutputResult(OT_ERROR_NONE);
7322         ExitNow();
7323     }
7324 
7325     if (aResult->mDiscover)
7326     {
7327         OutputFormat("| %-16s ", aResult->mNetworkName.m8);
7328 
7329         OutputFormat("| ");
7330         OutputBytes(aResult->mExtendedPanId.m8);
7331         OutputFormat(" ");
7332     }
7333 
7334     OutputFormat("| %04x | ", aResult->mPanId);
7335     OutputExtAddress(aResult->mExtAddress);
7336     OutputFormat(" | %2u ", aResult->mChannel);
7337     OutputFormat("| %3d ", aResult->mRssi);
7338     OutputLine("| %3u |", aResult->mLqi);
7339 
7340 exit:
7341     return;
7342 }
7343 
HandleEnergyScanResult(otEnergyScanResult * aResult,void * aContext)7344 void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext)
7345 {
7346     static_cast<Interpreter *>(aContext)->HandleEnergyScanResult(aResult);
7347 }
7348 
HandleEnergyScanResult(otEnergyScanResult * aResult)7349 void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult)
7350 {
7351     if (aResult == nullptr)
7352     {
7353         OutputResult(OT_ERROR_NONE);
7354         ExitNow();
7355     }
7356 
7357     OutputLine("| %2u | %4d |", aResult->mChannel, aResult->mMaxRssi);
7358 
7359 exit:
7360     return;
7361 }
7362 
Process(Arg aArgs[])7363 template <> otError Interpreter::Process<Cmd("singleton")>(Arg aArgs[])
7364 {
7365     OT_UNUSED_VARIABLE(aArgs);
7366 
7367     OutputLine(otThreadIsSingleton(GetInstancePtr()) ? "true" : "false");
7368 
7369     return OT_ERROR_NONE;
7370 }
7371 
7372 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
Process(Arg aArgs[])7373 template <> otError Interpreter::Process<Cmd("sntp")>(Arg aArgs[])
7374 {
7375     otError          error = OT_ERROR_NONE;
7376     uint16_t         port  = OT_SNTP_DEFAULT_SERVER_PORT;
7377     Ip6::MessageInfo messageInfo;
7378     otSntpQuery      query;
7379 
7380     if (aArgs[0] == "query")
7381     {
7382         VerifyOrExit(!mSntpQueryingInProgress, error = OT_ERROR_BUSY);
7383 
7384         if (!aArgs[1].IsEmpty())
7385         {
7386             SuccessOrExit(error = aArgs[1].ParseAsIp6Address(messageInfo.GetPeerAddr()));
7387         }
7388         else
7389         {
7390             // Use IPv6 address of default SNTP server.
7391             SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_SNTP_DEFAULT_SERVER_IP));
7392         }
7393 
7394         if (!aArgs[2].IsEmpty())
7395         {
7396             SuccessOrExit(error = aArgs[2].ParseAsUint16(port));
7397         }
7398 
7399         messageInfo.SetPeerPort(port);
7400 
7401         query.mMessageInfo = static_cast<const otMessageInfo *>(&messageInfo);
7402 
7403         SuccessOrExit(error = otSntpClientQuery(GetInstancePtr(), &query, &Interpreter::HandleSntpResponse, this));
7404 
7405         mSntpQueryingInProgress = true;
7406         error                   = OT_ERROR_PENDING;
7407     }
7408     else
7409     {
7410         error = OT_ERROR_INVALID_COMMAND;
7411     }
7412 
7413 exit:
7414     return error;
7415 }
7416 
HandleSntpResponse(void * aContext,uint64_t aTime,otError aResult)7417 void Interpreter::HandleSntpResponse(void *aContext, uint64_t aTime, otError aResult)
7418 {
7419     static_cast<Interpreter *>(aContext)->HandleSntpResponse(aTime, aResult);
7420 }
7421 
HandleSntpResponse(uint64_t aTime,otError aResult)7422 void Interpreter::HandleSntpResponse(uint64_t aTime, otError aResult)
7423 {
7424     if (aResult == OT_ERROR_NONE)
7425     {
7426         // Some Embedded C libraries do not support printing of 64-bit unsigned integers.
7427         // To simplify, unix epoch time and era number are printed separately.
7428         OutputLine("SNTP response - Unix time: %lu (era: %lu)", ToUlong(static_cast<uint32_t>(aTime)),
7429                    ToUlong(static_cast<uint32_t>(aTime >> 32)));
7430     }
7431     else
7432     {
7433         OutputLine("SNTP error - %s", otThreadErrorToString(aResult));
7434     }
7435 
7436     mSntpQueryingInProgress = false;
7437 
7438     OutputResult(OT_ERROR_NONE);
7439 }
7440 #endif // OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
7441 
7442 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
Process(Arg aArgs[])7443 template <> otError Interpreter::Process<Cmd("srp")>(Arg aArgs[])
7444 {
7445     otError error = OT_ERROR_NONE;
7446 
7447     if (aArgs[0].IsEmpty())
7448     {
7449 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
7450         OutputLine("client");
7451 #endif
7452 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
7453         OutputLine("server");
7454 #endif
7455         ExitNow();
7456     }
7457 
7458 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
7459     if (aArgs[0] == "client")
7460     {
7461         ExitNow(error = mSrpClient.Process(aArgs + 1));
7462     }
7463 #endif
7464 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
7465     if (aArgs[0] == "server")
7466     {
7467         ExitNow(error = mSrpServer.Process(aArgs + 1));
7468     }
7469 #endif
7470 
7471     error = OT_ERROR_INVALID_COMMAND;
7472 
7473 exit:
7474     return error;
7475 }
7476 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
7477 
Process(Arg aArgs[])7478 template <> otError Interpreter::Process<Cmd("state")>(Arg aArgs[])
7479 {
7480     otError error = OT_ERROR_NONE;
7481 
7482     if (aArgs[0].IsEmpty())
7483     {
7484         OutputLine("%s", otThreadDeviceRoleToString(otThreadGetDeviceRole(GetInstancePtr())));
7485     }
7486     else if (aArgs[0] == "detached")
7487     {
7488         error = otThreadBecomeDetached(GetInstancePtr());
7489     }
7490     else if (aArgs[0] == "child")
7491     {
7492         error = otThreadBecomeChild(GetInstancePtr());
7493     }
7494 #if OPENTHREAD_FTD
7495     else if (aArgs[0] == "router")
7496     {
7497         error = otThreadBecomeRouter(GetInstancePtr());
7498     }
7499     else if (aArgs[0] == "leader")
7500     {
7501         error = otThreadBecomeLeader(GetInstancePtr());
7502     }
7503 #endif
7504     else
7505     {
7506         error = OT_ERROR_INVALID_ARGS;
7507     }
7508 
7509     return error;
7510 }
7511 
Process(Arg aArgs[])7512 template <> otError Interpreter::Process<Cmd("thread")>(Arg aArgs[])
7513 {
7514     otError error = OT_ERROR_NONE;
7515 
7516     if (aArgs[0] == "start")
7517     {
7518         error = otThreadSetEnabled(GetInstancePtr(), true);
7519     }
7520     else if (aArgs[0] == "stop")
7521     {
7522         error = otThreadSetEnabled(GetInstancePtr(), false);
7523     }
7524     else if (aArgs[0] == "version")
7525     {
7526         OutputLine("%u", otThreadGetVersion());
7527     }
7528     else
7529     {
7530         error = OT_ERROR_INVALID_COMMAND;
7531     }
7532 
7533     return error;
7534 }
7535 
Process(Arg aArgs[])7536 template <> otError Interpreter::Process<Cmd("dataset")>(Arg aArgs[]) { return mDataset.Process(aArgs); }
7537 
Process(Arg aArgs[])7538 template <> otError Interpreter::Process<Cmd("txpower")>(Arg aArgs[])
7539 {
7540     otError error = OT_ERROR_NONE;
7541     int8_t  power;
7542 
7543     if (aArgs[0].IsEmpty())
7544     {
7545         SuccessOrExit(error = otPlatRadioGetTransmitPower(GetInstancePtr(), &power));
7546         OutputLine("%d dBm", power);
7547     }
7548     else
7549     {
7550         SuccessOrExit(error = aArgs[0].ParseAsInt8(power));
7551         error = otPlatRadioSetTransmitPower(GetInstancePtr(), power);
7552     }
7553 
7554 exit:
7555     return error;
7556 }
7557 
7558 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
Process(Arg aArgs[])7559 template <> otError Interpreter::Process<Cmd("tcp")>(Arg aArgs[]) { return mTcp.Process(aArgs); }
7560 #endif
7561 
Process(Arg aArgs[])7562 template <> otError Interpreter::Process<Cmd("udp")>(Arg aArgs[]) { return mUdp.Process(aArgs); }
7563 
Process(Arg aArgs[])7564 template <> otError Interpreter::Process<Cmd("unsecureport")>(Arg aArgs[])
7565 {
7566     otError error = OT_ERROR_NONE;
7567 
7568     if (aArgs[0] == "add")
7569     {
7570         error = ProcessSet(aArgs + 1, otIp6AddUnsecurePort);
7571     }
7572     else if (aArgs[0] == "remove")
7573     {
7574         if (aArgs[1] == "all")
7575         {
7576             otIp6RemoveAllUnsecurePorts(GetInstancePtr());
7577         }
7578         else
7579         {
7580             error = ProcessSet(aArgs + 1, otIp6RemoveUnsecurePort);
7581         }
7582     }
7583     else if (aArgs[0] == "get")
7584     {
7585         const uint16_t *ports;
7586         uint8_t         numPorts;
7587 
7588         ports = otIp6GetUnsecurePorts(GetInstancePtr(), &numPorts);
7589 
7590         if (ports != nullptr)
7591         {
7592             for (uint8_t i = 0; i < numPorts; i++)
7593             {
7594                 OutputFormat("%u ", ports[i]);
7595             }
7596         }
7597 
7598         OutputNewLine();
7599     }
7600     else
7601     {
7602         error = OT_ERROR_INVALID_COMMAND;
7603     }
7604 
7605     return error;
7606 }
7607 
7608 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
Process(Arg aArgs[])7609 template <> otError Interpreter::Process<Cmd("uptime")>(Arg aArgs[])
7610 {
7611     otError error = OT_ERROR_NONE;
7612 
7613     if (aArgs[0].IsEmpty())
7614     {
7615         char string[OT_UPTIME_STRING_SIZE];
7616 
7617         otInstanceGetUptimeAsString(GetInstancePtr(), string, sizeof(string));
7618         OutputLine("%s", string);
7619     }
7620     else if (aArgs[0] == "ms")
7621     {
7622         OutputUint64Line(otInstanceGetUptime(GetInstancePtr()));
7623     }
7624     else
7625     {
7626         error = OT_ERROR_INVALID_ARGS;
7627     }
7628 
7629     return error;
7630 }
7631 #endif
7632 
7633 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
Process(Arg aArgs[])7634 template <> otError Interpreter::Process<Cmd("commissioner")>(Arg aArgs[]) { return mCommissioner.Process(aArgs); }
7635 #endif
7636 
7637 #if OPENTHREAD_CONFIG_JOINER_ENABLE
Process(Arg aArgs[])7638 template <> otError Interpreter::Process<Cmd("joiner")>(Arg aArgs[]) { return mJoiner.Process(aArgs); }
7639 #endif
7640 
7641 #if OPENTHREAD_FTD
7642 /**
7643  * @cli joinerport
7644  * @code
7645  * joinerport
7646  * 1000
7647  * Done
7648  * @endcode
7649  * @par api_copy
7650  * #otThreadGetJoinerUdpPort
7651  */
Process(Arg aArgs[])7652 template <> otError Interpreter::Process<Cmd("joinerport")>(Arg aArgs[])
7653 {
7654     /**
7655      * @cli joinerport (set)
7656      * @code
7657      * joinerport 1000
7658      * Done
7659      * @endcode
7660      * @cparam joinerport @ca{udp-port}
7661      * @par api_copy
7662      * #otThreadSetJoinerUdpPort
7663      */
7664     return ProcessGetSet(aArgs, otThreadGetJoinerUdpPort, otThreadSetJoinerUdpPort);
7665 }
7666 #endif
7667 
7668 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
Process(Arg aArgs[])7669 template <> otError Interpreter::Process<Cmd("macfilter")>(Arg aArgs[])
7670 {
7671     otError error = OT_ERROR_NONE;
7672 
7673     if (aArgs[0].IsEmpty())
7674     {
7675         PrintMacFilter();
7676     }
7677     else if (aArgs[0] == "addr")
7678     {
7679         error = ProcessMacFilterAddress(aArgs + 1);
7680     }
7681     else if (aArgs[0] == "rss")
7682     {
7683         error = ProcessMacFilterRss(aArgs + 1);
7684     }
7685     else
7686     {
7687         error = OT_ERROR_INVALID_COMMAND;
7688     }
7689 
7690     return error;
7691 }
7692 
PrintMacFilter(void)7693 void Interpreter::PrintMacFilter(void)
7694 {
7695     otMacFilterEntry    entry;
7696     otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
7697 
7698     OutputLine("Address Mode: %s", MacFilterAddressModeToString(otLinkFilterGetAddressMode(GetInstancePtr())));
7699 
7700     while (otLinkFilterGetNextAddress(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
7701     {
7702         OutputMacFilterEntry(entry);
7703     }
7704 
7705     iterator = OT_MAC_FILTER_ITERATOR_INIT;
7706     OutputLine("RssIn List:");
7707 
7708     while (otLinkFilterGetNextRssIn(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
7709     {
7710         uint8_t i = 0;
7711 
7712         for (; i < OT_EXT_ADDRESS_SIZE; i++)
7713         {
7714             if (entry.mExtAddress.m8[i] != 0xff)
7715             {
7716                 break;
7717             }
7718         }
7719 
7720         if (i == OT_EXT_ADDRESS_SIZE)
7721         {
7722             OutputLine("Default rss : %d (lqi %u)", entry.mRssIn,
7723                        otLinkConvertRssToLinkQuality(GetInstancePtr(), entry.mRssIn));
7724         }
7725         else
7726         {
7727             OutputMacFilterEntry(entry);
7728         }
7729     }
7730 }
7731 
ProcessMacFilterAddress(Arg aArgs[])7732 otError Interpreter::ProcessMacFilterAddress(Arg aArgs[])
7733 {
7734     otError      error = OT_ERROR_NONE;
7735     otExtAddress extAddr;
7736 
7737     if (aArgs[0].IsEmpty())
7738     {
7739         otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
7740         otMacFilterEntry    entry;
7741 
7742         OutputLine("%s", MacFilterAddressModeToString(otLinkFilterGetAddressMode(GetInstancePtr())));
7743 
7744         while (otLinkFilterGetNextAddress(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
7745         {
7746             OutputMacFilterEntry(entry);
7747         }
7748     }
7749     else if (aArgs[0] == "disable")
7750     {
7751         VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
7752         otLinkFilterSetAddressMode(GetInstancePtr(), OT_MAC_FILTER_ADDRESS_MODE_DISABLED);
7753     }
7754     else if (aArgs[0] == "allowlist")
7755     {
7756         VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
7757         otLinkFilterSetAddressMode(GetInstancePtr(), OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST);
7758     }
7759     else if (aArgs[0] == "denylist")
7760     {
7761         VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
7762         otLinkFilterSetAddressMode(GetInstancePtr(), OT_MAC_FILTER_ADDRESS_MODE_DENYLIST);
7763     }
7764     else if (aArgs[0] == "add")
7765     {
7766         SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
7767         error = otLinkFilterAddAddress(GetInstancePtr(), &extAddr);
7768 
7769         VerifyOrExit(error == OT_ERROR_NONE || error == OT_ERROR_ALREADY);
7770 
7771         if (!aArgs[2].IsEmpty())
7772         {
7773             int8_t rss;
7774 
7775             SuccessOrExit(error = aArgs[2].ParseAsInt8(rss));
7776             SuccessOrExit(error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss));
7777         }
7778     }
7779     else if (aArgs[0] == "remove")
7780     {
7781         SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
7782         otLinkFilterRemoveAddress(GetInstancePtr(), &extAddr);
7783     }
7784     else if (aArgs[0] == "clear")
7785     {
7786         otLinkFilterClearAddresses(GetInstancePtr());
7787     }
7788     else
7789     {
7790         error = OT_ERROR_INVALID_COMMAND;
7791     }
7792 
7793 exit:
7794     return error;
7795 }
7796 
ProcessMacFilterRss(Arg aArgs[])7797 otError Interpreter::ProcessMacFilterRss(Arg aArgs[])
7798 {
7799     otError             error = OT_ERROR_NONE;
7800     otMacFilterEntry    entry;
7801     otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
7802     otExtAddress        extAddr;
7803     int8_t              rss;
7804 
7805     if (aArgs[0].IsEmpty())
7806     {
7807         while (otLinkFilterGetNextRssIn(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
7808         {
7809             uint8_t i = 0;
7810 
7811             for (; i < OT_EXT_ADDRESS_SIZE; i++)
7812             {
7813                 if (entry.mExtAddress.m8[i] != 0xff)
7814                 {
7815                     break;
7816                 }
7817             }
7818 
7819             if (i == OT_EXT_ADDRESS_SIZE)
7820             {
7821                 OutputLine("Default rss: %d (lqi %u)", entry.mRssIn,
7822                            otLinkConvertRssToLinkQuality(GetInstancePtr(), entry.mRssIn));
7823             }
7824             else
7825             {
7826                 OutputMacFilterEntry(entry);
7827             }
7828         }
7829     }
7830     else if (aArgs[0] == "add-lqi")
7831     {
7832         uint8_t linkQuality;
7833 
7834         SuccessOrExit(error = aArgs[2].ParseAsUint8(linkQuality));
7835         VerifyOrExit(linkQuality <= 3, error = OT_ERROR_INVALID_ARGS);
7836         rss = otLinkConvertLinkQualityToRss(GetInstancePtr(), linkQuality);
7837 
7838         if (aArgs[1] == "*")
7839         {
7840             otLinkFilterSetDefaultRssIn(GetInstancePtr(), rss);
7841         }
7842         else
7843         {
7844             SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
7845             error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss);
7846         }
7847     }
7848     else if (aArgs[0] == "add")
7849     {
7850         SuccessOrExit(error = aArgs[2].ParseAsInt8(rss));
7851 
7852         if (aArgs[1] == "*")
7853         {
7854             otLinkFilterSetDefaultRssIn(GetInstancePtr(), rss);
7855         }
7856         else
7857         {
7858             SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
7859             error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss);
7860         }
7861     }
7862     else if (aArgs[0] == "remove")
7863     {
7864         if (aArgs[1] == "*")
7865         {
7866             otLinkFilterClearDefaultRssIn(GetInstancePtr());
7867         }
7868         else
7869         {
7870             SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
7871             otLinkFilterRemoveRssIn(GetInstancePtr(), &extAddr);
7872         }
7873     }
7874     else if (aArgs[0] == "clear")
7875     {
7876         otLinkFilterClearAllRssIn(GetInstancePtr());
7877     }
7878     else
7879     {
7880         error = OT_ERROR_INVALID_COMMAND;
7881     }
7882 
7883 exit:
7884     return error;
7885 }
7886 
OutputMacFilterEntry(const otMacFilterEntry & aEntry)7887 void Interpreter::OutputMacFilterEntry(const otMacFilterEntry &aEntry)
7888 {
7889     OutputExtAddress(aEntry.mExtAddress);
7890 
7891     if (aEntry.mRssIn != OT_MAC_FILTER_FIXED_RSS_DISABLED)
7892     {
7893         OutputFormat(" : rss %d (lqi %d)", aEntry.mRssIn,
7894                      otLinkConvertRssToLinkQuality(GetInstancePtr(), aEntry.mRssIn));
7895     }
7896 
7897     OutputNewLine();
7898 }
7899 
MacFilterAddressModeToString(otMacFilterAddressMode aMode)7900 const char *Interpreter::MacFilterAddressModeToString(otMacFilterAddressMode aMode)
7901 {
7902     static const char *const kModeStrings[] = {
7903         "Disabled",  // (0) OT_MAC_FILTER_ADDRESS_MODE_DISABLED
7904         "Allowlist", // (1) OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST
7905         "Denylist",  // (2) OT_MAC_FILTER_ADDRESS_MODE_DENYLIST
7906     };
7907 
7908     static_assert(0 == OT_MAC_FILTER_ADDRESS_MODE_DISABLED, "OT_MAC_FILTER_ADDRESS_MODE_DISABLED value is incorrect");
7909     static_assert(1 == OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST, "OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST value is incorrect");
7910     static_assert(2 == OT_MAC_FILTER_ADDRESS_MODE_DENYLIST, "OT_MAC_FILTER_ADDRESS_MODE_DENYLIST value is incorrect");
7911 
7912     return Stringify(aMode, kModeStrings);
7913 }
7914 
7915 #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
7916 
Process(Arg aArgs[])7917 template <> otError Interpreter::Process<Cmd("mac")>(Arg aArgs[])
7918 {
7919     otError error = OT_ERROR_NONE;
7920 
7921     if (aArgs[0] == "retries")
7922     {
7923         if (aArgs[1] == "direct")
7924         {
7925             error = ProcessGetSet(aArgs + 2, otLinkGetMaxFrameRetriesDirect, otLinkSetMaxFrameRetriesDirect);
7926         }
7927 #if OPENTHREAD_FTD
7928         else if (aArgs[1] == "indirect")
7929         {
7930             error = ProcessGetSet(aArgs + 2, otLinkGetMaxFrameRetriesIndirect, otLinkSetMaxFrameRetriesIndirect);
7931         }
7932 #endif
7933         else
7934         {
7935             error = OT_ERROR_INVALID_ARGS;
7936         }
7937     }
7938 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
7939     else if (aArgs[0] == "send")
7940     {
7941         VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
7942 
7943         if (aArgs[1] == "datarequest")
7944         {
7945             error = otLinkSendDataRequest(GetInstancePtr());
7946         }
7947         else if (aArgs[1] == "emptydata")
7948         {
7949             error = otLinkSendEmptyData(GetInstancePtr());
7950         }
7951         else
7952         {
7953             error = OT_ERROR_INVALID_ARGS;
7954         }
7955     }
7956 #endif
7957     else
7958     {
7959         error = OT_ERROR_INVALID_COMMAND;
7960         ExitNow(); // To silence unused `exit` label warning when `REFERENCE_DEVICE_ENABLE` is not enabled.
7961     }
7962 
7963 exit:
7964     return error;
7965 }
7966 
7967 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
Process(Arg aArgs[])7968 template <> otError Interpreter::Process<Cmd("trel")>(Arg aArgs[])
7969 {
7970     otError error = OT_ERROR_NONE;
7971     bool    enable;
7972 
7973     if (aArgs[0].IsEmpty())
7974     {
7975         OutputEnabledDisabledStatus(otTrelIsEnabled(GetInstancePtr()));
7976     }
7977     else if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
7978     {
7979         otTrelSetEnabled(GetInstancePtr(), enable);
7980     }
7981     else if (aArgs[0] == "filter")
7982     {
7983         if (aArgs[1].IsEmpty())
7984         {
7985             OutputEnabledDisabledStatus(otTrelIsFilterEnabled(GetInstancePtr()));
7986         }
7987         else
7988         {
7989             SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable));
7990             otTrelSetFilterEnabled(GetInstancePtr(), enable);
7991         }
7992     }
7993     else if (aArgs[0] == "peers")
7994     {
7995         uint16_t           index = 0;
7996         otTrelPeerIterator iterator;
7997         const otTrelPeer  *peer;
7998         bool               isTable = true;
7999 
8000         if (aArgs[1] == "list")
8001         {
8002             isTable = false;
8003         }
8004         else
8005         {
8006             VerifyOrExit(aArgs[1].IsEmpty(), error = kErrorInvalidArgs);
8007         }
8008 
8009         if (isTable)
8010         {
8011             static const char *const kTrelPeerTableTitles[] = {"No", "Ext MAC Address", "Ext PAN Id",
8012                                                                "IPv6 Socket Address"};
8013 
8014             static const uint8_t kTrelPeerTableColumnWidths[] = {5, 18, 18, 50};
8015 
8016             OutputTableHeader(kTrelPeerTableTitles, kTrelPeerTableColumnWidths);
8017         }
8018 
8019         otTrelInitPeerIterator(GetInstancePtr(), &iterator);
8020 
8021         while ((peer = otTrelGetNextPeer(GetInstancePtr(), &iterator)) != nullptr)
8022         {
8023             if (!isTable)
8024             {
8025                 OutputFormat("%03u ExtAddr:", ++index);
8026                 OutputExtAddress(peer->mExtAddress);
8027                 OutputFormat(" ExtPanId:");
8028                 OutputBytes(peer->mExtPanId.m8);
8029                 OutputFormat(" SockAddr:");
8030                 OutputSockAddrLine(peer->mSockAddr);
8031             }
8032             else
8033             {
8034                 char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
8035 
8036                 OutputFormat("| %3u | ", ++index);
8037                 OutputExtAddress(peer->mExtAddress);
8038                 OutputFormat(" | ");
8039                 OutputBytes(peer->mExtPanId.m8);
8040                 otIp6SockAddrToString(&peer->mSockAddr, string, sizeof(string));
8041                 OutputLine(" | %-48s |", string);
8042             }
8043         }
8044     }
8045     else
8046     {
8047         error = OT_ERROR_INVALID_ARGS;
8048     }
8049 
8050 exit:
8051     return error;
8052 }
8053 #endif
8054 
Process(Arg aArgs[])8055 template <> otError Interpreter::Process<Cmd("vendor")>(Arg aArgs[])
8056 {
8057     Error error = OT_ERROR_INVALID_ARGS;
8058 
8059     /**
8060      * @cli vendor name
8061      * @code
8062      * vendor name
8063      * nest
8064      * Done
8065      * @endcode
8066      * @par api_copy
8067      * #otThreadGetVendorName
8068      */
8069     if (aArgs[0] == "name")
8070     {
8071         aArgs++;
8072 
8073 #if !OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
8074         error = ProcessGet(aArgs, otThreadGetVendorName);
8075 #else
8076         /**
8077          * @cli vendor name (name)
8078          * @code
8079          * vendor name nest
8080          * Done
8081          * @endcode
8082          * @par api_copy
8083          * #otThreadSetVendorName
8084          * @cparam vendor name @ca{name}
8085          */
8086         error = ProcessGetSet(aArgs, otThreadGetVendorName, otThreadSetVendorName);
8087 #endif
8088     }
8089     /**
8090      * @cli vendor model
8091      * @code
8092      * vendor model
8093      * Hub Max
8094      * Done
8095      * @endcode
8096      * @par api_copy
8097      * #otThreadGetVendorModel
8098      */
8099     else if (aArgs[0] == "model")
8100     {
8101         aArgs++;
8102 
8103 #if !OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
8104         error = ProcessGet(aArgs, otThreadGetVendorModel);
8105 #else
8106         /**
8107          * @cli vendor model (name)
8108          * @code
8109          * vendor model Hub\ Max
8110          * Done
8111          * @endcode
8112          * @par api_copy
8113          * #otThreadSetVendorModel
8114          * @cparam vendor model @ca{name}
8115          */
8116         error = ProcessGetSet(aArgs, otThreadGetVendorModel, otThreadSetVendorModel);
8117 #endif
8118     }
8119     /**
8120      * @cli vendor swversion
8121      * @code
8122      * vendor swversion
8123      * Marble3.5.1
8124      * Done
8125      * @endcode
8126      * @par api_copy
8127      * #otThreadGetVendorSwVersion
8128      */
8129     else if (aArgs[0] == "swversion")
8130     {
8131         aArgs++;
8132 
8133 #if !OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
8134         error = ProcessGet(aArgs, otThreadGetVendorSwVersion);
8135 #else
8136         /**
8137          * @cli vendor swversion (version)
8138          * @code
8139          * vendor swversion Marble3.5.1
8140          * Done
8141          * @endcode
8142          * @par api_copy
8143          * #otThreadSetVendorSwVersion
8144          * @cparam vendor swversion @ca{version}
8145          */
8146         error = ProcessGetSet(aArgs, otThreadGetVendorSwVersion, otThreadSetVendorSwVersion);
8147 #endif
8148     }
8149 
8150     return error;
8151 }
8152 
8153 #if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
8154 
Process(Arg aArgs[])8155 template <> otError Interpreter::Process<Cmd("networkdiagnostic")>(Arg aArgs[])
8156 {
8157     otError      error = OT_ERROR_NONE;
8158     otIp6Address address;
8159     uint8_t      tlvTypes[OT_NETWORK_DIAGNOSTIC_TYPELIST_MAX_ENTRIES];
8160     uint8_t      count = 0;
8161 
8162     SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
8163 
8164     for (Arg *arg = &aArgs[2]; !arg->IsEmpty(); arg++)
8165     {
8166         VerifyOrExit(count < sizeof(tlvTypes), error = OT_ERROR_INVALID_ARGS);
8167         SuccessOrExit(error = arg->ParseAsUint8(tlvTypes[count++]));
8168     }
8169 
8170     if (aArgs[0] == "get")
8171     {
8172         SuccessOrExit(error = otThreadSendDiagnosticGet(GetInstancePtr(), &address, tlvTypes, count,
8173                                                         &Interpreter::HandleDiagnosticGetResponse, this));
8174         SetCommandTimeout(kNetworkDiagnosticTimeoutMsecs);
8175         error = OT_ERROR_PENDING;
8176     }
8177     else if (aArgs[0] == "reset")
8178     {
8179         IgnoreError(otThreadSendDiagnosticReset(GetInstancePtr(), &address, tlvTypes, count));
8180     }
8181     else
8182     {
8183         error = OT_ERROR_INVALID_COMMAND;
8184     }
8185 
8186 exit:
8187     return error;
8188 }
8189 
HandleDiagnosticGetResponse(otError aError,otMessage * aMessage,const otMessageInfo * aMessageInfo,void * aContext)8190 void Interpreter::HandleDiagnosticGetResponse(otError              aError,
8191                                               otMessage           *aMessage,
8192                                               const otMessageInfo *aMessageInfo,
8193                                               void                *aContext)
8194 {
8195     static_cast<Interpreter *>(aContext)->HandleDiagnosticGetResponse(
8196         aError, aMessage, static_cast<const Ip6::MessageInfo *>(aMessageInfo));
8197 }
8198 
HandleDiagnosticGetResponse(otError aError,const otMessage * aMessage,const Ip6::MessageInfo * aMessageInfo)8199 void Interpreter::HandleDiagnosticGetResponse(otError                 aError,
8200                                               const otMessage        *aMessage,
8201                                               const Ip6::MessageInfo *aMessageInfo)
8202 {
8203     uint8_t               buf[16];
8204     uint16_t              bytesToPrint;
8205     uint16_t              bytesPrinted = 0;
8206     uint16_t              length;
8207     otNetworkDiagTlv      diagTlv;
8208     otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT;
8209 
8210     SuccessOrExit(aError);
8211 
8212     OutputFormat("DIAG_GET.rsp/ans from ");
8213     OutputIp6Address(aMessageInfo->mPeerAddr);
8214     OutputFormat(": ");
8215 
8216     length = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage);
8217 
8218     while (length > 0)
8219     {
8220         bytesToPrint = Min(length, static_cast<uint16_t>(sizeof(buf)));
8221         otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint);
8222 
8223         OutputBytes(buf, static_cast<uint8_t>(bytesToPrint));
8224 
8225         length -= bytesToPrint;
8226         bytesPrinted += bytesToPrint;
8227     }
8228 
8229     OutputNewLine();
8230 
8231     // Output Network Diagnostic TLV values in standard YAML format.
8232     while (otThreadGetNextDiagnosticTlv(aMessage, &iterator, &diagTlv) == OT_ERROR_NONE)
8233     {
8234         switch (diagTlv.mType)
8235         {
8236         case OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS:
8237             OutputFormat("Ext Address: '");
8238             OutputExtAddressLine(diagTlv.mData.mExtAddress);
8239             break;
8240         case OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS:
8241             OutputLine("Rloc16: 0x%04x", diagTlv.mData.mAddr16);
8242             break;
8243         case OT_NETWORK_DIAGNOSTIC_TLV_MODE:
8244             OutputLine("Mode:");
8245             OutputMode(kIndentSize, diagTlv.mData.mMode);
8246             break;
8247         case OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT:
8248             OutputLine("Timeout: %lu", ToUlong(diagTlv.mData.mTimeout));
8249             break;
8250         case OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY:
8251             OutputLine("Connectivity:");
8252             OutputConnectivity(kIndentSize, diagTlv.mData.mConnectivity);
8253             break;
8254         case OT_NETWORK_DIAGNOSTIC_TLV_ROUTE:
8255             OutputLine("Route:");
8256             OutputRoute(kIndentSize, diagTlv.mData.mRoute);
8257             break;
8258         case OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA:
8259             OutputLine("Leader Data:");
8260             OutputLeaderData(kIndentSize, diagTlv.mData.mLeaderData);
8261             break;
8262         case OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA:
8263             OutputFormat("Network Data: '");
8264             OutputBytesLine(diagTlv.mData.mNetworkData.m8, diagTlv.mData.mNetworkData.mCount);
8265             break;
8266         case OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST:
8267             OutputLine("IP6 Address List:");
8268             for (uint16_t i = 0; i < diagTlv.mData.mIp6AddrList.mCount; ++i)
8269             {
8270                 OutputFormat(kIndentSize, "- ");
8271                 OutputIp6AddressLine(diagTlv.mData.mIp6AddrList.mList[i]);
8272             }
8273             break;
8274         case OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS:
8275             OutputLine("MAC Counters:");
8276             OutputNetworkDiagMacCounters(kIndentSize, diagTlv.mData.mMacCounters);
8277             break;
8278         case OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL:
8279             OutputLine("Battery Level: %u%%", diagTlv.mData.mBatteryLevel);
8280             break;
8281         case OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE:
8282             OutputLine("Supply Voltage: %umV", diagTlv.mData.mSupplyVoltage);
8283             break;
8284         case OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE:
8285             OutputLine("Child Table:");
8286             for (uint16_t i = 0; i < diagTlv.mData.mChildTable.mCount; ++i)
8287             {
8288                 OutputFormat(kIndentSize, "- ");
8289                 OutputChildTableEntry(kIndentSize + 2, diagTlv.mData.mChildTable.mTable[i]);
8290             }
8291             break;
8292         case OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES:
8293             OutputFormat("Channel Pages: '");
8294             OutputBytes(diagTlv.mData.mChannelPages.m8, diagTlv.mData.mChannelPages.mCount);
8295             OutputLine("'");
8296             break;
8297         case OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT:
8298             OutputLine("Max Child Timeout: %lu", ToUlong(diagTlv.mData.mMaxChildTimeout));
8299             break;
8300         case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME:
8301             OutputLine("Vendor Name: %s", diagTlv.mData.mVendorName);
8302             break;
8303         case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL:
8304             OutputLine("Vendor Model: %s", diagTlv.mData.mVendorModel);
8305             break;
8306         case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION:
8307             OutputLine("Vendor SW Version: %s", diagTlv.mData.mVendorSwVersion);
8308             break;
8309         case OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION:
8310             OutputLine("Thread Stack Version: %s", diagTlv.mData.mThreadStackVersion);
8311             break;
8312         default:
8313             break;
8314         }
8315     }
8316 
8317 exit:
8318     return;
8319 }
8320 
OutputMode(uint8_t aIndentSize,const otLinkModeConfig & aMode)8321 void Interpreter::OutputMode(uint8_t aIndentSize, const otLinkModeConfig &aMode)
8322 {
8323     OutputLine(aIndentSize, "RxOnWhenIdle: %d", aMode.mRxOnWhenIdle);
8324     OutputLine(aIndentSize, "DeviceType: %d", aMode.mDeviceType);
8325     OutputLine(aIndentSize, "NetworkData: %d", aMode.mNetworkData);
8326 }
8327 
OutputConnectivity(uint8_t aIndentSize,const otNetworkDiagConnectivity & aConnectivity)8328 void Interpreter::OutputConnectivity(uint8_t aIndentSize, const otNetworkDiagConnectivity &aConnectivity)
8329 {
8330     OutputLine(aIndentSize, "ParentPriority: %d", aConnectivity.mParentPriority);
8331     OutputLine(aIndentSize, "LinkQuality3: %u", aConnectivity.mLinkQuality3);
8332     OutputLine(aIndentSize, "LinkQuality2: %u", aConnectivity.mLinkQuality2);
8333     OutputLine(aIndentSize, "LinkQuality1: %u", aConnectivity.mLinkQuality1);
8334     OutputLine(aIndentSize, "LeaderCost: %u", aConnectivity.mLeaderCost);
8335     OutputLine(aIndentSize, "IdSequence: %u", aConnectivity.mIdSequence);
8336     OutputLine(aIndentSize, "ActiveRouters: %u", aConnectivity.mActiveRouters);
8337     OutputLine(aIndentSize, "SedBufferSize: %u", aConnectivity.mSedBufferSize);
8338     OutputLine(aIndentSize, "SedDatagramCount: %u", aConnectivity.mSedDatagramCount);
8339 }
OutputRoute(uint8_t aIndentSize,const otNetworkDiagRoute & aRoute)8340 void Interpreter::OutputRoute(uint8_t aIndentSize, const otNetworkDiagRoute &aRoute)
8341 {
8342     OutputLine(aIndentSize, "IdSequence: %u", aRoute.mIdSequence);
8343     OutputLine(aIndentSize, "RouteData:");
8344 
8345     aIndentSize += kIndentSize;
8346     for (uint16_t i = 0; i < aRoute.mRouteCount; ++i)
8347     {
8348         OutputFormat(aIndentSize, "- ");
8349         OutputRouteData(aIndentSize + 2, aRoute.mRouteData[i]);
8350     }
8351 }
8352 
OutputRouteData(uint8_t aIndentSize,const otNetworkDiagRouteData & aRouteData)8353 void Interpreter::OutputRouteData(uint8_t aIndentSize, const otNetworkDiagRouteData &aRouteData)
8354 {
8355     OutputLine("RouteId: 0x%02x", aRouteData.mRouterId);
8356 
8357     OutputLine(aIndentSize, "LinkQualityOut: %u", aRouteData.mLinkQualityOut);
8358     OutputLine(aIndentSize, "LinkQualityIn: %u", aRouteData.mLinkQualityIn);
8359     OutputLine(aIndentSize, "RouteCost: %u", aRouteData.mRouteCost);
8360 }
8361 
OutputLeaderData(uint8_t aIndentSize,const otLeaderData & aLeaderData)8362 void Interpreter::OutputLeaderData(uint8_t aIndentSize, const otLeaderData &aLeaderData)
8363 {
8364     OutputLine(aIndentSize, "PartitionId: 0x%08lx", ToUlong(aLeaderData.mPartitionId));
8365     OutputLine(aIndentSize, "Weighting: %u", aLeaderData.mWeighting);
8366     OutputLine(aIndentSize, "DataVersion: %u", aLeaderData.mDataVersion);
8367     OutputLine(aIndentSize, "StableDataVersion: %u", aLeaderData.mStableDataVersion);
8368     OutputLine(aIndentSize, "LeaderRouterId: 0x%02x", aLeaderData.mLeaderRouterId);
8369 }
8370 
OutputNetworkDiagMacCounters(uint8_t aIndentSize,const otNetworkDiagMacCounters & aMacCounters)8371 void Interpreter::OutputNetworkDiagMacCounters(uint8_t aIndentSize, const otNetworkDiagMacCounters &aMacCounters)
8372 {
8373     OutputLine(aIndentSize, "IfInUnknownProtos: %lu", ToUlong(aMacCounters.mIfInUnknownProtos));
8374     OutputLine(aIndentSize, "IfInErrors: %lu", ToUlong(aMacCounters.mIfInErrors));
8375     OutputLine(aIndentSize, "IfOutErrors: %lu", ToUlong(aMacCounters.mIfOutErrors));
8376     OutputLine(aIndentSize, "IfInUcastPkts: %lu", ToUlong(aMacCounters.mIfInUcastPkts));
8377     OutputLine(aIndentSize, "IfInBroadcastPkts: %lu", ToUlong(aMacCounters.mIfInBroadcastPkts));
8378     OutputLine(aIndentSize, "IfInDiscards: %lu", ToUlong(aMacCounters.mIfInDiscards));
8379     OutputLine(aIndentSize, "IfOutUcastPkts: %lu", ToUlong(aMacCounters.mIfOutUcastPkts));
8380     OutputLine(aIndentSize, "IfOutBroadcastPkts: %lu", ToUlong(aMacCounters.mIfOutBroadcastPkts));
8381     OutputLine(aIndentSize, "IfOutDiscards: %lu", ToUlong(aMacCounters.mIfOutDiscards));
8382 }
8383 
OutputChildTableEntry(uint8_t aIndentSize,const otNetworkDiagChildEntry & aChildEntry)8384 void Interpreter::OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiagChildEntry &aChildEntry)
8385 {
8386     OutputLine("ChildId: 0x%04x", aChildEntry.mChildId);
8387 
8388     OutputLine(aIndentSize, "Timeout: %u", aChildEntry.mTimeout);
8389     OutputLine(aIndentSize, "Link Quality: %u", aChildEntry.mLinkQuality);
8390     OutputLine(aIndentSize, "Mode:");
8391     OutputMode(aIndentSize + kIndentSize, aChildEntry.mMode);
8392 }
8393 #endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
8394 
HandleDetachGracefullyResult(void * aContext)8395 void Interpreter::HandleDetachGracefullyResult(void *aContext)
8396 {
8397     static_cast<Interpreter *>(aContext)->HandleDetachGracefullyResult();
8398 }
8399 
HandleDetachGracefullyResult(void)8400 void Interpreter::HandleDetachGracefullyResult(void)
8401 {
8402     OutputLine("Finished detaching");
8403     OutputResult(OT_ERROR_NONE);
8404 }
8405 
8406 #if OPENTHREAD_FTD
HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo * aInfo,void * aContext)8407 void Interpreter::HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo *aInfo, void *aContext)
8408 {
8409     static_cast<Interpreter *>(aContext)->HandleDiscoveryRequest(*aInfo);
8410 }
8411 
HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo & aInfo)8412 void Interpreter::HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo &aInfo)
8413 {
8414     OutputFormat("~ Discovery Request from ");
8415     OutputExtAddress(aInfo.mExtAddress);
8416     OutputLine(": version=%u,joiner=%d", aInfo.mVersion, aInfo.mIsJoiner);
8417 }
8418 #endif
8419 
8420 #if OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK
HandleIp6Receive(otMessage * aMessage,void * aContext)8421 void Interpreter::HandleIp6Receive(otMessage *aMessage, void *aContext)
8422 {
8423     OT_UNUSED_VARIABLE(aContext);
8424 
8425     otMessageFree(aMessage);
8426 }
8427 #endif
8428 
8429 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
8430 
Initialize(otInstance * aInstance,otCliOutputCallback aCallback,void * aContext)8431 void Interpreter::Initialize(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
8432 {
8433     Instance *instance = static_cast<Instance *>(aInstance);
8434 
8435     Interpreter::sInterpreter = new (&sInterpreterRaw) Interpreter(instance, aCallback, aContext);
8436 }
8437 
OutputPrompt(void)8438 void Interpreter::OutputPrompt(void)
8439 {
8440 #if OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE
8441     static const char sPrompt[] = "> ";
8442 
8443     // The `OutputFormat()` below is adding the prompt which is not
8444     // part of any command output, so we set the `EmittingCommandOutput`
8445     // flag to false to avoid it being included in the command output
8446     // log (under `OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE`).
8447 
8448     SetEmittingCommandOutput(false);
8449     OutputFormat("%s", sPrompt);
8450     SetEmittingCommandOutput(true);
8451 #endif // OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE
8452 }
8453 
HandleTimer(Timer & aTimer)8454 void Interpreter::HandleTimer(Timer &aTimer)
8455 {
8456     static_cast<Interpreter *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleTimer();
8457 }
8458 
HandleTimer(void)8459 void Interpreter::HandleTimer(void)
8460 {
8461 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
8462     if (mLocateInProgress)
8463     {
8464         mLocateInProgress = false;
8465         OutputResult(OT_ERROR_RESPONSE_TIMEOUT);
8466     }
8467     else
8468 #endif
8469     {
8470         OutputResult(OT_ERROR_NONE);
8471     }
8472 }
8473 
SetCommandTimeout(uint32_t aTimeoutMilli)8474 void Interpreter::SetCommandTimeout(uint32_t aTimeoutMilli)
8475 {
8476     OT_ASSERT(mCommandIsPending);
8477     mTimer.Start(aTimeoutMilli);
8478 }
8479 
ProcessCommand(Arg aArgs[])8480 otError Interpreter::ProcessCommand(Arg aArgs[])
8481 {
8482 #define CmdEntry(aCommandString)                                   \
8483     {                                                              \
8484         aCommandString, &Interpreter::Process<Cmd(aCommandString)> \
8485     }
8486 
8487     static constexpr Command kCommands[] = {
8488 #if OPENTHREAD_FTD || OPENTHREAD_MTD
8489 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
8490         CmdEntry("ba"),
8491 #endif
8492 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
8493         CmdEntry("bbr"),
8494 #endif
8495 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
8496         CmdEntry("br"),
8497 #endif
8498         CmdEntry("bufferinfo"),
8499         CmdEntry("ccathreshold"),
8500 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8501         CmdEntry("ccm"),
8502 #endif
8503         CmdEntry("channel"),
8504 #if OPENTHREAD_FTD
8505         CmdEntry("child"),
8506         CmdEntry("childip"),
8507         CmdEntry("childmax"),
8508         CmdEntry("childrouterlinks"),
8509 #endif
8510         CmdEntry("childsupervision"),
8511         CmdEntry("childtimeout"),
8512 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
8513         CmdEntry("coap"),
8514 #endif
8515 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
8516         CmdEntry("coaps"),
8517 #endif
8518 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
8519         CmdEntry("coex"),
8520 #endif
8521 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
8522         CmdEntry("commissioner"),
8523 #endif
8524 #if OPENTHREAD_FTD
8525         CmdEntry("contextreusedelay"),
8526 #endif
8527         CmdEntry("counters"),
8528 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
8529         CmdEntry("csl"),
8530 #endif
8531         CmdEntry("dataset"),
8532 #if OPENTHREAD_FTD
8533         CmdEntry("delaytimermin"),
8534 #endif
8535         CmdEntry("detach"),
8536 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
8537 #if OPENTHREAD_FTD
8538         CmdEntry("deviceprops"),
8539 #endif
8540 #if OPENTHREAD_CONFIG_DIAG_ENABLE
8541         CmdEntry("diag"),
8542 #endif
8543 #if OPENTHREAD_FTD || OPENTHREAD_MTD
8544         CmdEntry("discover"),
8545         CmdEntry("dns"),
8546 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
8547         CmdEntry("domainname"),
8548 #endif
8549 #if OPENTHREAD_CONFIG_DUA_ENABLE
8550         CmdEntry("dua"),
8551 #endif
8552 #if OPENTHREAD_FTD
8553         CmdEntry("eidcache"),
8554 #endif
8555         CmdEntry("eui64"),
8556         CmdEntry("extaddr"),
8557         CmdEntry("extpanid"),
8558         CmdEntry("factoryreset"),
8559 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8560         CmdEntry("fake"),
8561 #endif
8562         CmdEntry("fem"),
8563 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
8564 #if OPENTHREAD_FTD || OPENTHREAD_MTD
8565 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
8566         CmdEntry("history"),
8567 #endif
8568         CmdEntry("ifconfig"),
8569         CmdEntry("ipaddr"),
8570         CmdEntry("ipmaddr"),
8571 #if OPENTHREAD_CONFIG_JOINER_ENABLE
8572         CmdEntry("joiner"),
8573 #endif
8574 #if OPENTHREAD_FTD
8575         CmdEntry("joinerport"),
8576 #endif
8577         CmdEntry("keysequence"),
8578         CmdEntry("leaderdata"),
8579 #if OPENTHREAD_FTD
8580         CmdEntry("leaderweight"),
8581 #endif
8582 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
8583         CmdEntry("linkmetrics"),
8584 #endif
8585 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
8586         CmdEntry("locate"),
8587 #endif
8588         CmdEntry("log"),
8589         CmdEntry("mac"),
8590 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
8591         CmdEntry("macfilter"),
8592 #endif
8593 #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
8594         CmdEntry("meshdiag"),
8595 #endif
8596 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8597         CmdEntry("mliid"),
8598 #endif
8599 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
8600         CmdEntry("mlr"),
8601 #endif
8602         CmdEntry("mode"),
8603         CmdEntry("multiradio"),
8604 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
8605         CmdEntry("nat64"),
8606 #endif
8607 #if OPENTHREAD_FTD
8608         CmdEntry("neighbor"),
8609 #endif
8610         CmdEntry("netdata"),
8611         CmdEntry("netstat"),
8612 #if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
8613         CmdEntry("networkdiagnostic"),
8614 #endif
8615 #if OPENTHREAD_FTD
8616         CmdEntry("networkidtimeout"),
8617 #endif
8618         CmdEntry("networkkey"),
8619 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
8620         CmdEntry("networkkeyref"),
8621 #endif
8622         CmdEntry("networkname"),
8623 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
8624         CmdEntry("networktime"),
8625 #endif
8626 #if OPENTHREAD_FTD
8627         CmdEntry("nexthop"),
8628 #endif
8629         CmdEntry("panid"),
8630         CmdEntry("parent"),
8631 #if OPENTHREAD_FTD
8632         CmdEntry("parentpriority"),
8633         CmdEntry("partitionid"),
8634 #endif
8635 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
8636         CmdEntry("ping"),
8637 #endif
8638         CmdEntry("platform"),
8639         CmdEntry("pollperiod"),
8640 #if OPENTHREAD_FTD
8641         CmdEntry("preferrouterid"),
8642 #endif
8643 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
8644         CmdEntry("prefix"),
8645 #endif
8646         CmdEntry("promiscuous"),
8647 #if OPENTHREAD_FTD
8648         CmdEntry("pskc"),
8649 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
8650         CmdEntry("pskcref"),
8651 #endif
8652 #endif
8653 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
8654         CmdEntry("radiofilter"),
8655 #endif
8656         CmdEntry("rcp"),
8657         CmdEntry("region"),
8658 #if OPENTHREAD_FTD
8659         CmdEntry("releaserouterid"),
8660 #endif
8661 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
8662         CmdEntry("reset"),
8663 #if OPENTHREAD_FTD || OPENTHREAD_MTD
8664         CmdEntry("rloc16"),
8665 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
8666         CmdEntry("route"),
8667 #endif
8668 #if OPENTHREAD_FTD
8669         CmdEntry("router"),
8670         CmdEntry("routerdowngradethreshold"),
8671         CmdEntry("routereligible"),
8672 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8673         CmdEntry("routeridrange"),
8674 #endif
8675         CmdEntry("routerselectionjitter"),
8676         CmdEntry("routerupgradethreshold"),
8677 #endif
8678         CmdEntry("scan"),
8679 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
8680         CmdEntry("service"),
8681 #endif
8682         CmdEntry("singleton"),
8683 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
8684         CmdEntry("sntp"),
8685 #endif
8686 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
8687         CmdEntry("srp"),
8688 #endif
8689         CmdEntry("state"),
8690 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
8691         CmdEntry("tcp"),
8692 #endif
8693         CmdEntry("thread"),
8694 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
8695         CmdEntry("trel"),
8696 #endif
8697 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8698         CmdEntry("tvcheck"),
8699 #endif
8700         CmdEntry("txpower"),
8701         CmdEntry("udp"),
8702         CmdEntry("unsecureport"),
8703 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
8704         CmdEntry("uptime"),
8705 #endif
8706 #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
8707         CmdEntry("vendor"),
8708 #endif
8709 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
8710         CmdEntry("version"),
8711     };
8712 
8713 #undef CmdEntry
8714 
8715     static_assert(BinarySearch::IsSorted(kCommands), "Command Table is not sorted");
8716 
8717     otError        error   = OT_ERROR_NONE;
8718     const Command *command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
8719 
8720     if (command != nullptr)
8721     {
8722         error = (this->*command->mHandler)(aArgs + 1);
8723     }
8724     else if (aArgs[0] == "help")
8725     {
8726         OutputCommandTable(kCommands);
8727 
8728         for (const UserCommandsEntry &entry : mUserCommands)
8729         {
8730             for (uint8_t i = 0; i < entry.mLength; i++)
8731             {
8732                 OutputLine("%s", entry.mCommands[i].mName);
8733             }
8734         }
8735     }
8736     else
8737     {
8738         error = ProcessUserCommands(aArgs);
8739     }
8740 
8741     return error;
8742 }
8743 
otCliInit(otInstance * aInstance,otCliOutputCallback aCallback,void * aContext)8744 extern "C" void otCliInit(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
8745 {
8746     Interpreter::Initialize(aInstance, aCallback, aContext);
8747 
8748 #if OPENTHREAD_CONFIG_CLI_VENDOR_COMMANDS_ENABLE && OPENTHREAD_CONFIG_CLI_MAX_USER_CMD_ENTRIES > 1
8749     otCliVendorSetUserCommands();
8750 #endif
8751 }
8752 
otCliInputLine(char * aBuf)8753 extern "C" void otCliInputLine(char *aBuf) { Interpreter::GetInterpreter().ProcessLine(aBuf); }
8754 
otCliSetUserCommands(const otCliCommand * aUserCommands,uint8_t aLength,void * aContext)8755 extern "C" otError otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext)
8756 {
8757     return Interpreter::GetInterpreter().SetUserCommands(aUserCommands, aLength, aContext);
8758 }
8759 
otCliOutputBytes(const uint8_t * aBytes,uint8_t aLength)8760 extern "C" void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength)
8761 {
8762     Interpreter::GetInterpreter().OutputBytes(aBytes, aLength);
8763 }
8764 
otCliOutputFormat(const char * aFmt,...)8765 extern "C" void otCliOutputFormat(const char *aFmt, ...)
8766 {
8767     va_list aAp;
8768     va_start(aAp, aFmt);
8769     Interpreter::GetInterpreter().OutputFormatV(aFmt, aAp);
8770     va_end(aAp);
8771 }
8772 
otCliAppendResult(otError aError)8773 extern "C" void otCliAppendResult(otError aError) { Interpreter::GetInterpreter().OutputResult(aError); }
8774 
otCliPlatLogv(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aFormat,va_list aArgs)8775 extern "C" void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)
8776 {
8777     OT_UNUSED_VARIABLE(aLogLevel);
8778     OT_UNUSED_VARIABLE(aLogRegion);
8779 
8780     VerifyOrExit(Interpreter::IsInitialized());
8781 
8782     // CLI output is being used for logging, so we set the flag
8783     // `EmittingCommandOutput` to false indicate this.
8784     Interpreter::GetInterpreter().SetEmittingCommandOutput(false);
8785     Interpreter::GetInterpreter().OutputFormatV(aFormat, aArgs);
8786     Interpreter::GetInterpreter().OutputNewLine();
8787     Interpreter::GetInterpreter().SetEmittingCommandOutput(true);
8788 
8789 exit:
8790     return;
8791 }
8792 
8793 } // namespace Cli
8794 } // namespace ot
8795