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