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