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