1 /*
2  *  Copyright (c) 2016, 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 Thread Network Data managed by the Thread Leader.
32  */
33 
34 #include "network_data_leader.hpp"
35 
36 #if OPENTHREAD_FTD
37 
38 #include "coap/coap_message.hpp"
39 #include "common/as_core_type.hpp"
40 #include "common/code_utils.hpp"
41 #include "common/debug.hpp"
42 #include "common/encoding.hpp"
43 #include "common/locator_getters.hpp"
44 #include "common/log.hpp"
45 #include "common/message.hpp"
46 #include "common/timer.hpp"
47 #include "instance/instance.hpp"
48 #include "mac/mac_types.hpp"
49 #include "meshcop/meshcop.hpp"
50 #include "thread/lowpan.hpp"
51 #include "thread/mle_router.hpp"
52 #include "thread/thread_netif.hpp"
53 #include "thread/thread_tlvs.hpp"
54 #include "thread/uri_paths.hpp"
55 
56 namespace ot {
57 namespace NetworkData {
58 
59 RegisterLogModule("NetworkData");
60 
Start(Mle::LeaderStartMode aStartMode)61 void Leader::Start(Mle::LeaderStartMode aStartMode)
62 {
63 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
64     OT_ASSERT(!mIsClone);
65 #endif
66 
67     mWaitingForNetDataSync = (aStartMode == Mle::kRestoringLeaderRoleAfterReset);
68 
69     if (mWaitingForNetDataSync)
70     {
71         mTimer.Start(kMaxNetDataSyncWait);
72     }
73 }
74 
IncrementVersion(void)75 void Leader::IncrementVersion(void)
76 {
77     if (Get<Mle::MleRouter>().IsLeader())
78     {
79         IncrementVersions(/* aIncludeStable */ false);
80     }
81 }
82 
IncrementVersionAndStableVersion(void)83 void Leader::IncrementVersionAndStableVersion(void)
84 {
85     if (Get<Mle::MleRouter>().IsLeader())
86     {
87         IncrementVersions(/* aIncludeStable */ true);
88     }
89 }
90 
IncrementVersions(const ChangedFlags & aFlags)91 void Leader::IncrementVersions(const ChangedFlags &aFlags)
92 {
93     if (aFlags.DidChange())
94     {
95         IncrementVersions(aFlags.DidStableChange());
96     }
97 }
98 
IncrementVersions(bool aIncludeStable)99 void Leader::IncrementVersions(bool aIncludeStable)
100 {
101 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
102     VerifyOrExit(!mIsClone);
103 #endif
104 
105     if (aIncludeStable)
106     {
107         mStableVersion++;
108     }
109 
110     mVersion++;
111     SignalNetDataChanged();
112     ExitNow();
113 
114 exit:
115     return;
116 }
117 
RemoveBorderRouter(uint16_t aRloc16,MatchMode aMatchMode)118 void Leader::RemoveBorderRouter(uint16_t aRloc16, MatchMode aMatchMode)
119 {
120     ChangedFlags flags;
121 
122     RemoveRloc(aRloc16, aMatchMode, flags);
123 
124     IncrementVersions(flags);
125 }
126 
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)127 template <> void Leader::HandleTmf<kUriServerData>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
128 {
129     ThreadNetworkDataTlv networkDataTlv;
130     uint16_t             rloc16;
131 
132     VerifyOrExit(Get<Mle::Mle>().IsLeader() && !mWaitingForNetDataSync);
133 
134     LogInfo("Received %s", UriToString<kUriServerData>());
135 
136     VerifyOrExit(aMessageInfo.GetPeerAddr().GetIid().IsRoutingLocator());
137 
138     switch (Tlv::Find<ThreadRloc16Tlv>(aMessage, rloc16))
139     {
140     case kErrorNone:
141         RemoveBorderRouter(rloc16, kMatchModeRloc16);
142         break;
143     case kErrorNotFound:
144         break;
145     default:
146         ExitNow();
147     }
148 
149     if (Tlv::FindTlv(aMessage, networkDataTlv) == kErrorNone)
150     {
151         VerifyOrExit(networkDataTlv.IsValid());
152 
153         {
154             NetworkData networkData(GetInstance(), networkDataTlv.GetTlvs(), networkDataTlv.GetLength());
155 
156             RegisterNetworkData(aMessageInfo.GetPeerAddr().GetIid().GetLocator(), networkData);
157         }
158     }
159 
160     SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
161 
162     LogInfo("Sent %s ack", UriToString<kUriServerData>());
163 
164 exit:
165     return;
166 }
167 
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)168 template <> void Leader::HandleTmf<kUriCommissionerSet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
169 {
170     MeshCoP::StateTlv::State state = MeshCoP::StateTlv::kReject;
171     uint16_t                 borderAgentRloc;
172     uint16_t                 sessionId;
173     uint16_t                 localSessionId;
174 
175     VerifyOrExit(Get<Mle::Mle>().IsLeader() && !mWaitingForNetDataSync);
176 
177     // Validate that there is no Border Agent Locator TLV. This also
178     // validates that all included TLVs are properly formatted.
179 
180     VerifyOrExit(Tlv::Find<MeshCoP::BorderAgentLocatorTlv>(aMessage, borderAgentRloc) == kErrorNotFound);
181 
182     SuccessOrExit(Tlv::Find<MeshCoP::CommissionerSessionIdTlv>(aMessage, sessionId));
183 
184     if (FindCommissioningSessionId(localSessionId) == kErrorNone)
185     {
186         VerifyOrExit(sessionId == localSessionId);
187     }
188 
189     // Add the Border Agent RLOC TLV from Network Data.
190 
191     if (FindBorderAgentRloc(borderAgentRloc) == kErrorNone)
192     {
193         SuccessOrExit(Tlv::Append<MeshCoP::BorderAgentLocatorTlv>(aMessage, borderAgentRloc));
194     }
195 
196     SuccessOrExit(SetCommissioningData(aMessage));
197 
198     state = MeshCoP::StateTlv::kAccept;
199 
200 exit:
201     if (Get<Mle::MleRouter>().IsLeader())
202     {
203         SendCommissioningSetResponse(aMessage, aMessageInfo, state);
204     }
205 }
206 
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)207 template <> void Leader::HandleTmf<kUriCommissionerGet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
208 {
209     uint16_t       length;
210     uint16_t       offset;
211     Coap::Message *response = nullptr;
212 
213     VerifyOrExit(Get<Mle::Mle>().IsLeader() && !mWaitingForNetDataSync);
214 
215     response = Get<Tmf::Agent>().NewPriorityResponseMessage(aMessage);
216     VerifyOrExit(response != nullptr);
217 
218     if (Tlv::FindTlvValueOffset(aMessage, MeshCoP::Tlv::kGet, offset, length) == kErrorNone)
219     {
220         // Append the requested sub-TLV types given in Get TLV.
221 
222         for (; length > 0; offset++, length--)
223         {
224             uint8_t             type;
225             const MeshCoP::Tlv *subTlv;
226 
227             IgnoreError(aMessage.Read(offset, type));
228 
229             subTlv = FindCommissioningDataSubTlv(type);
230 
231             if (subTlv != nullptr)
232             {
233                 SuccessOrExit(subTlv->AppendTo(*response));
234             }
235         }
236     }
237     else
238     {
239         // Append all sub-TLVs in the Commissioning Data.
240 
241         CommissioningDataTlv *dataTlv = FindCommissioningData();
242 
243         if (dataTlv != nullptr)
244         {
245             SuccessOrExit(response->AppendBytes(dataTlv->GetValue(), dataTlv->GetLength()));
246         }
247     }
248 
249     SuccessOrExit(Get<Tmf::Agent>().SendMessage(*response, aMessageInfo));
250     response = nullptr; // `SendMessage` takes ownership on success
251 
252     LogInfo("Sent %s response", UriToString<kUriCommissionerGet>());
253 
254 exit:
255     FreeMessage(response);
256 }
257 
SendCommissioningSetResponse(const Coap::Message & aRequest,const Ip6::MessageInfo & aMessageInfo,MeshCoP::StateTlv::State aState)258 void Leader::SendCommissioningSetResponse(const Coap::Message     &aRequest,
259                                           const Ip6::MessageInfo  &aMessageInfo,
260                                           MeshCoP::StateTlv::State aState)
261 {
262     Coap::Message *message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
263 
264     VerifyOrExit(message != nullptr);
265     SuccessOrExit(Tlv::Append<MeshCoP::StateTlv>(*message, aState));
266 
267     SuccessOrExit(Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
268     message = nullptr; // `SendMessage` takes ownership on success
269 
270     LogInfo("Sent %s response", UriToString<kUriCommissionerSet>());
271 
272 exit:
273     FreeMessage(message);
274 }
275 
RlocMatch(uint16_t aFirstRloc16,uint16_t aSecondRloc16,MatchMode aMatchMode)276 bool Leader::RlocMatch(uint16_t aFirstRloc16, uint16_t aSecondRloc16, MatchMode aMatchMode)
277 {
278     bool matched = false;
279 
280     switch (aMatchMode)
281     {
282     case kMatchModeRloc16:
283         matched = (aFirstRloc16 == aSecondRloc16);
284         break;
285 
286     case kMatchModeRouterId:
287         matched = Mle::RouterIdMatch(aFirstRloc16, aSecondRloc16);
288         break;
289     }
290 
291     return matched;
292 }
293 
Validate(const NetworkData & aNetworkData,uint16_t aRloc16)294 Error Leader::Validate(const NetworkData &aNetworkData, uint16_t aRloc16)
295 {
296     // Validate that the `aTlvs` contains well-formed TLVs, sub-TLVs,
297     // and entries all matching `aRloc16` (no other entry for other
298     // RLOCs and no duplicates TLVs).
299 
300     Error                 error = kErrorNone;
301     const NetworkDataTlv *end   = aNetworkData.GetTlvsEnd();
302 
303     for (const NetworkDataTlv *cur = aNetworkData.GetTlvsStart(); cur < end; cur = cur->GetNext())
304     {
305         NetworkData validatedSegment(aNetworkData.GetInstance(), aNetworkData.GetTlvsStart(), cur);
306 
307         VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = kErrorParse);
308 
309         switch (cur->GetType())
310         {
311         case NetworkDataTlv::kTypePrefix:
312         {
313             const PrefixTlv *prefix = As<PrefixTlv>(cur);
314 
315             VerifyOrExit(prefix->IsValid(), error = kErrorParse);
316 
317             // Ensure there is no duplicate Prefix TLVs with same prefix.
318             VerifyOrExit(validatedSegment.FindPrefix(prefix->GetPrefix(), prefix->GetPrefixLength()) == nullptr,
319                          error = kErrorParse);
320 
321             SuccessOrExit(error = ValidatePrefix(*prefix, aRloc16));
322             break;
323         }
324 
325         case NetworkDataTlv::kTypeService:
326         {
327             const ServiceTlv *service = As<ServiceTlv>(cur);
328             ServiceData       serviceData;
329 
330             VerifyOrExit(service->IsValid(), error = kErrorParse);
331 
332             service->GetServiceData(serviceData);
333 
334             // Ensure there is no duplicate Service TLV with same
335             // Enterprise Number and Service Data.
336             VerifyOrExit(validatedSegment.FindService(service->GetEnterpriseNumber(), serviceData,
337                                                       kServiceExactMatch) == nullptr,
338                          error = kErrorParse);
339 
340             SuccessOrExit(error = ValidateService(*service, aRloc16));
341             break;
342         }
343 
344         default:
345             break;
346         }
347     }
348 
349 exit:
350     return error;
351 }
352 
ValidatePrefix(const PrefixTlv & aPrefix,uint16_t aRloc16)353 Error Leader::ValidatePrefix(const PrefixTlv &aPrefix, uint16_t aRloc16)
354 {
355     // Validate that `aPrefix` TLV contains well-formed sub-TLVs and
356     // and entries all matching `aRloc16` (no other entry for other
357     // RLOCs).
358 
359     Error                 error                   = kErrorParse;
360     const NetworkDataTlv *subEnd                  = aPrefix.GetNext();
361     bool                  foundTempHasRoute       = false;
362     bool                  foundStableHasRoute     = false;
363     bool                  foundTempBorderRouter   = false;
364     bool                  foundStableBorderRouter = false;
365 
366     for (const NetworkDataTlv *subCur = aPrefix.GetSubTlvs(); subCur < subEnd; subCur = subCur->GetNext())
367     {
368         VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd);
369 
370         switch (subCur->GetType())
371         {
372         case NetworkDataTlv::kTypeBorderRouter:
373         {
374             const BorderRouterTlv *borderRouter = As<BorderRouterTlv>(subCur);
375 
376             // Ensure Prefix TLV contains at most one stable and one
377             // temporary Border Router sub-TLV and the sub-TLVs have
378             // a single entry.
379 
380             if (borderRouter->IsStable())
381             {
382                 VerifyOrExit(!foundStableBorderRouter);
383                 foundStableBorderRouter = true;
384             }
385             else
386             {
387                 VerifyOrExit(!foundTempBorderRouter);
388                 foundTempBorderRouter = true;
389             }
390 
391             VerifyOrExit(borderRouter->GetFirstEntry() == borderRouter->GetLastEntry());
392             VerifyOrExit(borderRouter->GetFirstEntry()->GetRloc() == aRloc16);
393             break;
394         }
395 
396         case NetworkDataTlv::kTypeHasRoute:
397         {
398             const HasRouteTlv *hasRoute = As<HasRouteTlv>(subCur);
399 
400             // Ensure Prefix TLV contains at most one stable and one
401             // temporary Has Route sub-TLV and the sub-TLVs have a
402             // single entry.
403 
404             if (hasRoute->IsStable())
405             {
406                 VerifyOrExit(!foundStableHasRoute);
407                 foundStableHasRoute = true;
408             }
409             else
410             {
411                 VerifyOrExit(!foundTempHasRoute);
412                 foundTempHasRoute = true;
413             }
414 
415             VerifyOrExit(hasRoute->GetFirstEntry() == hasRoute->GetLastEntry());
416             VerifyOrExit(hasRoute->GetFirstEntry()->GetRloc() == aRloc16);
417             break;
418         }
419 
420         default:
421             break;
422         }
423     }
424 
425     if (foundStableBorderRouter || foundTempBorderRouter || foundStableHasRoute || foundTempHasRoute)
426     {
427         error = kErrorNone;
428     }
429 
430 exit:
431     return error;
432 }
433 
ValidateService(const ServiceTlv & aService,uint16_t aRloc16)434 Error Leader::ValidateService(const ServiceTlv &aService, uint16_t aRloc16)
435 {
436     // Validate that `aService` TLV contains a single well-formed
437     // Server sub-TLV associated with `aRloc16`.
438 
439     Error                 error       = kErrorParse;
440     const NetworkDataTlv *subEnd      = aService.GetNext();
441     bool                  foundServer = false;
442 
443     for (const NetworkDataTlv *subCur = aService.GetSubTlvs(); subCur < subEnd; subCur = subCur->GetNext())
444     {
445         VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd);
446 
447         switch (subCur->GetType())
448         {
449         case NetworkDataTlv::kTypeServer:
450         {
451             const ServerTlv *server = As<ServerTlv>(subCur);
452 
453             VerifyOrExit(!foundServer);
454             foundServer = true;
455 
456             VerifyOrExit(server->IsValid() && server->GetServer16() == aRloc16);
457             break;
458         }
459 
460         default:
461             break;
462         }
463     }
464 
465     if (foundServer)
466     {
467         error = kErrorNone;
468     }
469 
470 exit:
471     return error;
472 }
473 
ContainsMatchingEntry(const PrefixTlv * aPrefix,bool aStable,const HasRouteEntry & aEntry)474 bool Leader::ContainsMatchingEntry(const PrefixTlv *aPrefix, bool aStable, const HasRouteEntry &aEntry)
475 {
476     // Check whether `aPrefix` has a Has Route sub-TLV with stable
477     // flag `aStable` containing a matching entry to `aEntry`.
478 
479     return (aPrefix == nullptr) ? false : ContainsMatchingEntry(aPrefix->FindSubTlv<HasRouteTlv>(aStable), aEntry);
480 }
481 
ContainsMatchingEntry(const HasRouteTlv * aHasRoute,const HasRouteEntry & aEntry)482 bool Leader::ContainsMatchingEntry(const HasRouteTlv *aHasRoute, const HasRouteEntry &aEntry)
483 {
484     // Check whether `aHasRoute` has a matching entry to `aEntry`.
485 
486     bool contains = false;
487 
488     VerifyOrExit(aHasRoute != nullptr);
489 
490     for (const HasRouteEntry *entry = aHasRoute->GetFirstEntry(); entry <= aHasRoute->GetLastEntry(); entry++)
491     {
492         if (*entry == aEntry)
493         {
494             contains = true;
495             break;
496         }
497     }
498 
499 exit:
500     return contains;
501 }
502 
ContainsMatchingEntry(const PrefixTlv * aPrefix,bool aStable,const BorderRouterEntry & aEntry)503 bool Leader::ContainsMatchingEntry(const PrefixTlv *aPrefix, bool aStable, const BorderRouterEntry &aEntry)
504 {
505     // Check whether `aPrefix` has a Border Router sub-TLV with stable
506     // flag `aStable` containing a matching entry to `aEntry`.
507 
508     return (aPrefix == nullptr) ? false : ContainsMatchingEntry(aPrefix->FindSubTlv<BorderRouterTlv>(aStable), aEntry);
509 }
510 
ContainsMatchingEntry(const BorderRouterTlv * aBorderRouter,const BorderRouterEntry & aEntry)511 bool Leader::ContainsMatchingEntry(const BorderRouterTlv *aBorderRouter, const BorderRouterEntry &aEntry)
512 {
513     // Check whether `aBorderRouter` has a matching entry to `aEntry`.
514 
515     bool contains = false;
516 
517     VerifyOrExit(aBorderRouter != nullptr);
518 
519     for (const BorderRouterEntry *entry = aBorderRouter->GetFirstEntry(); entry <= aBorderRouter->GetLastEntry();
520          entry++)
521     {
522         if (*entry == aEntry)
523         {
524             contains = true;
525             break;
526         }
527     }
528 
529 exit:
530     return contains;
531 }
532 
ContainsMatchingServer(const ServiceTlv * aService,const ServerTlv & aServer)533 bool Leader::ContainsMatchingServer(const ServiceTlv *aService, const ServerTlv &aServer)
534 {
535     // Check whether the `aService` has a matching Server sub-TLV
536     // same as `aServer`.
537 
538     bool contains = false;
539 
540     if (aService != nullptr)
541     {
542         const ServerTlv *server;
543         TlvIterator      subTlvIterator(*aService);
544 
545         while ((server = subTlvIterator.Iterate<ServerTlv>(aServer.IsStable())) != nullptr)
546         {
547             if (*server == aServer)
548             {
549                 contains = true;
550                 break;
551             }
552         }
553     }
554 
555     return contains;
556 }
557 
UpdatePrefix(PrefixTlv & aPrefix)558 Leader::UpdateStatus Leader::UpdatePrefix(PrefixTlv &aPrefix) { return UpdateTlv(aPrefix, aPrefix.GetSubTlvs()); }
559 
UpdateService(ServiceTlv & aService)560 Leader::UpdateStatus Leader::UpdateService(ServiceTlv &aService) { return UpdateTlv(aService, aService.GetSubTlvs()); }
561 
UpdateTlv(NetworkDataTlv & aTlv,const NetworkDataTlv * aSubTlvs)562 Leader::UpdateStatus Leader::UpdateTlv(NetworkDataTlv &aTlv, const NetworkDataTlv *aSubTlvs)
563 {
564     // If `aTlv` contains no sub-TLVs, remove it from Network Data,
565     // otherwise update its stable flag based on its sub-TLVs.
566 
567     UpdateStatus status = kTlvUpdated;
568 
569     if (aSubTlvs == aTlv.GetNext())
570     {
571         RemoveTlv(&aTlv);
572         ExitNow(status = kTlvRemoved);
573     }
574 
575     for (const NetworkDataTlv *subCur = aSubTlvs; subCur < aTlv.GetNext(); subCur = subCur->GetNext())
576     {
577         if (subCur->IsStable())
578         {
579             aTlv.SetStable();
580             ExitNow();
581         }
582     }
583 
584     aTlv.ClearStable();
585 
586 exit:
587     return status;
588 }
589 
590 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
591 
CheckForNetDataGettingFull(const NetworkData & aNetworkData,uint16_t aOldRloc16)592 void Leader::CheckForNetDataGettingFull(const NetworkData &aNetworkData, uint16_t aOldRloc16)
593 {
594     // Determines whether there is still room in Network Data to register
595     // `aNetworkData` entries. The `aNetworkData` MUST follow the format of
596     // local Network Data (e.g., all entries associated with the RLOC16 of
597     // this device). Network data getting full is signaled by invoking the
598     // `Get<Notifier>().SignalNetworkDataFull()` method.
599     //
600     // Input `aOldRloc16` can be used to indicate the old RLOC16 of the
601     // device. If provided, then entries matching old RLOC16 are first
602     // removed, before checking if new entries from @p aNetworkData can fit.
603 
604     if (!Get<Mle::MleRouter>().IsLeader())
605     {
606         // Create a clone of the leader's network data, and try to register
607         // `aNetworkData` into the copy (as if this device itself is the
608         // leader). `mIsClone` flag is used to mark the clone and ensure
609         // that the cloned instance does interact with other OT modules,
610         // e.g., does not start timer, or does not signal version change
611         // using `Get<ot::Notifier>().Signal()`, or allocate service or
612         // context ID.
613 
614         Leader leaderClone(GetInstance());
615 
616         leaderClone.MarkAsClone();
617         SuccessOrAssert(CopyNetworkData(kFullSet, leaderClone));
618 
619         if (aOldRloc16 != Mac::kShortAddrInvalid)
620         {
621             leaderClone.RemoveBorderRouter(aOldRloc16, kMatchModeRloc16);
622         }
623 
624         leaderClone.RegisterNetworkData(Get<Mle::Mle>().GetRloc16(), aNetworkData);
625     }
626 }
627 
MarkAsClone(void)628 void Leader::MarkAsClone(void)
629 {
630     mIsClone = true;
631     mContextIds.MarkAsClone();
632 }
633 
634 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
635 
RegisterNetworkData(uint16_t aRloc16,const NetworkData & aNetworkData)636 void Leader::RegisterNetworkData(uint16_t aRloc16, const NetworkData &aNetworkData)
637 {
638     Error        error = kErrorNone;
639     ChangedFlags flags;
640 
641     VerifyOrExit(Get<RouterTable>().IsAllocated(Mle::RouterIdFromRloc16(aRloc16)), error = kErrorNoRoute);
642 
643     // Validate that the `aNetworkData` contains well-formed TLVs, sub-TLVs,
644     // and entries all matching `aRloc16` (no other RLOCs).
645     SuccessOrExit(error = Validate(aNetworkData, aRloc16));
646 
647     // Remove all entries matching `aRloc16` excluding entries that are
648     // present in `aNetworkData`
649     RemoveRloc(aRloc16, kMatchModeRloc16, aNetworkData, flags);
650 
651     // Now add all new entries in `aTlvs` to Network Data.
652     for (const NetworkDataTlv *cur = aNetworkData.GetTlvsStart(); cur < aNetworkData.GetTlvsEnd(); cur = cur->GetNext())
653     {
654         switch (cur->GetType())
655         {
656         case NetworkDataTlv::kTypePrefix:
657             SuccessOrExit(error = AddPrefix(*As<PrefixTlv>(cur), flags));
658             break;
659 
660         case NetworkDataTlv::kTypeService:
661             SuccessOrExit(error = AddService(*As<ServiceTlv>(cur), flags));
662             break;
663 
664         default:
665             break;
666         }
667     }
668 
669     DumpDebg("Register", GetBytes(), GetLength());
670 
671 exit:
672     IncrementVersions(flags);
673 
674 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
675     if (error == kErrorNoBufs)
676     {
677         Get<Notifier>().SignalNetworkDataFull();
678     }
679 
680     if (!mIsClone)
681 #endif
682     {
683         if (error != kErrorNone)
684         {
685             LogNote("Failed to register network data: %s", ErrorToString(error));
686         }
687     }
688 }
689 
AddPrefix(const PrefixTlv & aPrefix,ChangedFlags & aChangedFlags)690 Error Leader::AddPrefix(const PrefixTlv &aPrefix, ChangedFlags &aChangedFlags)
691 {
692     Error      error     = kErrorNone;
693     PrefixTlv *dstPrefix = FindPrefix(aPrefix.GetPrefix(), aPrefix.GetPrefixLength());
694 
695     if (dstPrefix == nullptr)
696     {
697         dstPrefix = As<PrefixTlv>(AppendTlv(PrefixTlv::CalculateSize(aPrefix.GetPrefixLength())));
698         VerifyOrExit(dstPrefix != nullptr, error = kErrorNoBufs);
699 
700         dstPrefix->Init(aPrefix.GetDomainId(), aPrefix.GetPrefixLength(), aPrefix.GetPrefix());
701     }
702 
703     for (const NetworkDataTlv *subCur = aPrefix.GetSubTlvs(); subCur < aPrefix.GetNext(); subCur = subCur->GetNext())
704     {
705         switch (subCur->GetType())
706         {
707         case NetworkDataTlv::kTypeHasRoute:
708             SuccessOrExit(error = AddHasRoute(*As<HasRouteTlv>(subCur), *dstPrefix, aChangedFlags));
709             break;
710 
711         case NetworkDataTlv::kTypeBorderRouter:
712             SuccessOrExit(error = AddBorderRouter(*As<BorderRouterTlv>(subCur), *dstPrefix, aChangedFlags));
713             break;
714 
715         default:
716             break;
717         }
718     }
719 
720 exit:
721     if (dstPrefix != nullptr)
722     {
723         // `UpdatePrefix()` updates the TLV's stable flag based on
724         // its sub-TLVs, or removes the TLV if it contains no sub-TLV.
725         // This is called at `exit` to ensure that if appending
726         // sub-TLVs fail (e.g., out of space in network data), we
727         // remove an empty Prefix TLV.
728 
729         IgnoreReturnValue(UpdatePrefix(*dstPrefix));
730     }
731 
732     return error;
733 }
734 
AddService(const ServiceTlv & aService,ChangedFlags & aChangedFlags)735 Error Leader::AddService(const ServiceTlv &aService, ChangedFlags &aChangedFlags)
736 {
737     Error            error = kErrorNone;
738     ServiceTlv      *dstService;
739     ServiceData      serviceData;
740     const ServerTlv *server;
741 
742     aService.GetServiceData(serviceData);
743     dstService = FindService(aService.GetEnterpriseNumber(), serviceData, kServiceExactMatch);
744 
745     if (dstService == nullptr)
746     {
747         uint8_t serviceId;
748 
749         SuccessOrExit(error = AllocateServiceId(serviceId));
750 
751         dstService = As<ServiceTlv>(
752             AppendTlv(ServiceTlv::CalculateSize(aService.GetEnterpriseNumber(), serviceData.GetLength())));
753         VerifyOrExit(dstService != nullptr, error = kErrorNoBufs);
754 
755         dstService->Init(serviceId, aService.GetEnterpriseNumber(), serviceData);
756     }
757 
758     server = NetworkDataTlv::Find<ServerTlv>(aService.GetSubTlvs(), aService.GetNext());
759     OT_ASSERT(server != nullptr);
760 
761     SuccessOrExit(error = AddServer(*server, *dstService, aChangedFlags));
762 
763 exit:
764     if (dstService != nullptr)
765     {
766         // `UpdateService()` updates the TLV's stable flag based on
767         // its sub-TLVs, or removes the TLV if it contains no sub-TLV.
768         // This is called at `exit` to ensure that if appending
769         // sub-TLVs fail (e.g., out of space in network data), we
770         // remove an empty Service TLV.
771 
772         IgnoreReturnValue(UpdateService(*dstService));
773     }
774 
775     return error;
776 }
777 
AddHasRoute(const HasRouteTlv & aHasRoute,PrefixTlv & aDstPrefix,ChangedFlags & aChangedFlags)778 Error Leader::AddHasRoute(const HasRouteTlv &aHasRoute, PrefixTlv &aDstPrefix, ChangedFlags &aChangedFlags)
779 {
780     Error                error       = kErrorNone;
781     HasRouteTlv         *dstHasRoute = aDstPrefix.FindSubTlv<HasRouteTlv>(aHasRoute.IsStable());
782     const HasRouteEntry *entry       = aHasRoute.GetFirstEntry();
783 
784     if (dstHasRoute == nullptr)
785     {
786         // Ensure there is space for `HasRouteTlv` and a single entry.
787         VerifyOrExit(CanInsert(sizeof(HasRouteTlv) + sizeof(HasRouteEntry)), error = kErrorNoBufs);
788 
789         dstHasRoute = As<HasRouteTlv>(aDstPrefix.GetNext());
790         Insert(dstHasRoute, sizeof(HasRouteTlv));
791         aDstPrefix.IncreaseLength(sizeof(HasRouteTlv));
792         dstHasRoute->Init();
793 
794         if (aHasRoute.IsStable())
795         {
796             dstHasRoute->SetStable();
797         }
798     }
799 
800     VerifyOrExit(!ContainsMatchingEntry(dstHasRoute, *entry));
801 
802     VerifyOrExit(CanInsert(sizeof(HasRouteEntry)), error = kErrorNoBufs);
803 
804     Insert(dstHasRoute->GetNext(), sizeof(HasRouteEntry));
805     dstHasRoute->IncreaseLength(sizeof(HasRouteEntry));
806     aDstPrefix.IncreaseLength(sizeof(HasRouteEntry));
807 
808     *dstHasRoute->GetLastEntry() = *entry;
809     aChangedFlags.Update(*dstHasRoute);
810 
811 exit:
812     return error;
813 }
814 
AddBorderRouter(const BorderRouterTlv & aBorderRouter,PrefixTlv & aDstPrefix,ChangedFlags & aChangedFlags)815 Error Leader::AddBorderRouter(const BorderRouterTlv &aBorderRouter, PrefixTlv &aDstPrefix, ChangedFlags &aChangedFlags)
816 {
817     Error                    error           = kErrorNone;
818     BorderRouterTlv         *dstBorderRouter = aDstPrefix.FindSubTlv<BorderRouterTlv>(aBorderRouter.IsStable());
819     ContextTlv              *dstContext      = aDstPrefix.FindSubTlv<ContextTlv>();
820     uint8_t                  contextId       = 0;
821     const BorderRouterEntry *entry           = aBorderRouter.GetFirstEntry();
822 
823     if (dstContext == nullptr)
824     {
825         // Get a new Context ID first. This ensure that if we cannot
826         // get new Context ID, we fail and exit before potentially
827         // inserting a Border Router sub-TLV.
828         SuccessOrExit(error = mContextIds.GetUnallocatedId(contextId));
829     }
830 
831     if (dstBorderRouter == nullptr)
832     {
833         // Ensure there is space for `BorderRouterTlv` with a single entry
834         // and a `ContextTlv` (if not already present).
835         VerifyOrExit(CanInsert(sizeof(BorderRouterTlv) + sizeof(BorderRouterEntry) +
836                                ((dstContext == nullptr) ? sizeof(ContextTlv) : 0)),
837                      error = kErrorNoBufs);
838 
839         dstBorderRouter = As<BorderRouterTlv>(aDstPrefix.GetNext());
840         Insert(dstBorderRouter, sizeof(BorderRouterTlv));
841         aDstPrefix.IncreaseLength(sizeof(BorderRouterTlv));
842         dstBorderRouter->Init();
843 
844         if (aBorderRouter.IsStable())
845         {
846             dstBorderRouter->SetStable();
847         }
848     }
849 
850     if (dstContext == nullptr)
851     {
852         // Ensure there is space for a `ContextTlv` and a single entry.
853         VerifyOrExit(CanInsert(sizeof(BorderRouterEntry) + sizeof(ContextTlv)), error = kErrorNoBufs);
854 
855         dstContext = As<ContextTlv>(aDstPrefix.GetNext());
856         Insert(dstContext, sizeof(ContextTlv));
857         aDstPrefix.IncreaseLength(sizeof(ContextTlv));
858         dstContext->Init(static_cast<uint8_t>(contextId), aDstPrefix.GetPrefixLength());
859     }
860 
861     if (aBorderRouter.IsStable())
862     {
863         dstContext->SetStable();
864     }
865 
866     dstContext->SetCompress();
867     mContextIds.MarkAsInUse(dstContext->GetContextId());
868 
869     VerifyOrExit(!ContainsMatchingEntry(dstBorderRouter, *entry));
870 
871     VerifyOrExit(CanInsert(sizeof(BorderRouterEntry)), error = kErrorNoBufs);
872 
873     Insert(dstBorderRouter->GetNext(), sizeof(BorderRouterEntry));
874     dstBorderRouter->IncreaseLength(sizeof(BorderRouterEntry));
875     aDstPrefix.IncreaseLength(sizeof(BorderRouterEntry));
876     *dstBorderRouter->GetLastEntry() = *entry;
877     aChangedFlags.Update(*dstBorderRouter);
878 
879 exit:
880     return error;
881 }
882 
AddServer(const ServerTlv & aServer,ServiceTlv & aDstService,ChangedFlags & aChangedFlags)883 Error Leader::AddServer(const ServerTlv &aServer, ServiceTlv &aDstService, ChangedFlags &aChangedFlags)
884 {
885     Error      error = kErrorNone;
886     ServerTlv *dstServer;
887     ServerData serverData;
888     uint8_t    tlvSize = aServer.GetSize();
889 
890     VerifyOrExit(!ContainsMatchingServer(&aDstService, aServer));
891 
892     VerifyOrExit(CanInsert(tlvSize), error = kErrorNoBufs);
893 
894     aServer.GetServerData(serverData);
895 
896     dstServer = As<ServerTlv>(aDstService.GetNext());
897     Insert(dstServer, tlvSize);
898     dstServer->Init(aServer.GetServer16(), serverData);
899 
900     if (aServer.IsStable())
901     {
902         dstServer->SetStable();
903     }
904 
905     aDstService.IncreaseLength(tlvSize);
906     aChangedFlags.Update(*dstServer);
907 
908 exit:
909     return error;
910 }
911 
AllocateServiceId(uint8_t & aServiceId) const912 Error Leader::AllocateServiceId(uint8_t &aServiceId) const
913 {
914     Error   error = kErrorNotFound;
915     uint8_t serviceId;
916 
917 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
918     if (mIsClone)
919     {
920         aServiceId = kMinServiceId;
921         error      = kErrorNone;
922         ExitNow();
923     }
924 #endif
925 
926     for (serviceId = kMinServiceId; serviceId <= kMaxServiceId; serviceId++)
927     {
928         if (FindServiceById(serviceId) == nullptr)
929         {
930             aServiceId = serviceId;
931             error      = kErrorNone;
932             LogInfo("Allocated Service ID = %d", serviceId);
933             ExitNow();
934         }
935     }
936 
937 exit:
938     return error;
939 }
940 
FindServiceById(uint8_t aServiceId) const941 const ServiceTlv *Leader::FindServiceById(uint8_t aServiceId) const
942 {
943     const ServiceTlv *service;
944     TlvIterator       tlvIterator(GetTlvsStart(), GetTlvsEnd());
945 
946     while ((service = tlvIterator.Iterate<ServiceTlv>()) != nullptr)
947     {
948         if (service->GetServiceId() == aServiceId)
949         {
950             break;
951         }
952     }
953 
954     return service;
955 }
956 
RemoveRloc(uint16_t aRloc16,MatchMode aMatchMode,ChangedFlags & aChangedFlags)957 void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode, ChangedFlags &aChangedFlags)
958 {
959     NetworkData excludeNetworkData(GetInstance()); // Empty network data.
960 
961     RemoveRloc(aRloc16, aMatchMode, excludeNetworkData, aChangedFlags);
962 }
963 
RemoveRloc(uint16_t aRloc16,MatchMode aMatchMode,const NetworkData & aExcludeNetworkData,ChangedFlags & aChangedFlags)964 void Leader::RemoveRloc(uint16_t           aRloc16,
965                         MatchMode          aMatchMode,
966                         const NetworkData &aExcludeNetworkData,
967                         ChangedFlags      &aChangedFlags)
968 {
969     // Remove entries from Network Data matching `aRloc16` (using
970     // `aMatchMode` to determine the match) but exclude any entries
971     // that are present in `aExcludeNetworkData`. As entries are
972     // removed update `aChangedFlags` to indicate if Network Data
973     // (stable or not) got changed.
974 
975     NetworkDataTlv *cur = GetTlvsStart();
976 
977     while (cur < GetTlvsEnd())
978     {
979         switch (cur->GetType())
980         {
981         case NetworkDataTlv::kTypePrefix:
982         {
983             PrefixTlv       *prefix = As<PrefixTlv>(cur);
984             const PrefixTlv *excludePrefix =
985                 aExcludeNetworkData.FindPrefix(prefix->GetPrefix(), prefix->GetPrefixLength());
986 
987             RemoveRlocInPrefix(*prefix, aRloc16, aMatchMode, excludePrefix, aChangedFlags);
988 
989             if (UpdatePrefix(*prefix) == kTlvRemoved)
990             {
991                 // Do not update `cur` when TLV is removed.
992                 continue;
993             }
994 
995             break;
996         }
997 
998         case NetworkDataTlv::kTypeService:
999         {
1000             ServiceTlv       *service = As<ServiceTlv>(cur);
1001             ServiceData       serviceData;
1002             const ServiceTlv *excludeService;
1003 
1004             service->GetServiceData(serviceData);
1005 
1006             excludeService =
1007                 aExcludeNetworkData.FindService(service->GetEnterpriseNumber(), serviceData, kServiceExactMatch);
1008 
1009             RemoveRlocInService(*service, aRloc16, aMatchMode, excludeService, aChangedFlags);
1010 
1011             if (UpdateService(*service) == kTlvRemoved)
1012             {
1013                 // Do not update `cur` when TLV is removed.
1014                 continue;
1015             }
1016 
1017             break;
1018         }
1019 
1020         default:
1021             break;
1022         }
1023 
1024         cur = cur->GetNext();
1025     }
1026 }
1027 
RemoveRlocInPrefix(PrefixTlv & aPrefix,uint16_t aRloc16,MatchMode aMatchMode,const PrefixTlv * aExcludePrefix,ChangedFlags & aChangedFlags)1028 void Leader::RemoveRlocInPrefix(PrefixTlv       &aPrefix,
1029                                 uint16_t         aRloc16,
1030                                 MatchMode        aMatchMode,
1031                                 const PrefixTlv *aExcludePrefix,
1032                                 ChangedFlags    &aChangedFlags)
1033 {
1034     // Remove entries in `aPrefix` TLV matching the given `aRloc16`
1035     // excluding any entries that are present in `aExcludePrefix`.
1036 
1037     NetworkDataTlv *cur = aPrefix.GetSubTlvs();
1038     ContextTlv     *context;
1039 
1040     while (cur < aPrefix.GetNext())
1041     {
1042         switch (cur->GetType())
1043         {
1044         case NetworkDataTlv::kTypeHasRoute:
1045             RemoveRlocInHasRoute(aPrefix, *As<HasRouteTlv>(cur), aRloc16, aMatchMode, aExcludePrefix, aChangedFlags);
1046 
1047             if (cur->GetLength() == 0)
1048             {
1049                 aPrefix.DecreaseLength(sizeof(HasRouteTlv));
1050                 RemoveTlv(cur);
1051                 continue;
1052             }
1053 
1054             break;
1055 
1056         case NetworkDataTlv::kTypeBorderRouter:
1057             RemoveRlocInBorderRouter(aPrefix, *As<BorderRouterTlv>(cur), aRloc16, aMatchMode, aExcludePrefix,
1058                                      aChangedFlags);
1059 
1060             if (cur->GetLength() == 0)
1061             {
1062                 aPrefix.DecreaseLength(sizeof(BorderRouterTlv));
1063                 RemoveTlv(cur);
1064                 continue;
1065             }
1066 
1067             break;
1068 
1069         default:
1070             break;
1071         }
1072 
1073         cur = cur->GetNext();
1074     }
1075 
1076     if ((context = aPrefix.FindSubTlv<ContextTlv>()) != nullptr)
1077     {
1078         if (aPrefix.FindSubTlv<BorderRouterTlv>() == nullptr)
1079         {
1080             context->ClearCompress();
1081             mContextIds.ScheduleToRemove(context->GetContextId());
1082         }
1083         else
1084         {
1085             context->SetCompress();
1086             mContextIds.MarkAsInUse(context->GetContextId());
1087         }
1088     }
1089 }
1090 
RemoveRlocInService(ServiceTlv & aService,uint16_t aRloc16,MatchMode aMatchMode,const ServiceTlv * aExcludeService,ChangedFlags & aChangedFlags)1091 void Leader::RemoveRlocInService(ServiceTlv       &aService,
1092                                  uint16_t          aRloc16,
1093                                  MatchMode         aMatchMode,
1094                                  const ServiceTlv *aExcludeService,
1095                                  ChangedFlags     &aChangedFlags)
1096 {
1097     // Remove entries in `aService` TLV matching the given `aRloc16`
1098     // excluding any entries that are present in `aExcludeService`.
1099 
1100     NetworkDataTlv *start = aService.GetSubTlvs();
1101     ServerTlv      *server;
1102 
1103     while ((server = NetworkDataTlv::Find<ServerTlv>(start, aService.GetNext())) != nullptr)
1104     {
1105         if (RlocMatch(server->GetServer16(), aRloc16, aMatchMode) && !ContainsMatchingServer(aExcludeService, *server))
1106         {
1107             uint8_t subTlvSize = server->GetSize();
1108 
1109             aChangedFlags.Update(*server);
1110             RemoveTlv(server);
1111             aService.DecreaseLength(subTlvSize);
1112             continue;
1113         }
1114 
1115         start = server->GetNext();
1116     }
1117 }
1118 
RemoveRlocInHasRoute(PrefixTlv & aPrefix,HasRouteTlv & aHasRoute,uint16_t aRloc16,MatchMode aMatchMode,const PrefixTlv * aExcludePrefix,ChangedFlags & aChangedFlags)1119 void Leader::RemoveRlocInHasRoute(PrefixTlv       &aPrefix,
1120                                   HasRouteTlv     &aHasRoute,
1121                                   uint16_t         aRloc16,
1122                                   MatchMode        aMatchMode,
1123                                   const PrefixTlv *aExcludePrefix,
1124                                   ChangedFlags    &aChangedFlags)
1125 {
1126     // Remove entries in `aHasRoute` (a sub-TLV of `aPrefix` TLV)
1127     // matching the given `aRloc16` excluding entries that are present
1128     // in `aExcludePrefix`.
1129 
1130     HasRouteEntry *entry = aHasRoute.GetFirstEntry();
1131 
1132     while (entry <= aHasRoute.GetLastEntry())
1133     {
1134         if (RlocMatch(entry->GetRloc(), aRloc16, aMatchMode) &&
1135             !ContainsMatchingEntry(aExcludePrefix, aHasRoute.IsStable(), *entry))
1136         {
1137             aChangedFlags.Update(aHasRoute);
1138             aHasRoute.DecreaseLength(sizeof(HasRouteEntry));
1139             aPrefix.DecreaseLength(sizeof(HasRouteEntry));
1140             Remove(entry, sizeof(HasRouteEntry));
1141             continue;
1142         }
1143 
1144         entry = entry->GetNext();
1145     }
1146 }
1147 
RemoveRlocInBorderRouter(PrefixTlv & aPrefix,BorderRouterTlv & aBorderRouter,uint16_t aRloc16,MatchMode aMatchMode,const PrefixTlv * aExcludePrefix,ChangedFlags & aChangedFlags)1148 void Leader::RemoveRlocInBorderRouter(PrefixTlv       &aPrefix,
1149                                       BorderRouterTlv &aBorderRouter,
1150                                       uint16_t         aRloc16,
1151                                       MatchMode        aMatchMode,
1152                                       const PrefixTlv *aExcludePrefix,
1153                                       ChangedFlags    &aChangedFlags)
1154 {
1155     // Remove entries in `aBorderRouter` (a sub-TLV of `aPrefix` TLV)
1156     // matching the given `aRloc16` excluding entries that are present
1157     // in `aExcludePrefix`.
1158 
1159     BorderRouterEntry *entry = aBorderRouter.GetFirstEntry();
1160 
1161     while (entry <= aBorderRouter.GetLastEntry())
1162     {
1163         if (RlocMatch(entry->GetRloc(), aRloc16, aMatchMode) &&
1164             !ContainsMatchingEntry(aExcludePrefix, aBorderRouter.IsStable(), *entry))
1165         {
1166             aChangedFlags.Update(aBorderRouter);
1167             aBorderRouter.DecreaseLength(sizeof(BorderRouterEntry));
1168             aPrefix.DecreaseLength(sizeof(BorderRouterEntry));
1169             Remove(entry, sizeof(*entry));
1170             continue;
1171         }
1172 
1173         entry = entry->GetNext();
1174     }
1175 }
1176 
RemoveContext(uint8_t aContextId)1177 void Leader::RemoveContext(uint8_t aContextId)
1178 {
1179     NetworkDataTlv *start = GetTlvsStart();
1180     PrefixTlv      *prefix;
1181 
1182     while ((prefix = NetworkDataTlv::Find<PrefixTlv>(start, GetTlvsEnd())) != nullptr)
1183     {
1184         RemoveContext(*prefix, aContextId);
1185 
1186         if (UpdatePrefix(*prefix) == kTlvRemoved)
1187         {
1188             // Do not update `start` when TLV is removed.
1189             continue;
1190         }
1191 
1192         start = prefix->GetNext();
1193     }
1194 
1195     IncrementVersions(/* aIncludeStable */ true);
1196 }
1197 
RemoveContext(PrefixTlv & aPrefix,uint8_t aContextId)1198 void Leader::RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId)
1199 {
1200     NetworkDataTlv *start = aPrefix.GetSubTlvs();
1201     ContextTlv     *context;
1202 
1203     while ((context = NetworkDataTlv::Find<ContextTlv>(start, aPrefix.GetNext())) != nullptr)
1204     {
1205         if (context->GetContextId() == aContextId)
1206         {
1207             uint8_t subTlvSize = context->GetSize();
1208             RemoveTlv(context);
1209             aPrefix.DecreaseLength(subTlvSize);
1210             continue;
1211         }
1212 
1213         start = context->GetNext();
1214     }
1215 }
1216 
HandleNetworkDataRestoredAfterReset(void)1217 void Leader::HandleNetworkDataRestoredAfterReset(void)
1218 {
1219     const PrefixTlv *prefix;
1220     TlvIterator      tlvIterator(GetTlvsStart(), GetTlvsEnd());
1221     Iterator         iterator = kIteratorInit;
1222     ChangedFlags     flags;
1223     uint16_t         rloc16;
1224     uint16_t         sessionId;
1225 
1226     mWaitingForNetDataSync = false;
1227 
1228     // Remove entries in Network Data from any un-allocated Router ID.
1229     // This acts as a safeguard against an edge case where the leader
1230     // is reset at an inopportune time, such as right after it removed
1231     // an allocated router ID and sent MLE advertisement but before it
1232     // got the chance to send the updated Network Data to other
1233     // routers.
1234 
1235     while (GetNextServer(iterator, rloc16) == kErrorNone)
1236     {
1237         if (!Get<RouterTable>().IsAllocated(Mle::RouterIdFromRloc16(rloc16)))
1238         {
1239             // After we `RemoveRloc()` the Network Data gets changed
1240             // and the `iterator` will not be valid anymore. So we set
1241             // it to `kIteratorInit` to restart the loop.
1242 
1243             RemoveRloc(rloc16, kMatchModeRouterId, flags);
1244             iterator = kIteratorInit;
1245         }
1246     }
1247 
1248     IncrementVersions(flags);
1249 
1250     // Synchronize internal 6LoWPAN Context ID Set with the
1251     // recently obtained Network Data.
1252 
1253     while ((prefix = tlvIterator.Iterate<PrefixTlv>()) != nullptr)
1254     {
1255         const ContextTlv *context = prefix->FindSubTlv<ContextTlv>();
1256 
1257         if (context == nullptr)
1258         {
1259             continue;
1260         }
1261 
1262         mContextIds.MarkAsInUse(context->GetContextId());
1263 
1264         if (!context->IsCompress())
1265         {
1266             mContextIds.ScheduleToRemove(context->GetContextId());
1267         }
1268     }
1269 
1270     // Update Commissioning Data. We adopt the same session ID
1271     // (if any) and resign active commissioner (if any) by
1272     // clearing the Commissioning Data.
1273 
1274     if (FindCommissioningSessionId(sessionId) == kErrorNone)
1275     {
1276         Get<MeshCoP::Leader>().SetSessionId(sessionId);
1277     }
1278 
1279     if (FindBorderAgentRloc(rloc16) == kErrorNone)
1280     {
1281         Get<MeshCoP::Leader>().SetEmptyCommissionerData();
1282     }
1283 }
1284 
UpdateCommissioningData(uint16_t aDataLength,CommissioningDataTlv * & aDataTlv)1285 Error Leader::UpdateCommissioningData(uint16_t aDataLength, CommissioningDataTlv *&aDataTlv)
1286 {
1287     // First determine whether or not we can add Commissioning Data
1288     // TLV with the given `aDataLength`, taking into account that we
1289     // would remove the current Commissioning Data TLV. Then remove
1290     // the current TLV and append a new TLV with proper size which is
1291     // returned in `aDataTlv`.
1292 
1293     Error                 error   = kErrorNone;
1294     CommissioningDataTlv *dataTlv = FindCommissioningData();
1295     uint16_t              insertLength;
1296 
1297     if (dataTlv != nullptr)
1298     {
1299         insertLength = (aDataLength <= dataTlv->GetLength()) ? 0 : aDataLength - dataTlv->GetLength();
1300     }
1301     else
1302     {
1303         insertLength = sizeof(CommissioningDataTlv) + aDataLength;
1304     }
1305 
1306     VerifyOrExit(CanInsert(insertLength), error = kErrorNoBufs);
1307 
1308     if (dataTlv != nullptr)
1309     {
1310         RemoveTlv(dataTlv);
1311     }
1312 
1313     aDataTlv = As<CommissioningDataTlv>(AppendTlv(sizeof(CommissioningDataTlv) + aDataLength));
1314 
1315     OT_ASSERT(aDataTlv != nullptr);
1316 
1317     aDataTlv->Init();
1318     aDataTlv->SetLength(static_cast<uint8_t>(aDataLength));
1319 
1320     // The caller would fill the `aDataTlv` value.
1321 
1322     mVersion++;
1323     SignalNetDataChanged();
1324 
1325 exit:
1326     return error;
1327 }
1328 
SetCommissioningData(const void * aData,uint8_t aDataLength)1329 Error Leader::SetCommissioningData(const void *aData, uint8_t aDataLength)
1330 {
1331     Error                 error = kErrorNone;
1332     CommissioningDataTlv *dataTlv;
1333 
1334     SuccessOrExit(error = UpdateCommissioningData(aDataLength, dataTlv));
1335     memcpy(dataTlv->GetValue(), aData, aDataLength);
1336 
1337 exit:
1338     return error;
1339 }
1340 
SetCommissioningData(const Message & aMessage)1341 Error Leader::SetCommissioningData(const Message &aMessage)
1342 {
1343     Error                 error      = kErrorNone;
1344     uint16_t              dataLength = aMessage.GetLength() - aMessage.GetOffset();
1345     CommissioningDataTlv *dataTlv;
1346 
1347     SuccessOrExit(error = UpdateCommissioningData(dataLength, dataTlv));
1348     aMessage.ReadBytes(aMessage.GetOffset(), dataTlv->GetValue(), dataLength);
1349 
1350 exit:
1351     return error;
1352 }
1353 
HandleTimer(void)1354 void Leader::HandleTimer(void)
1355 {
1356     if (mWaitingForNetDataSync)
1357     {
1358         LogInfo("Timed out waiting for netdata on restoring leader role after reset");
1359         IgnoreError(Get<Mle::MleRouter>().BecomeDetached());
1360     }
1361     else
1362     {
1363         mContextIds.HandleTimer();
1364     }
1365 }
1366 
1367 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
ContainsOmrPrefix(const Ip6::Prefix & aPrefix)1368 bool Leader::ContainsOmrPrefix(const Ip6::Prefix &aPrefix)
1369 {
1370     PrefixTlv *prefixTlv;
1371     bool       contains = false;
1372 
1373     VerifyOrExit(BorderRouter::RoutingManager::IsValidOmrPrefix(aPrefix));
1374 
1375     prefixTlv = FindPrefix(aPrefix);
1376     VerifyOrExit(prefixTlv != nullptr);
1377 
1378     for (int i = 0; i < 2; i++)
1379     {
1380         const BorderRouterTlv *borderRouter = prefixTlv->FindSubTlv<BorderRouterTlv>(/* aStable */ (i == 0));
1381 
1382         if (borderRouter == nullptr)
1383         {
1384             continue;
1385         }
1386 
1387         for (const BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry();
1388              entry                          = entry->GetNext())
1389         {
1390             OnMeshPrefixConfig config;
1391 
1392             config.SetFrom(*prefixTlv, *borderRouter, *entry);
1393 
1394             if (BorderRouter::RoutingManager::IsValidOmrPrefix(config))
1395             {
1396                 ExitNow(contains = true);
1397             }
1398         }
1399     }
1400 
1401 exit:
1402     return contains;
1403 }
1404 #endif
1405 
1406 //---------------------------------------------------------------------------------------------------------------------
1407 // Leader::ContextIds
1408 
ContextIds(Instance & aInstance)1409 Leader::ContextIds::ContextIds(Instance &aInstance)
1410     : InstanceLocator(aInstance)
1411     , mReuseDelay(kReuseDelay)
1412 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
1413     , mIsClone(false)
1414 #endif
1415 {
1416 }
1417 
Clear(void)1418 void Leader::ContextIds::Clear(void)
1419 {
1420     for (uint8_t id = kMinId; id <= kMaxId; id++)
1421     {
1422         MarkAsUnallocated(id);
1423     }
1424 }
1425 
GetUnallocatedId(uint8_t & aId)1426 Error Leader::ContextIds::GetUnallocatedId(uint8_t &aId)
1427 {
1428     Error error = kErrorNotFound;
1429 
1430 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
1431     if (mIsClone)
1432     {
1433         aId   = kMinId;
1434         error = kErrorNone;
1435         ExitNow();
1436     }
1437 #endif
1438 
1439     for (uint8_t id = kMinId; id <= kMaxId; id++)
1440     {
1441         if (IsUnallocated(id))
1442         {
1443             aId   = id;
1444             error = kErrorNone;
1445             ExitNow();
1446         }
1447     }
1448 
1449 exit:
1450     return error;
1451 }
1452 
ScheduleToRemove(uint8_t aId)1453 void Leader::ContextIds::ScheduleToRemove(uint8_t aId)
1454 {
1455 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
1456     VerifyOrExit(!mIsClone);
1457 #endif
1458 
1459     VerifyOrExit(IsInUse(aId));
1460 
1461     SetRemoveTime(aId, TimerMilli::GetNow() + Time::SecToMsec(mReuseDelay));
1462     Get<Leader>().mTimer.FireAtIfEarlier(GetRemoveTime(aId));
1463 
1464 exit:
1465     return;
1466 }
1467 
SetRemoveTime(uint8_t aId,TimeMilli aTime)1468 void Leader::ContextIds::SetRemoveTime(uint8_t aId, TimeMilli aTime)
1469 {
1470     uint32_t time = aTime.GetValue();
1471 
1472     while ((time == kUnallocated) || (time == kInUse))
1473     {
1474         time++;
1475     }
1476 
1477     mRemoveTimes[aId - kMinId].SetValue(time);
1478 }
1479 
HandleTimer(void)1480 void Leader::ContextIds::HandleTimer(void)
1481 {
1482     TimeMilli now      = TimerMilli::GetNow();
1483     TimeMilli nextTime = now.GetDistantFuture();
1484 
1485 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
1486     OT_ASSERT(!mIsClone);
1487 #endif
1488 
1489     for (uint8_t id = kMinId; id <= kMaxId; id++)
1490     {
1491         if (IsUnallocated(id) || IsInUse(id))
1492         {
1493             continue;
1494         }
1495 
1496         if (now >= GetRemoveTime(id))
1497         {
1498             MarkAsUnallocated(id);
1499             Get<Leader>().RemoveContext(id);
1500         }
1501         else
1502         {
1503             nextTime = Min(nextTime, GetRemoveTime(id));
1504         }
1505     }
1506 
1507     if (nextTime != now.GetDistantFuture())
1508     {
1509         Get<Leader>().mTimer.FireAt(nextTime);
1510     }
1511 }
1512 
1513 } // namespace NetworkData
1514 } // namespace ot
1515 
1516 #endif // OPENTHREAD_FTD
1517