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/instance.hpp"
43 #include "common/locator_getters.hpp"
44 #include "common/log.hpp"
45 #include "common/random.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 kTypeUnicast:
662     case kTypeUnicastMeshLocalEid:
663         CountUnicastEntries(numEntries, numPreferredEntries);
664         desiredNumEntries = kDesiredNumUnicast;
665         break;
666     }
667 
668     UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
669 
670 exit:
671     return;
672 }
673 
CountAnycastEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const674 void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
675 {
676     // Count the number of matching "DNS/SRP Anycast" service entries
677     // in the Network Data (the match requires the entry to use same
678     // "sequence number" value). We prefer the entries associated with
679     // smaller RLCO16.
680 
681     Service::DnsSrpAnycast::ServiceData serviceData(mInfo.GetSequenceNumber());
682     const ServiceTlv                   *serviceTlv = nullptr;
683     ServiceData                         data;
684 
685     data.Init(&serviceData, serviceData.GetLength());
686 
687     while ((serviceTlv = Get<Leader>().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) !=
688            nullptr)
689     {
690         TlvIterator      subTlvIterator(*serviceTlv);
691         const ServerTlv *serverSubTlv;
692 
693         while ((serverSubTlv = subTlvIterator.Iterate<ServerTlv>()) != nullptr)
694         {
695             aNumEntries++;
696 
697             if (IsPreferred(serverSubTlv->GetServer16()))
698             {
699                 aNumPreferredEntries++;
700             }
701         }
702     }
703 }
704 
CountUnicastEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const705 void Publisher::DnsSrpServiceEntry::CountUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
706 {
707     // Count the number of "DNS/SRP Unicast" service entries in
708     // the Network Data.
709 
710     const ServiceTlv *serviceTlv = nullptr;
711     ServiceData       data;
712 
713     data.InitFrom(Service::DnsSrpUnicast::kServiceData);
714 
715     while ((serviceTlv = Get<Leader>().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) !=
716            nullptr)
717     {
718         TlvIterator      subTlvIterator(*serviceTlv);
719         const ServerTlv *serverSubTlv;
720 
721         while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
722         {
723             if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData))
724             {
725                 aNumEntries++;
726 
727                 // Generally, we prefer entries where the SRP/DNS server
728                 // address/port info is included in the service TLV data
729                 // over the ones where the info is included in the
730                 // server TLV data (i.e., we prefer infra-provided
731                 // SRP/DNS entry over a BR local one using ML-EID). If
732                 // our entry itself uses the service TLV data, then we
733                 // prefer based on the associated RLOC16.
734 
735                 if (GetType() == kTypeUnicast)
736                 {
737                     if (IsPreferred(serverSubTlv->GetServer16()))
738                     {
739                         aNumPreferredEntries++;
740                     }
741                 }
742                 else
743                 {
744                     aNumPreferredEntries++;
745                 }
746             }
747 
748             if (serverSubTlv->GetServerDataLength() >= sizeof(Service::DnsSrpUnicast::ServerData))
749             {
750                 aNumEntries++;
751 
752                 // If our entry also uses the server TLV data (with
753                 // ML-EID address), then the we prefer based on the
754                 // associated RLOC16.
755 
756                 if ((GetType() == kTypeUnicastMeshLocalEid) && IsPreferred(serverSubTlv->GetServer16()))
757                 {
758                     aNumPreferredEntries++;
759                 }
760             }
761         }
762     }
763 }
764 
765 //---------------------------------------------------------------------------------------------------------------------
766 // Publisher::DnsSrpServiceEntry::Info
767 
Info(Type aType,uint16_t aPortOrSeqNumber,const Ip6::Address * aAddress)768 Publisher::DnsSrpServiceEntry::Info::Info(Type aType, uint16_t aPortOrSeqNumber, const Ip6::Address *aAddress)
769     : mPortOrSeqNumber(aPortOrSeqNumber)
770     , mType(aType)
771 {
772     // It is important to `Clear()` the object since we compare all
773     // bytes using overload of operator `==`.
774 
775     Clear();
776 
777     mType            = aType;
778     mPortOrSeqNumber = aPortOrSeqNumber;
779 
780     if (aAddress != nullptr)
781     {
782         mAddress = *aAddress;
783     }
784 }
785 
786 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
787 
788 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
789 
790 //---------------------------------------------------------------------------------------------------------------------
791 // Publisher::PrefixEntry
792 
Publish(const OnMeshPrefixConfig & aConfig,Requester aRequester)793 void Publisher::PrefixEntry::Publish(const OnMeshPrefixConfig &aConfig, Requester aRequester)
794 {
795     LogInfo("Publishing OnMeshPrefix %s", aConfig.GetPrefix().ToString().AsCString());
796 
797     Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeOnMeshPrefix, aRequester);
798 }
799 
Publish(const ExternalRouteConfig & aConfig,Requester aRequester)800 void Publisher::PrefixEntry::Publish(const ExternalRouteConfig &aConfig, Requester aRequester)
801 {
802     LogInfo("Publishing ExternalRoute %s", aConfig.GetPrefix().ToString().AsCString());
803 
804     Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeExternalRoute, aRequester);
805 }
806 
Publish(const Ip6::Prefix & aPrefix,uint16_t aNewFlags,Type aNewType,Requester aRequester)807 void Publisher::PrefixEntry::Publish(const Ip6::Prefix &aPrefix,
808                                      uint16_t           aNewFlags,
809                                      Type               aNewType,
810                                      Requester          aRequester)
811 {
812     mRequester = aRequester;
813 
814     if (GetState() != kNoEntry)
815     {
816         // If this is an existing entry, check if there is a change in
817         // type, flags, or the prefix itself. If not, everything is
818         // as before. If something is different, first, remove the
819         // old entry from Network Data if it was added. Then, re-add
820         // the new prefix/flags (replacing the old entry). This
821         // ensures the changes are immediately reflected in the
822         // Network Data.
823 
824         State oldState = GetState();
825 
826         VerifyOrExit((mType != aNewType) || (mFlags != aNewFlags) || (mPrefix != aPrefix));
827 
828         Remove(/* aNextState */ kNoEntry);
829 
830         if ((mType == aNewType) && ((oldState == kAdded) || (oldState == kRemoving)))
831         {
832             mPrefix = aPrefix;
833             mFlags  = aNewFlags;
834             Add();
835         }
836     }
837 
838     VerifyOrExit(GetState() == kNoEntry);
839 
840     mType   = aNewType;
841     mPrefix = aPrefix;
842     mFlags  = aNewFlags;
843 
844     SetState(kToAdd);
845 
846 exit:
847     Process();
848 }
849 
Unpublish(void)850 void Publisher::PrefixEntry::Unpublish(void)
851 {
852     LogInfo("Unpublishing %s", mPrefix.ToString().AsCString());
853 
854     Remove(/* aNextState */ kNoEntry);
855 }
856 
HandleNotifierEvents(Events aEvents)857 void Publisher::PrefixEntry::HandleNotifierEvents(Events aEvents)
858 {
859     if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
860     {
861         Process();
862     }
863 }
864 
Add(void)865 void Publisher::PrefixEntry::Add(void)
866 {
867     // Adds the prefix entry to the network data.
868 
869     switch (mType)
870     {
871     case kTypeOnMeshPrefix:
872         SuccessOrExit(AddOnMeshPrefix());
873         break;
874 
875     case kTypeExternalRoute:
876         SuccessOrExit(AddExternalRoute());
877         break;
878     }
879 
880     Get<Notifier>().HandleServerDataUpdated();
881     SetState(kAdded);
882     Get<Publisher>().NotifyPrefixEntryChange(kEventEntryAdded, mPrefix);
883 
884 exit:
885     return;
886 }
887 
AddOnMeshPrefix(void)888 Error Publisher::PrefixEntry::AddOnMeshPrefix(void)
889 {
890     OnMeshPrefixConfig config;
891 
892     config.mPrefix = mPrefix;
893     config.mStable = true;
894     config.SetFromTlvFlags(mFlags);
895 
896     return Get<Local>().AddOnMeshPrefix(config);
897 }
898 
AddExternalRoute(void)899 Error Publisher::PrefixEntry::AddExternalRoute(void)
900 {
901     ExternalRouteConfig config;
902 
903     config.mPrefix = mPrefix;
904     config.mStable = true;
905     config.SetFromTlvFlags(static_cast<uint8_t>(mFlags));
906 
907     return Get<Local>().AddHasRoutePrefix(config);
908 }
909 
Remove(State aNextState)910 void Publisher::PrefixEntry::Remove(State aNextState)
911 {
912     // Remove the prefix entry from the network data.
913 
914     VerifyOrExit((GetState() == kAdded) || (GetState() == kRemoving));
915 
916     switch (mType)
917     {
918     case kTypeOnMeshPrefix:
919         IgnoreError(Get<Local>().RemoveOnMeshPrefix(mPrefix));
920         break;
921 
922     case kTypeExternalRoute:
923         IgnoreError(Get<Local>().RemoveHasRoutePrefix(mPrefix));
924         break;
925     }
926 
927     Get<Notifier>().HandleServerDataUpdated();
928     Get<Publisher>().NotifyPrefixEntryChange(kEventEntryRemoved, mPrefix);
929 
930 exit:
931     SetState(aNextState);
932 }
933 
Process(void)934 void Publisher::PrefixEntry::Process(void)
935 {
936     // This method checks the entries currently present in Network Data
937     // based on which it then decides whether or not take action
938     // (add/remove or keep monitoring).
939 
940     uint8_t numEntries          = 0;
941     uint8_t numPreferredEntries = 0;
942     uint8_t desiredNumEntries   = 0;
943 
944     // Do not make any changes if device is not attached, and wait
945     // for role change event.
946     VerifyOrExit(Get<Mle::Mle>().IsAttached());
947 
948     VerifyOrExit(GetState() != kNoEntry);
949 
950     switch (mType)
951     {
952     case kTypeOnMeshPrefix:
953         CountOnMeshPrefixEntries(numEntries, numPreferredEntries);
954         desiredNumEntries = kDesiredNumOnMeshPrefix;
955         break;
956     case kTypeExternalRoute:
957         CountExternalRouteEntries(numEntries, numPreferredEntries);
958         desiredNumEntries = kDesiredNumExternalRoute;
959         break;
960     }
961 
962     UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
963 
964 exit:
965     return;
966 }
967 
CountOnMeshPrefixEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const968 void Publisher::PrefixEntry::CountOnMeshPrefixEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
969 {
970     const PrefixTlv       *prefixTlv;
971     const BorderRouterTlv *brSubTlv;
972     int8_t                 preference             = BorderRouterEntry::PreferenceFromFlags(mFlags);
973     uint16_t               flagsWithoutPreference = BorderRouterEntry::FlagsWithoutPreference(mFlags);
974 
975     prefixTlv = Get<Leader>().FindPrefix(mPrefix);
976     VerifyOrExit(prefixTlv != nullptr);
977 
978     brSubTlv = prefixTlv->FindSubTlv<BorderRouterTlv>(/* aStable */ true);
979     VerifyOrExit(brSubTlv != nullptr);
980 
981     for (const BorderRouterEntry *entry = brSubTlv->GetFirstEntry(); entry <= brSubTlv->GetLastEntry();
982          entry                          = entry->GetNext())
983     {
984         uint16_t entryFlags      = entry->GetFlags();
985         int8_t   entryPreference = BorderRouterEntry::PreferenceFromFlags(entryFlags);
986 
987         // Count an existing entry in the network data if its flags
988         // match ours and and its preference is same or higher than our
989         // preference. We do not count matching entries at a lower
990         // preference than ours. This ensures that a device with higher
991         // preference entry publishes its entry even when there are many
992         // lower preference similar entries in the network data
993         // (potentially causing a lower preference entry to be removed).
994 
995         if ((BorderRouterEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
996             (entryPreference >= preference))
997         {
998             aNumEntries++;
999 
1000             // We prefer an entry if it has strictly higher preference
1001             // than ours or if it has same preference we use the associated
1002             // RLOC16.
1003 
1004             if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
1005             {
1006                 aNumPreferredEntries++;
1007             }
1008         }
1009     }
1010 
1011 exit:
1012     return;
1013 }
1014 
CountExternalRouteEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const1015 void Publisher::PrefixEntry::CountExternalRouteEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
1016 {
1017     const PrefixTlv   *prefixTlv;
1018     const HasRouteTlv *hrSubTlv;
1019     int8_t             preference             = HasRouteEntry::PreferenceFromFlags(static_cast<uint8_t>(mFlags));
1020     uint8_t            flagsWithoutPreference = HasRouteEntry::FlagsWithoutPreference(static_cast<uint8_t>(mFlags));
1021 
1022     prefixTlv = Get<Leader>().FindPrefix(mPrefix);
1023     VerifyOrExit(prefixTlv != nullptr);
1024 
1025     hrSubTlv = prefixTlv->FindSubTlv<HasRouteTlv>(/* aStable */ true);
1026     VerifyOrExit(hrSubTlv != nullptr);
1027 
1028     for (const HasRouteEntry *entry = hrSubTlv->GetFirstEntry(); entry <= hrSubTlv->GetLastEntry();
1029          entry                      = entry->GetNext())
1030     {
1031         uint8_t entryFlags      = entry->GetFlags();
1032         int8_t  entryPreference = HasRouteEntry::PreferenceFromFlags(entryFlags);
1033 
1034         // Count an existing entry in the network data if its flags
1035         // match ours and and its preference is same or higher than our
1036         // preference. We do not count matching entries at a lower
1037         // preference than ours. This ensures that a device with higher
1038         // preference entry publishes its entry even when there are many
1039         // lower preference similar entries in the network data
1040         // (potentially causing a lower preference entry to be removed).
1041 
1042         if ((HasRouteEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
1043             (entryPreference >= preference))
1044         {
1045             aNumEntries++;
1046 
1047             // We prefer an entry if it has strictly higher preference
1048             // than ours or if it has same preference with a smaller
1049             // RLOC16.
1050 
1051             if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
1052             {
1053                 aNumPreferredEntries++;
1054             }
1055         }
1056     }
1057 
1058 exit:
1059     return;
1060 }
1061 
1062 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
1063 
1064 } // namespace NetworkData
1065 } // namespace ot
1066 
1067 #endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
1068