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 
NetworkData(otInstance * aInstance,OutputImplementer & aOutputImplementer)46 NetworkData::NetworkData(otInstance *aInstance, OutputImplementer &aOutputImplementer)
47     : Utils(aInstance, aOutputImplementer)
48 {
49 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
50     mFullCallbackWasCalled = false;
51     otBorderRouterSetNetDataFullCallback(aInstance, HandleNetdataFull, this);
52 #endif
53 }
54 
PrefixFlagsToString(const otBorderRouterConfig & aConfig,FlagsString & aString)55 void NetworkData::PrefixFlagsToString(const otBorderRouterConfig &aConfig, FlagsString &aString)
56 {
57     char *flagsPtr = &aString[0];
58 
59     if (aConfig.mPreferred)
60     {
61         *flagsPtr++ = 'p';
62     }
63 
64     if (aConfig.mSlaac)
65     {
66         *flagsPtr++ = 'a';
67     }
68 
69     if (aConfig.mDhcp)
70     {
71         *flagsPtr++ = 'd';
72     }
73 
74     if (aConfig.mConfigure)
75     {
76         *flagsPtr++ = 'c';
77     }
78 
79     if (aConfig.mDefaultRoute)
80     {
81         *flagsPtr++ = 'r';
82     }
83 
84     if (aConfig.mOnMesh)
85     {
86         *flagsPtr++ = 'o';
87     }
88 
89     if (aConfig.mStable)
90     {
91         *flagsPtr++ = 's';
92     }
93 
94     if (aConfig.mNdDns)
95     {
96         *flagsPtr++ = 'n';
97     }
98 
99     if (aConfig.mDp)
100     {
101         *flagsPtr++ = 'D';
102     }
103 
104     *flagsPtr = '\0';
105 }
106 
OutputPrefix(const otBorderRouterConfig & aConfig)107 void NetworkData::OutputPrefix(const otBorderRouterConfig &aConfig)
108 {
109     FlagsString flagsString;
110 
111     OutputIp6Prefix(aConfig.mPrefix);
112 
113     PrefixFlagsToString(aConfig, flagsString);
114 
115     if (flagsString[0] != '\0')
116     {
117         OutputFormat(" %s", flagsString);
118     }
119 
120     OutputLine(" %s %04x", PreferenceToString(aConfig.mPreference), aConfig.mRloc16);
121 }
122 
RouteFlagsToString(const otExternalRouteConfig & aConfig,FlagsString & aString)123 void NetworkData::RouteFlagsToString(const otExternalRouteConfig &aConfig, FlagsString &aString)
124 {
125     char *flagsPtr = &aString[0];
126 
127     if (aConfig.mStable)
128     {
129         *flagsPtr++ = 's';
130     }
131 
132     if (aConfig.mNat64)
133     {
134         *flagsPtr++ = 'n';
135     }
136 
137     if (aConfig.mAdvPio)
138     {
139         *flagsPtr++ = 'a';
140     }
141 
142     *flagsPtr = '\0';
143 }
144 
OutputRoute(const otExternalRouteConfig & aConfig)145 void NetworkData::OutputRoute(const otExternalRouteConfig &aConfig)
146 {
147     FlagsString flagsString;
148 
149     OutputIp6Prefix(aConfig.mPrefix);
150 
151     RouteFlagsToString(aConfig, flagsString);
152 
153     if (flagsString[0] != '\0')
154     {
155         OutputFormat(" %s", flagsString);
156     }
157 
158     OutputLine(" %s %04x", PreferenceToString(aConfig.mPreference), aConfig.mRloc16);
159 }
160 
OutputService(const otServiceConfig & aConfig)161 void NetworkData::OutputService(const otServiceConfig &aConfig)
162 {
163     OutputFormat("%lu ", ToUlong(aConfig.mEnterpriseNumber));
164     OutputBytes(aConfig.mServiceData, aConfig.mServiceDataLength);
165     OutputFormat(" ");
166     OutputBytes(aConfig.mServerConfig.mServerData, aConfig.mServerConfig.mServerDataLength);
167 
168     if (aConfig.mServerConfig.mStable)
169     {
170         OutputFormat(" s");
171     }
172 
173     OutputLine(" %04x %u", aConfig.mServerConfig.mRloc16, aConfig.mServiceId);
174 }
175 
176 /**
177  * @cli netdata length
178  * @code
179  * netdata length
180  * 23
181  * Done
182  * @endcode
183  * @par api_copy
184  * #otNetDataGetLength
185  */
Process(Arg aArgs[])186 template <> otError NetworkData::Process<Cmd("length")>(Arg aArgs[])
187 {
188     otError error = OT_ERROR_NONE;
189 
190     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
191     OutputLine("%u", otNetDataGetLength(GetInstancePtr()));
192 
193 exit:
194     return error;
195 }
196 
Process(Arg aArgs[])197 template <> otError NetworkData::Process<Cmd("maxlength")>(Arg aArgs[])
198 {
199     otError error = OT_ERROR_NONE;
200 
201     /**
202      * @cli netdata maxlength
203      * @code
204      * netdata maxlength
205      * 40
206      * Done
207      * @endcode
208      * @par api_copy
209      * #otNetDataGetMaxLength
210      */
211     if (aArgs[0].IsEmpty())
212     {
213         OutputLine("%u", otNetDataGetMaxLength(GetInstancePtr()));
214     }
215     /**
216      * @cli netdata maxlength reset
217      * @code
218      * netdata maxlength reset
219      * Done
220      * @endcode
221      * @par api_copy
222      * #otNetDataResetMaxLength
223      */
224     else if (aArgs[0] == "reset")
225     {
226         otNetDataResetMaxLength(GetInstancePtr());
227     }
228     else
229     {
230         error = OT_ERROR_INVALID_ARGS;
231     }
232 
233     return error;
234 }
235 
236 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
Process(Arg aArgs[])237 template <> otError NetworkData::Process<Cmd("publish")>(Arg aArgs[])
238 {
239     otError error = OT_ERROR_NONE;
240 
241 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
242     if (aArgs[0] == "dnssrp")
243     {
244         /**
245          * @cli netdata publish dnssrp anycast
246          * @code
247          * netdata publish dnssrp anycast 1
248          * Done
249          * @endcode
250          * @cparam netdata publish dnssrp anycast @ca{seq-num}
251          * @par
252          * Publishes a DNS/SRP Service Anycast Address with a sequence number. Any current
253          * DNS/SRP Service entry being published from a previous `publish dnssrp{anycast|unicast}`
254          * command is removed and replaced with the new arguments.
255          * @par
256          * `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` must be enabled.
257          * @csa{netdata publish dnssrp unicast (addr,port)}
258          * @csa{netdata publish dnssrp unicast (mle)}
259          * @sa otNetDataPublishDnsSrpServiceAnycast
260          * @endcli
261          */
262         if (aArgs[1] == "anycast")
263         {
264             uint8_t sequenceNumber;
265 
266             SuccessOrExit(error = aArgs[2].ParseAsUint8(sequenceNumber));
267             otNetDataPublishDnsSrpServiceAnycast(GetInstancePtr(), sequenceNumber);
268             ExitNow();
269         }
270 
271         if (aArgs[1] == "unicast")
272         {
273             otIp6Address address;
274             uint16_t     port;
275 
276             /**
277              * @cli netdata publish dnssrp unicast (mle)
278              * @code
279              * netdata publish dnssrp unicast 50152
280              * Done
281              * @endcode
282              * @cparam netdata publish dnssrp unicast @ca{port}
283              * @par
284              * Publishes the device's Mesh-Local EID with a port number. MLE and port information is
285              * included in the Server TLV data. To use a different Unicast address, use the
286              * `netdata publish dnssrp unicast (addr,port)` command.
287              * @par
288              * Any current DNS/SRP Service entry being published from a previous
289              * `publish dnssrp{anycast|unicast}` command is removed and replaced with the new arguments.
290              * @par
291              * `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` must be enabled.
292              * @csa{netdata publish dnssrp unicast (addr,port)}
293              * @csa{netdata publish dnssrp anycast}
294              * @sa otNetDataPublishDnsSrpServiceUnicastMeshLocalEid
295              */
296             if (aArgs[3].IsEmpty())
297             {
298                 SuccessOrExit(error = aArgs[2].ParseAsUint16(port));
299                 otNetDataPublishDnsSrpServiceUnicastMeshLocalEid(GetInstancePtr(), port);
300                 ExitNow();
301             }
302 
303             /**
304              * @cli netdata publish dnssrp unicast (addr,port)
305              * @code
306              * netdata publish dnssrp unicast fd00::1234 51525
307              * Done
308              * @endcode
309              * @cparam netdata publish dnssrp unicast @ca{address} @ca{port}
310              * @par
311              * Publishes a DNS/SRP Service Unicast Address with an address and port number. The address
312              * and port information is included in Service TLV data. Any current DNS/SRP Service entry being
313              * published from a previous `publish dnssrp{anycast|unicast}` command is removed and replaced
314              * with the new arguments.
315              * @par
316              * `OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE` must be enabled.
317              * @csa{netdata publish dnssrp unicast (mle)}
318              * @csa{netdata publish dnssrp anycast}
319              * @sa otNetDataPublishDnsSrpServiceUnicast
320              */
321             SuccessOrExit(error = aArgs[2].ParseAsIp6Address(address));
322             SuccessOrExit(error = aArgs[3].ParseAsUint16(port));
323             otNetDataPublishDnsSrpServiceUnicast(GetInstancePtr(), &address, port);
324             ExitNow();
325         }
326     }
327 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
328 
329 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
330     /**
331      * @cli netdata publish prefix
332      * @code
333      * netdata publish prefix fd00:1234:5678::/64 paos med
334      * Done
335      * @endcode
336      * @cparam netdata publish prefix @ca{prefix} [@ca{padcrosnD}] [@ca{high}|@ca{med}|@ca{low}]
337      * OT CLI uses mapped arguments to configure #otBorderRouterConfig values. @moreinfo{the @overview}.
338      * @par
339      * Publish an on-mesh prefix entry. @moreinfo{@netdata}.
340      * @sa otNetDataPublishOnMeshPrefix
341      */
342     if (aArgs[0] == "prefix")
343     {
344         otBorderRouterConfig config;
345 
346         SuccessOrExit(error = ParsePrefix(aArgs + 1, config));
347         error = otNetDataPublishOnMeshPrefix(GetInstancePtr(), &config);
348         ExitNow();
349     }
350 
351     /**
352      * @cli netdata publish route
353      * @code
354      * netdata publish route fd00:1234:5678::/64 s high
355      * Done
356      * @endcode
357      * @cparam publish route @ca{prefix} [@ca{sn}] [@ca{high}|@ca{med}|@ca{low}]
358      * OT CLI uses mapped arguments to configure #otExternalRouteConfig values. @moreinfo{the @overview}.
359      * @par
360      * Publish an external route entry. @moreinfo{@netdata}.
361      * @sa otNetDataPublishExternalRoute
362      */
363     if (aArgs[0] == "route")
364     {
365         otExternalRouteConfig config;
366 
367         SuccessOrExit(error = ParseRoute(aArgs + 1, config));
368         error = otNetDataPublishExternalRoute(GetInstancePtr(), &config);
369         ExitNow();
370     }
371 
372     /**
373      * @cli netdata publish replace
374      * @code
375      * netdata publish replace ::/0 fd00:1234:5678::/64 s high
376      * Done
377      * @endcode
378      * @cparam netdata publish replace @ca{oldprefix} @ca{prefix} [@ca{sn}] [@ca{high}|@ca{med}|@ca{low}]
379      * OT CLI uses mapped arguments to configure #otExternalRouteConfig values. @moreinfo{the @overview}.
380      * @par
381      * Replaces a previously published external route entry. @moreinfo{@netdata}.
382      * @sa otNetDataReplacePublishedExternalRoute
383      */
384     if (aArgs[0] == "replace")
385     {
386         otIp6Prefix           prefix;
387         otExternalRouteConfig config;
388 
389         SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
390         SuccessOrExit(error = ParseRoute(aArgs + 2, config));
391         error = otNetDataReplacePublishedExternalRoute(GetInstancePtr(), &prefix, &config);
392         ExitNow();
393     }
394 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
395 
396     error = OT_ERROR_INVALID_ARGS;
397 
398 exit:
399     return error;
400 }
401 
Process(Arg aArgs[])402 template <> otError NetworkData::Process<Cmd("unpublish")>(Arg aArgs[])
403 {
404     otError error = OT_ERROR_NONE;
405 
406 /**
407  * @cli netdata unpublish dnssrp
408  * @code
409  * netdata unpublish dnssrp
410  * Done
411  * @endcode
412  * @par api_copy
413  * #otNetDataUnpublishDnsSrpService
414  */
415 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
416     if (aArgs[0] == "dnssrp")
417     {
418         otNetDataUnpublishDnsSrpService(GetInstancePtr());
419         ExitNow();
420     }
421 #endif
422 
423 /**
424  * @cli netdata unpublish (prefix)
425  * @code
426  * netdata unpublish fd00:1234:5678::/64
427  * Done
428  * @endcode
429  * @cparam netdata unpublish @ca{prefix}
430  * @par api_copy
431  * #otNetDataUnpublishPrefix
432  * @par
433  * @moreinfo{@netdata}.
434  */
435 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
436     {
437         otIp6Prefix prefix;
438 
439         if (aArgs[0].ParseAsIp6Prefix(prefix) == OT_ERROR_NONE)
440         {
441             error = otNetDataUnpublishPrefix(GetInstancePtr(), &prefix);
442             ExitNow();
443         }
444     }
445 #endif
446 
447     error = OT_ERROR_INVALID_ARGS;
448 
449 exit:
450     return error;
451 }
452 #endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
453 
454 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
455 /**
456  * @cli netdata register
457  * @code
458  * netdata register
459  * Done
460  * @endcode
461  * @par
462  * Register configured prefixes, routes, and services with the Leader.
463  * @par
464  * OT CLI checks for `OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE`. If OTBR is enabled, it
465  * registers local Network Data with the Leader. Otherwise, it calls the CLI function `otServerRegister`.
466  * @moreinfo{@netdata}.
467  * @csa{prefix add}
468  * @sa otBorderRouterRegister
469  * @sa otServerAddService
470  */
Process(Arg aArgs[])471 template <> otError NetworkData::Process<Cmd("register")>(Arg aArgs[])
472 {
473     OT_UNUSED_VARIABLE(aArgs);
474 
475     otError error = OT_ERROR_NONE;
476 
477 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
478     SuccessOrExit(error = otBorderRouterRegister(GetInstancePtr()));
479 #else
480     SuccessOrExit(error = otServerRegister(GetInstancePtr()));
481 #endif
482 
483 exit:
484     return error;
485 }
486 #endif
487 
Process(Arg aArgs[])488 template <> otError NetworkData::Process<Cmd("steeringdata")>(Arg aArgs[])
489 {
490     otError           error;
491     otExtAddress      addr;
492     otJoinerDiscerner discerner;
493 
494     VerifyOrExit(aArgs[0] == "check", error = OT_ERROR_INVALID_ARGS);
495 
496     error = ParseJoinerDiscerner(aArgs[1], discerner);
497 
498     if (error == OT_ERROR_NOT_FOUND)
499     {
500         discerner.mLength = 0;
501         error             = aArgs[1].ParseAsHexString(addr.m8);
502     }
503 
504     SuccessOrExit(error);
505 
506     /**
507      * @cli netdata steeringdata check (discerner)
508      * @code
509      * netdata steeringdata check 0xabc/12
510      * Done
511      * @endcode
512      * @code
513      * netdata steeringdata check 0xdef/12
514      * Error 23: NotFound
515      * @endcode
516      * @cparam netdata steeringdata check @ca{discerner}
517      * *   `discerner`: The %Joiner discerner in format `{number}/{length}`.
518      * @par api_copy
519      * #otNetDataSteeringDataCheckJoinerWithDiscerner
520      * @csa{joiner discerner}
521      */
522     if (discerner.mLength)
523     {
524         error = otNetDataSteeringDataCheckJoinerWithDiscerner(GetInstancePtr(), &discerner);
525     }
526     /**
527      * @cli netdata steeringdata check (eui64)
528      * @code
529      * netdata steeringdata check d45e64fa83f81cf7
530      * Done
531      * @endcode
532      * @cparam netdata steeringdata check @ca{eui64}
533      * *   `eui64`: The IEEE EUI-64 of the %Joiner.
534      * @par api_copy
535      * #otNetDataSteeringDataCheckJoiner
536      * @csa{eui64}
537      */
538     else
539     {
540         error = otNetDataSteeringDataCheckJoiner(GetInstancePtr(), &addr);
541     }
542 
543 exit:
544     return error;
545 }
546 
GetNextPrefix(otNetworkDataIterator * aIterator,otBorderRouterConfig * aConfig,bool aLocal)547 otError NetworkData::GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig, bool aLocal)
548 {
549     otError error;
550 
551     if (aLocal)
552     {
553 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
554         error = otBorderRouterGetNextOnMeshPrefix(GetInstancePtr(), aIterator, aConfig);
555 #else
556         error = OT_ERROR_NOT_FOUND;
557 #endif
558     }
559     else
560     {
561         error = otNetDataGetNextOnMeshPrefix(GetInstancePtr(), aIterator, aConfig);
562     }
563 
564     return error;
565 }
566 
OutputNetworkData(bool aLocal,uint16_t aRloc16)567 void NetworkData::OutputNetworkData(bool aLocal, uint16_t aRloc16)
568 {
569     otNetworkDataIterator  iterator = OT_NETWORK_DATA_ITERATOR_INIT;
570     otBorderRouterConfig   prefix;
571     otExternalRouteConfig  route;
572     otServiceConfig        service;
573     otLowpanContextInfo    context;
574     otCommissioningDataset dataset;
575 
576     OutputLine("Prefixes:");
577 
578     while (GetNextPrefix(&iterator, &prefix, aLocal) == OT_ERROR_NONE)
579     {
580         if ((aRloc16 == kAnyRloc16) || (aRloc16 == prefix.mRloc16))
581         {
582             OutputPrefix(prefix);
583         }
584     }
585 
586     OutputLine("Routes:");
587     iterator = OT_NETWORK_DATA_ITERATOR_INIT;
588 
589     while (GetNextRoute(&iterator, &route, aLocal) == OT_ERROR_NONE)
590     {
591         if ((aRloc16 == kAnyRloc16) || (aRloc16 == route.mRloc16))
592         {
593             OutputRoute(route);
594         }
595     }
596 
597     OutputLine("Services:");
598     iterator = OT_NETWORK_DATA_ITERATOR_INIT;
599 
600     while (GetNextService(&iterator, &service, aLocal) == OT_ERROR_NONE)
601     {
602         if ((aRloc16 == kAnyRloc16) || (aRloc16 == service.mServerConfig.mRloc16))
603         {
604             OutputService(service);
605         }
606     }
607 
608     VerifyOrExit(!aLocal);
609     VerifyOrExit(aRloc16 == kAnyRloc16);
610 
611     OutputLine("Contexts:");
612     iterator = OT_NETWORK_DATA_ITERATOR_INIT;
613 
614     while (otNetDataGetNextLowpanContextInfo(GetInstancePtr(), &iterator, &context) == OT_ERROR_NONE)
615     {
616         OutputIp6Prefix(context.mPrefix);
617         OutputLine(" %u %c", context.mContextId, context.mCompressFlag ? 'c' : '-');
618     }
619 
620     otNetDataGetCommissioningDataset(GetInstancePtr(), &dataset);
621 
622     OutputLine("Commissioning:");
623 
624     dataset.mIsSessionIdSet ? OutputFormat("%u ", dataset.mSessionId) : OutputFormat("- ");
625     dataset.mIsLocatorSet ? OutputFormat("%04x ", dataset.mLocator) : OutputFormat("- ");
626     dataset.mIsJoinerUdpPortSet ? OutputFormat("%u ", dataset.mJoinerUdpPort) : OutputFormat("- ");
627     dataset.mIsSteeringDataSet ? OutputBytes(dataset.mSteeringData.m8, dataset.mSteeringData.mLength)
628                                : OutputFormat("-");
629 
630     if (dataset.mHasExtraTlv)
631     {
632         OutputFormat(" e");
633     }
634 
635     OutputNewLine();
636 
637 exit:
638     return;
639 }
640 
GetNextRoute(otNetworkDataIterator * aIterator,otExternalRouteConfig * aConfig,bool aLocal)641 otError NetworkData::GetNextRoute(otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig, bool aLocal)
642 {
643     otError error;
644 
645     if (aLocal)
646     {
647 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
648         error = otBorderRouterGetNextRoute(GetInstancePtr(), aIterator, aConfig);
649 #else
650         error = OT_ERROR_NOT_FOUND;
651 #endif
652     }
653     else
654     {
655         error = otNetDataGetNextRoute(GetInstancePtr(), aIterator, aConfig);
656     }
657 
658     return error;
659 }
660 
GetNextService(otNetworkDataIterator * aIterator,otServiceConfig * aConfig,bool aLocal)661 otError NetworkData::GetNextService(otNetworkDataIterator *aIterator, otServiceConfig *aConfig, bool aLocal)
662 {
663     otError error;
664 
665     if (aLocal)
666     {
667 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
668         error = otServerGetNextService(GetInstancePtr(), aIterator, aConfig);
669 #else
670         error = OT_ERROR_NOT_FOUND;
671 #endif
672     }
673     else
674     {
675         error = otNetDataGetNextService(GetInstancePtr(), aIterator, aConfig);
676     }
677 
678     return error;
679 }
680 
OutputBinary(bool aLocal)681 otError NetworkData::OutputBinary(bool aLocal)
682 {
683     otError error;
684     uint8_t data[255];
685     uint8_t len = sizeof(data);
686 
687     if (aLocal)
688     {
689 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
690         error = otBorderRouterGetNetData(GetInstancePtr(), false, data, &len);
691 #else
692         error = OT_ERROR_NOT_IMPLEMENTED;
693 #endif
694     }
695     else
696     {
697         error = otNetDataGet(GetInstancePtr(), false, data, &len);
698     }
699     SuccessOrExit(error);
700 
701     OutputBytesLine(data, static_cast<uint8_t>(len));
702 
703 exit:
704     return error;
705 }
706 
707 /**
708  * @cli netdata show
709  * @code
710  * netdata show
711  * Prefixes:
712  * fd00:dead:beef:cafe::/64 paros med dc00
713  * Routes:
714  * fd49:7770:7fc5:0::/64 s med 4000
715  * Services:
716  * 44970 5d c000 s 4000 0
717  * 44970 01 9a04b000000e10 s 4000 1
718  * Contexts:
719  * fd00:dead:beef:cafe::/64 1 c
720  * Commissioning:
721  * 1248 dc00 9988 00000000000120000000000000000000 e
722  * Done
723  * @endcode
724  * @code
725  * netdata show -x
726  * 08040b02174703140040fd00deadbeefcafe0504dc00330007021140
727  * Done
728  * @endcode
729  * @code
730  * netdata show 0xdc00
731  * Prefixes:
732  * fd00:dead:beef:cafe::/64 paros med dc00
733  * Routes:
734  * Services:
735  * Done
736  * @cparam netdata show [@ca{-x}|@ca{rloc16}]
737  * *   The optional `-x` argument gets Network Data as hex-encoded TLVs.
738  * *   The optional `rloc16` argument gets all prefix/route/service entries associated with a given RLOC16.
739  * @par
740  * `netdata show` from OT CLI gets full Network Data received from the Leader. This command uses several
741  * API functions to combine prefixes, routes, and services, including #otNetDataGetNextOnMeshPrefix,
742  * #otNetDataGetNextRoute, #otNetDataGetNextService and #otNetDataGetNextLowpanContextInfo.
743  * @par
744  * On-mesh prefixes are listed under `Prefixes` header:
745  * * The on-mesh prefix
746  * * Flags
747  *   * p: Preferred flag
748  *   * a: Stateless IPv6 Address Autoconfiguration flag
749  *   * d: DHCPv6 IPv6 Address Configuration flag
750  *   * c: DHCPv6 Other Configuration flag
751  *   * r: Default Route flag
752  *   * o: On Mesh flag
753  *   * s: Stable flag
754  *   * n: Nd Dns flag
755  *   * D: Domain Prefix flag (only available for Thread 1.2).
756  * * Preference `high`, `med`, or `low`
757  * * RLOC16 of device which added the on-mesh prefix
758  * @par
759  * External Routes are listed under `Routes` header:
760  * * The route prefix
761  * * Flags
762  *   * s: Stable flag
763  *   * n: NAT64 flag
764  * * Preference `high`, `med`, or `low`
765  * * RLOC16 of device which added the route prefix
766  * @par
767  * Service entries are listed under `Services` header:
768  * * Enterprise number
769  * * Service data (as hex bytes)
770  * * Server data (as hex bytes)
771  * * Flags
772  *   * s: Stable flag
773  * * RLOC16 of devices which added the service entry
774  * * Service ID
775  * @par
776  * 6LoWPAN Context IDs are listed under `Contexts` header:
777  * * The prefix
778  * * Context ID
779  * * Compress flag (`c` if marked or `-` otherwise).
780  * @par
781  * Commissioning Dataset information is printed under `Commissioning` header:
782  * * Session ID if present in Dataset or `-` otherwise
783  * * Border Agent RLOC16 (in hex) if present in Dataset or `-` otherwise
784  * * Joiner UDP port number if present in Dataset or `-` otherwise
785  * * Steering Data (as hex bytes) if present in Dataset or `-` otherwise
786  * * Flags:
787  *   * e: If Dataset contains any extra unknown TLV
788  * @par
789  * @moreinfo{@netdata}.
790  * @csa{br omrprefix}
791  * @csa{br onlinkprefix}
792  * @sa otBorderRouterGetNetData
793  */
Process(Arg aArgs[])794 template <> otError NetworkData::Process<Cmd("show")>(Arg aArgs[])
795 {
796     otError  error  = OT_ERROR_INVALID_ARGS;
797     uint16_t rloc16 = kAnyRloc16;
798     bool     local  = false;
799     bool     binary = false;
800 
801     for (uint8_t i = 0; !aArgs[i].IsEmpty(); i++)
802     {
803         /**
804          * @cli netdata show local
805          * @code
806          * netdata show local
807          * Prefixes:
808          * fd00:dead:beef:cafe::/64 paros med dc00
809          * Routes:
810          * Services:
811          * Done
812          * @endcode
813          * @code
814          * netdata show local -x
815          * 08040b02174703140040fd00deadbeefcafe0504dc00330007021140
816          * Done
817          * @endcode
818          * @cparam netdata show local [@ca{-x}]
819          * *   The optional `-x` argument gets local Network Data as hex-encoded TLVs.
820          * @par
821          * Print local Network Data to sync with the Leader.
822          * @csa{netdata show}
823          */
824         if (aArgs[i] == "local")
825         {
826             local = true;
827         }
828         else if (aArgs[i] == "-x")
829         {
830             binary = true;
831         }
832         else
833         {
834             SuccessOrExit(error = aArgs[i].ParseAsUint16(rloc16));
835         }
836     }
837 
838     if (local || binary)
839     {
840         VerifyOrExit(rloc16 == kAnyRloc16, error = OT_ERROR_INVALID_ARGS);
841     }
842 
843     if (binary)
844     {
845         error = OutputBinary(local);
846     }
847     else
848     {
849         OutputNetworkData(local, rloc16);
850         error = OT_ERROR_NONE;
851     }
852 
853 exit:
854     return error;
855 }
856 
857 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
Process(Arg aArgs[])858 template <> otError NetworkData::Process<Cmd("full")>(Arg aArgs[])
859 {
860     otError error = OT_ERROR_NONE;
861 
862     /**
863      * @cli netdata full
864      * @code
865      * netdata full
866      * no
867      * Done
868      * @endcode
869      * @par
870      * Print "yes" or "no" indicating whether or not the "net data full" callback has been invoked since start of
871      * Thread operation or since the last time `netdata full reset` was used to reset the flag.
872      * This command requires `OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL`.
873      * The "net data full" callback is invoked whenever:
874      * - The device is acting as a leader and receives a Network Data registration from a Border Router (BR) that it
875      *   cannot add to Network Data (running out of space).
876      * - The device is acting as a BR and new entries cannot be added to its local Network Data.
877      * - The device is acting as a BR and tries to register its local Network Data entries with the leader, but
878      *   determines that its local entries will not fit.
879      * @sa otBorderRouterSetNetDataFullCallback
880      */
881     if (aArgs[0].IsEmpty())
882     {
883         OutputLine(mFullCallbackWasCalled ? "yes" : "no");
884     }
885     /**
886      * @cli netdata full reset
887      * @code
888      * netdata full reset
889      * Done
890      * @endcode
891      * @par
892      * Reset the flag tracking whether "net data full" callback was invoked.
893      */
894     else if (aArgs[0] == "reset")
895     {
896         VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
897         mFullCallbackWasCalled = false;
898     }
899     else
900     {
901         error = OT_ERROR_INVALID_ARGS;
902     }
903 
904 exit:
905     return error;
906 }
907 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
908 
Process(Arg aArgs[])909 otError NetworkData::Process(Arg aArgs[])
910 {
911 #define CmdEntry(aCommandString)                                   \
912     {                                                              \
913         aCommandString, &NetworkData::Process<Cmd(aCommandString)> \
914     }
915 
916     static constexpr Command kCommands[] = {
917 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
918         CmdEntry("full"),
919 #endif
920         CmdEntry("length"),
921         CmdEntry("maxlength"),
922 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
923         CmdEntry("publish"),
924 #endif
925 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
926         CmdEntry("register"),
927 #endif
928         CmdEntry("show"),
929         CmdEntry("steeringdata"),
930 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
931         CmdEntry("unpublish"),
932 #endif
933     };
934 
935 #undef CmdEntry
936 
937     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
938 
939     otError        error = OT_ERROR_INVALID_COMMAND;
940     const Command *command;
941 
942     /**
943      * @cli netdata help
944      * @code
945      * netdata help
946      * length
947      * maxlength
948      * publish
949      * register
950      * show
951      * steeringdata
952      * unpublish
953      * Done
954      * @endcode
955      * @par
956      * Gets a list of `netdata` CLI commands.
957      * @sa @netdata
958      */
959     if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
960     {
961         OutputCommandTable(kCommands);
962         ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
963     }
964 
965     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
966     VerifyOrExit(command != nullptr);
967 
968     error = (this->*command->mHandler)(aArgs + 1);
969 
970 exit:
971     return error;
972 }
973 
974 } // namespace Cli
975 } // namespace ot
976