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