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, ®ionCode));
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