Coverage Report

Created: 2025-05-12 06:47

/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
5.14k
    : InstanceLocator(aInstance)
50
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
51
5.14k
    , mDnsSrpServiceEntry(aInstance)
52
#endif
53
5.14k
    , mTimer(aInstance)
54
5.14k
{
55
5.14k
#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
5.14k
    for (PrefixEntry &entry : mPrefixEntries)
63
30.8k
    {
64
30.8k
        entry.Init(aInstance);
65
30.8k
    }
66
5.14k
#endif
67
5.14k
}
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
5.46k
{
221
5.46k
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
222
5.46k
    mDnsSrpServiceEntry.HandleNotifierEvents(aEvents);
223
5.46k
#endif
224
225
5.46k
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
226
5.46k
    for (PrefixEntry &entry : mPrefixEntries)
227
32.7k
    {
228
32.7k
        entry.HandleNotifierEvents(aEvents);
229
32.7k
    }
230
5.46k
#endif
231
5.46k
}
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
0
{
252
0
    VerifyOrExit(mState != aState);
253
254
0
    LogInfo("%s - State: %s -> %s", ToString(/* aIncludeState */ false).AsCString(), StateToString(mState),
255
0
            StateToString(aState));
256
0
    mState = aState;
257
258
0
exit:
259
0
    return;
260
0
}
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
5.14k
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
0
{
508
0
    LogInfo("Publishing DNS/SRP service unicast (ml-eid, port:%u, ver:%u)", aPort, aVersion);
509
0
    Publish(Info::InfoUnicast(kTypeUnicastMeshLocalEid, Get<Mle::Mle>().GetMeshLocalEid(), aPort, aVersion));
510
0
}
511
512
void Publisher::DnsSrpServiceEntry::Publish(const Info &aInfo)
513
0
{
514
0
    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
0
    mInfo = aInfo;
526
0
    SetState(kToAdd);
527
528
0
    Process();
529
530
0
exit:
531
0
    return;
532
0
}
533
534
void Publisher::DnsSrpServiceEntry::Unpublish(void)
535
0
{
536
0
    LogInfo("Unpublishing DNS/SRP service");
537
538
0
    Remove(/* aNextState */ kNoEntry);
539
0
}
540
541
void Publisher::DnsSrpServiceEntry::HandleNotifierEvents(Events aEvents)
542
5.46k
{
543
5.46k
    if ((GetType() == kTypeUnicastMeshLocalEid) && aEvents.Contains(kEventThreadMeshLocalAddrChanged))
544
0
    {
545
0
        mInfo.SetAddress(Get<Mle::Mle>().GetMeshLocalEid());
546
547
0
        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
0
    }
558
559
5.46k
    if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
560
5.30k
    {
561
5.30k
        Process();
562
5.30k
    }
563
5.46k
}
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
0
{
596
    // Removes the service entry from network data (if it was added).
597
598
0
    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
0
exit:
620
0
    SetState(aNextState);
621
0
}
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
5.30k
{
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
5.30k
    uint8_t numEntries          = 0;
639
5.30k
    uint8_t numPreferredEntries = 0;
640
5.30k
    uint8_t desiredNumEntries   = 0;
641
642
    // Do not make any changes if device is not attached, and wait
643
    // for role change event.
644
5.30k
    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
5.30k
exit:
680
5.30k
    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
0
{
763
    // It is important to `Clear()` the object since we compare all
764
    // bytes using overload of operator `==`.
765
766
0
    Clear();
767
768
0
    mType            = aType;
769
0
    mPortOrSeqNumber = aPortOrSeqNumber;
770
0
    mVersion         = aVersion;
771
772
0
    if (aAddress != nullptr)
773
0
    {
774
0
        mAddress = *aAddress;
775
0
    }
776
0
}
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
32.7k
{
851
32.7k
    if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged))
852
31.8k
    {
853
31.8k
        Process();
854
31.8k
    }
855
32.7k
}
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
31.8k
{
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
31.8k
    uint8_t numEntries          = 0;
933
31.8k
    uint8_t numPreferredEntries = 0;
934
31.8k
    uint8_t desiredNumEntries   = 0;
935
936
    // Do not make any changes if device is not attached, and wait
937
    // for role change event.
938
31.8k
    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
31.8k
exit:
957
31.8k
    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