1 /*
2 * Copyright (c) 2021, 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 contains implementation of the CLI output module.
32 */
33
34 #include "cli_utils.hpp"
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #if OPENTHREAD_FTD || OPENTHREAD_MTD
41 #include <openthread/dns.h>
42 #endif
43 #include <openthread/logging.h>
44
45 #include "cli/cli.hpp"
46 #include "common/string.hpp"
47
48 namespace ot {
49 namespace Cli {
50
51 const char Utils::kUnknownString[] = "unknown";
52
OutputImplementer(otCliOutputCallback aCallback,void * aCallbackContext)53 OutputImplementer::OutputImplementer(otCliOutputCallback aCallback, void *aCallbackContext)
54 : mCallback(aCallback)
55 , mCallbackContext(aCallbackContext)
56 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
57 , mOutputLength(0)
58 , mEmittingCommandOutput(true)
59 #endif
60 {
61 }
62
OutputFormat(const char * aFormat,...)63 void Utils::OutputFormat(const char *aFormat, ...)
64 {
65 va_list args;
66
67 va_start(args, aFormat);
68 OutputFormatV(aFormat, args);
69 va_end(args);
70 }
71
OutputFormat(uint8_t aIndentSize,const char * aFormat,...)72 void Utils::OutputFormat(uint8_t aIndentSize, const char *aFormat, ...)
73 {
74 va_list args;
75
76 OutputSpaces(aIndentSize);
77
78 va_start(args, aFormat);
79 OutputFormatV(aFormat, args);
80 va_end(args);
81 }
82
OutputLine(const char * aFormat,...)83 void Utils::OutputLine(const char *aFormat, ...)
84 {
85 va_list args;
86
87 va_start(args, aFormat);
88 OutputFormatV(aFormat, args);
89 va_end(args);
90
91 OutputNewLine();
92 }
93
OutputLine(uint8_t aIndentSize,const char * aFormat,...)94 void Utils::OutputLine(uint8_t aIndentSize, const char *aFormat, ...)
95 {
96 va_list args;
97
98 OutputSpaces(aIndentSize);
99
100 va_start(args, aFormat);
101 OutputFormatV(aFormat, args);
102 va_end(args);
103
104 OutputNewLine();
105 }
106
OutputNewLine(void)107 void Utils::OutputNewLine(void) { OutputFormat("\r\n"); }
108
OutputSpaces(uint8_t aCount)109 void Utils::OutputSpaces(uint8_t aCount) { OutputFormat("%*s", aCount, ""); }
110
OutputBytes(const uint8_t * aBytes,uint16_t aLength)111 void Utils::OutputBytes(const uint8_t *aBytes, uint16_t aLength)
112 {
113 for (uint16_t i = 0; i < aLength; i++)
114 {
115 OutputFormat("%02x", aBytes[i]);
116 }
117 }
118
OutputBytesLine(const uint8_t * aBytes,uint16_t aLength)119 void Utils::OutputBytesLine(const uint8_t *aBytes, uint16_t aLength)
120 {
121 OutputBytes(aBytes, aLength);
122 OutputNewLine();
123 }
124
Uint64ToString(uint64_t aUint64,Uint64StringBuffer & aBuffer)125 const char *Utils::Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer)
126 {
127 char *cur = &aBuffer.mChars[Uint64StringBuffer::kSize - 1];
128
129 *cur = '\0';
130
131 if (aUint64 == 0)
132 {
133 *(--cur) = '0';
134 }
135 else
136 {
137 for (; aUint64 != 0; aUint64 /= 10)
138 {
139 *(--cur) = static_cast<char>('0' + static_cast<uint8_t>(aUint64 % 10));
140 }
141 }
142
143 return cur;
144 }
145
OutputUint64(uint64_t aUint64)146 void Utils::OutputUint64(uint64_t aUint64)
147 {
148 Uint64StringBuffer buffer;
149
150 OutputFormat("%s", Uint64ToString(aUint64, buffer));
151 }
152
OutputUint64Line(uint64_t aUint64)153 void Utils::OutputUint64Line(uint64_t aUint64)
154 {
155 OutputUint64(aUint64);
156 OutputNewLine();
157 }
158
OutputEnabledDisabledStatus(bool aEnabled)159 void Utils::OutputEnabledDisabledStatus(bool aEnabled) { OutputLine(aEnabled ? "Enabled" : "Disabled"); }
160
161 #if OPENTHREAD_FTD || OPENTHREAD_MTD
162
OutputIp6Address(const otIp6Address & aAddress)163 void Utils::OutputIp6Address(const otIp6Address &aAddress)
164 {
165 char string[OT_IP6_ADDRESS_STRING_SIZE];
166
167 otIp6AddressToString(&aAddress, string, sizeof(string));
168
169 return OutputFormat("%s", string);
170 }
171
OutputIp6AddressLine(const otIp6Address & aAddress)172 void Utils::OutputIp6AddressLine(const otIp6Address &aAddress)
173 {
174 OutputIp6Address(aAddress);
175 OutputNewLine();
176 }
177
OutputIp6Prefix(const otIp6Prefix & aPrefix)178 void Utils::OutputIp6Prefix(const otIp6Prefix &aPrefix)
179 {
180 char string[OT_IP6_PREFIX_STRING_SIZE];
181
182 otIp6PrefixToString(&aPrefix, string, sizeof(string));
183
184 OutputFormat("%s", string);
185 }
186
OutputIp6PrefixLine(const otIp6Prefix & aPrefix)187 void Utils::OutputIp6PrefixLine(const otIp6Prefix &aPrefix)
188 {
189 OutputIp6Prefix(aPrefix);
190 OutputNewLine();
191 }
192
OutputIp6Prefix(const otIp6NetworkPrefix & aPrefix)193 void Utils::OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix)
194 {
195 OutputFormat("%x:%x:%x:%x::/64", (aPrefix.m8[0] << 8) | aPrefix.m8[1], (aPrefix.m8[2] << 8) | aPrefix.m8[3],
196 (aPrefix.m8[4] << 8) | aPrefix.m8[5], (aPrefix.m8[6] << 8) | aPrefix.m8[7]);
197 }
198
OutputIp6PrefixLine(const otIp6NetworkPrefix & aPrefix)199 void Utils::OutputIp6PrefixLine(const otIp6NetworkPrefix &aPrefix)
200 {
201 OutputIp6Prefix(aPrefix);
202 OutputNewLine();
203 }
204
OutputSockAddr(const otSockAddr & aSockAddr)205 void Utils::OutputSockAddr(const otSockAddr &aSockAddr)
206 {
207 char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
208
209 otIp6SockAddrToString(&aSockAddr, string, sizeof(string));
210
211 return OutputFormat("%s", string);
212 }
213
OutputSockAddrLine(const otSockAddr & aSockAddr)214 void Utils::OutputSockAddrLine(const otSockAddr &aSockAddr)
215 {
216 OutputSockAddr(aSockAddr);
217 OutputNewLine();
218 }
219
OutputDnsTxtData(const uint8_t * aTxtData,uint16_t aTxtDataLength)220 void Utils::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength)
221 {
222 otDnsTxtEntry entry;
223 otDnsTxtEntryIterator iterator;
224 bool isFirst = true;
225
226 otDnsInitTxtEntryIterator(&iterator, aTxtData, aTxtDataLength);
227
228 OutputFormat("[");
229
230 while (otDnsGetNextTxtEntry(&iterator, &entry) == OT_ERROR_NONE)
231 {
232 if (!isFirst)
233 {
234 OutputFormat(", ");
235 }
236
237 if (entry.mKey == nullptr)
238 {
239 // A null `mKey` indicates that the key in the entry is
240 // longer than the recommended max key length, so the entry
241 // could not be parsed. In this case, the whole entry is
242 // returned encoded in `mValue`.
243
244 OutputFormat("[");
245 OutputBytes(entry.mValue, entry.mValueLength);
246 OutputFormat("]");
247 }
248 else
249 {
250 OutputFormat("%s", entry.mKey);
251
252 if (entry.mValue != nullptr)
253 {
254 OutputFormat("=");
255 OutputBytes(entry.mValue, entry.mValueLength);
256 }
257 }
258
259 isFirst = false;
260 }
261
262 OutputFormat("]");
263 }
264
PercentageToString(uint16_t aValue,PercentageStringBuffer & aBuffer)265 const char *Utils::PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer)
266 {
267 uint32_t scaledValue = aValue;
268 StringWriter writer(aBuffer.mChars, sizeof(aBuffer.mChars));
269
270 scaledValue = (scaledValue * 10000) / 0xffff;
271 writer.Append("%u.%02u", static_cast<uint16_t>(scaledValue / 100), static_cast<uint16_t>(scaledValue % 100));
272
273 return aBuffer.mChars;
274 }
275
276 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
277
OutputFormatV(const char * aFormat,va_list aArguments)278 void Utils::OutputFormatV(const char *aFormat, va_list aArguments) { mImplementer.OutputV(aFormat, aArguments); }
279
OutputV(const char * aFormat,va_list aArguments)280 void OutputImplementer::OutputV(const char *aFormat, va_list aArguments)
281 {
282 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
283 va_list args;
284 int charsWritten;
285 bool truncated = false;
286
287 va_copy(args, aArguments);
288 #endif
289
290 mCallback(mCallbackContext, aFormat, aArguments);
291
292 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
293 VerifyOrExit(mEmittingCommandOutput);
294
295 charsWritten = vsnprintf(&mOutputString[mOutputLength], sizeof(mOutputString) - mOutputLength, aFormat, args);
296
297 VerifyOrExit(charsWritten >= 0, mOutputLength = 0);
298
299 if (static_cast<uint32_t>(charsWritten) >= sizeof(mOutputString) - mOutputLength)
300 {
301 truncated = true;
302 mOutputLength = sizeof(mOutputString) - 1;
303 }
304 else
305 {
306 mOutputLength += charsWritten;
307 }
308
309 while (true)
310 {
311 char *lineEnd = strchr(mOutputString, '\r');
312
313 if (lineEnd == nullptr)
314 {
315 break;
316 }
317
318 *lineEnd = '\0';
319
320 if (lineEnd > mOutputString)
321 {
322 otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Output: %s", mOutputString);
323 }
324
325 lineEnd++;
326
327 while ((*lineEnd == '\n') || (*lineEnd == '\r'))
328 {
329 lineEnd++;
330 }
331
332 // Example of the pointers and lengths.
333 //
334 // - mOutputString = "hi\r\nmore"
335 // - mOutputLength = 8
336 // - lineEnd = &mOutputString[4]
337 //
338 //
339 // 0 1 2 3 4 5 6 7 8 9
340 // +----+----+----+----+----+----+----+----+----+---
341 // | h | i | \r | \n | m | o | r | e | \0 |
342 // +----+----+----+----+----+----+----+----+----+---
343 // ^ ^
344 // | |
345 // lineEnd mOutputString[mOutputLength]
346 //
347 //
348 // New length is `&mOutputString[8] - &mOutputString[4] -> 4`.
349 //
350 // We move (newLen + 1 = 5) chars from `lineEnd` to start of
351 // `mOutputString` which will include the `\0` char.
352 //
353 // If `lineEnd` and `mOutputString[mOutputLength]` are the same
354 // the code works correctly as well (new length set to zero and
355 // the `\0` is copied).
356
357 mOutputLength = static_cast<uint16_t>(&mOutputString[mOutputLength] - lineEnd);
358 memmove(mOutputString, lineEnd, mOutputLength + 1);
359 }
360
361 if (truncated)
362 {
363 otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Output: %s ...", mOutputString);
364 mOutputLength = 0;
365 }
366
367 exit:
368 va_end(args);
369 #endif // OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
370 }
371
372 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
LogInput(const Arg * aArgs)373 void Utils::LogInput(const Arg *aArgs)
374 {
375 String<kInputOutputLogStringSize> inputString;
376
377 for (bool isFirst = true; !aArgs->IsEmpty(); aArgs++, isFirst = false)
378 {
379 inputString.Append(isFirst ? "%s" : " %s", aArgs->GetCString());
380 }
381
382 otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Input: %s", inputString.AsCString());
383 }
384 #endif
385
OutputTableHeader(uint8_t aNumColumns,const char * const aTitles[],const uint8_t aWidths[])386 void Utils::OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[])
387 {
388 for (uint8_t index = 0; index < aNumColumns; index++)
389 {
390 const char *title = aTitles[index];
391 uint8_t width = aWidths[index];
392 size_t titleLength = strlen(title);
393
394 if (titleLength + 2 <= width)
395 {
396 // `title` fits in column width so we write it with extra space
397 // at beginning and end ("| Title |").
398
399 OutputFormat("| %*s", -static_cast<int>(width - 1), title);
400 }
401 else
402 {
403 // Use narrow style (no space at beginning) and write as many
404 // chars from `title` as it can fit in the given column width
405 // ("|Title|").
406
407 OutputFormat("|%*.*s", -static_cast<int>(width), width, title);
408 }
409 }
410
411 OutputLine("|");
412 OutputTableSeparator(aNumColumns, aWidths);
413 }
414
OutputTableSeparator(uint8_t aNumColumns,const uint8_t aWidths[])415 void Utils::OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[])
416 {
417 for (uint8_t index = 0; index < aNumColumns; index++)
418 {
419 OutputFormat("+");
420
421 for (uint8_t width = aWidths[index]; width != 0; width--)
422 {
423 OutputFormat("-");
424 }
425 }
426
427 OutputLine("+");
428 }
429
ParseEnableOrDisable(const Arg & aArg,bool & aEnable)430 otError Utils::ParseEnableOrDisable(const Arg &aArg, bool &aEnable)
431 {
432 otError error = OT_ERROR_NONE;
433
434 if (aArg == "enable")
435 {
436 aEnable = true;
437 }
438 else if (aArg == "disable")
439 {
440 aEnable = false;
441 }
442 else
443 {
444 error = OT_ERROR_INVALID_COMMAND;
445 }
446
447 return error;
448 }
449
ProcessEnableDisable(Arg aArgs[],SetEnabledHandler aSetEnabledHandler)450 otError Utils::ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler)
451 {
452 otError error = OT_ERROR_NONE;
453 bool enable;
454
455 if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
456 {
457 aSetEnabledHandler(GetInstancePtr(), enable);
458 }
459 else
460 {
461 error = OT_ERROR_INVALID_COMMAND;
462 }
463
464 return error;
465 }
466
ProcessEnableDisable(Arg aArgs[],SetEnabledHandlerFailable aSetEnabledHandler)467 otError Utils::ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler)
468 {
469 otError error = OT_ERROR_NONE;
470 bool enable;
471
472 if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
473 {
474 error = aSetEnabledHandler(GetInstancePtr(), enable);
475 }
476 else
477 {
478 error = OT_ERROR_INVALID_COMMAND;
479 }
480
481 return error;
482 }
483
ProcessEnableDisable(Arg aArgs[],IsEnabledHandler aIsEnabledHandler,SetEnabledHandler aSetEnabledHandler)484 otError Utils::ProcessEnableDisable(Arg aArgs[],
485 IsEnabledHandler aIsEnabledHandler,
486 SetEnabledHandler aSetEnabledHandler)
487 {
488 otError error = OT_ERROR_NONE;
489
490 if (aArgs[0].IsEmpty())
491 {
492 OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
493 }
494 else
495 {
496 error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
497 }
498
499 return error;
500 }
501
ProcessEnableDisable(Arg aArgs[],IsEnabledHandler aIsEnabledHandler,SetEnabledHandlerFailable aSetEnabledHandler)502 otError Utils::ProcessEnableDisable(Arg aArgs[],
503 IsEnabledHandler aIsEnabledHandler,
504 SetEnabledHandlerFailable aSetEnabledHandler)
505 {
506 otError error = OT_ERROR_NONE;
507
508 if (aArgs[0].IsEmpty())
509 {
510 OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
511 }
512 else
513 {
514 error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
515 }
516
517 return error;
518 }
519
ParseJoinerDiscerner(Arg & aArg,otJoinerDiscerner & aDiscerner)520 otError Utils::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner)
521 {
522 otError error;
523 char *separator;
524
525 VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
526
527 separator = strstr(aArg.GetCString(), "/");
528
529 VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND);
530
531 SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsUint8(separator + 1, aDiscerner.mLength));
532 VerifyOrExit(aDiscerner.mLength > 0 && aDiscerner.mLength <= 64, error = OT_ERROR_INVALID_ARGS);
533 *separator = '\0';
534 error = aArg.ParseAsUint64(aDiscerner.mValue);
535
536 exit:
537 return error;
538 }
539
ParsePreference(const Arg & aArg,otRoutePreference & aPreference)540 otError Utils::ParsePreference(const Arg &aArg, otRoutePreference &aPreference)
541 {
542 otError error = OT_ERROR_NONE;
543
544 if (aArg == "high")
545 {
546 aPreference = OT_ROUTE_PREFERENCE_HIGH;
547 }
548 else if (aArg == "med")
549 {
550 aPreference = OT_ROUTE_PREFERENCE_MED;
551 }
552 else if (aArg == "low")
553 {
554 aPreference = OT_ROUTE_PREFERENCE_LOW;
555 }
556 else
557 {
558 error = OT_ERROR_INVALID_ARGS;
559 }
560
561 return error;
562 }
563
PreferenceToString(signed int aPreference)564 const char *Utils::PreferenceToString(signed int aPreference)
565 {
566 const char *str = "";
567
568 switch (aPreference)
569 {
570 case OT_ROUTE_PREFERENCE_LOW:
571 str = "low";
572 break;
573
574 case OT_ROUTE_PREFERENCE_MED:
575 str = "med";
576 break;
577
578 case OT_ROUTE_PREFERENCE_HIGH:
579 str = "high";
580 break;
581
582 default:
583 break;
584 }
585
586 return str;
587 }
588
589 #if OPENTHREAD_FTD || OPENTHREAD_MTD
ParseToIp6Address(otInstance * aInstance,const Arg & aArg,otIp6Address & aAddress,bool & aSynthesized)590 otError Utils::ParseToIp6Address(otInstance *aInstance, const Arg &aArg, otIp6Address &aAddress, bool &aSynthesized)
591 {
592 Error error = OT_ERROR_NONE;
593
594 VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
595 error = aArg.ParseAsIp6Address(aAddress);
596 aSynthesized = false;
597
598 if (error != OT_ERROR_NONE)
599 {
600 // It might be an IPv4 address, let's have a try.
601 otIp4Address ip4Address;
602
603 // Do not touch the error value if we failed to parse it as an IPv4 address.
604 SuccessOrExit(aArg.ParseAsIp4Address(ip4Address));
605 SuccessOrExit(error = otNat64SynthesizeIp6Address(aInstance, &ip4Address, &aAddress));
606 aSynthesized = true;
607 }
608
609 exit:
610 return error;
611 }
612
613 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
ParsePrefix(Arg aArgs[],otBorderRouterConfig & aConfig)614 otError Utils::ParsePrefix(Arg aArgs[], otBorderRouterConfig &aConfig)
615 {
616 otError error = OT_ERROR_NONE;
617
618 ClearAllBytes(aConfig);
619
620 SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
621 aArgs++;
622
623 for (; !aArgs->IsEmpty(); aArgs++)
624 {
625 otRoutePreference preference;
626
627 if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
628 {
629 aConfig.mPreference = preference;
630 }
631 else
632 {
633 for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
634 {
635 switch (*arg)
636 {
637 case 'p':
638 aConfig.mPreferred = true;
639 break;
640
641 case 'a':
642 aConfig.mSlaac = true;
643 break;
644
645 case 'd':
646 aConfig.mDhcp = true;
647 break;
648
649 case 'c':
650 aConfig.mConfigure = true;
651 break;
652
653 case 'r':
654 aConfig.mDefaultRoute = true;
655 break;
656
657 case 'o':
658 aConfig.mOnMesh = true;
659 break;
660
661 case 's':
662 aConfig.mStable = true;
663 break;
664
665 case 'n':
666 aConfig.mNdDns = true;
667 break;
668
669 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
670 case 'D':
671 aConfig.mDp = true;
672 break;
673 #endif
674 case '-':
675 break;
676
677 default:
678 ExitNow(error = OT_ERROR_INVALID_ARGS);
679 }
680 }
681 }
682 }
683
684 exit:
685 return error;
686 }
687
ParseRoute(Arg aArgs[],otExternalRouteConfig & aConfig)688 otError Utils::ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig)
689 {
690 otError error = OT_ERROR_NONE;
691
692 ClearAllBytes(aConfig);
693
694 SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
695 aArgs++;
696
697 for (; !aArgs->IsEmpty(); aArgs++)
698 {
699 otRoutePreference preference;
700
701 if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
702 {
703 aConfig.mPreference = preference;
704 }
705 else
706 {
707 for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
708 {
709 switch (*arg)
710 {
711 case 's':
712 aConfig.mStable = true;
713 break;
714
715 case 'n':
716 aConfig.mNat64 = true;
717 break;
718
719 case 'a':
720 aConfig.mAdvPio = true;
721 break;
722
723 case '-':
724 break;
725
726 default:
727 ExitNow(error = OT_ERROR_INVALID_ARGS);
728 }
729 }
730 }
731 }
732
733 exit:
734 return error;
735 }
736 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
737 #endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD
738
LinkModeToString(const otLinkModeConfig & aLinkMode,char (& aStringBuffer)[kLinkModeStringSize])739 const char *Utils::LinkModeToString(const otLinkModeConfig &aLinkMode, char (&aStringBuffer)[kLinkModeStringSize])
740 {
741 char *flagsPtr = &aStringBuffer[0];
742
743 if (aLinkMode.mRxOnWhenIdle)
744 {
745 *flagsPtr++ = 'r';
746 }
747
748 if (aLinkMode.mDeviceType)
749 {
750 *flagsPtr++ = 'd';
751 }
752
753 if (aLinkMode.mNetworkData)
754 {
755 *flagsPtr++ = 'n';
756 }
757
758 if (flagsPtr == &aStringBuffer[0])
759 {
760 *flagsPtr++ = '-';
761 }
762
763 *flagsPtr = '\0';
764
765 return aStringBuffer;
766 }
767
AddressOriginToString(uint8_t aOrigin)768 const char *Utils::AddressOriginToString(uint8_t aOrigin)
769 {
770 static const char *const kOriginStrings[4] = {
771 "thread", // 0, OT_ADDRESS_ORIGIN_THREAD
772 "slaac", // 1, OT_ADDRESS_ORIGIN_SLAAC
773 "dhcp6", // 2, OT_ADDRESS_ORIGIN_DHCPV6
774 "manual", // 3, OT_ADDRESS_ORIGIN_MANUAL
775 };
776
777 static_assert(0 == OT_ADDRESS_ORIGIN_THREAD, "OT_ADDRESS_ORIGIN_THREAD value is incorrect");
778 static_assert(1 == OT_ADDRESS_ORIGIN_SLAAC, "OT_ADDRESS_ORIGIN_SLAAC value is incorrect");
779 static_assert(2 == OT_ADDRESS_ORIGIN_DHCPV6, "OT_ADDRESS_ORIGIN_DHCPV6 value is incorrect");
780 static_assert(3 == OT_ADDRESS_ORIGIN_MANUAL, "OT_ADDRESS_ORIGIN_MANUAL value is incorrect");
781
782 return Stringify(aOrigin, kOriginStrings);
783 }
784
785 } // namespace Cli
786 } // namespace ot
787