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/code_utils.hpp"
40 #include "common/instance.hpp"
41 #include "common/locator_getters.hpp"
42 #include "common/random.hpp"
43 #include "thread/network_data_local.hpp"
44 #include "thread/network_data_service.hpp"
45
46 namespace ot {
47 namespace NetworkData {
48
49 //---------------------------------------------------------------------------------------------------------------------
50 // Publisher
51
Publisher(Instance & aInstance)52 Publisher::Publisher(Instance &aInstance)
53 : InstanceLocator(aInstance)
54 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
55 , mDnsSrpServiceEntry(aInstance)
56 #endif
57 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
58 , mPrefixCallback(nullptr)
59 , mPrefixCallbackContext(nullptr)
60 #endif
61 , mTimer(aInstance, Publisher::HandleTimer)
62 {
63 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
64 // Since the `PrefixEntry` type is used in an array,
65 // we cannot use a constructor with an argument (e.g.,
66 // we cannot use `InstacneLocator`) so we use
67 // `IntanceLocatorInit` and `Init()` the entries one
68 // by one.
69
70 for (PrefixEntry &entry : mPrefixEntries)
71 {
72 entry.Init(aInstance);
73 }
74 #endif
75 }
76
77 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
78
SetPrefixCallback(PrefixCallback aCallback,void * aContext)79 void Publisher::SetPrefixCallback(PrefixCallback aCallback, void *aContext)
80 {
81 mPrefixCallback = aCallback;
82 mPrefixCallbackContext = aContext;
83 }
84
PublishOnMeshPrefix(const OnMeshPrefixConfig & aConfig)85 Error Publisher::PublishOnMeshPrefix(const OnMeshPrefixConfig &aConfig)
86 {
87 Error error;
88 PrefixEntry *entry;
89
90 VerifyOrExit(aConfig.IsValid(GetInstance()), error = kErrorInvalidArgs);
91 VerifyOrExit(aConfig.mStable, error = kErrorInvalidArgs);
92
93 SuccessOrExit(error = AllocatePrefixEntry(aConfig.GetPrefix(), entry));
94 entry->Publish(aConfig);
95
96 exit:
97 return error;
98 }
99
PublishExternalRoute(const ExternalRouteConfig & aConfig)100 Error Publisher::PublishExternalRoute(const ExternalRouteConfig &aConfig)
101 {
102 Error error;
103 PrefixEntry *entry;
104
105 VerifyOrExit(aConfig.IsValid(GetInstance()), error = kErrorInvalidArgs);
106 VerifyOrExit(aConfig.mStable, error = kErrorInvalidArgs);
107
108 SuccessOrExit(error = AllocatePrefixEntry(aConfig.GetPrefix(), entry));
109 entry->Publish(aConfig);
110
111 exit:
112 return error;
113 }
114
IsPrefixAdded(const Ip6::Prefix & aPrefix) const115 bool Publisher::IsPrefixAdded(const Ip6::Prefix &aPrefix) const
116 {
117 bool isAdded = false;
118 const PrefixEntry *entry;
119
120 entry = FindMatchingPrefixEntry(aPrefix);
121 VerifyOrExit(entry != nullptr);
122
123 isAdded = entry->IsAdded();
124
125 exit:
126 return isAdded;
127 }
128
UnpublishPrefix(const Ip6::Prefix & aPrefix)129 Error Publisher::UnpublishPrefix(const Ip6::Prefix &aPrefix)
130 {
131 Error error = kErrorNone;
132 PrefixEntry *entry;
133
134 entry = FindMatchingPrefixEntry(aPrefix);
135 VerifyOrExit(entry != nullptr, error = kErrorNotFound);
136
137 entry->Unpublish();
138
139 exit:
140 return error;
141 }
142
AllocatePrefixEntry(const Ip6::Prefix & aPrefix,PrefixEntry * & aEntry)143 Error Publisher::AllocatePrefixEntry(const Ip6::Prefix &aPrefix, PrefixEntry *&aEntry)
144 {
145 Error error = kErrorNoBufs;
146
147 VerifyOrExit(FindMatchingPrefixEntry(aPrefix) == nullptr, error = kErrorAlready);
148
149 for (PrefixEntry &entry : mPrefixEntries)
150 {
151 if (!entry.IsInUse())
152 {
153 aEntry = &entry;
154 ExitNow(error = kErrorNone);
155 }
156 }
157
158 exit:
159 return error;
160 }
161
FindMatchingPrefixEntry(const Ip6::Prefix & aPrefix)162 Publisher::PrefixEntry *Publisher::FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix)
163 {
164 return const_cast<PrefixEntry *>(const_cast<const Publisher *>(this)->FindMatchingPrefixEntry(aPrefix));
165 }
166
FindMatchingPrefixEntry(const Ip6::Prefix & aPrefix) const167 const Publisher::PrefixEntry *Publisher::FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix) const
168 {
169 const PrefixEntry *prefixEntry = nullptr;
170
171 for (const PrefixEntry &entry : mPrefixEntries)
172 {
173 if (entry.IsInUse() && entry.Matches(aPrefix))
174 {
175 prefixEntry = &entry;
176 break;
177 }
178 }
179
180 return prefixEntry;
181 }
182
IsAPrefixEntry(const Entry & aEntry) const183 bool Publisher::IsAPrefixEntry(const Entry &aEntry) const
184 {
185 return (&mPrefixEntries[0] <= &aEntry) && (&aEntry < OT_ARRAY_END(mPrefixEntries));
186 }
187
NotifyPrefixEntryChange(Event aEvent,const Ip6::Prefix & aPrefix) const188 void Publisher::NotifyPrefixEntryChange(Event aEvent, const Ip6::Prefix &aPrefix) const
189 {
190 if (mPrefixCallback != nullptr)
191 {
192 mPrefixCallback(static_cast<otNetDataPublisherEvent>(aEvent), &aPrefix, mPrefixCallbackContext);
193 }
194 }
195
196 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
197
HandleNotifierEvents(Events aEvents)198 void Publisher::HandleNotifierEvents(Events aEvents)
199 {
200 OT_UNUSED_VARIABLE(aEvents);
201
202 bool registerWithLeader = false;
203
204 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
205 if (mDnsSrpServiceEntry.HandleNotifierEvents(aEvents))
206 {
207 registerWithLeader = true;
208 }
209 #endif
210
211 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
212 for (PrefixEntry &entry : mPrefixEntries)
213 {
214 entry.HandleNotifierEvents(aEvents);
215 }
216 #endif
217
218 if (registerWithLeader)
219 {
220 Get<Notifier>().HandleServerDataUpdated();
221 }
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 bool registerWithLeader = false;
232
233 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
234 if (mDnsSrpServiceEntry.HandleTimer())
235 {
236 registerWithLeader = true;
237 }
238 #endif
239
240 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
241 for (PrefixEntry &entry : mPrefixEntries)
242 {
243 if (entry.HandleTimer())
244 {
245 registerWithLeader = true;
246 }
247 }
248 #endif
249
250 if (registerWithLeader)
251 {
252 Get<Notifier>().HandleServerDataUpdated();
253 }
254 }
255
256 //---------------------------------------------------------------------------------------------------------------------
257 // Publisher::Entry
258
SetState(State aState)259 void Publisher::Entry::SetState(State aState)
260 {
261 VerifyOrExit(mState != aState);
262
263 otLogInfoNetData("Publisher: %s - State: %s -> %s", ToString(/* aIncludeState */ false).AsCString(),
264 StateToString(mState), 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::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 otLogInfoNetData("Publisher: %s in netdata - total:%d, preferred:%d, desired:%d", ToString().AsCString(),
292 aNumEntries, 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 += kExtraDelayToRemovePeferred;
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 bool Publisher::Entry::HandleTimer(void)
363 {
364 bool registerWithLeader = false;
365
366 // Timer is used to delay adding/removing the entry. If we have
367 // reached `mUpdateTime` add or remove the entry. Otherwise,
368 // restart the timer (note that timer can be shared between
369 // different published entries). This method returns a `bool`
370 // indicating whether or not anything in local Network Data got
371 // changed so to notify the leader and register the changes.
372
373 VerifyOrExit((GetState() == kAdding) || (GetState() == kRemoving));
374
375 if (mUpdateTime <= TimerMilli::GetNow())
376 {
377 registerWithLeader = (GetState() == kAdding) ? Add() : Remove(/* aNextState */ kToAdd);
378 }
379 else
380 {
381 Get<Publisher>().GetTimer().FireAtIfEarlier(mUpdateTime);
382 }
383
384 exit:
385 return registerWithLeader;
386 }
387
Add(void)388 bool Publisher::Entry::Add(void)
389 {
390 bool registerWithLeader = false;
391
392 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
393 if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
394 {
395 registerWithLeader = static_cast<DnsSrpServiceEntry *>(this)->Add();
396 ExitNow();
397 }
398 #endif
399
400 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
401 if (Get<Publisher>().IsAPrefixEntry(*this))
402 {
403 registerWithLeader = static_cast<PrefixEntry *>(this)->Add();
404 ExitNow();
405 }
406 #endif
407
408 exit:
409 return registerWithLeader;
410 }
411
Remove(State aNextState)412 bool Publisher::Entry::Remove(State aNextState)
413 {
414 bool registerWithLeader = false;
415
416 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
417 if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
418 {
419 registerWithLeader = static_cast<DnsSrpServiceEntry *>(this)->Remove(aNextState);
420 ExitNow();
421 }
422 #endif
423
424 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
425 if (Get<Publisher>().IsAPrefixEntry(*this))
426 {
427 registerWithLeader = static_cast<PrefixEntry *>(this)->Remove(aNextState);
428 ExitNow();
429 }
430 #endif
431
432 exit:
433 return registerWithLeader;
434 }
435
ToString(bool aIncludeState) const436 Publisher::Entry::InfoString Publisher::Entry::ToString(bool aIncludeState) const
437 {
438 InfoString string;
439
440 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
441 if (Get<Publisher>().IsADnsSrpServiceEntry(*this))
442 {
443 string.Append("DNS/SRP service");
444 ExitNow();
445 }
446 #endif
447
448 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
449 if (Get<Publisher>().IsAPrefixEntry(*this))
450 {
451 const PrefixEntry &prefixEntry = *static_cast<const PrefixEntry *>(this);
452
453 switch (prefixEntry.mType)
454 {
455 case PrefixEntry::kTypeOnMeshPrefix:
456 string.Append("OnMeshPrefix ");
457 break;
458
459 case PrefixEntry::kTypeExternalRoute:
460 string.Append("ExternalRoute ");
461 break;
462 }
463
464 string.Append(prefixEntry.mPrefix.ToString().AsCString());
465 ExitNow();
466 }
467 #endif
468
469 exit:
470 if (aIncludeState)
471 {
472 string.Append(" (state:%s)", StateToString(GetState()));
473 }
474
475 return string;
476 }
477
LogUpdateTime(void) const478 void Publisher::Entry::LogUpdateTime(void) const
479 {
480 otLogInfoNetData("Publisher: %s - update in %u msec", ToString().AsCString(), mUpdateTime - TimerMilli::GetNow());
481 }
482
StateToString(State aState)483 const char *Publisher::Entry::StateToString(State aState)
484 {
485 static const char *const kStateStrings[] = {
486 "NoEntry", // (0) kNoEntry
487 "ToAdd", // (1) kToAdd
488 "Adding", // (2) kAdding
489 "Added", // (3) kAdded
490 "Removing", // (4) kRemoving
491 };
492
493 static_assert(0 == kNoEntry, "kNoEntry value is not correct");
494 static_assert(1 == kToAdd, "kToAdd value is not correct");
495 static_assert(2 == kAdding, "kAdding value is not correct");
496 static_assert(3 == kAdded, "kAdded value is not correct");
497 static_assert(4 == kRemoving, "kRemoving value is not correct");
498
499 return kStateStrings[aState];
500 }
501
502 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
503
504 //---------------------------------------------------------------------------------------------------------------------
505 // Publisher::DnsSrpServiceEntry
506
DnsSrpServiceEntry(Instance & aInstance)507 Publisher::DnsSrpServiceEntry::DnsSrpServiceEntry(Instance &aInstance)
508 : mCallback(nullptr)
509 , mCallbackContext(nullptr)
510 {
511 Init(aInstance);
512 }
513
SetCallback(DnsSrpServiceCallback aCallback,void * aContext)514 void Publisher::DnsSrpServiceEntry::SetCallback(DnsSrpServiceCallback aCallback, void *aContext)
515 {
516 mCallback = aCallback;
517 mCallbackContext = aContext;
518 }
519
PublishAnycast(uint8_t aSequenceNumber)520 void Publisher::DnsSrpServiceEntry::PublishAnycast(uint8_t aSequenceNumber)
521 {
522 otLogInfoNetData("Publisher: Publishing DNS/SRP service anycast (seq-num:%d)", aSequenceNumber);
523 Publish(Info::InfoAnycast(aSequenceNumber));
524 }
525
PublishUnicast(const Ip6::Address & aAddress,uint16_t aPort)526 void Publisher::DnsSrpServiceEntry::PublishUnicast(const Ip6::Address &aAddress, uint16_t aPort)
527 {
528 otLogInfoNetData("Publisher: Publishing DNS/SRP service unicast (%s, port:%d)", aAddress.ToString().AsCString(),
529 aPort);
530 Publish(Info::InfoUnicast(kTypeUnicast, aAddress, aPort));
531 }
532
PublishUnicast(uint16_t aPort)533 void Publisher::DnsSrpServiceEntry::PublishUnicast(uint16_t aPort)
534 {
535 otLogInfoNetData("Publisher: Publishing DNS/SRP service unicast (ml-eid, port:%d)", aPort);
536 Publish(Info::InfoUnicast(kTypeUnicastMeshLocalEid, Get<Mle::Mle>().GetMeshLocal64(), aPort));
537 }
538
Publish(const Info & aInfo)539 void Publisher::DnsSrpServiceEntry::Publish(const Info &aInfo)
540 {
541 bool registerWithLeader = false;
542
543 if (GetState() != kNoEntry)
544 {
545 if (aInfo == mInfo)
546 {
547 otLogInfoNetData("Publisher: %s is already being published", ToString().AsCString());
548 ExitNow();
549 }
550
551 registerWithLeader = Remove(/* aNextState */ kNoEntry);
552 }
553
554 mInfo = aInfo;
555 SetState(kToAdd);
556
557 Process();
558
559 if (registerWithLeader)
560 {
561 Get<Notifier>().HandleServerDataUpdated();
562 }
563
564 exit:
565 return;
566 }
567
Unpublish(void)568 void Publisher::DnsSrpServiceEntry::Unpublish(void)
569 {
570 bool registerWithLeader;
571
572 otLogInfoNetData("Publisher: Unpublishing DNS/SRP service");
573
574 registerWithLeader = Remove(/* aNextState */ kNoEntry);
575
576 if (registerWithLeader)
577 {
578 Get<Notifier>().HandleServerDataUpdated();
579 }
580 }
581
HandleNotifierEvents(Events aEvents)582 bool Publisher::DnsSrpServiceEntry::HandleNotifierEvents(Events aEvents)
583 {
584 bool registerWithLeader = false;
585
586 if ((GetType() == kTypeUnicastMeshLocalEid) && aEvents.Contains(kEventThreadMeshLocalAddrChanged))
587 {
588 mInfo.SetAddress(Get<Mle::Mle>().GetMeshLocal64());
589
590 if (GetState() == kAdded)
591 {
592 // If the entry is already added, we need to update it
593 // so we remove it and add it back immediately with
594 // the new mesh-local address.
595
596 Remove(/* aNextState */ kAdding);
597 Add();
598 registerWithLeader = true;
599 }
600 }
601
602 if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
603 {
604 Process();
605 }
606
607 return registerWithLeader;
608 }
609
Add(void)610 bool Publisher::DnsSrpServiceEntry::Add(void)
611 {
612 // Adds the service entry to the network data.
613
614 bool registerWithLeader = false;
615
616 switch (GetType())
617 {
618 case kTypeAnycast:
619 SuccessOrExit(Get<Service::Manager>().Add<Service::DnsSrpAnycast>(
620 Service::DnsSrpAnycast::ServiceData(mInfo.GetSequenceNumber())));
621 break;
622
623 case kTypeUnicast:
624 SuccessOrExit(Get<Service::Manager>().Add<Service::DnsSrpUnicast>(
625 Service::DnsSrpUnicast::ServiceData(mInfo.GetAddress(), mInfo.GetPort())));
626 break;
627
628 case kTypeUnicastMeshLocalEid:
629 SuccessOrExit(Get<Service::Manager>().Add<Service::DnsSrpUnicast>(
630 Service::DnsSrpUnicast::ServerData(mInfo.GetAddress(), mInfo.GetPort())));
631 break;
632 }
633
634 registerWithLeader = true;
635 SetState(kAdded);
636 Notify(kEventEntryAdded);
637
638 exit:
639 return registerWithLeader;
640 }
641
Remove(State aNextState)642 bool Publisher::DnsSrpServiceEntry::Remove(State aNextState)
643 {
644 // Removes the service entry from network data (if it was added).
645
646 bool registerWithLeader = false;
647
648 VerifyOrExit((GetState() == kAdded) || (GetState() == kRemoving));
649
650 switch (GetType())
651 {
652 case kTypeAnycast:
653 SuccessOrExit(Get<Service::Manager>().Remove<Service::DnsSrpAnycast>(
654 Service::DnsSrpAnycast::ServiceData(mInfo.GetSequenceNumber())));
655 break;
656
657 case kTypeUnicast:
658 SuccessOrExit(Get<Service::Manager>().Remove<Service::DnsSrpUnicast>(
659 Service::DnsSrpUnicast::ServiceData(mInfo.GetAddress(), mInfo.GetPort())));
660 break;
661
662 case kTypeUnicastMeshLocalEid:
663 SuccessOrExit(Get<Service::Manager>().Remove<Service::DnsSrpUnicast>());
664 break;
665 }
666
667 registerWithLeader = true;
668 Notify(kEventEntryRemoved);
669
670 exit:
671 SetState(aNextState);
672 return registerWithLeader;
673 }
674
Notify(Event aEvent) const675 void Publisher::DnsSrpServiceEntry::Notify(Event aEvent) const
676 {
677 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
678 Get<Srp::Server>().HandleNetDataPublisherEvent(aEvent);
679 #endif
680
681 if (mCallback != nullptr)
682 {
683 mCallback(static_cast<otNetDataPublisherEvent>(aEvent), mCallbackContext);
684 }
685 }
686
Process(void)687 void Publisher::DnsSrpServiceEntry::Process(void)
688 {
689 // This method checks the entries currently present in Network Data
690 // based on which it then decides whether or not take action
691 // (add/remove or keep monitoring).
692
693 uint8_t numEntries = 0;
694 uint8_t numPreferredEntries = 0;
695 uint8_t desiredNumEntries = 0;
696
697 // Do not make any changes if device is not attached, and wait
698 // for role change event.
699 VerifyOrExit(Get<Mle::Mle>().IsAttached());
700
701 VerifyOrExit(GetState() != kNoEntry);
702
703 switch (GetType())
704 {
705 case kTypeAnycast:
706 CountAnycastEntries(numEntries, numPreferredEntries);
707 desiredNumEntries = kDesiredNumAnycast;
708 break;
709
710 case kTypeUnicast:
711 case kTypeUnicastMeshLocalEid:
712 CountUnicastEntries(numEntries, numPreferredEntries);
713 desiredNumEntries = kDesiredNumUnicast;
714 break;
715 }
716
717 UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
718
719 exit:
720 return;
721 }
722
CountAnycastEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const723 void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
724 {
725 // Count the number of matching "DNS/SRP Anycast" service entries
726 // in the Network Data (the match requires the entry to use same
727 // "sequence number" value). We prefer the entries associated with
728 // smaller RLCO16.
729
730 Service::DnsSrpAnycast::ServiceData serviceData(mInfo.GetSequenceNumber());
731 const ServiceTlv * serviceTlv = nullptr;
732
733 while ((serviceTlv = Get<Leader>().FindNextService(
734 serviceTlv, Service::kThreadEnterpriseNumber, reinterpret_cast<const uint8_t *>(&serviceData),
735 serviceData.GetLength(), NetworkData::kServicePrefixMatch)) != nullptr)
736 {
737 TlvIterator subTlvIterator(*serviceTlv);
738 const ServerTlv *serverSubTlv;
739
740 while ((serverSubTlv = subTlvIterator.Iterate<ServerTlv>()) != nullptr)
741 {
742 aNumEntries++;
743
744 if (IsPreferred(serverSubTlv->GetServer16()))
745 {
746 aNumPreferredEntries++;
747 }
748 }
749 }
750 }
751
CountUnicastEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const752 void Publisher::DnsSrpServiceEntry::CountUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
753 {
754 // Count the number of "DNS/SRP Unicast" service entries in
755 // the Network Data.
756
757 const ServiceTlv *serviceTlv = nullptr;
758
759 while ((serviceTlv = Get<Leader>().FindNextService(
760 serviceTlv, Service::kThreadEnterpriseNumber, &Service::DnsSrpUnicast::kServiceData,
761 sizeof(Service::DnsSrpUnicast::kServiceData), NetworkData::kServicePrefixMatch)) != nullptr)
762 {
763 TlvIterator subTlvIterator(*serviceTlv);
764 const ServerTlv *serverSubTlv;
765
766 while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
767 {
768 if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData))
769 {
770 aNumEntries++;
771
772 // Generally, we prefer entries where the SRP/DNS server
773 // address/port info is included in the service TLV data
774 // over the ones where the info is included in the
775 // server TLV data (i.e., we prefer infra-provided
776 // SRP/DNS entry over a BR local one using ML-EID). If
777 // our entry itself uses the service TLV data, then we
778 // prefer based on the associated RLOC16.
779
780 if (GetType() == kTypeUnicast)
781 {
782 if (IsPreferred(serverSubTlv->GetServer16()))
783 {
784 aNumPreferredEntries++;
785 }
786 }
787 else
788 {
789 aNumPreferredEntries++;
790 }
791 }
792
793 if (serverSubTlv->GetServerDataLength() >= sizeof(Service::DnsSrpUnicast::ServerData))
794 {
795 aNumEntries++;
796
797 // If our entry also uses the server TLV data (with
798 // ML-EID address), then the we prefer based on the
799 // associated RLOC16.
800
801 if ((GetType() == kTypeUnicastMeshLocalEid) && IsPreferred(serverSubTlv->GetServer16()))
802 {
803 aNumPreferredEntries++;
804 }
805 }
806 }
807 }
808 }
809
810 //---------------------------------------------------------------------------------------------------------------------
811 // Publisher::DnsSrpServiceEntry::Info
812
Info(Type aType,uint16_t aPortOrSeqNumber,const Ip6::Address * aAddress)813 Publisher::DnsSrpServiceEntry::Info::Info(Type aType, uint16_t aPortOrSeqNumber, const Ip6::Address *aAddress)
814 : mPortOrSeqNumber(aPortOrSeqNumber)
815 , mType(aType)
816 {
817 // It is important to `Clear()` the object since we compare all
818 // bytes using overload of operator `==`.
819
820 Clear();
821
822 mType = aType;
823 mPortOrSeqNumber = aPortOrSeqNumber;
824
825 if (aAddress != nullptr)
826 {
827 mAddress = *aAddress;
828 }
829 }
830
831 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
832
833 //---------------------------------------------------------------------------------------------------------------------
834 // Publisher::PrefixEntry
835
Publish(const OnMeshPrefixConfig & aConfig)836 void Publisher::PrefixEntry::Publish(const OnMeshPrefixConfig &aConfig)
837 {
838 otLogInfoNetData("Publisher: Publishing OnMeshPrefix %s", aConfig.GetPrefix().ToString().AsCString());
839
840 mType = kTypeOnMeshPrefix;
841 mPrefix = aConfig.GetPrefix();
842 mFlags = aConfig.ConvertToTlvFlags();
843
844 SetState(kToAdd);
845
846 Process();
847 }
848
Publish(const ExternalRouteConfig & aConfig)849 void Publisher::PrefixEntry::Publish(const ExternalRouteConfig &aConfig)
850 {
851 otLogInfoNetData("Publisher: Publishing ExternalRoute %s", aConfig.GetPrefix().ToString().AsCString());
852
853 mType = kTypeExternalRoute;
854 mPrefix = aConfig.GetPrefix();
855 mFlags = aConfig.ConvertToTlvFlags();
856
857 SetState(kToAdd);
858
859 Process();
860 }
861
Unpublish(void)862 void Publisher::PrefixEntry::Unpublish(void)
863 {
864 bool registerWithLeader = false;
865
866 otLogInfoNetData("Publisher: Unpublishing %s", mPrefix.ToString().AsCString());
867
868 registerWithLeader = Remove(/* aNextState */ kNoEntry);
869
870 if (registerWithLeader)
871 {
872 Get<Notifier>().HandleServerDataUpdated();
873 }
874 }
875
HandleNotifierEvents(Events aEvents)876 void Publisher::PrefixEntry::HandleNotifierEvents(Events aEvents)
877 {
878 if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
879 {
880 Process();
881 }
882 }
883
Add(void)884 bool Publisher::PrefixEntry::Add(void)
885 {
886 // Adds the prefix entry to the network data.
887
888 bool registerWithLeader = false;
889
890 switch (mType)
891 {
892 case kTypeOnMeshPrefix:
893 SuccessOrExit(AddOnMeshPrefix());
894 break;
895
896 case kTypeExternalRoute:
897 SuccessOrExit(AddExternalRoute());
898 break;
899 }
900
901 registerWithLeader = true;
902 SetState(kAdded);
903 Get<Publisher>().NotifyPrefixEntryChange(kEventEntryAdded, mPrefix);
904
905 exit:
906 return registerWithLeader;
907 }
908
AddOnMeshPrefix(void)909 Error Publisher::PrefixEntry::AddOnMeshPrefix(void)
910 {
911 OnMeshPrefixConfig config;
912
913 config.mPrefix = mPrefix;
914 config.mStable = true;
915 config.SetFromTlvFlags(mFlags);
916
917 return Get<Local>().AddOnMeshPrefix(config);
918 }
919
AddExternalRoute(void)920 Error Publisher::PrefixEntry::AddExternalRoute(void)
921 {
922 ExternalRouteConfig config;
923
924 config.mPrefix = mPrefix;
925 config.mStable = true;
926 config.SetFromTlvFlags(static_cast<uint8_t>(mFlags));
927
928 return Get<Local>().AddHasRoutePrefix(config);
929 }
930
Remove(State aNextState)931 bool Publisher::PrefixEntry::Remove(State aNextState)
932 {
933 // Remove the prefix entry from the network data.
934
935 bool registerWithLeader = false;
936
937 VerifyOrExit((GetState() == kAdded) || (GetState() == kRemoving));
938
939 switch (mType)
940 {
941 case kTypeOnMeshPrefix:
942 IgnoreError(Get<Local>().RemoveOnMeshPrefix(mPrefix));
943 break;
944
945 case kTypeExternalRoute:
946 IgnoreError(Get<Local>().RemoveHasRoutePrefix(mPrefix));
947 break;
948 }
949
950 registerWithLeader = true;
951 Get<Publisher>().NotifyPrefixEntryChange(kEventEntryRemoved, mPrefix);
952
953 exit:
954 SetState(aNextState);
955 return registerWithLeader;
956 }
957
Process(void)958 void Publisher::PrefixEntry::Process(void)
959 {
960 // This method checks the entries currently present in Network Data
961 // based on which it then decides whether or not take action
962 // (add/remove or keep monitoring).
963
964 uint8_t numEntries = 0;
965 uint8_t numPreferredEntries = 0;
966 uint8_t desiredNumEntries = 0;
967
968 // Do not make any changes if device is not attached, and wait
969 // for role change event.
970 VerifyOrExit(Get<Mle::Mle>().IsAttached());
971
972 VerifyOrExit(GetState() != kNoEntry);
973
974 switch (mType)
975 {
976 case kTypeOnMeshPrefix:
977 CountOnMeshPrefixEntries(numEntries, numPreferredEntries);
978 desiredNumEntries = kDesiredNumOnMeshPrefix;
979 break;
980 case kTypeExternalRoute:
981 CountExternalRouteEntries(numEntries, numPreferredEntries);
982 desiredNumEntries = kDesiredNumExternalRoute;
983 break;
984 }
985
986 UpdateState(numEntries, numPreferredEntries, desiredNumEntries);
987
988 exit:
989 return;
990 }
991
CountOnMeshPrefixEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const992 void Publisher::PrefixEntry::CountOnMeshPrefixEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
993 {
994 const PrefixTlv * prefixTlv;
995 const BorderRouterTlv *brSubTlv;
996 int8_t preference = BorderRouterEntry::PreferenceFromFlags(mFlags);
997 uint16_t flagsWithoutPreference = BorderRouterEntry::FlagsWithoutPreference(mFlags);
998
999 prefixTlv = Get<Leader>().FindPrefix(mPrefix);
1000 VerifyOrExit(prefixTlv != nullptr);
1001
1002 brSubTlv = prefixTlv->FindSubTlv<BorderRouterTlv>(/* aStable */ true);
1003 VerifyOrExit(brSubTlv != nullptr);
1004
1005 for (const BorderRouterEntry *entry = brSubTlv->GetFirstEntry(); entry <= brSubTlv->GetLastEntry();
1006 entry = entry->GetNext())
1007 {
1008 uint16_t entryFlags = entry->GetFlags();
1009 int8_t entryPreference = BorderRouterEntry::PreferenceFromFlags(entryFlags);
1010
1011 // Count an existing entry in the network data if its flags
1012 // match ours and and its preference is same or higher than our
1013 // preference. We do not count matching entries at a lower
1014 // preference than ours. This ensures that a device with higher
1015 // preference entry publishes its entry even when there are many
1016 // lower preference similar entries in the network data
1017 // (potentially causing a lower preference entry to be removed).
1018
1019 if ((BorderRouterEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
1020 (entryPreference >= preference))
1021 {
1022 aNumEntries++;
1023
1024 // We prefer an entry if it has strictly higher preference
1025 // than ours or if it has same preference we use the associated
1026 // RLOC16.
1027
1028 if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
1029 {
1030 aNumPreferredEntries++;
1031 }
1032 }
1033 }
1034
1035 exit:
1036 return;
1037 }
1038
CountExternalRouteEntries(uint8_t & aNumEntries,uint8_t & aNumPreferredEntries) const1039 void Publisher::PrefixEntry::CountExternalRouteEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
1040 {
1041 const PrefixTlv * prefixTlv;
1042 const HasRouteTlv *hrSubTlv;
1043 int8_t preference = HasRouteEntry::PreferenceFromFlags(static_cast<uint8_t>(mFlags));
1044 uint8_t flagsWithoutPreference = HasRouteEntry::FlagsWithoutPreference(static_cast<uint8_t>(mFlags));
1045
1046 prefixTlv = Get<Leader>().FindPrefix(mPrefix);
1047 VerifyOrExit(prefixTlv != nullptr);
1048
1049 hrSubTlv = prefixTlv->FindSubTlv<HasRouteTlv>(/* aStable */ true);
1050 VerifyOrExit(hrSubTlv != nullptr);
1051
1052 for (const HasRouteEntry *entry = hrSubTlv->GetFirstEntry(); entry <= hrSubTlv->GetLastEntry();
1053 entry = entry->GetNext())
1054 {
1055 uint8_t entryFlags = entry->GetFlags();
1056 int8_t entryPreference = HasRouteEntry::PreferenceFromFlags(entryFlags);
1057
1058 // Count an existing entry in the network data if its flags
1059 // match ours and and its preference is same or higher than our
1060 // preference. We do not count matching entries at a lower
1061 // preference than ours. This ensures that a device with higher
1062 // preference entry publishes its entry even when there are many
1063 // lower preference similar entries in the network data
1064 // (potentially causing a lower preference entry to be removed).
1065
1066 if ((HasRouteEntry::FlagsWithoutPreference(entryFlags) == flagsWithoutPreference) &&
1067 (entryPreference >= preference))
1068 {
1069 aNumEntries++;
1070
1071 // We prefer an entry if it has strictly higher preference
1072 // than ours or if it has same preference with a smaller
1073 // RLOC16.
1074
1075 if ((entryPreference > preference) || IsPreferred(entry->GetRloc()))
1076 {
1077 aNumPreferredEntries++;
1078 }
1079 }
1080 }
1081
1082 exit:
1083 return;
1084 }
1085
1086 } // namespace NetworkData
1087 } // namespace ot
1088
1089 #endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
1090