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