1 /*
2 * Copyright (c) 2021, 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 the Network Data Publisher.
32 *
33 */
34
35 #include "network_data_publisher.hpp"
36
37 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
38
39 #include "common/array.hpp"
40 #include "common/code_utils.hpp"
41 #include "common/const_cast.hpp"
42 #include "common/locator_getters.hpp"
43 #include "common/log.hpp"
44 #include "common/random.hpp"
45 #include "instance/instance.hpp"
46 #include "thread/network_data_local.hpp"
47
48 namespace ot {
49 namespace NetworkData {
50
51 RegisterLogModule("NetDataPublshr");
52
53 //---------------------------------------------------------------------------------------------------------------------
54 // Publisher
55
Publisher(Instance & aInstance)56 Publisher::Publisher(Instance &aInstance)
57 : InstanceLocator(aInstance)
58 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
59 , mDnsSrpServiceEntry(aInstance)
60 #endif
61 , mTimer(aInstance)
62 {
63 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
64 // Since the `PrefixEntry` type is used in an array,
65 // we cannot use a constructor with an argument (e.g.,
66 // we cannot use `InstanceLocator`) so we use
67 // `InstanceLocatorInit` and `Init()` the entries one
68 // by one.
69
70 for (PrefixEntry &entry : mPrefixEntries)
71 {
72 entry.Init(aInstance);
73 }
74 #endif
75 }
76
77 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
78
PublishOnMeshPrefix(const OnMeshPrefixConfig & aConfig,Requester aRequester)79 Error Publisher::PublishOnMeshPrefix(const OnMeshPrefixConfig &aConfig, Requester aRequester)
80 {
81 Error error = kErrorNone;
82 PrefixEntry *entry;
83
84 VerifyOrExit(aConfig.IsValid(GetInstance()), error = kErrorInvalidArgs);
85 VerifyOrExit(aConfig.mStable, error = kErrorInvalidArgs);
86
87 entry = FindOrAllocatePrefixEntry(aConfig.GetPrefix(), aRequester);
88 VerifyOrExit(entry != nullptr, error = kErrorNoBufs);
89
90 entry->Publish(aConfig, aRequester);
91
92 exit:
93 return error;
94 }
95
PublishExternalRoute(const ExternalRouteConfig & aConfig,Requester aRequester)96 Error Publisher::PublishExternalRoute(const ExternalRouteConfig &aConfig, Requester aRequester)
97 {
98 return ReplacePublishedExternalRoute(aConfig.GetPrefix(), aConfig, aRequester);
99 }
100
ReplacePublishedExternalRoute(const Ip6::Prefix & aPrefix,const ExternalRouteConfig & aConfig,Requester aRequester)101 Error Publisher::ReplacePublishedExternalRoute(const Ip6::Prefix &aPrefix,
102 const ExternalRouteConfig &aConfig,
103 Requester aRequester)
104 {
105 Error error = kErrorNone;
106 PrefixEntry *entry;
107
108 VerifyOrExit(aConfig.IsValid(GetInstance()), error = kErrorInvalidArgs);
109 VerifyOrExit(aConfig.mStable, error = kErrorInvalidArgs);
110
111 entry = FindOrAllocatePrefixEntry(aPrefix, aRequester);
112 VerifyOrExit(entry != nullptr, error = kErrorNoBufs);
113
114 entry->Publish(aConfig, aRequester);
115
116 exit:
117 return error;
118 }
119
IsPrefixAdded(const Ip6::Prefix & aPrefix) const120 bool Publisher::IsPrefixAdded(const Ip6::Prefix &aPrefix) const
121 {
122 bool isAdded = false;
123 const PrefixEntry *entry;
124
125 entry = FindMatchingPrefixEntry(aPrefix);
126 VerifyOrExit(entry != nullptr);
127
128 isAdded = entry->IsAdded();
129
130 exit:
131 return isAdded;
132 }
133
UnpublishPrefix(const Ip6::Prefix & aPrefix)134 Error Publisher::UnpublishPrefix(const Ip6::Prefix &aPrefix)
135 {
136 Error error = kErrorNone;
137 PrefixEntry *entry;
138
139 entry = FindMatchingPrefixEntry(aPrefix);
140 VerifyOrExit(entry != nullptr, error = kErrorNotFound);
141
142 entry->Unpublish();
143
144 exit:
145 return error;
146 }
147
FindOrAllocatePrefixEntry(const Ip6::Prefix & aPrefix,Requester aRequester)148 Publisher::PrefixEntry *Publisher::FindOrAllocatePrefixEntry(const Ip6::Prefix &aPrefix, Requester aRequester)
149 {
150 // Returns a matching prefix entry if found, otherwise tries
151 // to allocate a new entry.
152
153 PrefixEntry *prefixEntry = nullptr;
154 uint16_t numEntries = 0;
155 uint8_t maxEntries = 0;
156
157 for (PrefixEntry &entry : mPrefixEntries)
158 {
159 if (entry.IsInUse())
160 {
161 if (entry.GetRequester() == aRequester)
162 {
163 numEntries++;
164 }
165
166 if (entry.Matches(aPrefix))
167 {
168 prefixEntry = &entry;
169 ExitNow();
170 }
171 }
172 else if (prefixEntry == nullptr)
173 {
174 prefixEntry = &entry;
175 }
176 }
177
178 switch (aRequester)
179 {
180 case kFromUser:
181 maxEntries = kMaxUserPrefixEntries;
182 break;
183 case kFromRoutingManager:
184 maxEntries = kMaxRoutingManagerPrefixEntries;
185 break;
186 }
187
188 VerifyOrExit(numEntries < maxEntries, prefixEntry = nullptr);
189
190 exit:
191 return prefixEntry;
192 }
193
FindMatchingPrefixEntry(const Ip6::Prefix & aPrefix)194 Publisher::PrefixEntry *Publisher::FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix)
195 {
196 return AsNonConst(AsConst(this)->FindMatchingPrefixEntry(aPrefix));
197 }
198
FindMatchingPrefixEntry(const Ip6::Prefix & aPrefix) const199 const Publisher::PrefixEntry *Publisher::FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix) const
200 {
201 const PrefixEntry *prefixEntry = nullptr;
202
203 for (const PrefixEntry &entry : mPrefixEntries)
204 {
205 if (entry.IsInUse() && entry.Matches(aPrefix))
206 {
207 prefixEntry = &entry;
208 break;
209 }
210 }
211
212 return prefixEntry;
213 }
214
IsAPrefixEntry(const Entry & aEntry) const215 bool Publisher::IsAPrefixEntry(const Entry &aEntry) const
216 {
217 return (&mPrefixEntries[0] <= &aEntry) && (&aEntry < GetArrayEnd(mPrefixEntries));
218 }
219
NotifyPrefixEntryChange(Event aEvent,const Ip6::Prefix & aPrefix) const220 void Publisher::NotifyPrefixEntryChange(Event aEvent, const Ip6::Prefix &aPrefix) const
221 {
222 mPrefixCallback.InvokeIfSet(static_cast<otNetDataPublisherEvent>(aEvent), &aPrefix);
223 }
224
225 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
226
HandleNotifierEvents(Events aEvents)227 void Publisher::HandleNotifierEvents(Events aEvents)
228 {
229 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
230 mDnsSrpServiceEntry.HandleNotifierEvents(aEvents);
231 #endif
232
233 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
234 for (PrefixEntry &entry : mPrefixEntries)
235 {
236 entry.HandleNotifierEvents(aEvents);
237 }
238 #endif
239 }
240
HandleTimer(void)241 void Publisher::HandleTimer(void)
242 {
243 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
244 mDnsSrpServiceEntry.HandleTimer();
245 #endif
246
247 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
248 for (PrefixEntry &entry : mPrefixEntries)
249 {
250 entry.HandleTimer();
251 }
252 #endif
253 }
254
255 //---------------------------------------------------------------------------------------------------------------------
256 // Publisher::Entry
257
SetState(State aState)258 void Publisher::Entry::SetState(State aState)
259 {
260 VerifyOrExit(mState != aState);
261
262 LogInfo("%s - State: %s -> %s", ToString(/* aIncludeState */ false).AsCString(), StateToString(mState),
263 StateToString(aState));
264 mState = aState;
265
266 exit:
267 return;
268 }
269
IsPreferred(uint16_t aRloc16) const270 bool Publisher::Entry::IsPreferred(uint16_t aRloc16) const
271 {
272 // Indicates whether or not an entry from `aRloc16` is preferred
273 // over our entry (based on our RLOC). We prefer an entry from a
274 // router over an entry from an end-device (e.g., a REED). If both
275 // are the same type, then the one with smaller RLOC16 is preferred.
276
277 bool isOtherRouter = Mle::IsRouterRloc16(aRloc16);
278
279 return (Get<Mle::Mle>().IsRouterOrLeader() == isOtherRouter) ? (aRloc16 < Get<Mle::Mle>().GetRloc16())
280 : isOtherRouter;
281 }
282
UpdateState(uint8_t aNumEntries,uint8_t aNumPreferredEntries,uint8_t aDesiredNumEntries)283 void Publisher::Entry::UpdateState(uint8_t aNumEntries, uint8_t aNumPreferredEntries, uint8_t aDesiredNumEntries)
284 {
285 // This method uses the info about number existing entries (total
286 // and preferred) in Network Data along with the desired number of
287 // entries we aim to have in the Network Data to decide whether or
288 // not to take any action (add or remove our entry).
289
290 LogInfo("%s in netdata - total:%d, preferred:%d, desired:%d", ToString().AsCString(), aNumEntries,
291 aNumPreferredEntries, aDesiredNumEntries);
292
293 switch (GetState())
294 {
295 case kNoEntry:
296 break;
297
298 case kToAdd:
299 // Our entry is ready to be added. If there are too few existing
300 // entries, we start adding our entry (start the timer with a
301 // random delay before adding the entry).
302
303 if (aNumEntries < aDesiredNumEntries)
304 {
305 mUpdateTime = TimerMilli::GetNow() + Random::NonCrypto::GetUint32InRange(1, kMaxDelayToAdd);
306 SetState(kAdding);
307 Get<Publisher>().GetTimer().FireAtIfEarlier(mUpdateTime);
308 LogUpdateTime();
309 }
310 break;
311
312 case kAdding:
313 // Our entry is being added (waiting time before we add). If we
314 // now see that there are enough entries, we stop adding the
315 // entry.
316
317 if (aNumEntries >= aDesiredNumEntries)
318 {
319 SetState(kToAdd);
320 }
321 break;
322
323 case kAdded:
324 // Our entry is already added in the Network Data. If there are
325 // enough entries, do nothing and keep monitoring. If we see now
326 // that there are too many entries, we start removing our entry
327 // after a random delay time. If our entry itself is preferred
328 // over other entries (indicated by `aNumPreferredEntries <
329 // aDesiredNumEntries`) we add an extra delay before removing
330 // the entry. This gives higher chance for a non-preferred
331 // entry from another device to be removed before our entry.
332
333 if (aNumEntries > aDesiredNumEntries)
334 {
335 mUpdateTime = TimerMilli::GetNow() + Random::NonCrypto::GetUint32InRange(1, kMaxDelayToRemove);
336
337 if (aNumPreferredEntries < aDesiredNumEntries)
338 {
339 mUpdateTime += kExtraDelayToRemovePreferred;
340 }
341
342 SetState(kRemoving);
343 Get<Publisher>().GetTimer().FireAtIfEarlier(mUpdateTime);
344 LogUpdateTime();
345 }
346 break;
347
348 case kRemoving:
349 // Our entry is being removed (wait time before remove). If we
350 // now see that there are enough or too few entries, we stop
351 // removing our entry.
352
353 if (aNumEntries <= aDesiredNumEntries)
354 {
355 SetState(kAdded);
356 }
357 break;
358 }
359 }
360
HandleTimer(void)361 void Publisher::Entry::HandleTimer(void)
362 {
363 // Timer is used to delay adding/removing the entry. If we have
364 // reached `mUpdateTime` add or remove the entry. Otherwise,
365 // restart the timer (note that timer can be shared between
366 // different published entries).
367
368 VerifyOrExit((GetState() == kAdding) || (GetState() == kRemoving));
369
370 if (mUpdateTime <= TimerMilli::GetNow())
371 {
372 if (GetState() == kAdding)
373 {
374 Add();
375 }
376 else
377 {
378 Remove(/* aNextState */ kToAdd);
379 }
380 }
381 else
382 {
383 Get<Publisher>().GetTimer().FireAtIfEarlier(mUpdateTime);
384 }
385
386 exit:
387 return;
388 }
389
Add(void)390 void Publisher::Entry::Add(void)
391 {
392 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
393 if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
394 {
395 static_cast<DnsSrpServiceEntry *>(this)->Add();
396 }
397 #endif
398
399 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
400 if (Get<Publisher>().IsAPrefixEntry(*this))
401 {
402 static_cast<PrefixEntry *>(this)->Add();
403 }
404 #endif
405 }
406
Remove(State aNextState)407 void Publisher::Entry::Remove(State aNextState)
408 {
409 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
410 if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
411 {
412 static_cast<DnsSrpServiceEntry *>(this)->Remove(aNextState);
413 }
414 #endif
415
416 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
417 if (Get<Publisher>().IsAPrefixEntry(*this))
418 {
419 static_cast<PrefixEntry *>(this)->Remove(aNextState);
420 }
421 #endif
422 }
423
ToString(bool aIncludeState) const424 Publisher::Entry::InfoString Publisher::Entry::ToString(bool aIncludeState) const
425 {
426 InfoString string;
427
428 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
429 if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
430 {
431 string.Append("DNS/SRP service");
432 ExitNow();
433 }
434 #endif
435
436 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
437 if (Get<Publisher>().IsAPrefixEntry(*this))
438 {
439 const PrefixEntry &prefixEntry = *static_cast<const PrefixEntry *>(this);
440
441 switch (prefixEntry.mType)
442 {
443 case PrefixEntry::kTypeOnMeshPrefix:
444 string.Append("OnMeshPrefix ");
445 break;
446
447 case PrefixEntry::kTypeExternalRoute:
448 string.Append("ExternalRoute ");
449 break;
450 }
451
452 string.Append("%s", prefixEntry.mPrefix.ToString().AsCString());
453 ExitNow();
454 }
455 #endif
456
457 exit:
458 if (aIncludeState)
459 {
460 string.Append(" (state:%s)", StateToString(GetState()));
461 }
462
463 return string;
464 }
465
LogUpdateTime(void) const466 void Publisher::Entry::LogUpdateTime(void) const
467 {
468 LogInfo("%s - update in %lu msec", ToString().AsCString(), ToUlong(mUpdateTime - TimerMilli::GetNow()));
469 }
470
StateToString(State aState)471 const char *Publisher::Entry::StateToString(State aState)
472 {
473 static const char *const kStateStrings[] = {
474 "NoEntry", // (0) kNoEntry
475 "ToAdd", // (1) kToAdd
476 "Adding", // (2) kAdding
477 "Added", // (3) kAdded
478 "Removing", // (4) kRemoving
479 };
480
481 static_assert(0 == kNoEntry, "kNoEntry value is not correct");
482 static_assert(1 == kToAdd, "kToAdd value is not correct");
483 static_assert(2 == kAdding, "kAdding value is not correct");
484 static_assert(3 == kAdded, "kAdded value is not correct");
485 static_assert(4 == kRemoving, "kRemoving value is not correct");
486
487 return kStateStrings[aState];
488 }
489
490 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
491
492 //---------------------------------------------------------------------------------------------------------------------
493 // Publisher::DnsSrpServiceEntry
494
DnsSrpServiceEntry(Instance & aInstance)495 Publisher::DnsSrpServiceEntry::DnsSrpServiceEntry(Instance &aInstance) { Init(aInstance); }
496
PublishAnycast(uint8_t aSequenceNumber)497 void Publisher::DnsSrpServiceEntry::PublishAnycast(uint8_t aSequenceNumber)
498 {
499 LogInfo("Publishing DNS/SRP service anycast (seq-num:%d)", aSequenceNumber);
500 Publish(Info::InfoAnycast(aSequenceNumber));
501 }
502
PublishUnicast(const Ip6::Address & aAddress,uint16_t aPort)503 void Publisher::DnsSrpServiceEntry::PublishUnicast(const Ip6::Address &aAddress, uint16_t aPort)
504 {
505 LogInfo("Publishing DNS/SRP service unicast (%s, port:%d)", aAddress.ToString().AsCString(), aPort);
506 Publish(Info::InfoUnicast(kTypeUnicast, aAddress, aPort));
507 }
508
PublishUnicast(uint16_t aPort)509 void Publisher::DnsSrpServiceEntry::PublishUnicast(uint16_t aPort)
510 {
511 LogInfo("Publishing DNS/SRP service unicast (ml-eid, port:%d)", aPort);
512 Publish(Info::InfoUnicast(kTypeUnicastMeshLocalEid, Get<Mle::Mle>().GetMeshLocalEid(), aPort));
513 }
514
Publish(const Info & aInfo)515 void Publisher::DnsSrpServiceEntry::Publish(const Info &aInfo)
516 {
517 if (GetState() != kNoEntry)
518 {
519 if (aInfo == mInfo)
520 {
521 LogInfo("%s is already being published", ToString().AsCString());
522 ExitNow();
523 }
524
525 Remove(/* aNextState */ kNoEntry);
526 }
527
528 mInfo = aInfo;
529 SetState(kToAdd);
530
531 Process();
532
533 exit:
534 return;
535 }
536
Unpublish(void)537 void Publisher::DnsSrpServiceEntry::Unpublish(void)
538 {
539 LogInfo("Unpublishing DNS/SRP service");
540
541 Remove(/* aNextState */ kNoEntry);
542 }
543
HandleNotifierEvents(Events aEvents)544 void Publisher::DnsSrpServiceEntry::HandleNotifierEvents(Events aEvents)
545 {
546 if ((GetType() == kTypeUnicastMeshLocalEid) && aEvents.Contains(kEventThreadMeshLocalAddrChanged))
547 {
548 mInfo.SetAddress(Get<Mle::Mle>().GetMeshLocalEid());
549
550 if (GetState() == kAdded)
551 {
552 // If the entry is already added, we need to update it
553 // so we remove it and add it back immediately with
554 // the new mesh-local address.
555
556 Remove(/* aNextState */ kAdding);
557 Add();
558 Get<Notifier>().HandleServerDataUpdated();
559 }
560 }
561
562 if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
563 {
564 Process();
565 }
566 }
567
Add(void)568 void Publisher::DnsSrpServiceEntry::Add(void)
569 {
570 // Adds the service entry to the network data.
571
572 switch (GetType())
573 {
574 case kTypeAnycast:
575 SuccessOrExit(Get<Service::Manager>().AddDnsSrpAnycastService(mInfo.GetSequenceNumber()));
576 break;
577
578 case kTypeUnicast:
579 SuccessOrExit(
580 Get<Service::Manager>().AddDnsSrpUnicastServiceWithAddrInServiceData(mInfo.GetAddress(), mInfo.GetPort()));
581 break;
582
583 case kTypeUnicastMeshLocalEid:
584 SuccessOrExit(
585 Get<Service::Manager>().AddDnsSrpUnicastServiceWithAddrInServerData(mInfo.GetAddress(), mInfo.GetPort()));
586 break;
587 }
588
589 Get<Notifier>().HandleServerDataUpdated();
590 SetState(kAdded);
591 Notify(kEventEntryAdded);
592
593 exit:
594 return;
595 }
596
Remove(State aNextState)597 void Publisher::DnsSrpServiceEntry::Remove(State aNextState)
598 {
599 // Removes the service entry from network data (if it was added).
600
601 VerifyOrExit((GetState() == kAdded) || (GetState() == kRemoving));
602
603 switch (GetType())
604 {
605 case kTypeAnycast:
606 SuccessOrExit(Get<Service::Manager>().RemoveDnsSrpAnycastService(mInfo.GetSequenceNumber()));
607 break;
608
609 case kTypeUnicast:
610 SuccessOrExit(Get<Service::Manager>().RemoveDnsSrpUnicastServiceWithAddrInServiceData(mInfo.GetAddress(),
611 mInfo.GetPort()));
612 break;
613
614 case kTypeUnicastMeshLocalEid:
615 SuccessOrExit(Get<Service::Manager>().RemoveDnsSrpUnicastServiceWithAddrInServerData());
616 break;
617 }
618
619 Get<Notifier>().HandleServerDataUpdated();
620 Notify(kEventEntryRemoved);
621
622 exit:
623 SetState(aNextState);
624 }
625
Notify(Event aEvent) const626 void Publisher::DnsSrpServiceEntry::Notify(Event aEvent) const
627 {
628 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
629 Get<Srp::Server>().HandleNetDataPublisherEvent(aEvent);
630 #endif
631
632 mCallback.InvokeIfSet(static_cast<otNetDataPublisherEvent>(aEvent));
633 }
634
Process(void)635 void Publisher::DnsSrpServiceEntry::Process(void)
636 {
637 // This method checks the entries currently present in Network Data
638 // based on which it then decides whether or not take action
639 // (add/remove or keep monitoring).
640
641 uint8_t numEntries = 0;
642 uint8_t numPreferredEntries = 0;
643 uint8_t desiredNumEntries = 0;
644
645 // Do not make any changes if device is not attached, and wait
646 // for role change event.
647 VerifyOrExit(Get<Mle::Mle>().IsAttached());
648
649 VerifyOrExit(GetState() != kNoEntry);
650
651 switch (GetType())
652 {
653 case kTypeAnycast:
654 CountAnycastEntries(numEntries, numPreferredEntries);
655 desiredNumEntries = kDesiredNumAnycast;
656 break;
657
658 case kTypeUnicastMeshLocalEid:
659 CountUnicastEntries(Service::kAddrInServerData, numEntries, numPreferredEntries);
660 desiredNumEntries = kDesiredNumUnicast;
661
662 if (HasAnyServiceDataUnicastEntry() || HasAnyAnycastEntry())
663 {
664 // If there is any service data unicast entry or anycast
665 // entry, we set the desired number of server data
666 // unicast entries to zero to remove any such previously
667 // added unicast entry.
668
669 desiredNumEntries = 0;
670 }
671
672 break;
673
674 case kTypeUnicast:
675 desiredNumEntries = kDesiredNumUnicast;
676 CountUnicastEntries(Service::kAddrInServiceData, numEntries, numPreferredEntries);
677 break;
678 }
679
680 UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
681
682 exit:
683 return;
684 }
685
CountAnycastEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const686 void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
687 {
688 // Count the number of matching "DNS/SRP Anycast" service entries
689 // in the Network Data (the match requires the entry to use same
690 // "sequence number" value). We prefer the entries associated with
691 // smaller RLCO16.
692
693 Service::Manager::Iterator iterator;
694 Service::DnsSrpAnycastInfo anycastInfo;
695
696 while (Get<Service::Manager>().GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNone)
697 {
698 if (anycastInfo.mSequenceNumber == mInfo.GetSequenceNumber())
699 {
700 aNumEntries++;
701
702 if (IsPreferred(anycastInfo.mRloc16))
703 {
704 aNumPreferredEntries++;
705 }
706 }
707 }
708 }
709
HasAnyAnycastEntry(void) const710 bool Publisher::DnsSrpServiceEntry::HasAnyAnycastEntry(void) const
711 {
712 Service::Manager::Iterator iterator;
713 Service::DnsSrpAnycastInfo anycastInfo;
714
715 return (Get<Service::Manager>().GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNone);
716 }
717
CountUnicastEntries(Service::DnsSrpUnicastType aType,uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const718 void Publisher::DnsSrpServiceEntry::CountUnicastEntries(Service::DnsSrpUnicastType aType,
719 uint8_t &aNumEntries,
720 uint8_t &aNumPreferredEntries) const
721 {
722 // Count the number of DNS/SRP unicast entries in the Network Data.
723
724 Service::Manager::Iterator iterator;
725 Service::DnsSrpUnicastInfo unicastInfo;
726
727 while (Get<Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, aType, unicastInfo) == kErrorNone)
728 {
729 aNumEntries++;
730
731 if (IsPreferred(unicastInfo.mRloc16))
732 {
733 aNumPreferredEntries++;
734 }
735 }
736 }
737
HasAnyServiceDataUnicastEntry(void) const738 bool Publisher::DnsSrpServiceEntry::HasAnyServiceDataUnicastEntry(void) const
739 {
740 Service::Manager::Iterator iterator;
741 Service::DnsSrpUnicastInfo unicastInfo;
742 Service::DnsSrpUnicastType type = Service::kAddrInServiceData;
743
744 return (Get<Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo) == kErrorNone);
745 }
746
747 //---------------------------------------------------------------------------------------------------------------------
748 // Publisher::DnsSrpServiceEntry::Info
749
Info(Type aType,uint16_t aPortOrSeqNumber,const Ip6::Address * aAddress)750 Publisher::DnsSrpServiceEntry::Info::Info(Type aType, uint16_t aPortOrSeqNumber, const Ip6::Address *aAddress)
751 : mPortOrSeqNumber(aPortOrSeqNumber)
752 , mType(aType)
753 {
754 // It is important to `Clear()` the object since we compare all
755 // bytes using overload of operator `==`.
756
757 Clear();
758
759 mType = aType;
760 mPortOrSeqNumber = aPortOrSeqNumber;
761
762 if (aAddress != nullptr)
763 {
764 mAddress = *aAddress;
765 }
766 }
767
768 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
769
770 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
771
772 //---------------------------------------------------------------------------------------------------------------------
773 // Publisher::PrefixEntry
774
Publish(const OnMeshPrefixConfig & aConfig,Requester aRequester)775 void Publisher::PrefixEntry::Publish(const OnMeshPrefixConfig &aConfig, Requester aRequester)
776 {
777 LogInfo("Publishing OnMeshPrefix %s", aConfig.GetPrefix().ToString().AsCString());
778
779 Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeOnMeshPrefix, aRequester);
780 }
781
Publish(const ExternalRouteConfig & aConfig,Requester aRequester)782 void Publisher::PrefixEntry::Publish(const ExternalRouteConfig &aConfig, Requester aRequester)
783 {
784 LogInfo("Publishing ExternalRoute %s", aConfig.GetPrefix().ToString().AsCString());
785
786 Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeExternalRoute, aRequester);
787 }
788
Publish(const Ip6::Prefix & aPrefix,uint16_t aNewFlags,Type aNewType,Requester aRequester)789 void Publisher::PrefixEntry::Publish(const Ip6::Prefix &aPrefix,
790 uint16_t aNewFlags,
791 Type aNewType,
792 Requester aRequester)
793 {
794 mRequester = aRequester;
795
796 if (GetState() != kNoEntry)
797 {
798 // If this is an existing entry, check if there is a change in
799 // type, flags, or the prefix itself. If not, everything is
800 // as before. If something is different, first, remove the
801 // old entry from Network Data if it was added. Then, re-add
802 // the new prefix/flags (replacing the old entry). This
803 // ensures the changes are immediately reflected in the
804 // Network Data.
805
806 State oldState = GetState();
807
808 VerifyOrExit((mType != aNewType) || (mFlags != aNewFlags) || (mPrefix != aPrefix));
809
810 Remove(/* aNextState */ kNoEntry);
811
812 if ((mType == aNewType) && ((oldState == kAdded) || (oldState == kRemoving)))
813 {
814 mPrefix = aPrefix;
815 mFlags = aNewFlags;
816 Add();
817 }
818 }
819
820 VerifyOrExit(GetState() == kNoEntry);
821
822 mType = aNewType;
823 mPrefix = aPrefix;
824 mFlags = aNewFlags;
825
826 SetState(kToAdd);
827
828 exit:
829 Process();
830 }
831
Unpublish(void)832 void Publisher::PrefixEntry::Unpublish(void)
833 {
834 LogInfo("Unpublishing %s", mPrefix.ToString().AsCString());
835
836 Remove(/* aNextState */ kNoEntry);
837 }
838
HandleNotifierEvents(Events aEvents)839 void Publisher::PrefixEntry::HandleNotifierEvents(Events aEvents)
840 {
841 if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
842 {
843 Process();
844 }
845 }
846
Add(void)847 void Publisher::PrefixEntry::Add(void)
848 {
849 // Adds the prefix entry to the network data.
850
851 switch (mType)
852 {
853 case kTypeOnMeshPrefix:
854 SuccessOrExit(AddOnMeshPrefix());
855 break;
856
857 case kTypeExternalRoute:
858 SuccessOrExit(AddExternalRoute());
859 break;
860 }
861
862 Get<Notifier>().HandleServerDataUpdated();
863 SetState(kAdded);
864 Get<Publisher>().NotifyPrefixEntryChange(kEventEntryAdded, mPrefix);
865
866 exit:
867 return;
868 }
869
AddOnMeshPrefix(void)870 Error Publisher::PrefixEntry::AddOnMeshPrefix(void)
871 {
872 OnMeshPrefixConfig config;
873
874 config.mPrefix = mPrefix;
875 config.mStable = true;
876 config.SetFromTlvFlags(mFlags);
877
878 return Get<Local>().AddOnMeshPrefix(config);
879 }
880
AddExternalRoute(void)881 Error Publisher::PrefixEntry::AddExternalRoute(void)
882 {
883 ExternalRouteConfig config;
884
885 config.mPrefix = mPrefix;
886 config.mStable = true;
887 config.SetFromTlvFlags(static_cast<uint8_t>(mFlags));
888
889 return Get<Local>().AddHasRoutePrefix(config);
890 }
891
Remove(State aNextState)892 void Publisher::PrefixEntry::Remove(State aNextState)
893 {
894 // Remove the prefix entry from the network data.
895
896 VerifyOrExit((GetState() == kAdded) || (GetState() == kRemoving));
897
898 switch (mType)
899 {
900 case kTypeOnMeshPrefix:
901 IgnoreError(Get<Local>().RemoveOnMeshPrefix(mPrefix));
902 break;
903
904 case kTypeExternalRoute:
905 IgnoreError(Get<Local>().RemoveHasRoutePrefix(mPrefix));
906 break;
907 }
908
909 Get<Notifier>().HandleServerDataUpdated();
910 Get<Publisher>().NotifyPrefixEntryChange(kEventEntryRemoved, mPrefix);
911
912 exit:
913 SetState(aNextState);
914 }
915
Process(void)916 void Publisher::PrefixEntry::Process(void)
917 {
918 // This method checks the entries currently present in Network Data
919 // based on which it then decides whether or not take action
920 // (add/remove or keep monitoring).
921
922 uint8_t numEntries = 0;
923 uint8_t numPreferredEntries = 0;
924 uint8_t desiredNumEntries = 0;
925
926 // Do not make any changes if device is not attached, and wait
927 // for role change event.
928 VerifyOrExit(Get<Mle::Mle>().IsAttached());
929
930 VerifyOrExit(GetState() != kNoEntry);
931
932 switch (mType)
933 {
934 case kTypeOnMeshPrefix:
935 CountOnMeshPrefixEntries(numEntries, numPreferredEntries);
936 desiredNumEntries = kDesiredNumOnMeshPrefix;
937 break;
938 case kTypeExternalRoute:
939 CountExternalRouteEntries(numEntries, numPreferredEntries);
940 desiredNumEntries = kDesiredNumExternalRoute;
941 break;
942 }
943
944 UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
945
946 exit:
947 return;
948 }
949
CountOnMeshPrefixEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const950 void Publisher::PrefixEntry::CountOnMeshPrefixEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
951 {
952 const PrefixTlv *prefixTlv;
953 const BorderRouterTlv *brSubTlv;
954 int8_t preference = BorderRouterEntry::PreferenceFromFlags(mFlags);
955 uint16_t flagsWithoutPreference = BorderRouterEntry::FlagsWithoutPreference(mFlags);
956
957 prefixTlv = Get<Leader>().FindPrefix(mPrefix);
958 VerifyOrExit(prefixTlv != nullptr);
959
960 brSubTlv = prefixTlv->FindSubTlv<BorderRouterTlv>(/* aStable */ true);
961 VerifyOrExit(brSubTlv != nullptr);
962
963 for (const BorderRouterEntry *entry = brSubTlv->GetFirstEntry(); entry <= brSubTlv->GetLastEntry();
964 entry = entry->GetNext())
965 {
966 uint16_t entryFlags = entry->GetFlags();
967 int8_t entryPreference = BorderRouterEntry::PreferenceFromFlags(entryFlags);
968
969 // Count an existing entry in the network data if its flags
970 // match ours and and its preference is same or higher than our
971 // preference. We do not count matching entries at a lower
972 // preference than ours. This ensures that a device with higher
973 // preference entry publishes its entry even when there are many
974 // lower preference similar entries in the network data
975 // (potentially causing a lower preference entry to be removed).
976
977 if ((BorderRouterEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
978 (entryPreference >= preference))
979 {
980 aNumEntries++;
981
982 // We prefer an entry if it has strictly higher preference
983 // than ours or if it has same preference we use the associated
984 // RLOC16.
985
986 if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
987 {
988 aNumPreferredEntries++;
989 }
990 }
991 }
992
993 exit:
994 return;
995 }
996
CountExternalRouteEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const997 void Publisher::PrefixEntry::CountExternalRouteEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
998 {
999 const PrefixTlv *prefixTlv;
1000 const HasRouteTlv *hrSubTlv;
1001 int8_t preference = HasRouteEntry::PreferenceFromFlags(static_cast<uint8_t>(mFlags));
1002 uint8_t flagsWithoutPreference = HasRouteEntry::FlagsWithoutPreference(static_cast<uint8_t>(mFlags));
1003
1004 prefixTlv = Get<Leader>().FindPrefix(mPrefix);
1005 VerifyOrExit(prefixTlv != nullptr);
1006
1007 hrSubTlv = prefixTlv->FindSubTlv<HasRouteTlv>(/* aStable */ true);
1008 VerifyOrExit(hrSubTlv != nullptr);
1009
1010 for (const HasRouteEntry *entry = hrSubTlv->GetFirstEntry(); entry <= hrSubTlv->GetLastEntry();
1011 entry = entry->GetNext())
1012 {
1013 uint8_t entryFlags = entry->GetFlags();
1014 int8_t entryPreference = HasRouteEntry::PreferenceFromFlags(entryFlags);
1015
1016 // Count an existing entry in the network data if its flags
1017 // match ours and and its preference is same or higher than our
1018 // preference. We do not count matching entries at a lower
1019 // preference than ours. This ensures that a device with higher
1020 // preference entry publishes its entry even when there are many
1021 // lower preference similar entries in the network data
1022 // (potentially causing a lower preference entry to be removed).
1023
1024 if ((HasRouteEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
1025 (entryPreference >= preference))
1026 {
1027 aNumEntries++;
1028
1029 // We prefer an entry if it has strictly higher preference
1030 // than ours or if it has same preference with a smaller
1031 // RLOC16.
1032
1033 if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
1034 {
1035 aNumPreferredEntries++;
1036 }
1037 }
1038 }
1039
1040 exit:
1041 return;
1042 }
1043
1044 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
1045
1046 } // namespace NetworkData
1047 } // namespace ot
1048
1049 #endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
1050