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("%lu ", ToUlong(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 /**
163  * @cli netdata length
164  * @code
165  * netdata length
166  * 23
167  * Done
168  * @endcode
169  * @par api_copy
170  * #otNetDataGetLength
171  */
Process(Arg aArgs[])172 template <> otError NetworkData::Process<Cmd("length")>(Arg aArgs[])
173 {
174     otError error = OT_ERROR_NONE;
175 
176     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
177     OutputLine("%u", otNetDataGetLength(GetInstancePtr()));
178 
179 exit:
180     return error;
181 }
182 
Process(Arg aArgs[])183 template <> otError NetworkData::Process<Cmd("maxlength")>(Arg aArgs[])
184 {
185     otError error = OT_ERROR_NONE;
186 
187     /**
188      * @cli netdata maxlength
189      * @code
190      * netdata maxlength
191      * 40
192      * Done
193      * @endcode
194      * @par api_copy
195      * #otNetDataGetMaxLength
196      */
197     if (aArgs[0].IsEmpty())
198     {
199         OutputLine("%u", otNetDataGetMaxLength(GetInstancePtr()));
200     }
201     /**
202      * @cli netdata maxlength reset
203      * @code
204      * netdata maxlength reset
205      * Done
206      * @endcode
207      * @par api_copy
208      * #otNetDataResetMaxLength
209      */
210     else if (aArgs[0] == "reset")
211     {
212         otNetDataResetMaxLength(GetInstancePtr());
213     }
214     else
215     {
216         error = OT_ERROR_INVALID_ARGS;
217     }
218 
219     return error;
220 }
221 
222 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
Process(Arg aArgs[])223 template <> otError NetworkData::Process<Cmd("publish")>(Arg aArgs[])
224 {
225     otError error = OT_ERROR_NONE;
226 
227 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
228     if (aArgs[0] == "dnssrp")
229     {
230         /**
231          * @cli netdata publish dnssrp anycast
232          * @code
233          * netdata publish dnssrp anycast 1
234          * Done
235          * @endcode
236          * @cparam netdata publish dnssrp anycast @ca{seq-num}
237          * @par
238          * Publishes a DNS/SRP Service Anycast Address with a sequence number. Any current
239          * DNS/SRP Service entry being published from a previous `publish dnssrp{anycast|unicast}`
240          * command is removed and replaced with the new arguments.
241          * @par
242          * `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` must be enabled.
243          * @csa{netdata publish dnssrp unicast (addr,port)}
244          * @csa{netdata publish dnssrp unicast (mle)}
245          * @sa otNetDataPublishDnsSrpServiceAnycast
246          * @endcli
247          */
248         if (aArgs[1] == "anycast")
249         {
250             uint8_t sequenceNumber;
251 
252             SuccessOrExit(error = aArgs[2].ParseAsUint8(sequenceNumber));
253             otNetDataPublishDnsSrpServiceAnycast(GetInstancePtr(), sequenceNumber);
254             ExitNow();
255         }
256 
257         if (aArgs[1] == "unicast")
258         {
259             otIp6Address address;
260             uint16_t     port;
261 
262             /**
263              * @cli netdata publish dnssrp unicast (mle)
264              * @code
265              * netdata publish dnssrp unicast 50152
266              * Done
267              * @endcode
268              * @cparam netdata publish dnssrp unicast @ca{port}
269              * @par
270              * Publishes the device's Mesh-Local EID with a port number. MLE and port information is
271              * included in the Server TLV data. To use a different Unicast address, use the
272              * `netdata publish dnssrp unicast (addr,port)` command.
273              * @par
274              * Any current DNS/SRP Service entry being published from a previous
275              * `publish dnssrp{anycast|unicast}` command is removed and replaced with the new arguments.
276              * @par
277              * `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` must be enabled.
278              * @csa{netdata publish dnssrp unicast (addr,port)}
279              * @csa{netdata publish dnssrp anycast}
280              * @sa otNetDataPublishDnsSrpServiceUnicastMeshLocalEid
281              */
282             if (aArgs[3].IsEmpty())
283             {
284                 SuccessOrExit(error = aArgs[2].ParseAsUint16(port));
285                 otNetDataPublishDnsSrpServiceUnicastMeshLocalEid(GetInstancePtr(), port);
286                 ExitNow();
287             }
288 
289             /**
290              * @cli netdata publish dnssrp unicast (addr,port)
291              * @code
292              * netdata publish dnssrp unicast fd00::1234 51525
293              * Done
294              * @endcode
295              * @cparam netdata publish dnssrp unicast @ca{address} @ca{port}
296              * @par
297              * Publishes a DNS/SRP Service Unicast Address with an address and port number. The address
298              * and port information is included in Service TLV data. Any current DNS/SRP Service entry being
299              * published from a previous `publish dnssrp{anycast|unicast}` command is removed and replaced
300              * with the new arguments.
301              * @par
302              * `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` must be enabled.
303              * @csa{netdata publish dnssrp unicast (mle)}
304              * @csa{netdata publish dnssrp anycast}
305              * @sa otNetDataPublishDnsSrpServiceUnicast
306              */
307             SuccessOrExit(error = aArgs[2].ParseAsIp6Address(address));
308             SuccessOrExit(error = aArgs[3].ParseAsUint16(port));
309             otNetDataPublishDnsSrpServiceUnicast(GetInstancePtr(), &address, port);
310             ExitNow();
311         }
312     }
313 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
314 
315 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
316     /**
317      * @cli netdata publish prefix
318      * @code
319      * netdata publish prefix fd00:1234:5678::/64 paos med
320      * Done
321      * @endcode
322      * @cparam netdata publish prefix @ca{prefix} [@ca{padcrosnD}] [@ca{high}|@ca{med}|@ca{low}]
323      * OT CLI uses mapped arguments to configure #otBorderRouterConfig values. @moreinfo{the @overview}.
324      * @par
325      * Publish an on-mesh prefix entry. @moreinfo{@netdata}.
326      * @sa otNetDataPublishOnMeshPrefix
327      */
328     if (aArgs[0] == "prefix")
329     {
330         otBorderRouterConfig config;
331 
332         SuccessOrExit(error = Interpreter::ParsePrefix(aArgs + 1, config));
333         error = otNetDataPublishOnMeshPrefix(GetInstancePtr(), &config);
334         ExitNow();
335     }
336 
337     /**
338      * @cli netdata publish route
339      * @code
340      * netdata publish route fd00:1234:5678::/64 s high
341      * Done
342      * @endcode
343      * @cparam publish route @ca{prefix} [@ca{sn}] [@ca{high}|@ca{med}|@ca{low}]
344      * OT CLI uses mapped arguments to configure #otExternalRouteConfig values. @moreinfo{the @overview}.
345      * @par
346      * Publish an external route entry. @moreinfo{@netdata}.
347      * @sa otNetDataPublishExternalRoute
348      */
349     if (aArgs[0] == "route")
350     {
351         otExternalRouteConfig config;
352 
353         SuccessOrExit(error = Interpreter::ParseRoute(aArgs + 1, config));
354         error = otNetDataPublishExternalRoute(GetInstancePtr(), &config);
355         ExitNow();
356     }
357 
358     /**
359      * @cli netdata publish replace
360      * @code
361      * netdata publish replace ::/0 fd00:1234:5678::/64 s high
362      * Done
363      * @endcode
364      * @cparam netdata publish replace @ca{oldprefix} @ca{prefix} [@ca{sn}] [@ca{high}|@ca{med}|@ca{low}]
365      * OT CLI uses mapped arguments to configure #otExternalRouteConfig values. @moreinfo{the @overview}.
366      * @par
367      * Replaces a previously published external route entry. @moreinfo{@netdata}.
368      * @sa otNetDataReplacePublishedExternalRoute
369      */
370     if (aArgs[0] == "replace")
371     {
372         otIp6Prefix           prefix;
373         otExternalRouteConfig config;
374 
375         SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
376         SuccessOrExit(error = Interpreter::ParseRoute(aArgs + 2, config));
377         error = otNetDataReplacePublishedExternalRoute(GetInstancePtr(), &prefix, &config);
378         ExitNow();
379     }
380 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
381 
382     error = OT_ERROR_INVALID_ARGS;
383 
384 exit:
385     return error;
386 }
387 
Process(Arg aArgs[])388 template <> otError NetworkData::Process<Cmd("unpublish")>(Arg aArgs[])
389 {
390     otError error = OT_ERROR_NONE;
391 
392 /**
393  * @cli netdata unpublish dnssrp
394  * @code
395  * netdata unpublish dnssrp
396  * Done
397  * @endcode
398  * @par api_copy
399  * #otNetDataUnpublishDnsSrpService
400  */
401 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
402     if (aArgs[0] == "dnssrp")
403     {
404         otNetDataUnpublishDnsSrpService(GetInstancePtr());
405         ExitNow();
406     }
407 #endif
408 
409 /**
410  * @cli netdata unpublish (prefix)
411  * @code
412  * netdata unpublish fd00:1234:5678::/64
413  * Done
414  * @endcode
415  * @cparam netdata unpublish @ca{prefix}
416  * @par api_copy
417  * #otNetDataUnpublishPrefix
418  * @par
419  * @moreinfo{@netdata}.
420  */
421 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
422     {
423         otIp6Prefix prefix;
424 
425         if (aArgs[0].ParseAsIp6Prefix(prefix) == OT_ERROR_NONE)
426         {
427             error = otNetDataUnpublishPrefix(GetInstancePtr(), &prefix);
428             ExitNow();
429         }
430     }
431 #endif
432 
433     error = OT_ERROR_INVALID_ARGS;
434 
435 exit:
436     return error;
437 }
438 #endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
439 
440 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
441 /**
442  * @cli netdata register
443  * @code
444  * netdata register
445  * Done
446  * @endcode
447  * @par
448  * Register configured prefixes, routes, and services with the Leader.
449  * @par
450  * OT CLI checks for `OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE`. If OTBR is enabled, it
451  * registers local Network Data with the Leader. Otherwise, it calls the CLI function `otServerRegister`.
452  * @moreinfo{@netdata}.
453  * @csa{prefix add}
454  * @sa otBorderRouterRegister
455  * @sa otServerAddService
456  */
Process(Arg aArgs[])457 template <> otError NetworkData::Process<Cmd("register")>(Arg aArgs[])
458 {
459     OT_UNUSED_VARIABLE(aArgs);
460 
461     otError error = OT_ERROR_NONE;
462 
463 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
464     SuccessOrExit(error = otBorderRouterRegister(GetInstancePtr()));
465 #else
466     SuccessOrExit(error = otServerRegister(GetInstancePtr()));
467 #endif
468 
469 exit:
470     return error;
471 }
472 #endif
473 
Process(Arg aArgs[])474 template <> otError NetworkData::Process<Cmd("steeringdata")>(Arg aArgs[])
475 {
476     otError           error;
477     otExtAddress      addr;
478     otJoinerDiscerner discerner;
479 
480     VerifyOrExit(aArgs[0] == "check", error = OT_ERROR_INVALID_ARGS);
481 
482     error = Interpreter::ParseJoinerDiscerner(aArgs[1], discerner);
483 
484     if (error == OT_ERROR_NOT_FOUND)
485     {
486         discerner.mLength = 0;
487         error             = aArgs[1].ParseAsHexString(addr.m8);
488     }
489 
490     SuccessOrExit(error);
491 
492     /**
493      * @cli netdata steeringdata check (discerner)
494      * @code
495      * netdata steeringdata check 0xabc/12
496      * Done
497      * @endcode
498      * @code
499      * netdata steeringdata check 0xdef/12
500      * Error 23: NotFound
501      * @endcode
502      * @cparam netdata steeringdata check @ca{discerner}
503      * *   `discerner`: The %Joiner discerner in format `{number}/{length}`.
504      * @par api_copy
505      * #otNetDataSteeringDataCheckJoinerWithDiscerner
506      * @csa{joiner discerner}
507      */
508     if (discerner.mLength)
509     {
510         error = otNetDataSteeringDataCheckJoinerWithDiscerner(GetInstancePtr(), &discerner);
511     }
512     /**
513      * @cli netdata steeringdata check (eui64)
514      * @code
515      * netdata steeringdata check d45e64fa83f81cf7
516      * Done
517      * @endcode
518      * @cparam netdata steeringdata check @ca{eui64}
519      * *   `eui64`: The IEEE EUI-64 of the %Joiner.
520      * @par api_copy
521      * #otNetDataSteeringDataCheckJoiner
522      * @csa{eui64}
523      */
524     else
525     {
526         error = otNetDataSteeringDataCheckJoiner(GetInstancePtr(), &addr);
527     }
528 
529 exit:
530     return error;
531 }
532 
GetNextPrefix(otNetworkDataIterator * aIterator,otBorderRouterConfig * aConfig,bool aLocal)533 otError NetworkData::GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig, bool aLocal)
534 {
535     otError error;
536 
537     if (aLocal)
538     {
539 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
540         error = otBorderRouterGetNextOnMeshPrefix(GetInstancePtr(), aIterator, aConfig);
541 #else
542         error = OT_ERROR_NOT_FOUND;
543 #endif
544     }
545     else
546     {
547         error = otNetDataGetNextOnMeshPrefix(GetInstancePtr(), aIterator, aConfig);
548     }
549 
550     return error;
551 }
552 
OutputPrefixes(bool aLocal)553 void NetworkData::OutputPrefixes(bool aLocal)
554 {
555     otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
556     otBorderRouterConfig  config;
557 
558     OutputLine("Prefixes:");
559 
560     while (GetNextPrefix(&iterator, &config, aLocal) == OT_ERROR_NONE)
561     {
562         OutputPrefix(config);
563     }
564 }
565 
GetNextRoute(otNetworkDataIterator * aIterator,otExternalRouteConfig * aConfig,bool aLocal)566 otError NetworkData::GetNextRoute(otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig, bool aLocal)
567 {
568     otError error;
569 
570     if (aLocal)
571     {
572 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
573         error = otBorderRouterGetNextRoute(GetInstancePtr(), aIterator, aConfig);
574 #else
575         error = OT_ERROR_NOT_FOUND;
576 #endif
577     }
578     else
579     {
580         error = otNetDataGetNextRoute(GetInstancePtr(), aIterator, aConfig);
581     }
582 
583     return error;
584 }
585 
OutputRoutes(bool aLocal)586 void NetworkData::OutputRoutes(bool aLocal)
587 {
588     otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
589     otExternalRouteConfig config;
590 
591     OutputLine("Routes:");
592 
593     while (GetNextRoute(&iterator, &config, aLocal) == OT_ERROR_NONE)
594     {
595         OutputRoute(config);
596     }
597 }
598 
GetNextService(otNetworkDataIterator * aIterator,otServiceConfig * aConfig,bool aLocal)599 otError NetworkData::GetNextService(otNetworkDataIterator *aIterator, otServiceConfig *aConfig, bool aLocal)
600 {
601     otError error;
602 
603     if (aLocal)
604     {
605 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
606         error = otServerGetNextService(GetInstancePtr(), aIterator, aConfig);
607 #else
608         error = OT_ERROR_NOT_FOUND;
609 #endif
610     }
611     else
612     {
613         error = otNetDataGetNextService(GetInstancePtr(), aIterator, aConfig);
614     }
615 
616     return error;
617 }
618 
OutputServices(bool aLocal)619 void NetworkData::OutputServices(bool aLocal)
620 {
621     otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
622     otServiceConfig       config;
623 
624     OutputLine("Services:");
625 
626     while (GetNextService(&iterator, &config, aLocal) == OT_ERROR_NONE)
627     {
628         OutputService(config);
629     }
630 }
631 
OutputLowpanContexts(bool aLocal)632 void NetworkData::OutputLowpanContexts(bool aLocal)
633 {
634     otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
635     otLowpanContextInfo   info;
636 
637     VerifyOrExit(!aLocal);
638 
639     OutputLine("Contexts:");
640 
641     while (otNetDataGetNextLowpanContextInfo(GetInstancePtr(), &iterator, &info) == OT_ERROR_NONE)
642     {
643         OutputIp6Prefix(info.mPrefix);
644         OutputLine(" %u %c", info.mContextId, info.mCompressFlag ? 'c' : '-');
645     }
646 
647 exit:
648     return;
649 }
650 
OutputBinary(bool aLocal)651 otError NetworkData::OutputBinary(bool aLocal)
652 {
653     otError error;
654     uint8_t data[255];
655     uint8_t len = sizeof(data);
656 
657     if (aLocal)
658     {
659 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
660         error = otBorderRouterGetNetData(GetInstancePtr(), false, data, &len);
661 #else
662         error = OT_ERROR_NOT_IMPLEMENTED;
663 #endif
664     }
665     else
666     {
667         error = otNetDataGet(GetInstancePtr(), false, data, &len);
668     }
669     SuccessOrExit(error);
670 
671     OutputBytesLine(data, static_cast<uint8_t>(len));
672 
673 exit:
674     return error;
675 }
676 
677 /**
678  * @cli netdata show
679  * @code
680  * netdata show
681  * Prefixes:
682  * fd00:dead:beef:cafe::/64 paros med dc00
683  * Routes:
684  * fd49:7770:7fc5:0::/64 s med 4000
685  * Services:
686  * 44970 5d c000 s 4000
687  * 44970 01 9a04b000000e10 s 4000
688  * Contexts:
689  * fd00:dead:beef:cafe::/64 1 c
690  * Done
691  * @endcode
692  * @code
693  * netdata show -x
694  * 08040b02174703140040fd00deadbeefcafe0504dc00330007021140
695  * Done
696  * @endcode
697  * @cparam netdata show [@ca{-x}]
698  * *   The optional `-x` argument gets Network Data as hex-encoded TLVs.
699  * @par
700  * `netdata show` from OT CLI gets full Network Data received from the Leader. This command uses several
701  * API functions to combine prefixes, routes, and services, including #otNetDataGetNextOnMeshPrefix,
702  * #otNetDataGetNextRoute, #otNetDataGetNextService and #otNetDataGetNextLowpanContextInfo.
703  * @par
704  * On-mesh prefixes are listed under `Prefixes` header:
705  * * The on-mesh prefix
706  * * Flags
707  *   * p: Preferred flag
708  *   * a: Stateless IPv6 Address Autoconfiguration flag
709  *   * d: DHCPv6 IPv6 Address Configuration flag
710  *   * c: DHCPv6 Other Configuration flag
711  *   * r: Default Route flag
712  *   * o: On Mesh flag
713  *   * s: Stable flag
714  *   * n: Nd Dns flag
715  *   * D: Domain Prefix flag (only available for Thread 1.2).
716  * * Preference `high`, `med`, or `low`
717  * * RLOC16 of device which added the on-mesh prefix
718  * @par
719  * External Routes are listed under `Routes` header:
720  * * The route prefix
721  * * Flags
722  *   * s: Stable flag
723  *   * n: NAT64 flag
724  * * Preference `high`, `med`, or `low`
725  * * RLOC16 of device which added the route prefix
726  * @par
727  * Service entries are listed under `Services` header:
728  * * Enterprise number
729  * * Service data (as hex bytes)
730  * * Server data (as hex bytes)
731  * * Flags
732  *   * s: Stable flag
733  * * RLOC16 of devices which added the service entry
734  * @par
735  * 6LoWPAN Context IDs are listed under `Contexts` header:
736  * * The prefix
737  * * Context ID
738  * * Compress flag (`c` if marked or `-` otherwise).
739  * @par
740  * @moreinfo{@netdata}.
741  * @csa{br omrprefix}
742  * @csa{br onlinkprefix}
743  * @sa otBorderRouterGetNetData
744  */
Process(Arg aArgs[])745 template <> otError NetworkData::Process<Cmd("show")>(Arg aArgs[])
746 {
747     otError error  = OT_ERROR_INVALID_ARGS;
748     bool    local  = false;
749     bool    binary = false;
750 
751     for (uint8_t i = 0; !aArgs[i].IsEmpty(); i++)
752     {
753         /**
754          * @cli netdata show local
755          * @code
756          * netdata show local
757          * Prefixes:
758          * fd00:dead:beef:cafe::/64 paros med dc00
759          * Routes:
760          * Services:
761          * Done
762          * @endcode
763          * @code
764          * netdata show local -x
765          * 08040b02174703140040fd00deadbeefcafe0504dc00330007021140
766          * Done
767          * @endcode
768          * @cparam netdata show local [@ca{-x}]
769          * *   The optional `-x` argument gets local Network Data as hex-encoded TLVs.
770          * @par
771          * Print local Network Data to sync with the Leader.
772          * @csa{netdata show}
773          */
774         if (aArgs[i] == "local")
775         {
776             local = true;
777         }
778         else if (aArgs[i] == "-x")
779         {
780             binary = true;
781         }
782         else
783         {
784             ExitNow(error = OT_ERROR_INVALID_ARGS);
785         }
786     }
787 
788     if (binary)
789     {
790         error = OutputBinary(local);
791     }
792     else
793     {
794         OutputPrefixes(local);
795         OutputRoutes(local);
796         OutputServices(local);
797         OutputLowpanContexts(local);
798         error = OT_ERROR_NONE;
799     }
800 
801 exit:
802     return error;
803 }
804 
Process(Arg aArgs[])805 otError NetworkData::Process(Arg aArgs[])
806 {
807 #define CmdEntry(aCommandString)                                   \
808     {                                                              \
809         aCommandString, &NetworkData::Process<Cmd(aCommandString)> \
810     }
811 
812     static constexpr Command kCommands[] = {
813         CmdEntry("length"),
814         CmdEntry("maxlength"),
815 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
816         CmdEntry("publish"),
817 #endif
818 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
819         CmdEntry("register"),
820 #endif
821         CmdEntry("show"),
822         CmdEntry("steeringdata"),
823 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
824         CmdEntry("unpublish"),
825 #endif
826     };
827 
828 #undef CmdEntry
829 
830     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
831 
832     otError        error = OT_ERROR_INVALID_COMMAND;
833     const Command *command;
834 
835     /**
836      * @cli netdata help
837      * @code
838      * netdata help
839      * length
840      * maxlength
841      * publish
842      * register
843      * show
844      * steeringdata
845      * unpublish
846      * Done
847      * @endcode
848      * @par
849      * Gets a list of `netdata` CLI commands.
850      * @sa @netdata
851      */
852     if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
853     {
854         OutputCommandTable(kCommands);
855         ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
856     }
857 
858     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
859     VerifyOrExit(command != nullptr);
860 
861     error = (this->*command->mHandler)(aArgs + 1);
862 
863 exit:
864     return error;
865 }
866 
867 } // namespace Cli
868 } // namespace ot
869