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