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