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