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