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/code_utils.hpp"
40 #include "common/instance.hpp"
41 #include "common/locator_getters.hpp"
42 #include "common/random.hpp"
43 #include "thread/network_data_local.hpp"
44 #include "thread/network_data_service.hpp"
45 
46 namespace ot {
47 namespace NetworkData {
48 
49 //---------------------------------------------------------------------------------------------------------------------
50 // Publisher
51 
Publisher(Instance & aInstance)52 Publisher::Publisher(Instance &aInstance)
53     : InstanceLocator(aInstance)
54 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
55     , mDnsSrpServiceEntry(aInstance)
56 #endif
57 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
58     , mPrefixCallback(nullptr)
59     , mPrefixCallbackContext(nullptr)
60 #endif
61     , mTimer(aInstance, Publisher::HandleTimer)
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 `InstacneLocator`) so we use
67     // `IntanceLocatorInit`  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 
SetPrefixCallback(PrefixCallback aCallback,void * aContext)79 void Publisher::SetPrefixCallback(PrefixCallback aCallback, void *aContext)
80 {
81     mPrefixCallback        = aCallback;
82     mPrefixCallbackContext = aContext;
83 }
84 
PublishOnMeshPrefix(const OnMeshPrefixConfig & aConfig)85 Error Publisher::PublishOnMeshPrefix(const OnMeshPrefixConfig &aConfig)
86 {
87     Error        error;
88     PrefixEntry *entry;
89 
90     VerifyOrExit(aConfig.IsValid(GetInstance()), error = kErrorInvalidArgs);
91     VerifyOrExit(aConfig.mStable, error = kErrorInvalidArgs);
92 
93     SuccessOrExit(error = AllocatePrefixEntry(aConfig.GetPrefix(), entry));
94     entry->Publish(aConfig);
95 
96 exit:
97     return error;
98 }
99 
PublishExternalRoute(const ExternalRouteConfig & aConfig)100 Error Publisher::PublishExternalRoute(const ExternalRouteConfig &aConfig)
101 {
102     Error        error;
103     PrefixEntry *entry;
104 
105     VerifyOrExit(aConfig.IsValid(GetInstance()), error = kErrorInvalidArgs);
106     VerifyOrExit(aConfig.mStable, error = kErrorInvalidArgs);
107 
108     SuccessOrExit(error = AllocatePrefixEntry(aConfig.GetPrefix(), entry));
109     entry->Publish(aConfig);
110 
111 exit:
112     return error;
113 }
114 
IsPrefixAdded(const Ip6::Prefix & aPrefix) const115 bool Publisher::IsPrefixAdded(const Ip6::Prefix &aPrefix) const
116 {
117     bool               isAdded = false;
118     const PrefixEntry *entry;
119 
120     entry = FindMatchingPrefixEntry(aPrefix);
121     VerifyOrExit(entry != nullptr);
122 
123     isAdded = entry->IsAdded();
124 
125 exit:
126     return isAdded;
127 }
128 
UnpublishPrefix(const Ip6::Prefix & aPrefix)129 Error Publisher::UnpublishPrefix(const Ip6::Prefix &aPrefix)
130 {
131     Error        error = kErrorNone;
132     PrefixEntry *entry;
133 
134     entry = FindMatchingPrefixEntry(aPrefix);
135     VerifyOrExit(entry != nullptr, error = kErrorNotFound);
136 
137     entry->Unpublish();
138 
139 exit:
140     return error;
141 }
142 
AllocatePrefixEntry(const Ip6::Prefix & aPrefix,PrefixEntry * & aEntry)143 Error Publisher::AllocatePrefixEntry(const Ip6::Prefix &aPrefix, PrefixEntry *&aEntry)
144 {
145     Error error = kErrorNoBufs;
146 
147     VerifyOrExit(FindMatchingPrefixEntry(aPrefix) == nullptr, error = kErrorAlready);
148 
149     for (PrefixEntry &entry : mPrefixEntries)
150     {
151         if (!entry.IsInUse())
152         {
153             aEntry = &entry;
154             ExitNow(error = kErrorNone);
155         }
156     }
157 
158 exit:
159     return error;
160 }
161 
FindMatchingPrefixEntry(const Ip6::Prefix & aPrefix)162 Publisher::PrefixEntry *Publisher::FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix)
163 {
164     return const_cast<PrefixEntry *>(const_cast<const Publisher *>(this)->FindMatchingPrefixEntry(aPrefix));
165 }
166 
FindMatchingPrefixEntry(const Ip6::Prefix & aPrefix) const167 const Publisher::PrefixEntry *Publisher::FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix) const
168 {
169     const PrefixEntry *prefixEntry = nullptr;
170 
171     for (const PrefixEntry &entry : mPrefixEntries)
172     {
173         if (entry.IsInUse() && entry.Matches(aPrefix))
174         {
175             prefixEntry = &entry;
176             break;
177         }
178     }
179 
180     return prefixEntry;
181 }
182 
IsAPrefixEntry(const Entry & aEntry) const183 bool Publisher::IsAPrefixEntry(const Entry &aEntry) const
184 {
185     return (&mPrefixEntries[0] <= &aEntry) && (&aEntry < OT_ARRAY_END(mPrefixEntries));
186 }
187 
NotifyPrefixEntryChange(Event aEvent,const Ip6::Prefix & aPrefix) const188 void Publisher::NotifyPrefixEntryChange(Event aEvent, const Ip6::Prefix &aPrefix) const
189 {
190     if (mPrefixCallback != nullptr)
191     {
192         mPrefixCallback(static_cast<otNetDataPublisherEvent>(aEvent), &aPrefix, mPrefixCallbackContext);
193     }
194 }
195 
196 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
197 
HandleNotifierEvents(Events aEvents)198 void Publisher::HandleNotifierEvents(Events aEvents)
199 {
200     OT_UNUSED_VARIABLE(aEvents);
201 
202     bool registerWithLeader = false;
203 
204 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
205     if (mDnsSrpServiceEntry.HandleNotifierEvents(aEvents))
206     {
207         registerWithLeader = true;
208     }
209 #endif
210 
211 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
212     for (PrefixEntry &entry : mPrefixEntries)
213     {
214         entry.HandleNotifierEvents(aEvents);
215     }
216 #endif
217 
218     if (registerWithLeader)
219     {
220         Get<Notifier>().HandleServerDataUpdated();
221     }
222 }
223 
HandleTimer(Timer & aTimer)224 void Publisher::HandleTimer(Timer &aTimer)
225 {
226     aTimer.Get<Publisher>().HandleTimer();
227 }
228 
HandleTimer(void)229 void Publisher::HandleTimer(void)
230 {
231     bool registerWithLeader = false;
232 
233 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
234     if (mDnsSrpServiceEntry.HandleTimer())
235     {
236         registerWithLeader = true;
237     }
238 #endif
239 
240 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
241     for (PrefixEntry &entry : mPrefixEntries)
242     {
243         if (entry.HandleTimer())
244         {
245             registerWithLeader = true;
246         }
247     }
248 #endif
249 
250     if (registerWithLeader)
251     {
252         Get<Notifier>().HandleServerDataUpdated();
253     }
254 }
255 
256 //---------------------------------------------------------------------------------------------------------------------
257 // Publisher::Entry
258 
SetState(State aState)259 void Publisher::Entry::SetState(State aState)
260 {
261     VerifyOrExit(mState != aState);
262 
263     otLogInfoNetData("Publisher: %s - State: %s -> %s", ToString(/* aIncludeState */ false).AsCString(),
264                      StateToString(mState), 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::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     otLogInfoNetData("Publisher: %s in netdata - total:%d, preferred:%d, desired:%d", ToString().AsCString(),
292                      aNumEntries, 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 += kExtraDelayToRemovePeferred;
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 bool Publisher::Entry::HandleTimer(void)
363 {
364     bool registerWithLeader = false;
365 
366     // Timer is used to delay adding/removing the entry. If we have
367     // reached `mUpdateTime` add or remove the entry. Otherwise,
368     // restart the timer (note that timer can be shared between
369     // different published entries). This method returns a `bool`
370     // indicating whether or not anything in local Network Data got
371     // changed so to notify the leader and register the changes.
372 
373     VerifyOrExit((GetState() == kAdding) || (GetState() == kRemoving));
374 
375     if (mUpdateTime <= TimerMilli::GetNow())
376     {
377         registerWithLeader = (GetState() == kAdding) ? Add() : Remove(/* aNextState */ kToAdd);
378     }
379     else
380     {
381         Get<Publisher>().GetTimer().FireAtIfEarlier(mUpdateTime);
382     }
383 
384 exit:
385     return registerWithLeader;
386 }
387 
Add(void)388 bool Publisher::Entry::Add(void)
389 {
390     bool registerWithLeader = false;
391 
392 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
393     if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
394     {
395         registerWithLeader = static_cast<DnsSrpServiceEntry *>(this)->Add();
396         ExitNow();
397     }
398 #endif
399 
400 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
401     if (Get<Publisher>().IsAPrefixEntry(*this))
402     {
403         registerWithLeader = static_cast<PrefixEntry *>(this)->Add();
404         ExitNow();
405     }
406 #endif
407 
408 exit:
409     return registerWithLeader;
410 }
411 
Remove(State aNextState)412 bool Publisher::Entry::Remove(State aNextState)
413 {
414     bool registerWithLeader = false;
415 
416 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
417     if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
418     {
419         registerWithLeader = static_cast<DnsSrpServiceEntry *>(this)->Remove(aNextState);
420         ExitNow();
421     }
422 #endif
423 
424 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
425     if (Get<Publisher>().IsAPrefixEntry(*this))
426     {
427         registerWithLeader = static_cast<PrefixEntry *>(this)->Remove(aNextState);
428         ExitNow();
429     }
430 #endif
431 
432 exit:
433     return registerWithLeader;
434 }
435 
ToString(bool aIncludeState) const436 Publisher::Entry::InfoString Publisher::Entry::ToString(bool aIncludeState) const
437 {
438     InfoString string;
439 
440 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
441     if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
442     {
443         string.Append("DNS/SRP service");
444         ExitNow();
445     }
446 #endif
447 
448 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
449     if (Get<Publisher>().IsAPrefixEntry(*this))
450     {
451         const PrefixEntry &prefixEntry = *static_cast<const PrefixEntry *>(this);
452 
453         switch (prefixEntry.mType)
454         {
455         case PrefixEntry::kTypeOnMeshPrefix:
456             string.Append("OnMeshPrefix ");
457             break;
458 
459         case PrefixEntry::kTypeExternalRoute:
460             string.Append("ExternalRoute ");
461             break;
462         }
463 
464         string.Append(prefixEntry.mPrefix.ToString().AsCString());
465         ExitNow();
466     }
467 #endif
468 
469 exit:
470     if (aIncludeState)
471     {
472         string.Append(" (state:%s)", StateToString(GetState()));
473     }
474 
475     return string;
476 }
477 
LogUpdateTime(void) const478 void Publisher::Entry::LogUpdateTime(void) const
479 {
480     otLogInfoNetData("Publisher: %s - update in %u msec", ToString().AsCString(), mUpdateTime - TimerMilli::GetNow());
481 }
482 
StateToString(State aState)483 const char *Publisher::Entry::StateToString(State aState)
484 {
485     static const char *const kStateStrings[] = {
486         "NoEntry",  // (0) kNoEntry
487         "ToAdd",    // (1) kToAdd
488         "Adding",   // (2) kAdding
489         "Added",    // (3) kAdded
490         "Removing", // (4) kRemoving
491     };
492 
493     static_assert(0 == kNoEntry, "kNoEntry value is not correct");
494     static_assert(1 == kToAdd, "kToAdd value is not correct");
495     static_assert(2 == kAdding, "kAdding value is not correct");
496     static_assert(3 == kAdded, "kAdded value is not correct");
497     static_assert(4 == kRemoving, "kRemoving value is not correct");
498 
499     return kStateStrings[aState];
500 }
501 
502 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
503 
504 //---------------------------------------------------------------------------------------------------------------------
505 // Publisher::DnsSrpServiceEntry
506 
DnsSrpServiceEntry(Instance & aInstance)507 Publisher::DnsSrpServiceEntry::DnsSrpServiceEntry(Instance &aInstance)
508     : mCallback(nullptr)
509     , mCallbackContext(nullptr)
510 {
511     Init(aInstance);
512 }
513 
SetCallback(DnsSrpServiceCallback aCallback,void * aContext)514 void Publisher::DnsSrpServiceEntry::SetCallback(DnsSrpServiceCallback aCallback, void *aContext)
515 {
516     mCallback        = aCallback;
517     mCallbackContext = aContext;
518 }
519 
PublishAnycast(uint8_t aSequenceNumber)520 void Publisher::DnsSrpServiceEntry::PublishAnycast(uint8_t aSequenceNumber)
521 {
522     otLogInfoNetData("Publisher: Publishing DNS/SRP service anycast (seq-num:%d)", aSequenceNumber);
523     Publish(Info::InfoAnycast(aSequenceNumber));
524 }
525 
PublishUnicast(const Ip6::Address & aAddress,uint16_t aPort)526 void Publisher::DnsSrpServiceEntry::PublishUnicast(const Ip6::Address &aAddress, uint16_t aPort)
527 {
528     otLogInfoNetData("Publisher: Publishing DNS/SRP service unicast (%s, port:%d)", aAddress.ToString().AsCString(),
529                      aPort);
530     Publish(Info::InfoUnicast(kTypeUnicast, aAddress, aPort));
531 }
532 
PublishUnicast(uint16_t aPort)533 void Publisher::DnsSrpServiceEntry::PublishUnicast(uint16_t aPort)
534 {
535     otLogInfoNetData("Publisher: Publishing DNS/SRP service unicast (ml-eid, port:%d)", aPort);
536     Publish(Info::InfoUnicast(kTypeUnicastMeshLocalEid, Get<Mle::Mle>().GetMeshLocal64(), aPort));
537 }
538 
Publish(const Info & aInfo)539 void Publisher::DnsSrpServiceEntry::Publish(const Info &aInfo)
540 {
541     bool registerWithLeader = false;
542 
543     if (GetState() != kNoEntry)
544     {
545         if (aInfo == mInfo)
546         {
547             otLogInfoNetData("Publisher: %s is already being published", ToString().AsCString());
548             ExitNow();
549         }
550 
551         registerWithLeader = Remove(/* aNextState */ kNoEntry);
552     }
553 
554     mInfo = aInfo;
555     SetState(kToAdd);
556 
557     Process();
558 
559     if (registerWithLeader)
560     {
561         Get<Notifier>().HandleServerDataUpdated();
562     }
563 
564 exit:
565     return;
566 }
567 
Unpublish(void)568 void Publisher::DnsSrpServiceEntry::Unpublish(void)
569 {
570     bool registerWithLeader;
571 
572     otLogInfoNetData("Publisher: Unpublishing DNS/SRP service");
573 
574     registerWithLeader = Remove(/* aNextState */ kNoEntry);
575 
576     if (registerWithLeader)
577     {
578         Get<Notifier>().HandleServerDataUpdated();
579     }
580 }
581 
HandleNotifierEvents(Events aEvents)582 bool Publisher::DnsSrpServiceEntry::HandleNotifierEvents(Events aEvents)
583 {
584     bool registerWithLeader = false;
585 
586     if ((GetType() == kTypeUnicastMeshLocalEid) && aEvents.Contains(kEventThreadMeshLocalAddrChanged))
587     {
588         mInfo.SetAddress(Get<Mle::Mle>().GetMeshLocal64());
589 
590         if (GetState() == kAdded)
591         {
592             // If the entry is already added, we need to update it
593             // so we remove it and add it back immediately with
594             // the new mesh-local address.
595 
596             Remove(/* aNextState */ kAdding);
597             Add();
598             registerWithLeader = true;
599         }
600     }
601 
602     if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
603     {
604         Process();
605     }
606 
607     return registerWithLeader;
608 }
609 
Add(void)610 bool Publisher::DnsSrpServiceEntry::Add(void)
611 {
612     // Adds the service entry to the network data.
613 
614     bool registerWithLeader = false;
615 
616     switch (GetType())
617     {
618     case kTypeAnycast:
619         SuccessOrExit(Get<Service::Manager>().Add<Service::DnsSrpAnycast>(
620             Service::DnsSrpAnycast::ServiceData(mInfo.GetSequenceNumber())));
621         break;
622 
623     case kTypeUnicast:
624         SuccessOrExit(Get<Service::Manager>().Add<Service::DnsSrpUnicast>(
625             Service::DnsSrpUnicast::ServiceData(mInfo.GetAddress(), mInfo.GetPort())));
626         break;
627 
628     case kTypeUnicastMeshLocalEid:
629         SuccessOrExit(Get<Service::Manager>().Add<Service::DnsSrpUnicast>(
630             Service::DnsSrpUnicast::ServerData(mInfo.GetAddress(), mInfo.GetPort())));
631         break;
632     }
633 
634     registerWithLeader = true;
635     SetState(kAdded);
636     Notify(kEventEntryAdded);
637 
638 exit:
639     return registerWithLeader;
640 }
641 
Remove(State aNextState)642 bool Publisher::DnsSrpServiceEntry::Remove(State aNextState)
643 {
644     // Removes the service entry from network data (if it was added).
645 
646     bool registerWithLeader = false;
647 
648     VerifyOrExit((GetState() == kAdded) || (GetState() == kRemoving));
649 
650     switch (GetType())
651     {
652     case kTypeAnycast:
653         SuccessOrExit(Get<Service::Manager>().Remove<Service::DnsSrpAnycast>(
654             Service::DnsSrpAnycast::ServiceData(mInfo.GetSequenceNumber())));
655         break;
656 
657     case kTypeUnicast:
658         SuccessOrExit(Get<Service::Manager>().Remove<Service::DnsSrpUnicast>(
659             Service::DnsSrpUnicast::ServiceData(mInfo.GetAddress(), mInfo.GetPort())));
660         break;
661 
662     case kTypeUnicastMeshLocalEid:
663         SuccessOrExit(Get<Service::Manager>().Remove<Service::DnsSrpUnicast>());
664         break;
665     }
666 
667     registerWithLeader = true;
668     Notify(kEventEntryRemoved);
669 
670 exit:
671     SetState(aNextState);
672     return registerWithLeader;
673 }
674 
Notify(Event aEvent) const675 void Publisher::DnsSrpServiceEntry::Notify(Event aEvent) const
676 {
677 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
678     Get<Srp::Server>().HandleNetDataPublisherEvent(aEvent);
679 #endif
680 
681     if (mCallback != nullptr)
682     {
683         mCallback(static_cast<otNetDataPublisherEvent>(aEvent), mCallbackContext);
684     }
685 }
686 
Process(void)687 void Publisher::DnsSrpServiceEntry::Process(void)
688 {
689     // This method checks the entries currently present in Network Data
690     // based on which it then decides whether or not take action
691     // (add/remove or keep monitoring).
692 
693     uint8_t numEntries          = 0;
694     uint8_t numPreferredEntries = 0;
695     uint8_t desiredNumEntries   = 0;
696 
697     // Do not make any changes if device is not attached, and wait
698     // for role change event.
699     VerifyOrExit(Get<Mle::Mle>().IsAttached());
700 
701     VerifyOrExit(GetState() != kNoEntry);
702 
703     switch (GetType())
704     {
705     case kTypeAnycast:
706         CountAnycastEntries(numEntries, numPreferredEntries);
707         desiredNumEntries = kDesiredNumAnycast;
708         break;
709 
710     case kTypeUnicast:
711     case kTypeUnicastMeshLocalEid:
712         CountUnicastEntries(numEntries, numPreferredEntries);
713         desiredNumEntries = kDesiredNumUnicast;
714         break;
715     }
716 
717     UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
718 
719 exit:
720     return;
721 }
722 
CountAnycastEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const723 void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
724 {
725     // Count the number of matching "DNS/SRP Anycast" service entries
726     // in the Network Data (the match requires the entry to use same
727     // "sequence number" value). We prefer the entries associated with
728     // smaller RLCO16.
729 
730     Service::DnsSrpAnycast::ServiceData serviceData(mInfo.GetSequenceNumber());
731     const ServiceTlv *                  serviceTlv = nullptr;
732 
733     while ((serviceTlv = Get<Leader>().FindNextService(
734                 serviceTlv, Service::kThreadEnterpriseNumber, reinterpret_cast<const uint8_t *>(&serviceData),
735                 serviceData.GetLength(), NetworkData::kServicePrefixMatch)) != nullptr)
736     {
737         TlvIterator      subTlvIterator(*serviceTlv);
738         const ServerTlv *serverSubTlv;
739 
740         while ((serverSubTlv = subTlvIterator.Iterate<ServerTlv>()) != nullptr)
741         {
742             aNumEntries++;
743 
744             if (IsPreferred(serverSubTlv->GetServer16()))
745             {
746                 aNumPreferredEntries++;
747             }
748         }
749     }
750 }
751 
CountUnicastEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const752 void Publisher::DnsSrpServiceEntry::CountUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
753 {
754     // Count the number of "DNS/SRP Unicast" service entries in
755     // the Network Data.
756 
757     const ServiceTlv *serviceTlv = nullptr;
758 
759     while ((serviceTlv = Get<Leader>().FindNextService(
760                 serviceTlv, Service::kThreadEnterpriseNumber, &Service::DnsSrpUnicast::kServiceData,
761                 sizeof(Service::DnsSrpUnicast::kServiceData), NetworkData::kServicePrefixMatch)) != nullptr)
762     {
763         TlvIterator      subTlvIterator(*serviceTlv);
764         const ServerTlv *serverSubTlv;
765 
766         while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
767         {
768             if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData))
769             {
770                 aNumEntries++;
771 
772                 // Generally, we prefer entries where the SRP/DNS server
773                 // address/port info is included in the service TLV data
774                 // over the ones where the info is included in the
775                 // server TLV data (i.e., we prefer infra-provided
776                 // SRP/DNS entry over a BR local one using ML-EID). If
777                 // our entry itself uses the service TLV data, then we
778                 // prefer based on the associated RLOC16.
779 
780                 if (GetType() == kTypeUnicast)
781                 {
782                     if (IsPreferred(serverSubTlv->GetServer16()))
783                     {
784                         aNumPreferredEntries++;
785                     }
786                 }
787                 else
788                 {
789                     aNumPreferredEntries++;
790                 }
791             }
792 
793             if (serverSubTlv->GetServerDataLength() >= sizeof(Service::DnsSrpUnicast::ServerData))
794             {
795                 aNumEntries++;
796 
797                 // If our entry also uses the server TLV data (with
798                 // ML-EID address), then the we prefer based on the
799                 // associated RLOC16.
800 
801                 if ((GetType() == kTypeUnicastMeshLocalEid) && IsPreferred(serverSubTlv->GetServer16()))
802                 {
803                     aNumPreferredEntries++;
804                 }
805             }
806         }
807     }
808 }
809 
810 //---------------------------------------------------------------------------------------------------------------------
811 // Publisher::DnsSrpServiceEntry::Info
812 
Info(Type aType,uint16_t aPortOrSeqNumber,const Ip6::Address * aAddress)813 Publisher::DnsSrpServiceEntry::Info::Info(Type aType, uint16_t aPortOrSeqNumber, const Ip6::Address *aAddress)
814     : mPortOrSeqNumber(aPortOrSeqNumber)
815     , mType(aType)
816 {
817     // It is important to `Clear()` the object since we compare all
818     // bytes using overload of operator `==`.
819 
820     Clear();
821 
822     mType            = aType;
823     mPortOrSeqNumber = aPortOrSeqNumber;
824 
825     if (aAddress != nullptr)
826     {
827         mAddress = *aAddress;
828     }
829 }
830 
831 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
832 
833 //---------------------------------------------------------------------------------------------------------------------
834 // Publisher::PrefixEntry
835 
Publish(const OnMeshPrefixConfig & aConfig)836 void Publisher::PrefixEntry::Publish(const OnMeshPrefixConfig &aConfig)
837 {
838     otLogInfoNetData("Publisher: Publishing OnMeshPrefix %s", aConfig.GetPrefix().ToString().AsCString());
839 
840     mType   = kTypeOnMeshPrefix;
841     mPrefix = aConfig.GetPrefix();
842     mFlags  = aConfig.ConvertToTlvFlags();
843 
844     SetState(kToAdd);
845 
846     Process();
847 }
848 
Publish(const ExternalRouteConfig & aConfig)849 void Publisher::PrefixEntry::Publish(const ExternalRouteConfig &aConfig)
850 {
851     otLogInfoNetData("Publisher: Publishing ExternalRoute %s", aConfig.GetPrefix().ToString().AsCString());
852 
853     mType   = kTypeExternalRoute;
854     mPrefix = aConfig.GetPrefix();
855     mFlags  = aConfig.ConvertToTlvFlags();
856 
857     SetState(kToAdd);
858 
859     Process();
860 }
861 
Unpublish(void)862 void Publisher::PrefixEntry::Unpublish(void)
863 {
864     bool registerWithLeader = false;
865 
866     otLogInfoNetData("Publisher: Unpublishing %s", mPrefix.ToString().AsCString());
867 
868     registerWithLeader = Remove(/* aNextState */ kNoEntry);
869 
870     if (registerWithLeader)
871     {
872         Get<Notifier>().HandleServerDataUpdated();
873     }
874 }
875 
HandleNotifierEvents(Events aEvents)876 void Publisher::PrefixEntry::HandleNotifierEvents(Events aEvents)
877 {
878     if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
879     {
880         Process();
881     }
882 }
883 
Add(void)884 bool Publisher::PrefixEntry::Add(void)
885 {
886     // Adds the prefix entry to the network data.
887 
888     bool registerWithLeader = false;
889 
890     switch (mType)
891     {
892     case kTypeOnMeshPrefix:
893         SuccessOrExit(AddOnMeshPrefix());
894         break;
895 
896     case kTypeExternalRoute:
897         SuccessOrExit(AddExternalRoute());
898         break;
899     }
900 
901     registerWithLeader = true;
902     SetState(kAdded);
903     Get<Publisher>().NotifyPrefixEntryChange(kEventEntryAdded, mPrefix);
904 
905 exit:
906     return registerWithLeader;
907 }
908 
AddOnMeshPrefix(void)909 Error Publisher::PrefixEntry::AddOnMeshPrefix(void)
910 {
911     OnMeshPrefixConfig config;
912 
913     config.mPrefix = mPrefix;
914     config.mStable = true;
915     config.SetFromTlvFlags(mFlags);
916 
917     return Get<Local>().AddOnMeshPrefix(config);
918 }
919 
AddExternalRoute(void)920 Error Publisher::PrefixEntry::AddExternalRoute(void)
921 {
922     ExternalRouteConfig config;
923 
924     config.mPrefix = mPrefix;
925     config.mStable = true;
926     config.SetFromTlvFlags(static_cast<uint8_t>(mFlags));
927 
928     return Get<Local>().AddHasRoutePrefix(config);
929 }
930 
Remove(State aNextState)931 bool Publisher::PrefixEntry::Remove(State aNextState)
932 {
933     // Remove the prefix entry from the network data.
934 
935     bool registerWithLeader = false;
936 
937     VerifyOrExit((GetState() == kAdded) || (GetState() == kRemoving));
938 
939     switch (mType)
940     {
941     case kTypeOnMeshPrefix:
942         IgnoreError(Get<Local>().RemoveOnMeshPrefix(mPrefix));
943         break;
944 
945     case kTypeExternalRoute:
946         IgnoreError(Get<Local>().RemoveHasRoutePrefix(mPrefix));
947         break;
948     }
949 
950     registerWithLeader = true;
951     Get<Publisher>().NotifyPrefixEntryChange(kEventEntryRemoved, mPrefix);
952 
953 exit:
954     SetState(aNextState);
955     return registerWithLeader;
956 }
957 
Process(void)958 void Publisher::PrefixEntry::Process(void)
959 {
960     // This method checks the entries currently present in Network Data
961     // based on which it then decides whether or not take action
962     // (add/remove or keep monitoring).
963 
964     uint8_t numEntries          = 0;
965     uint8_t numPreferredEntries = 0;
966     uint8_t desiredNumEntries   = 0;
967 
968     // Do not make any changes if device is not attached, and wait
969     // for role change event.
970     VerifyOrExit(Get<Mle::Mle>().IsAttached());
971 
972     VerifyOrExit(GetState() != kNoEntry);
973 
974     switch (mType)
975     {
976     case kTypeOnMeshPrefix:
977         CountOnMeshPrefixEntries(numEntries, numPreferredEntries);
978         desiredNumEntries = kDesiredNumOnMeshPrefix;
979         break;
980     case kTypeExternalRoute:
981         CountExternalRouteEntries(numEntries, numPreferredEntries);
982         desiredNumEntries = kDesiredNumExternalRoute;
983         break;
984     }
985 
986     UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
987 
988 exit:
989     return;
990 }
991 
CountOnMeshPrefixEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const992 void Publisher::PrefixEntry::CountOnMeshPrefixEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
993 {
994     const PrefixTlv *      prefixTlv;
995     const BorderRouterTlv *brSubTlv;
996     int8_t                 preference             = BorderRouterEntry::PreferenceFromFlags(mFlags);
997     uint16_t               flagsWithoutPreference = BorderRouterEntry::FlagsWithoutPreference(mFlags);
998 
999     prefixTlv = Get<Leader>().FindPrefix(mPrefix);
1000     VerifyOrExit(prefixTlv != nullptr);
1001 
1002     brSubTlv = prefixTlv->FindSubTlv<BorderRouterTlv>(/* aStable */ true);
1003     VerifyOrExit(brSubTlv != nullptr);
1004 
1005     for (const BorderRouterEntry *entry = brSubTlv->GetFirstEntry(); entry <= brSubTlv->GetLastEntry();
1006          entry                          = entry->GetNext())
1007     {
1008         uint16_t entryFlags      = entry->GetFlags();
1009         int8_t   entryPreference = BorderRouterEntry::PreferenceFromFlags(entryFlags);
1010 
1011         // Count an existing entry in the network data if its flags
1012         // match ours and and its preference is same or higher than our
1013         // preference. We do not count matching entries at a lower
1014         // preference than ours. This ensures that a device with higher
1015         // preference entry publishes its entry even when there are many
1016         // lower preference similar entries in the network data
1017         // (potentially causing a lower preference entry to be removed).
1018 
1019         if ((BorderRouterEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
1020             (entryPreference >= preference))
1021         {
1022             aNumEntries++;
1023 
1024             // We prefer an entry if it has strictly higher preference
1025             // than ours or if it has same preference we use the associated
1026             // RLOC16.
1027 
1028             if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
1029             {
1030                 aNumPreferredEntries++;
1031             }
1032         }
1033     }
1034 
1035 exit:
1036     return;
1037 }
1038 
CountExternalRouteEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const1039 void Publisher::PrefixEntry::CountExternalRouteEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
1040 {
1041     const PrefixTlv *  prefixTlv;
1042     const HasRouteTlv *hrSubTlv;
1043     int8_t             preference             = HasRouteEntry::PreferenceFromFlags(static_cast<uint8_t>(mFlags));
1044     uint8_t            flagsWithoutPreference = HasRouteEntry::FlagsWithoutPreference(static_cast<uint8_t>(mFlags));
1045 
1046     prefixTlv = Get<Leader>().FindPrefix(mPrefix);
1047     VerifyOrExit(prefixTlv != nullptr);
1048 
1049     hrSubTlv = prefixTlv->FindSubTlv<HasRouteTlv>(/* aStable */ true);
1050     VerifyOrExit(hrSubTlv != nullptr);
1051 
1052     for (const HasRouteEntry *entry = hrSubTlv->GetFirstEntry(); entry <= hrSubTlv->GetLastEntry();
1053          entry                      = entry->GetNext())
1054     {
1055         uint8_t entryFlags      = entry->GetFlags();
1056         int8_t  entryPreference = HasRouteEntry::PreferenceFromFlags(entryFlags);
1057 
1058         // Count an existing entry in the network data if its flags
1059         // match ours and and its preference is same or higher than our
1060         // preference. We do not count matching entries at a lower
1061         // preference than ours. This ensures that a device with higher
1062         // preference entry publishes its entry even when there are many
1063         // lower preference similar entries in the network data
1064         // (potentially causing a lower preference entry to be removed).
1065 
1066         if ((HasRouteEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
1067             (entryPreference >= preference))
1068         {
1069             aNumEntries++;
1070 
1071             // We prefer an entry if it has strictly higher preference
1072             // than ours or if it has same preference with a smaller
1073             // RLOC16.
1074 
1075             if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
1076             {
1077                 aNumPreferredEntries++;
1078             }
1079         }
1080     }
1081 
1082 exit:
1083     return;
1084 }
1085 
1086 } // namespace NetworkData
1087 } // namespace ot
1088 
1089 #endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
1090