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