1 /*
2 * Copyright (c) 2021, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements the Network Data Publisher.
32 *
33 */
34
35 #include "network_data_publisher.hpp"
36
37 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
38
39 #include "common/array.hpp"
40 #include "common/code_utils.hpp"
41 #include "common/const_cast.hpp"
42 #include "common/locator_getters.hpp"
43 #include "common/log.hpp"
44 #include "common/random.hpp"
45 #include "instance/instance.hpp"
46 #include "thread/network_data_local.hpp"
47 #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 kTypeUnicastMeshLocalEid:
662 {
663 Service::DnsSrpAnycast::Info anycastInfo;
664 bool hasServiceDataEntry;
665
666 CountServerDataUnicastEntries(numEntries, numPreferredEntries, hasServiceDataEntry);
667 desiredNumEntries = kDesiredNumUnicast;
668
669 if (hasServiceDataEntry || (Get<Service::Manager>().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone))
670 {
671 // If there is any service data unicast entry or anycast
672 // entry, we set the desired number of server data
673 // unicast entries to zero to remove any such previously
674 // added unicast entry.
675
676 desiredNumEntries = 0;
677 }
678
679 break;
680 }
681
682 case kTypeUnicast:
683 desiredNumEntries = kDesiredNumUnicast;
684 CountServiceDataUnicastEntries(numEntries, numPreferredEntries);
685 break;
686 }
687
688 UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
689
690 exit:
691 return;
692 }
693
CountAnycastEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const694 void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
695 {
696 // Count the number of matching "DNS/SRP Anycast" service entries
697 // in the Network Data (the match requires the entry to use same
698 // "sequence number" value). We prefer the entries associated with
699 // smaller RLCO16.
700
701 Service::DnsSrpAnycast::ServiceData serviceData(mInfo.GetSequenceNumber());
702 const ServiceTlv *serviceTlv = nullptr;
703 ServiceData data;
704
705 data.Init(&serviceData, serviceData.GetLength());
706
707 while ((serviceTlv = Get<Leader>().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) !=
708 nullptr)
709 {
710 TlvIterator subTlvIterator(*serviceTlv);
711 const ServerTlv *serverSubTlv;
712
713 while ((serverSubTlv = subTlvIterator.Iterate<ServerTlv>()) != nullptr)
714 {
715 aNumEntries++;
716
717 if (IsPreferred(serverSubTlv->GetServer16()))
718 {
719 aNumPreferredEntries++;
720 }
721 }
722 }
723 }
724
CountServerDataUnicastEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries,bool & aHasServiceDataEntry) const725 void Publisher::DnsSrpServiceEntry::CountServerDataUnicastEntries(uint8_t &aNumEntries,
726 uint8_t &aNumPreferredEntries,
727 bool &aHasServiceDataEntry) const
728 {
729 // Count the number of server data DNS/SRP unicast entries in the
730 // Network Data. Also determine whether there is any service data
731 // DNS/SRP unicast entry (update `aHasServiceDataEntry`).
732
733 const ServiceTlv *serviceTlv = nullptr;
734 ServiceData data;
735
736 aHasServiceDataEntry = false;
737
738 data.InitFrom(Service::DnsSrpUnicast::kServiceData);
739
740 while ((serviceTlv = Get<Leader>().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) !=
741 nullptr)
742 {
743 TlvIterator subTlvIterator(*serviceTlv);
744 const ServerTlv *serverSubTlv;
745
746 if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData))
747 {
748 aHasServiceDataEntry = true;
749 }
750
751 while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
752 {
753 if (serverSubTlv->GetServerDataLength() < sizeof(Service::DnsSrpUnicast::ServerData))
754 {
755 continue;
756 }
757
758 aNumEntries++;
759
760 if (IsPreferred(serverSubTlv->GetServer16()))
761 {
762 aNumPreferredEntries++;
763 }
764 }
765 }
766 }
767
CountServiceDataUnicastEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const768 void Publisher::DnsSrpServiceEntry::CountServiceDataUnicastEntries(uint8_t &aNumEntries,
769 uint8_t &aNumPreferredEntries) const
770 {
771 // Count the number of service data DNS/SRP unicast entries in
772 // the Network Data.
773
774 const ServiceTlv *serviceTlv = nullptr;
775 ServiceData data;
776
777 data.InitFrom(Service::DnsSrpUnicast::kServiceData);
778
779 while ((serviceTlv = Get<Leader>().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) !=
780 nullptr)
781 {
782 TlvIterator subTlvIterator(*serviceTlv);
783 const ServerTlv *serverSubTlv;
784
785 if (serviceTlv->GetServiceDataLength() < sizeof(Service::DnsSrpUnicast::ServiceData))
786 {
787 continue;
788 }
789
790 while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
791 {
792 aNumEntries++;
793
794 if (IsPreferred(serverSubTlv->GetServer16()))
795 {
796 aNumPreferredEntries++;
797 }
798 }
799 }
800 }
801
802 //---------------------------------------------------------------------------------------------------------------------
803 // Publisher::DnsSrpServiceEntry::Info
804
Info(Type aType,uint16_t aPortOrSeqNumber,const Ip6::Address * aAddress)805 Publisher::DnsSrpServiceEntry::Info::Info(Type aType, uint16_t aPortOrSeqNumber, const Ip6::Address *aAddress)
806 : mPortOrSeqNumber(aPortOrSeqNumber)
807 , mType(aType)
808 {
809 // It is important to `Clear()` the object since we compare all
810 // bytes using overload of operator `==`.
811
812 Clear();
813
814 mType = aType;
815 mPortOrSeqNumber = aPortOrSeqNumber;
816
817 if (aAddress != nullptr)
818 {
819 mAddress = *aAddress;
820 }
821 }
822
823 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
824
825 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
826
827 //---------------------------------------------------------------------------------------------------------------------
828 // Publisher::PrefixEntry
829
Publish(const OnMeshPrefixConfig & aConfig,Requester aRequester)830 void Publisher::PrefixEntry::Publish(const OnMeshPrefixConfig &aConfig, Requester aRequester)
831 {
832 LogInfo("Publishing OnMeshPrefix %s", aConfig.GetPrefix().ToString().AsCString());
833
834 Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeOnMeshPrefix, aRequester);
835 }
836
Publish(const ExternalRouteConfig & aConfig,Requester aRequester)837 void Publisher::PrefixEntry::Publish(const ExternalRouteConfig &aConfig, Requester aRequester)
838 {
839 LogInfo("Publishing ExternalRoute %s", aConfig.GetPrefix().ToString().AsCString());
840
841 Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeExternalRoute, aRequester);
842 }
843
Publish(const Ip6::Prefix & aPrefix,uint16_t aNewFlags,Type aNewType,Requester aRequester)844 void Publisher::PrefixEntry::Publish(const Ip6::Prefix &aPrefix,
845 uint16_t aNewFlags,
846 Type aNewType,
847 Requester aRequester)
848 {
849 mRequester = aRequester;
850
851 if (GetState() != kNoEntry)
852 {
853 // If this is an existing entry, check if there is a change in
854 // type, flags, or the prefix itself. If not, everything is
855 // as before. If something is different, first, remove the
856 // old entry from Network Data if it was added. Then, re-add
857 // the new prefix/flags (replacing the old entry). This
858 // ensures the changes are immediately reflected in the
859 // Network Data.
860
861 State oldState = GetState();
862
863 VerifyOrExit((mType != aNewType) || (mFlags != aNewFlags) || (mPrefix != aPrefix));
864
865 Remove(/* aNextState */ kNoEntry);
866
867 if ((mType == aNewType) && ((oldState == kAdded) || (oldState == kRemoving)))
868 {
869 mPrefix = aPrefix;
870 mFlags = aNewFlags;
871 Add();
872 }
873 }
874
875 VerifyOrExit(GetState() == kNoEntry);
876
877 mType = aNewType;
878 mPrefix = aPrefix;
879 mFlags = aNewFlags;
880
881 SetState(kToAdd);
882
883 exit:
884 Process();
885 }
886
Unpublish(void)887 void Publisher::PrefixEntry::Unpublish(void)
888 {
889 LogInfo("Unpublishing %s", mPrefix.ToString().AsCString());
890
891 Remove(/* aNextState */ kNoEntry);
892 }
893
HandleNotifierEvents(Events aEvents)894 void Publisher::PrefixEntry::HandleNotifierEvents(Events aEvents)
895 {
896 if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
897 {
898 Process();
899 }
900 }
901
Add(void)902 void Publisher::PrefixEntry::Add(void)
903 {
904 // Adds the prefix entry to the network data.
905
906 switch (mType)
907 {
908 case kTypeOnMeshPrefix:
909 SuccessOrExit(AddOnMeshPrefix());
910 break;
911
912 case kTypeExternalRoute:
913 SuccessOrExit(AddExternalRoute());
914 break;
915 }
916
917 Get<Notifier>().HandleServerDataUpdated();
918 SetState(kAdded);
919 Get<Publisher>().NotifyPrefixEntryChange(kEventEntryAdded, mPrefix);
920
921 exit:
922 return;
923 }
924
AddOnMeshPrefix(void)925 Error Publisher::PrefixEntry::AddOnMeshPrefix(void)
926 {
927 OnMeshPrefixConfig config;
928
929 config.mPrefix = mPrefix;
930 config.mStable = true;
931 config.SetFromTlvFlags(mFlags);
932
933 return Get<Local>().AddOnMeshPrefix(config);
934 }
935
AddExternalRoute(void)936 Error Publisher::PrefixEntry::AddExternalRoute(void)
937 {
938 ExternalRouteConfig config;
939
940 config.mPrefix = mPrefix;
941 config.mStable = true;
942 config.SetFromTlvFlags(static_cast<uint8_t>(mFlags));
943
944 return Get<Local>().AddHasRoutePrefix(config);
945 }
946
Remove(State aNextState)947 void Publisher::PrefixEntry::Remove(State aNextState)
948 {
949 // Remove the prefix entry from the network data.
950
951 VerifyOrExit((GetState() == kAdded) || (GetState() == kRemoving));
952
953 switch (mType)
954 {
955 case kTypeOnMeshPrefix:
956 IgnoreError(Get<Local>().RemoveOnMeshPrefix(mPrefix));
957 break;
958
959 case kTypeExternalRoute:
960 IgnoreError(Get<Local>().RemoveHasRoutePrefix(mPrefix));
961 break;
962 }
963
964 Get<Notifier>().HandleServerDataUpdated();
965 Get<Publisher>().NotifyPrefixEntryChange(kEventEntryRemoved, mPrefix);
966
967 exit:
968 SetState(aNextState);
969 }
970
Process(void)971 void Publisher::PrefixEntry::Process(void)
972 {
973 // This method checks the entries currently present in Network Data
974 // based on which it then decides whether or not take action
975 // (add/remove or keep monitoring).
976
977 uint8_t numEntries = 0;
978 uint8_t numPreferredEntries = 0;
979 uint8_t desiredNumEntries = 0;
980
981 // Do not make any changes if device is not attached, and wait
982 // for role change event.
983 VerifyOrExit(Get<Mle::Mle>().IsAttached());
984
985 VerifyOrExit(GetState() != kNoEntry);
986
987 switch (mType)
988 {
989 case kTypeOnMeshPrefix:
990 CountOnMeshPrefixEntries(numEntries, numPreferredEntries);
991 desiredNumEntries = kDesiredNumOnMeshPrefix;
992 break;
993 case kTypeExternalRoute:
994 CountExternalRouteEntries(numEntries, numPreferredEntries);
995 desiredNumEntries = kDesiredNumExternalRoute;
996 break;
997 }
998
999 UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
1000
1001 exit:
1002 return;
1003 }
1004
CountOnMeshPrefixEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const1005 void Publisher::PrefixEntry::CountOnMeshPrefixEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
1006 {
1007 const PrefixTlv *prefixTlv;
1008 const BorderRouterTlv *brSubTlv;
1009 int8_t preference = BorderRouterEntry::PreferenceFromFlags(mFlags);
1010 uint16_t flagsWithoutPreference = BorderRouterEntry::FlagsWithoutPreference(mFlags);
1011
1012 prefixTlv = Get<Leader>().FindPrefix(mPrefix);
1013 VerifyOrExit(prefixTlv != nullptr);
1014
1015 brSubTlv = prefixTlv->FindSubTlv<BorderRouterTlv>(/* aStable */ true);
1016 VerifyOrExit(brSubTlv != nullptr);
1017
1018 for (const BorderRouterEntry *entry = brSubTlv->GetFirstEntry(); entry <= brSubTlv->GetLastEntry();
1019 entry = entry->GetNext())
1020 {
1021 uint16_t entryFlags = entry->GetFlags();
1022 int8_t entryPreference = BorderRouterEntry::PreferenceFromFlags(entryFlags);
1023
1024 // Count an existing entry in the network data if its flags
1025 // match ours and and its preference is same or higher than our
1026 // preference. We do not count matching entries at a lower
1027 // preference than ours. This ensures that a device with higher
1028 // preference entry publishes its entry even when there are many
1029 // lower preference similar entries in the network data
1030 // (potentially causing a lower preference entry to be removed).
1031
1032 if ((BorderRouterEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
1033 (entryPreference >= preference))
1034 {
1035 aNumEntries++;
1036
1037 // We prefer an entry if it has strictly higher preference
1038 // than ours or if it has same preference we use the associated
1039 // RLOC16.
1040
1041 if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
1042 {
1043 aNumPreferredEntries++;
1044 }
1045 }
1046 }
1047
1048 exit:
1049 return;
1050 }
1051
CountExternalRouteEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const1052 void Publisher::PrefixEntry::CountExternalRouteEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
1053 {
1054 const PrefixTlv *prefixTlv;
1055 const HasRouteTlv *hrSubTlv;
1056 int8_t preference = HasRouteEntry::PreferenceFromFlags(static_cast<uint8_t>(mFlags));
1057 uint8_t flagsWithoutPreference = HasRouteEntry::FlagsWithoutPreference(static_cast<uint8_t>(mFlags));
1058
1059 prefixTlv = Get<Leader>().FindPrefix(mPrefix);
1060 VerifyOrExit(prefixTlv != nullptr);
1061
1062 hrSubTlv = prefixTlv->FindSubTlv<HasRouteTlv>(/* aStable */ true);
1063 VerifyOrExit(hrSubTlv != nullptr);
1064
1065 for (const HasRouteEntry *entry = hrSubTlv->GetFirstEntry(); entry <= hrSubTlv->GetLastEntry();
1066 entry = entry->GetNext())
1067 {
1068 uint8_t entryFlags = entry->GetFlags();
1069 int8_t entryPreference = HasRouteEntry::PreferenceFromFlags(entryFlags);
1070
1071 // Count an existing entry in the network data if its flags
1072 // match ours and and its preference is same or higher than our
1073 // preference. We do not count matching entries at a lower
1074 // preference than ours. This ensures that a device with higher
1075 // preference entry publishes its entry even when there are many
1076 // lower preference similar entries in the network data
1077 // (potentially causing a lower preference entry to be removed).
1078
1079 if ((HasRouteEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
1080 (entryPreference >= preference))
1081 {
1082 aNumEntries++;
1083
1084 // We prefer an entry if it has strictly higher preference
1085 // than ours or if it has same preference with a smaller
1086 // RLOC16.
1087
1088 if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
1089 {
1090 aNumPreferredEntries++;
1091 }
1092 }
1093 }
1094
1095 exit:
1096 return;
1097 }
1098
1099 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
1100
1101 } // namespace NetworkData
1102 } // namespace ot
1103
1104 #endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
1105