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