1 /*
2 * Copyright (c) 2020, 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 CLI commands for Network Data.
32 */
33
34 #include "cli_network_data.hpp"
35
36 #include <openthread/border_router.h>
37 #include <openthread/netdata_publisher.h>
38 #include <openthread/server.h>
39
40 #include "cli/cli.hpp"
41 #include "common/encoding.hpp"
42
43 namespace ot {
44 namespace Cli {
45
PrefixFlagsToString(const otBorderRouterConfig & aConfig,FlagsString & aString)46 void NetworkData::PrefixFlagsToString(const otBorderRouterConfig &aConfig, FlagsString &aString)
47 {
48 char *flagsPtr = &aString[0];
49
50 if (aConfig.mPreferred)
51 {
52 *flagsPtr++ = 'p';
53 }
54
55 if (aConfig.mSlaac)
56 {
57 *flagsPtr++ = 'a';
58 }
59
60 if (aConfig.mDhcp)
61 {
62 *flagsPtr++ = 'd';
63 }
64
65 if (aConfig.mConfigure)
66 {
67 *flagsPtr++ = 'c';
68 }
69
70 if (aConfig.mDefaultRoute)
71 {
72 *flagsPtr++ = 'r';
73 }
74
75 if (aConfig.mOnMesh)
76 {
77 *flagsPtr++ = 'o';
78 }
79
80 if (aConfig.mStable)
81 {
82 *flagsPtr++ = 's';
83 }
84
85 if (aConfig.mNdDns)
86 {
87 *flagsPtr++ = 'n';
88 }
89
90 if (aConfig.mDp)
91 {
92 *flagsPtr++ = 'D';
93 }
94
95 *flagsPtr = '\0';
96 }
97
OutputPrefix(const otBorderRouterConfig & aConfig)98 void NetworkData::OutputPrefix(const otBorderRouterConfig &aConfig)
99 {
100 FlagsString flagsString;
101
102 OutputIp6Prefix(aConfig.mPrefix);
103
104 PrefixFlagsToString(aConfig, flagsString);
105
106 if (flagsString[0] != '\0')
107 {
108 OutputFormat(" %s", flagsString);
109 }
110
111 OutputLine(" %s %04x", Interpreter::PreferenceToString(aConfig.mPreference), aConfig.mRloc16);
112 }
113
RouteFlagsToString(const otExternalRouteConfig & aConfig,FlagsString & aString)114 void NetworkData::RouteFlagsToString(const otExternalRouteConfig &aConfig, FlagsString &aString)
115 {
116 char *flagsPtr = &aString[0];
117
118 if (aConfig.mStable)
119 {
120 *flagsPtr++ = 's';
121 }
122
123 if (aConfig.mNat64)
124 {
125 *flagsPtr++ = 'n';
126 }
127
128 *flagsPtr = '\0';
129 }
130
OutputRoute(const otExternalRouteConfig & aConfig)131 void NetworkData::OutputRoute(const otExternalRouteConfig &aConfig)
132 {
133 FlagsString flagsString;
134
135 OutputIp6Prefix(aConfig.mPrefix);
136
137 RouteFlagsToString(aConfig, flagsString);
138
139 if (flagsString[0] != '\0')
140 {
141 OutputFormat(" %s", flagsString);
142 }
143
144 OutputLine(" %s %04x", Interpreter::PreferenceToString(aConfig.mPreference), aConfig.mRloc16);
145 }
146
OutputService(const otServiceConfig & aConfig)147 void NetworkData::OutputService(const otServiceConfig &aConfig)
148 {
149 OutputFormat("%u ", aConfig.mEnterpriseNumber);
150 OutputBytes(aConfig.mServiceData, aConfig.mServiceDataLength);
151 OutputFormat(" ");
152 OutputBytes(aConfig.mServerConfig.mServerData, aConfig.mServerConfig.mServerDataLength);
153
154 if (aConfig.mServerConfig.mStable)
155 {
156 OutputFormat(" s");
157 }
158
159 OutputLine(" %04x", aConfig.mServerConfig.mRloc16);
160 }
161
162 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
Process(Arg aArgs[])163 template <> otError NetworkData::Process<Cmd("publish")>(Arg aArgs[])
164 {
165 otError error = OT_ERROR_NONE;
166
167 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
168 if (aArgs[0] == "dnssrp")
169 {
170 /**
171 * @cli netdata publish dnssrp anycast
172 * @code
173 * netdata publish dnssrp anycast 1
174 * Done
175 * @endcode
176 * @cparam netdata publish dnssrp anycast @ca{seq-num}
177 * @par
178 * Publishes a DNS/SRP Service Anycast Address with a sequence number. Any current
179 * DNS/SRP Service entry being published from a previous `publish dnssrp{anycast|unicast}`
180 * command is removed and replaced with the new arguments.
181 * @par
182 * `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` must be enabled.
183 * @csa{netdata publish dnssrp unicast (addr,port)}
184 * @csa{netdata publish dnssrp unicast (mle)}
185 * @sa otNetDataPublishDnsSrpServiceAnycast
186 * @endcli
187 */
188 if (aArgs[1] == "anycast")
189 {
190 uint8_t sequenceNumber;
191
192 SuccessOrExit(error = aArgs[2].ParseAsUint8(sequenceNumber));
193 otNetDataPublishDnsSrpServiceAnycast(GetInstancePtr(), sequenceNumber);
194 ExitNow();
195 }
196
197 if (aArgs[1] == "unicast")
198 {
199 otIp6Address address;
200 uint16_t port;
201
202 /**
203 * @cli netdata publish dnssrp unicast (mle)
204 * @code
205 * netdata publish dnssrp unicast 50152
206 * Done
207 * @endcode
208 * @cparam netdata publish dnssrp unicast @ca{port}
209 * @par
210 * Publishes the device's Mesh-Local EID with a port number. MLE and port information is
211 * included in the Server TLV data. To use a different Unicast address, use the
212 * `netdata publish dnssrp unicast (addr,port)` command.
213 * @par
214 * Any current DNS/SRP Service entry being published from a previous
215 * `publish dnssrp{anycast|unicast}` command is removed and replaced with the new arguments.
216 * @par
217 * `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` must be enabled.
218 * @csa{netdata publish dnssrp unicast (addr,port)}
219 * @csa{netdata publish dnssrp anycast}
220 * @sa otNetDataPublishDnsSrpServiceUnicastMeshLocalEid
221 */
222 if (aArgs[3].IsEmpty())
223 {
224 SuccessOrExit(error = aArgs[2].ParseAsUint16(port));
225 otNetDataPublishDnsSrpServiceUnicastMeshLocalEid(GetInstancePtr(), port);
226 ExitNow();
227 }
228
229 /**
230 * @cli netdata publish dnssrp unicast (addr,port)
231 * @code
232 * netdata publish dnssrp unicast fd00::1234 51525
233 * Done
234 * @endcode
235 * @cparam netdata publish dnssrp unicast @ca{address} @ca{port}
236 * @par
237 * Publishes a DNS/SRP Service Unicast Address with an address and port number. The address
238 * and port information is included in Service TLV data. Any current DNS/SRP Service entry being
239 * published from a previous `publish dnssrp{anycast|unicast}` command is removed and replaced
240 * with the new arguments.
241 * @par
242 * `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` must be enabled.
243 * @csa{netdata publish dnssrp unicast (mle)}
244 * @csa{netdata publish dnssrp anycast}
245 * @sa otNetDataPublishDnsSrpServiceUnicast
246 */
247 SuccessOrExit(error = aArgs[2].ParseAsIp6Address(address));
248 SuccessOrExit(error = aArgs[3].ParseAsUint16(port));
249 otNetDataPublishDnsSrpServiceUnicast(GetInstancePtr(), &address, port);
250 ExitNow();
251 }
252 }
253 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
254
255 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
256 /**
257 * @cli netdata publish prefix
258 * @code
259 * netdata publish prefix fd00:1234:5678::/64 paos med
260 * Done
261 * @endcode
262 * @cparam netdata publish prefix @ca{prefix} [@ca{padcrosnD}] [@ca{high}|@ca{med}|@ca{low}]
263 * OT CLI uses mapped arguments to configure #otBorderRouterConfig values. @moreinfo{the @overview}.
264 * @par
265 * Publish an on-mesh prefix entry. @moreinfo{@netdata}.
266 * @sa otNetDataPublishOnMeshPrefix
267 */
268 if (aArgs[0] == "prefix")
269 {
270 otBorderRouterConfig config;
271
272 SuccessOrExit(error = Interpreter::ParsePrefix(aArgs + 1, config));
273 error = otNetDataPublishOnMeshPrefix(GetInstancePtr(), &config);
274 ExitNow();
275 }
276
277 /**
278 * @cli netdata publish route
279 * @code
280 * netdata publish route fd00:1234:5678::/64 s high
281 * Done
282 * @endcode
283 * @cparam publish route @ca{prefix} [@ca{sn}] [@ca{high}|@ca{med}|@ca{low}]
284 * OT CLI uses mapped arguments to configure #otExternalRouteConfig values. @moreinfo{the @overview}.
285 * @par
286 * Publish an external route entry. @moreinfo{@netdata}.
287 * @sa otNetDataPublishExternalRoute
288 */
289 if (aArgs[0] == "route")
290 {
291 otExternalRouteConfig config;
292
293 SuccessOrExit(error = Interpreter::ParseRoute(aArgs + 1, config));
294 error = otNetDataPublishExternalRoute(GetInstancePtr(), &config);
295 ExitNow();
296 }
297 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
298
299 error = OT_ERROR_INVALID_ARGS;
300
301 exit:
302 return error;
303 }
304
Process(Arg aArgs[])305 template <> otError NetworkData::Process<Cmd("unpublish")>(Arg aArgs[])
306 {
307 otError error = OT_ERROR_NONE;
308
309 /**
310 * @cli netdata unpublish dnssrp
311 * @code
312 * netdata unpublish dnssrp
313 * Done
314 * @endcode
315 * @par api_copy
316 * #otNetDataUnpublishDnsSrpService
317 */
318 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
319 if (aArgs[0] == "dnssrp")
320 {
321 otNetDataUnpublishDnsSrpService(GetInstancePtr());
322 ExitNow();
323 }
324 #endif
325
326 /**
327 * @cli netdata unpublish (prefix)
328 * @code
329 * netdata unpublish fd00:1234:5678::/64
330 * Done
331 * @endcode
332 * @cparam netdata unpublish @ca{prefix}
333 * @par api_copy
334 * #otNetDataUnpublishPrefix
335 * @par
336 * @moreinfo{@netdata}.
337 */
338 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
339 {
340 otIp6Prefix prefix;
341
342 if (aArgs[0].ParseAsIp6Prefix(prefix) == OT_ERROR_NONE)
343 {
344 error = otNetDataUnpublishPrefix(GetInstancePtr(), &prefix);
345 ExitNow();
346 }
347 }
348 #endif
349
350 error = OT_ERROR_INVALID_ARGS;
351
352 exit:
353 return error;
354 }
355 #endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
356
357 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
358 /**
359 * @cli netdata register
360 * @code
361 * netdata register
362 * Done
363 * @endcode
364 * @par
365 * Register configured prefixes, routes, and services with the Leader.
366 * @par
367 * OT CLI checks for `OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE`. If OTBR is enabled, it
368 * registers local Network Data with the Leader. Otherwise, it calls the CLI function `otServerRegister`.
369 * @moreinfo{@netdata}.
370 * @csa{prefix add}
371 * @sa otBorderRouterRegister
372 * @sa otServerAddService
373 */
Process(Arg aArgs[])374 template <> otError NetworkData::Process<Cmd("register")>(Arg aArgs[])
375 {
376 OT_UNUSED_VARIABLE(aArgs);
377
378 otError error = OT_ERROR_NONE;
379
380 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
381 SuccessOrExit(error = otBorderRouterRegister(GetInstancePtr()));
382 #else
383 SuccessOrExit(error = otServerRegister(GetInstancePtr()));
384 #endif
385
386 exit:
387 return error;
388 }
389 #endif
390
Process(Arg aArgs[])391 template <> otError NetworkData::Process<Cmd("steeringdata")>(Arg aArgs[])
392 {
393 otError error;
394 otExtAddress addr;
395 otJoinerDiscerner discerner;
396
397 VerifyOrExit(aArgs[0] == "check", error = OT_ERROR_INVALID_ARGS);
398
399 error = Interpreter::ParseJoinerDiscerner(aArgs[1], discerner);
400
401 if (error == OT_ERROR_NOT_FOUND)
402 {
403 discerner.mLength = 0;
404 error = aArgs[1].ParseAsHexString(addr.m8);
405 }
406
407 SuccessOrExit(error);
408
409 /**
410 * @cli netdata steeringdata check (discerner)
411 * @code
412 * netdata steeringdata check 0xabc/12
413 * Done
414 * @endcode
415 * @code
416 * netdata steeringdata check 0xdef/12
417 * Error 23: NotFound
418 * @endcode
419 * @cparam netdata steeringdata check @ca{discerner}
420 * * `discerner`: The %Joiner discerner in format `{number}/{length}`.
421 * @par api_copy
422 * #otNetDataSteeringDataCheckJoinerWithDiscerner
423 * @csa{joiner discerner}
424 */
425 if (discerner.mLength)
426 {
427 error = otNetDataSteeringDataCheckJoinerWithDiscerner(GetInstancePtr(), &discerner);
428 }
429 /**
430 * @cli netdata steeringdata check (eui64)
431 * @code
432 * netdata steeringdata check d45e64fa83f81cf7
433 * Done
434 * @endcode
435 * @cparam netdata steeringdata check @ca{eui64}
436 * * `eui64`: The IEEE EUI-64 of the %Joiner.
437 * @par api_copy
438 * #otNetDataSteeringDataCheckJoiner
439 * @csa{eui64}
440 */
441 else
442 {
443 error = otNetDataSteeringDataCheckJoiner(GetInstancePtr(), &addr);
444 }
445
446 exit:
447 return error;
448 }
449
GetNextPrefix(otNetworkDataIterator * aIterator,otBorderRouterConfig * aConfig,bool aLocal)450 otError NetworkData::GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig, bool aLocal)
451 {
452 otError error;
453
454 if (aLocal)
455 {
456 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
457 error = otBorderRouterGetNextOnMeshPrefix(GetInstancePtr(), aIterator, aConfig);
458 #else
459 error = OT_ERROR_NOT_FOUND;
460 #endif
461 }
462 else
463 {
464 error = otNetDataGetNextOnMeshPrefix(GetInstancePtr(), aIterator, aConfig);
465 }
466
467 return error;
468 }
469
OutputPrefixes(bool aLocal)470 void NetworkData::OutputPrefixes(bool aLocal)
471 {
472 otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
473 otBorderRouterConfig config;
474
475 OutputLine("Prefixes:");
476
477 while (GetNextPrefix(&iterator, &config, aLocal) == OT_ERROR_NONE)
478 {
479 OutputPrefix(config);
480 }
481 }
482
GetNextRoute(otNetworkDataIterator * aIterator,otExternalRouteConfig * aConfig,bool aLocal)483 otError NetworkData::GetNextRoute(otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig, bool aLocal)
484 {
485 otError error;
486
487 if (aLocal)
488 {
489 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
490 error = otBorderRouterGetNextRoute(GetInstancePtr(), aIterator, aConfig);
491 #else
492 error = OT_ERROR_NOT_FOUND;
493 #endif
494 }
495 else
496 {
497 error = otNetDataGetNextRoute(GetInstancePtr(), aIterator, aConfig);
498 }
499
500 return error;
501 }
502
OutputRoutes(bool aLocal)503 void NetworkData::OutputRoutes(bool aLocal)
504 {
505 otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
506 otExternalRouteConfig config;
507
508 OutputLine("Routes:");
509
510 while (GetNextRoute(&iterator, &config, aLocal) == OT_ERROR_NONE)
511 {
512 OutputRoute(config);
513 }
514 }
515
GetNextService(otNetworkDataIterator * aIterator,otServiceConfig * aConfig,bool aLocal)516 otError NetworkData::GetNextService(otNetworkDataIterator *aIterator, otServiceConfig *aConfig, bool aLocal)
517 {
518 otError error;
519
520 if (aLocal)
521 {
522 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
523 error = otServerGetNextService(GetInstancePtr(), aIterator, aConfig);
524 #else
525 error = OT_ERROR_NOT_FOUND;
526 #endif
527 }
528 else
529 {
530 error = otNetDataGetNextService(GetInstancePtr(), aIterator, aConfig);
531 }
532
533 return error;
534 }
535
OutputServices(bool aLocal)536 void NetworkData::OutputServices(bool aLocal)
537 {
538 otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
539 otServiceConfig config;
540
541 OutputLine("Services:");
542
543 while (GetNextService(&iterator, &config, aLocal) == OT_ERROR_NONE)
544 {
545 OutputService(config);
546 }
547 }
548
OutputBinary(bool aLocal)549 otError NetworkData::OutputBinary(bool aLocal)
550 {
551 otError error;
552 uint8_t data[255];
553 uint8_t len = sizeof(data);
554
555 if (aLocal)
556 {
557 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
558 error = otBorderRouterGetNetData(GetInstancePtr(), false, data, &len);
559 #else
560 error = OT_ERROR_NOT_IMPLEMENTED;
561 #endif
562 }
563 else
564 {
565 error = otNetDataGet(GetInstancePtr(), false, data, &len);
566 }
567 SuccessOrExit(error);
568
569 OutputBytesLine(data, static_cast<uint8_t>(len));
570
571 exit:
572 return error;
573 }
574
575 /**
576 * @cli netdata show
577 * @code
578 * netdata show
579 * Prefixes:
580 * fd00:dead:beef:cafe::/64 paros med dc00
581 * Routes:
582 * fd49:7770:7fc5:0::/64 s med 4000
583 * Services:
584 * 44970 5d c000 s 4000
585 * 44970 01 9a04b000000e10 s 4000
586 * Done
587 * @endcode
588 * @code
589 * netdata show -x
590 * 08040b02174703140040fd00deadbeefcafe0504dc00330007021140
591 * Done
592 * @endcode
593 * @cparam netdata show [@ca{-x}]
594 * * The optional `-x` argument gets Network Data as hex-encoded TLVs.
595 * @par
596 * `netdata show` from OT CLI gets full Network Data received from the Leader. This command uses several
597 * API functions to combine prefixes, routes, and services, including #otNetDataGetNextOnMeshPrefix,
598 * #otNetDataGetNextRoute, and #otNetDataGetNextService.
599 * @par
600 * @moreinfo{@netdata}.
601 * @csa{br omrprefix}
602 * @csa{br onlinkprefix}
603 * @sa otBorderRouterGetNetData
604 */
Process(Arg aArgs[])605 template <> otError NetworkData::Process<Cmd("show")>(Arg aArgs[])
606 {
607 otError error = OT_ERROR_INVALID_ARGS;
608 bool local = false;
609 bool binary = false;
610
611 for (uint8_t i = 0; !aArgs[i].IsEmpty(); i++)
612 {
613 /**
614 * @cli netdata show local
615 * @code
616 * netdata show local
617 * Prefixes:
618 * fd00:dead:beef:cafe::/64 paros med dc00
619 * Routes:
620 * Services:
621 * Done
622 * @endcode
623 * @code
624 * netdata show local -x
625 * 08040b02174703140040fd00deadbeefcafe0504dc00330007021140
626 * Done
627 * @endcode
628 * @cparam netdata show local [@ca{-x}]
629 * * The optional `-x` argument gets local Network Data as hex-encoded TLVs.
630 * @par
631 * Print local Network Data to sync with the Leader.
632 * @csa{netdata show}
633 */
634 if (aArgs[i] == "local")
635 {
636 local = true;
637 }
638 else if (aArgs[i] == "-x")
639 {
640 binary = true;
641 }
642 else
643 {
644 ExitNow(error = OT_ERROR_INVALID_ARGS);
645 }
646 }
647
648 if (binary)
649 {
650 error = OutputBinary(local);
651 }
652 else
653 {
654 OutputPrefixes(local);
655 OutputRoutes(local);
656 OutputServices(local);
657 error = OT_ERROR_NONE;
658 }
659
660 exit:
661 return error;
662 }
663
Process(Arg aArgs[])664 otError NetworkData::Process(Arg aArgs[])
665 {
666 #define CmdEntry(aCommandString) \
667 { \
668 aCommandString, &NetworkData::Process<Cmd(aCommandString)> \
669 }
670
671 static constexpr Command kCommands[] = {
672 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
673 CmdEntry("publish"),
674 #endif
675 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
676 CmdEntry("register"),
677 #endif
678 CmdEntry("show"),
679 CmdEntry("steeringdata"),
680 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
681 CmdEntry("unpublish"),
682 #endif
683 };
684
685 #undef CmdEntry
686
687 static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
688
689 otError error = OT_ERROR_INVALID_COMMAND;
690 const Command *command;
691
692 /**
693 * @cli netdata help
694 * @code
695 * netdata help
696 * help
697 * publish
698 * register
699 * show
700 * steeringdata
701 * unpublish
702 * Done
703 * @endcode
704 * @par
705 * Gets a list of `netdata` CLI commands.
706 * @sa @netdata
707 */
708 if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
709 {
710 OutputCommandTable(kCommands);
711 ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
712 }
713
714 command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
715 VerifyOrExit(command != nullptr);
716
717 error = (this->*command->mHandler)(aArgs + 1);
718
719 exit:
720 return error;
721 }
722
723 } // namespace Cli
724 } // namespace ot
725