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