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