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