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