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