• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         LogWarnOnError(error, "register network data");
684     }
685 }
686 
AddPrefix(const PrefixTlv & aPrefix,ChangedFlags & aChangedFlags)687 Error Leader::AddPrefix(const PrefixTlv &aPrefix, ChangedFlags &aChangedFlags)
688 {
689     Error      error     = kErrorNone;
690     PrefixTlv *dstPrefix = FindPrefix(aPrefix.GetPrefix(), aPrefix.GetPrefixLength());
691 
692     if (dstPrefix == nullptr)
693     {
694         dstPrefix = As<PrefixTlv>(AppendTlv(PrefixTlv::CalculateSize(aPrefix.GetPrefixLength())));
695         VerifyOrExit(dstPrefix != nullptr, error = kErrorNoBufs);
696 
697         dstPrefix->Init(aPrefix.GetDomainId(), aPrefix.GetPrefixLength(), aPrefix.GetPrefix());
698     }
699 
700     for (const NetworkDataTlv *subCur = aPrefix.GetSubTlvs(); subCur < aPrefix.GetNext(); subCur = subCur->GetNext())
701     {
702         switch (subCur->GetType())
703         {
704         case NetworkDataTlv::kTypeHasRoute:
705             SuccessOrExit(error = AddHasRoute(*As<HasRouteTlv>(subCur), *dstPrefix, aChangedFlags));
706             break;
707 
708         case NetworkDataTlv::kTypeBorderRouter:
709             SuccessOrExit(error = AddBorderRouter(*As<BorderRouterTlv>(subCur), *dstPrefix, aChangedFlags));
710             break;
711 
712         default:
713             break;
714         }
715     }
716 
717 exit:
718     if (dstPrefix != nullptr)
719     {
720         // `UpdatePrefix()` updates the TLV's stable flag based on
721         // its sub-TLVs, or removes the TLV if it contains no sub-TLV.
722         // This is called at `exit` to ensure that if appending
723         // sub-TLVs fail (e.g., out of space in network data), we
724         // remove an empty Prefix TLV.
725 
726         IgnoreReturnValue(UpdatePrefix(*dstPrefix));
727     }
728 
729     return error;
730 }
731 
AddService(const ServiceTlv & aService,ChangedFlags & aChangedFlags)732 Error Leader::AddService(const ServiceTlv &aService, ChangedFlags &aChangedFlags)
733 {
734     Error            error = kErrorNone;
735     ServiceTlv      *dstService;
736     ServiceData      serviceData;
737     const ServerTlv *server;
738 
739     aService.GetServiceData(serviceData);
740     dstService = FindService(aService.GetEnterpriseNumber(), serviceData, kServiceExactMatch);
741 
742     if (dstService == nullptr)
743     {
744         uint8_t serviceId;
745 
746         SuccessOrExit(error = AllocateServiceId(serviceId));
747 
748         dstService = As<ServiceTlv>(
749             AppendTlv(ServiceTlv::CalculateSize(aService.GetEnterpriseNumber(), serviceData.GetLength())));
750         VerifyOrExit(dstService != nullptr, error = kErrorNoBufs);
751 
752         dstService->Init(serviceId, aService.GetEnterpriseNumber(), serviceData);
753     }
754 
755     server = NetworkDataTlv::Find<ServerTlv>(aService.GetSubTlvs(), aService.GetNext());
756     OT_ASSERT(server != nullptr);
757 
758     SuccessOrExit(error = AddServer(*server, *dstService, aChangedFlags));
759 
760 exit:
761     if (dstService != nullptr)
762     {
763         // `UpdateService()` updates the TLV's stable flag based on
764         // its sub-TLVs, or removes the TLV if it contains no sub-TLV.
765         // This is called at `exit` to ensure that if appending
766         // sub-TLVs fail (e.g., out of space in network data), we
767         // remove an empty Service TLV.
768 
769         IgnoreReturnValue(UpdateService(*dstService));
770     }
771 
772     return error;
773 }
774 
AddHasRoute(const HasRouteTlv & aHasRoute,PrefixTlv & aDstPrefix,ChangedFlags & aChangedFlags)775 Error Leader::AddHasRoute(const HasRouteTlv &aHasRoute, PrefixTlv &aDstPrefix, ChangedFlags &aChangedFlags)
776 {
777     Error                error       = kErrorNone;
778     HasRouteTlv         *dstHasRoute = aDstPrefix.FindSubTlv<HasRouteTlv>(aHasRoute.IsStable());
779     const HasRouteEntry *entry       = aHasRoute.GetFirstEntry();
780 
781     if (dstHasRoute == nullptr)
782     {
783         // Ensure there is space for `HasRouteTlv` and a single entry.
784         VerifyOrExit(CanInsert(sizeof(HasRouteTlv) + sizeof(HasRouteEntry)), error = kErrorNoBufs);
785 
786         dstHasRoute = As<HasRouteTlv>(aDstPrefix.GetNext());
787         Insert(dstHasRoute, sizeof(HasRouteTlv));
788         aDstPrefix.IncreaseLength(sizeof(HasRouteTlv));
789         dstHasRoute->Init();
790 
791         if (aHasRoute.IsStable())
792         {
793             dstHasRoute->SetStable();
794         }
795     }
796 
797     VerifyOrExit(!ContainsMatchingEntry(dstHasRoute, *entry));
798 
799     VerifyOrExit(CanInsert(sizeof(HasRouteEntry)), error = kErrorNoBufs);
800 
801     Insert(dstHasRoute->GetNext(), sizeof(HasRouteEntry));
802     dstHasRoute->IncreaseLength(sizeof(HasRouteEntry));
803     aDstPrefix.IncreaseLength(sizeof(HasRouteEntry));
804 
805     *dstHasRoute->GetLastEntry() = *entry;
806     aChangedFlags.Update(*dstHasRoute);
807 
808 exit:
809     return error;
810 }
811 
AddBorderRouter(const BorderRouterTlv & aBorderRouter,PrefixTlv & aDstPrefix,ChangedFlags & aChangedFlags)812 Error Leader::AddBorderRouter(const BorderRouterTlv &aBorderRouter, PrefixTlv &aDstPrefix, ChangedFlags &aChangedFlags)
813 {
814     Error                    error           = kErrorNone;
815     BorderRouterTlv         *dstBorderRouter = aDstPrefix.FindSubTlv<BorderRouterTlv>(aBorderRouter.IsStable());
816     ContextTlv              *dstContext      = aDstPrefix.FindSubTlv<ContextTlv>();
817     uint8_t                  contextId       = 0;
818     const BorderRouterEntry *entry           = aBorderRouter.GetFirstEntry();
819 
820     if (dstContext == nullptr)
821     {
822         // Get a new Context ID first. This ensure that if we cannot
823         // get new Context ID, we fail and exit before potentially
824         // inserting a Border Router sub-TLV.
825         SuccessOrExit(error = mContextIds.GetUnallocatedId(contextId));
826     }
827 
828     if (dstBorderRouter == nullptr)
829     {
830         // Ensure there is space for `BorderRouterTlv` with a single entry
831         // and a `ContextTlv` (if not already present).
832         VerifyOrExit(CanInsert(sizeof(BorderRouterTlv) + sizeof(BorderRouterEntry) +
833                                ((dstContext == nullptr) ? sizeof(ContextTlv) : 0)),
834                      error = kErrorNoBufs);
835 
836         dstBorderRouter = As<BorderRouterTlv>(aDstPrefix.GetNext());
837         Insert(dstBorderRouter, sizeof(BorderRouterTlv));
838         aDstPrefix.IncreaseLength(sizeof(BorderRouterTlv));
839         dstBorderRouter->Init();
840 
841         if (aBorderRouter.IsStable())
842         {
843             dstBorderRouter->SetStable();
844         }
845     }
846 
847     if (dstContext == nullptr)
848     {
849         // Ensure there is space for a `ContextTlv` and a single entry.
850         VerifyOrExit(CanInsert(sizeof(BorderRouterEntry) + sizeof(ContextTlv)), error = kErrorNoBufs);
851 
852         dstContext = As<ContextTlv>(aDstPrefix.GetNext());
853         Insert(dstContext, sizeof(ContextTlv));
854         aDstPrefix.IncreaseLength(sizeof(ContextTlv));
855         dstContext->Init(static_cast<uint8_t>(contextId), aDstPrefix.GetPrefixLength());
856     }
857 
858     if (aBorderRouter.IsStable())
859     {
860         dstContext->SetStable();
861     }
862 
863     dstContext->SetCompress();
864     mContextIds.MarkAsInUse(dstContext->GetContextId());
865 
866     VerifyOrExit(!ContainsMatchingEntry(dstBorderRouter, *entry));
867 
868     VerifyOrExit(CanInsert(sizeof(BorderRouterEntry)), error = kErrorNoBufs);
869 
870     Insert(dstBorderRouter->GetNext(), sizeof(BorderRouterEntry));
871     dstBorderRouter->IncreaseLength(sizeof(BorderRouterEntry));
872     aDstPrefix.IncreaseLength(sizeof(BorderRouterEntry));
873     *dstBorderRouter->GetLastEntry() = *entry;
874     aChangedFlags.Update(*dstBorderRouter);
875 
876 exit:
877     return error;
878 }
879 
AddServer(const ServerTlv & aServer,ServiceTlv & aDstService,ChangedFlags & aChangedFlags)880 Error Leader::AddServer(const ServerTlv &aServer, ServiceTlv &aDstService, ChangedFlags &aChangedFlags)
881 {
882     Error      error = kErrorNone;
883     ServerTlv *dstServer;
884     ServerData serverData;
885     uint8_t    tlvSize = aServer.GetSize();
886 
887     VerifyOrExit(!ContainsMatchingServer(&aDstService, aServer));
888 
889     VerifyOrExit(CanInsert(tlvSize), error = kErrorNoBufs);
890 
891     aServer.GetServerData(serverData);
892 
893     dstServer = As<ServerTlv>(aDstService.GetNext());
894     Insert(dstServer, tlvSize);
895     dstServer->Init(aServer.GetServer16(), serverData);
896 
897     if (aServer.IsStable())
898     {
899         dstServer->SetStable();
900     }
901 
902     aDstService.IncreaseLength(tlvSize);
903     aChangedFlags.Update(*dstServer);
904 
905 exit:
906     return error;
907 }
908 
AllocateServiceId(uint8_t & aServiceId) const909 Error Leader::AllocateServiceId(uint8_t &aServiceId) const
910 {
911     Error   error = kErrorNotFound;
912     uint8_t serviceId;
913 
914 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
915     if (mIsClone)
916     {
917         aServiceId = kMinServiceId;
918         error      = kErrorNone;
919         ExitNow();
920     }
921 #endif
922 
923     for (serviceId = kMinServiceId; serviceId <= kMaxServiceId; serviceId++)
924     {
925         if (FindServiceById(serviceId) == nullptr)
926         {
927             aServiceId = serviceId;
928             error      = kErrorNone;
929             LogInfo("Allocated Service ID = %d", serviceId);
930             ExitNow();
931         }
932     }
933 
934 exit:
935     return error;
936 }
937 
FindServiceById(uint8_t aServiceId) const938 const ServiceTlv *Leader::FindServiceById(uint8_t aServiceId) const
939 {
940     const ServiceTlv *service;
941     TlvIterator       tlvIterator(GetTlvsStart(), GetTlvsEnd());
942 
943     while ((service = tlvIterator.Iterate<ServiceTlv>()) != nullptr)
944     {
945         if (service->GetServiceId() == aServiceId)
946         {
947             break;
948         }
949     }
950 
951     return service;
952 }
953 
RemoveRloc(uint16_t aRloc16,MatchMode aMatchMode,ChangedFlags & aChangedFlags)954 void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode, ChangedFlags &aChangedFlags)
955 {
956     NetworkData excludeNetworkData(GetInstance()); // Empty network data.
957 
958     RemoveRloc(aRloc16, aMatchMode, excludeNetworkData, aChangedFlags);
959 }
960 
RemoveRloc(uint16_t aRloc16,MatchMode aMatchMode,const NetworkData & aExcludeNetworkData,ChangedFlags & aChangedFlags)961 void Leader::RemoveRloc(uint16_t           aRloc16,
962                         MatchMode          aMatchMode,
963                         const NetworkData &aExcludeNetworkData,
964                         ChangedFlags      &aChangedFlags)
965 {
966     // Remove entries from Network Data matching `aRloc16` (using
967     // `aMatchMode` to determine the match) but exclude any entries
968     // that are present in `aExcludeNetworkData`. As entries are
969     // removed update `aChangedFlags` to indicate if Network Data
970     // (stable or not) got changed.
971 
972     NetworkDataTlv *cur = GetTlvsStart();
973 
974     while (cur < GetTlvsEnd())
975     {
976         switch (cur->GetType())
977         {
978         case NetworkDataTlv::kTypePrefix:
979         {
980             PrefixTlv       *prefix = As<PrefixTlv>(cur);
981             const PrefixTlv *excludePrefix =
982                 aExcludeNetworkData.FindPrefix(prefix->GetPrefix(), prefix->GetPrefixLength());
983 
984             RemoveRlocInPrefix(*prefix, aRloc16, aMatchMode, excludePrefix, aChangedFlags);
985 
986             if (UpdatePrefix(*prefix) == kTlvRemoved)
987             {
988                 // Do not update `cur` when TLV is removed.
989                 continue;
990             }
991 
992             break;
993         }
994 
995         case NetworkDataTlv::kTypeService:
996         {
997             ServiceTlv       *service = As<ServiceTlv>(cur);
998             ServiceData       serviceData;
999             const ServiceTlv *excludeService;
1000 
1001             service->GetServiceData(serviceData);
1002 
1003             excludeService =
1004                 aExcludeNetworkData.FindService(service->GetEnterpriseNumber(), serviceData, kServiceExactMatch);
1005 
1006             RemoveRlocInService(*service, aRloc16, aMatchMode, excludeService, aChangedFlags);
1007 
1008             if (UpdateService(*service) == kTlvRemoved)
1009             {
1010                 // Do not update `cur` when TLV is removed.
1011                 continue;
1012             }
1013 
1014             break;
1015         }
1016 
1017         default:
1018             break;
1019         }
1020 
1021         cur = cur->GetNext();
1022     }
1023 }
1024 
RemoveRlocInPrefix(PrefixTlv & aPrefix,uint16_t aRloc16,MatchMode aMatchMode,const PrefixTlv * aExcludePrefix,ChangedFlags & aChangedFlags)1025 void Leader::RemoveRlocInPrefix(PrefixTlv       &aPrefix,
1026                                 uint16_t         aRloc16,
1027                                 MatchMode        aMatchMode,
1028                                 const PrefixTlv *aExcludePrefix,
1029                                 ChangedFlags    &aChangedFlags)
1030 {
1031     // Remove entries in `aPrefix` TLV matching the given `aRloc16`
1032     // excluding any entries that are present in `aExcludePrefix`.
1033 
1034     NetworkDataTlv *cur = aPrefix.GetSubTlvs();
1035     ContextTlv     *context;
1036 
1037     while (cur < aPrefix.GetNext())
1038     {
1039         switch (cur->GetType())
1040         {
1041         case NetworkDataTlv::kTypeHasRoute:
1042             RemoveRlocInHasRoute(aPrefix, *As<HasRouteTlv>(cur), aRloc16, aMatchMode, aExcludePrefix, aChangedFlags);
1043 
1044             if (cur->GetLength() == 0)
1045             {
1046                 aPrefix.DecreaseLength(sizeof(HasRouteTlv));
1047                 RemoveTlv(cur);
1048                 continue;
1049             }
1050 
1051             break;
1052 
1053         case NetworkDataTlv::kTypeBorderRouter:
1054             RemoveRlocInBorderRouter(aPrefix, *As<BorderRouterTlv>(cur), aRloc16, aMatchMode, aExcludePrefix,
1055                                      aChangedFlags);
1056 
1057             if (cur->GetLength() == 0)
1058             {
1059                 aPrefix.DecreaseLength(sizeof(BorderRouterTlv));
1060                 RemoveTlv(cur);
1061                 continue;
1062             }
1063 
1064             break;
1065 
1066         default:
1067             break;
1068         }
1069 
1070         cur = cur->GetNext();
1071     }
1072 
1073     if ((context = aPrefix.FindSubTlv<ContextTlv>()) != nullptr)
1074     {
1075         if (aPrefix.FindSubTlv<BorderRouterTlv>() == nullptr)
1076         {
1077             context->ClearCompress();
1078             mContextIds.ScheduleToRemove(context->GetContextId());
1079         }
1080         else
1081         {
1082             context->SetCompress();
1083             mContextIds.MarkAsInUse(context->GetContextId());
1084         }
1085     }
1086 }
1087 
RemoveRlocInService(ServiceTlv & aService,uint16_t aRloc16,MatchMode aMatchMode,const ServiceTlv * aExcludeService,ChangedFlags & aChangedFlags)1088 void Leader::RemoveRlocInService(ServiceTlv       &aService,
1089                                  uint16_t          aRloc16,
1090                                  MatchMode         aMatchMode,
1091                                  const ServiceTlv *aExcludeService,
1092                                  ChangedFlags     &aChangedFlags)
1093 {
1094     // Remove entries in `aService` TLV matching the given `aRloc16`
1095     // excluding any entries that are present in `aExcludeService`.
1096 
1097     NetworkDataTlv *start = aService.GetSubTlvs();
1098     ServerTlv      *server;
1099 
1100     while ((server = NetworkDataTlv::Find<ServerTlv>(start, aService.GetNext())) != nullptr)
1101     {
1102         if (RlocMatch(server->GetServer16(), aRloc16, aMatchMode) && !ContainsMatchingServer(aExcludeService, *server))
1103         {
1104             uint8_t subTlvSize = server->GetSize();
1105 
1106             aChangedFlags.Update(*server);
1107             RemoveTlv(server);
1108             aService.DecreaseLength(subTlvSize);
1109             continue;
1110         }
1111 
1112         start = server->GetNext();
1113     }
1114 }
1115 
RemoveRlocInHasRoute(PrefixTlv & aPrefix,HasRouteTlv & aHasRoute,uint16_t aRloc16,MatchMode aMatchMode,const PrefixTlv * aExcludePrefix,ChangedFlags & aChangedFlags)1116 void Leader::RemoveRlocInHasRoute(PrefixTlv       &aPrefix,
1117                                   HasRouteTlv     &aHasRoute,
1118                                   uint16_t         aRloc16,
1119                                   MatchMode        aMatchMode,
1120                                   const PrefixTlv *aExcludePrefix,
1121                                   ChangedFlags    &aChangedFlags)
1122 {
1123     // Remove entries in `aHasRoute` (a sub-TLV of `aPrefix` TLV)
1124     // matching the given `aRloc16` excluding entries that are present
1125     // in `aExcludePrefix`.
1126 
1127     HasRouteEntry *entry = aHasRoute.GetFirstEntry();
1128 
1129     while (entry <= aHasRoute.GetLastEntry())
1130     {
1131         if (RlocMatch(entry->GetRloc(), aRloc16, aMatchMode) &&
1132             !ContainsMatchingEntry(aExcludePrefix, aHasRoute.IsStable(), *entry))
1133         {
1134             aChangedFlags.Update(aHasRoute);
1135             aHasRoute.DecreaseLength(sizeof(HasRouteEntry));
1136             aPrefix.DecreaseLength(sizeof(HasRouteEntry));
1137             Remove(entry, sizeof(HasRouteEntry));
1138             continue;
1139         }
1140 
1141         entry = entry->GetNext();
1142     }
1143 }
1144 
RemoveRlocInBorderRouter(PrefixTlv & aPrefix,BorderRouterTlv & aBorderRouter,uint16_t aRloc16,MatchMode aMatchMode,const PrefixTlv * aExcludePrefix,ChangedFlags & aChangedFlags)1145 void Leader::RemoveRlocInBorderRouter(PrefixTlv       &aPrefix,
1146                                       BorderRouterTlv &aBorderRouter,
1147                                       uint16_t         aRloc16,
1148                                       MatchMode        aMatchMode,
1149                                       const PrefixTlv *aExcludePrefix,
1150                                       ChangedFlags    &aChangedFlags)
1151 {
1152     // Remove entries in `aBorderRouter` (a sub-TLV of `aPrefix` TLV)
1153     // matching the given `aRloc16` excluding entries that are present
1154     // in `aExcludePrefix`.
1155 
1156     BorderRouterEntry *entry = aBorderRouter.GetFirstEntry();
1157 
1158     while (entry <= aBorderRouter.GetLastEntry())
1159     {
1160         if (RlocMatch(entry->GetRloc(), aRloc16, aMatchMode) &&
1161             !ContainsMatchingEntry(aExcludePrefix, aBorderRouter.IsStable(), *entry))
1162         {
1163             aChangedFlags.Update(aBorderRouter);
1164             aBorderRouter.DecreaseLength(sizeof(BorderRouterEntry));
1165             aPrefix.DecreaseLength(sizeof(BorderRouterEntry));
1166             Remove(entry, sizeof(*entry));
1167             continue;
1168         }
1169 
1170         entry = entry->GetNext();
1171     }
1172 }
1173 
RemoveContext(uint8_t aContextId)1174 void Leader::RemoveContext(uint8_t aContextId)
1175 {
1176     NetworkDataTlv *start = GetTlvsStart();
1177     PrefixTlv      *prefix;
1178 
1179     while ((prefix = NetworkDataTlv::Find<PrefixTlv>(start, GetTlvsEnd())) != nullptr)
1180     {
1181         RemoveContext(*prefix, aContextId);
1182 
1183         if (UpdatePrefix(*prefix) == kTlvRemoved)
1184         {
1185             // Do not update `start` when TLV is removed.
1186             continue;
1187         }
1188 
1189         start = prefix->GetNext();
1190     }
1191 
1192     IncrementVersions(/* aIncludeStable */ true);
1193 }
1194 
RemoveContext(PrefixTlv & aPrefix,uint8_t aContextId)1195 void Leader::RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId)
1196 {
1197     NetworkDataTlv *start = aPrefix.GetSubTlvs();
1198     ContextTlv     *context;
1199 
1200     while ((context = NetworkDataTlv::Find<ContextTlv>(start, aPrefix.GetNext())) != nullptr)
1201     {
1202         if (context->GetContextId() == aContextId)
1203         {
1204             uint8_t subTlvSize = context->GetSize();
1205             RemoveTlv(context);
1206             aPrefix.DecreaseLength(subTlvSize);
1207             continue;
1208         }
1209 
1210         start = context->GetNext();
1211     }
1212 }
1213 
HandleNetworkDataRestoredAfterReset(void)1214 void Leader::HandleNetworkDataRestoredAfterReset(void)
1215 {
1216     const PrefixTlv *prefix;
1217     TlvIterator      tlvIterator(GetTlvsStart(), GetTlvsEnd());
1218     ChangedFlags     flags;
1219     uint16_t         rloc16;
1220     uint16_t         sessionId;
1221     Rlocs            rlocs;
1222 
1223     mWaitingForNetDataSync = false;
1224 
1225     // Remove entries in Network Data from any un-allocated Router ID.
1226     // This acts as a safeguard against an edge case where the leader
1227     // is reset at an inopportune time, such as right after it removed
1228     // an allocated router ID and sent MLE advertisement but before it
1229     // got the chance to send the updated Network Data to other
1230     // routers.
1231 
1232     FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
1233 
1234     for (uint16_t rloc : rlocs)
1235     {
1236         if (!Get<RouterTable>().IsAllocated(Mle::RouterIdFromRloc16(rloc)))
1237         {
1238             RemoveRloc(rloc, kMatchModeRouterId, flags);
1239         }
1240     }
1241 
1242     IncrementVersions(flags);
1243 
1244     // Synchronize internal 6LoWPAN Context ID Set with the
1245     // recently obtained Network Data.
1246 
1247     while ((prefix = tlvIterator.Iterate<PrefixTlv>()) != nullptr)
1248     {
1249         const ContextTlv *context = prefix->FindSubTlv<ContextTlv>();
1250 
1251         if (context == nullptr)
1252         {
1253             continue;
1254         }
1255 
1256         mContextIds.MarkAsInUse(context->GetContextId());
1257 
1258         if (!context->IsCompress())
1259         {
1260             mContextIds.ScheduleToRemove(context->GetContextId());
1261         }
1262     }
1263 
1264     // Update Commissioning Data. We adopt the same session ID
1265     // (if any) and resign active commissioner (if any) by
1266     // clearing the Commissioning Data.
1267 
1268     if (FindCommissioningSessionId(sessionId) == kErrorNone)
1269     {
1270         Get<MeshCoP::Leader>().SetSessionId(sessionId);
1271     }
1272 
1273     if (FindBorderAgentRloc(rloc16) == kErrorNone)
1274     {
1275         Get<MeshCoP::Leader>().SetEmptyCommissionerData();
1276     }
1277 }
1278 
UpdateCommissioningData(uint16_t aDataLength,CommissioningDataTlv * & aDataTlv)1279 Error Leader::UpdateCommissioningData(uint16_t aDataLength, CommissioningDataTlv *&aDataTlv)
1280 {
1281     // First determine whether or not we can add Commissioning Data
1282     // TLV with the given `aDataLength`, taking into account that we
1283     // would remove the current Commissioning Data TLV. Then remove
1284     // the current TLV and append a new TLV with proper size which is
1285     // returned in `aDataTlv`.
1286 
1287     Error                 error   = kErrorNone;
1288     CommissioningDataTlv *dataTlv = FindCommissioningData();
1289     uint16_t              insertLength;
1290 
1291     if (dataTlv != nullptr)
1292     {
1293         insertLength = (aDataLength <= dataTlv->GetLength()) ? 0 : aDataLength - dataTlv->GetLength();
1294     }
1295     else
1296     {
1297         insertLength = sizeof(CommissioningDataTlv) + aDataLength;
1298     }
1299 
1300     VerifyOrExit(CanInsert(insertLength), error = kErrorNoBufs);
1301 
1302     if (dataTlv != nullptr)
1303     {
1304         RemoveTlv(dataTlv);
1305     }
1306 
1307     aDataTlv = As<CommissioningDataTlv>(AppendTlv(sizeof(CommissioningDataTlv) + aDataLength));
1308 
1309     OT_ASSERT(aDataTlv != nullptr);
1310 
1311     aDataTlv->Init();
1312     aDataTlv->SetLength(static_cast<uint8_t>(aDataLength));
1313 
1314     // The caller would fill the `aDataTlv` value.
1315 
1316     mVersion++;
1317     SignalNetDataChanged();
1318 
1319 exit:
1320     return error;
1321 }
1322 
SetCommissioningData(const void * aData,uint8_t aDataLength)1323 Error Leader::SetCommissioningData(const void *aData, uint8_t aDataLength)
1324 {
1325     Error                 error = kErrorNone;
1326     CommissioningDataTlv *dataTlv;
1327 
1328     SuccessOrExit(error = UpdateCommissioningData(aDataLength, dataTlv));
1329     memcpy(dataTlv->GetValue(), aData, aDataLength);
1330 
1331 exit:
1332     return error;
1333 }
1334 
SetCommissioningData(const Message & aMessage)1335 Error Leader::SetCommissioningData(const Message &aMessage)
1336 {
1337     Error                 error      = kErrorNone;
1338     uint16_t              dataLength = aMessage.GetLength() - aMessage.GetOffset();
1339     CommissioningDataTlv *dataTlv;
1340 
1341     SuccessOrExit(error = UpdateCommissioningData(dataLength, dataTlv));
1342     aMessage.ReadBytes(aMessage.GetOffset(), dataTlv->GetValue(), dataLength);
1343 
1344 exit:
1345     return error;
1346 }
1347 
HandleTimer(void)1348 void Leader::HandleTimer(void)
1349 {
1350     if (mWaitingForNetDataSync)
1351     {
1352         LogInfo("Timed out waiting for netdata on restoring leader role after reset");
1353         IgnoreError(Get<Mle::MleRouter>().BecomeDetached());
1354     }
1355     else
1356     {
1357         mContextIds.HandleTimer();
1358     }
1359 }
1360 
1361 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
ContainsOmrPrefix(const Ip6::Prefix & aPrefix)1362 bool Leader::ContainsOmrPrefix(const Ip6::Prefix &aPrefix)
1363 {
1364     PrefixTlv *prefixTlv;
1365     bool       contains = false;
1366 
1367     VerifyOrExit(BorderRouter::RoutingManager::IsValidOmrPrefix(aPrefix));
1368 
1369     prefixTlv = FindPrefix(aPrefix);
1370     VerifyOrExit(prefixTlv != nullptr);
1371 
1372     for (int i = 0; i < 2; i++)
1373     {
1374         const BorderRouterTlv *borderRouter = prefixTlv->FindSubTlv<BorderRouterTlv>(/* aStable */ (i == 0));
1375 
1376         if (borderRouter == nullptr)
1377         {
1378             continue;
1379         }
1380 
1381         for (const BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry();
1382              entry                          = entry->GetNext())
1383         {
1384             OnMeshPrefixConfig config;
1385 
1386             config.SetFrom(*prefixTlv, *borderRouter, *entry);
1387 
1388             if (BorderRouter::RoutingManager::IsValidOmrPrefix(config))
1389             {
1390                 ExitNow(contains = true);
1391             }
1392         }
1393     }
1394 
1395 exit:
1396     return contains;
1397 }
1398 #endif
1399 
1400 //---------------------------------------------------------------------------------------------------------------------
1401 // Leader::ContextIds
1402 
ContextIds(Instance & aInstance)1403 Leader::ContextIds::ContextIds(Instance &aInstance)
1404     : InstanceLocator(aInstance)
1405     , mReuseDelay(kReuseDelay)
1406 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
1407     , mIsClone(false)
1408 #endif
1409 {
1410 }
1411 
Clear(void)1412 void Leader::ContextIds::Clear(void)
1413 {
1414     for (uint8_t id = kMinId; id <= kMaxId; id++)
1415     {
1416         MarkAsUnallocated(id);
1417     }
1418 }
1419 
GetUnallocatedId(uint8_t & aId)1420 Error Leader::ContextIds::GetUnallocatedId(uint8_t &aId)
1421 {
1422     Error error = kErrorNotFound;
1423 
1424 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
1425     if (mIsClone)
1426     {
1427         aId   = kMinId;
1428         error = kErrorNone;
1429         ExitNow();
1430     }
1431 #endif
1432 
1433     for (uint8_t id = kMinId; id <= kMaxId; id++)
1434     {
1435         if (IsUnallocated(id))
1436         {
1437             aId   = id;
1438             error = kErrorNone;
1439             ExitNow();
1440         }
1441     }
1442 
1443 exit:
1444     return error;
1445 }
1446 
ScheduleToRemove(uint8_t aId)1447 void Leader::ContextIds::ScheduleToRemove(uint8_t aId)
1448 {
1449 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
1450     VerifyOrExit(!mIsClone);
1451 #endif
1452 
1453     VerifyOrExit(IsInUse(aId));
1454 
1455     SetRemoveTime(aId, TimerMilli::GetNow() + Time::SecToMsec(mReuseDelay));
1456     Get<Leader>().mTimer.FireAtIfEarlier(GetRemoveTime(aId));
1457 
1458 exit:
1459     return;
1460 }
1461 
SetRemoveTime(uint8_t aId,TimeMilli aTime)1462 void Leader::ContextIds::SetRemoveTime(uint8_t aId, TimeMilli aTime)
1463 {
1464     uint32_t time = aTime.GetValue();
1465 
1466     while ((time == kUnallocated) || (time == kInUse))
1467     {
1468         time++;
1469     }
1470 
1471     mRemoveTimes[aId - kMinId].SetValue(time);
1472 }
1473 
HandleTimer(void)1474 void Leader::ContextIds::HandleTimer(void)
1475 {
1476     TimeMilli now      = TimerMilli::GetNow();
1477     TimeMilli nextTime = now.GetDistantFuture();
1478 
1479 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
1480     OT_ASSERT(!mIsClone);
1481 #endif
1482 
1483     for (uint8_t id = kMinId; id <= kMaxId; id++)
1484     {
1485         if (IsUnallocated(id) || IsInUse(id))
1486         {
1487             continue;
1488         }
1489 
1490         if (now >= GetRemoveTime(id))
1491         {
1492             MarkAsUnallocated(id);
1493             Get<Leader>().RemoveContext(id);
1494         }
1495         else
1496         {
1497             nextTime = Min(nextTime, GetRemoveTime(id));
1498         }
1499     }
1500 
1501     if (nextTime != now.GetDistantFuture())
1502     {
1503         Get<Leader>().mTimer.FireAt(nextTime);
1504     }
1505 }
1506 
1507 } // namespace NetworkData
1508 } // namespace ot
1509 
1510 #endif // OPENTHREAD_FTD
1511