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 #include "coap/coap_message.hpp"
37 #include "common/code_utils.hpp"
38 #include "common/debug.hpp"
39 #include "common/encoding.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/logging.hpp"
42 #include "common/message.hpp"
43 #include "common/random.hpp"
44 #include "common/timer.hpp"
45 #include "instance/instance.hpp"
46 #include "mac/mac_types.hpp"
47 #include "thread/lowpan.hpp"
48 #include "thread/mle_router.hpp"
49 #include "thread/thread_netif.hpp"
50 #include "thread/thread_tlvs.hpp"
51 #include "thread/uri_paths.hpp"
52 
53 namespace ot {
54 namespace NetworkData {
55 
56 RegisterLogModule("NetworkData");
57 
Leader(Instance & aInstance)58 Leader::Leader(Instance &aInstance)
59     : MutableNetworkData(aInstance, mTlvBuffer, 0, sizeof(mTlvBuffer))
60     , mMaxLength(0)
61 #if OPENTHREAD_FTD
62 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
63     , mIsClone(false)
64 #endif
65     , mWaitingForNetDataSync(false)
66     , mContextIds(aInstance)
67     , mTimer(aInstance)
68 #endif
69 {
70     Reset();
71 }
72 
Reset(void)73 void Leader::Reset(void)
74 {
75     mVersion       = Random::NonCrypto::GetUint8();
76     mStableVersion = Random::NonCrypto::GetUint8();
77     SetLength(0);
78     SignalNetDataChanged();
79 
80 #if OPENTHREAD_FTD
81     mContextIds.Clear();
82 #endif
83 }
84 
GetServiceId(uint32_t aEnterpriseNumber,const ServiceData & aServiceData,bool aServerStable,uint8_t & aServiceId) const85 Error Leader::GetServiceId(uint32_t           aEnterpriseNumber,
86                            const ServiceData &aServiceData,
87                            bool               aServerStable,
88                            uint8_t           &aServiceId) const
89 {
90     Error         error    = kErrorNotFound;
91     Iterator      iterator = kIteratorInit;
92     ServiceConfig serviceConfig;
93     ServiceData   serviceData;
94 
95     while (GetNextService(iterator, serviceConfig) == kErrorNone)
96     {
97         serviceConfig.GetServiceData(serviceData);
98 
99         if (aEnterpriseNumber == serviceConfig.mEnterpriseNumber && aServiceData == serviceData &&
100             aServerStable == serviceConfig.mServerConfig.mStable)
101         {
102             aServiceId = serviceConfig.mServiceId;
103             ExitNow(error = kErrorNone);
104         }
105     }
106 
107 exit:
108     return error;
109 }
110 
GetPreferredNat64Prefix(ExternalRouteConfig & aConfig) const111 Error Leader::GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const
112 {
113     Error               error    = kErrorNotFound;
114     Iterator            iterator = kIteratorInit;
115     ExternalRouteConfig config;
116 
117     while (GetNextExternalRoute(iterator, config) == kErrorNone)
118     {
119         if (!config.mNat64 || !config.GetPrefix().IsValidNat64())
120         {
121             continue;
122         }
123 
124         if ((error == kErrorNotFound) || (config.mPreference > aConfig.mPreference) ||
125             (config.mPreference == aConfig.mPreference && config.GetPrefix() < aConfig.GetPrefix()))
126         {
127             aConfig = config;
128             error   = kErrorNone;
129         }
130     }
131 
132     return error;
133 }
134 
FindNextMatchingPrefixTlv(const Ip6::Address & aAddress,const PrefixTlv * aPrevTlv) const135 const PrefixTlv *Leader::FindNextMatchingPrefixTlv(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const
136 {
137     // This method iterates over Prefix TLVs which match a given IPv6
138     // `aAddress`. If `aPrevTlv` is `nullptr` we start from the
139     // beginning. Otherwise, we search for a match after `aPrevTlv`.
140     // This method returns a pointer to the next matching Prefix TLV
141     // when found, or `nullptr` if no match is found.
142 
143     const PrefixTlv *prefixTlv;
144     TlvIterator      tlvIterator((aPrevTlv == nullptr) ? GetTlvsStart() : aPrevTlv->GetNext(), GetTlvsEnd());
145 
146     while ((prefixTlv = tlvIterator.Iterate<PrefixTlv>()) != nullptr)
147     {
148         if (aAddress.MatchesPrefix(prefixTlv->GetPrefix(), prefixTlv->GetPrefixLength()))
149         {
150             break;
151         }
152     }
153 
154     return prefixTlv;
155 }
156 
GetContext(const Ip6::Address & aAddress,Lowpan::Context & aContext) const157 Error Leader::GetContext(const Ip6::Address &aAddress, Lowpan::Context &aContext) const
158 {
159     const PrefixTlv  *prefixTlv = nullptr;
160     const ContextTlv *contextTlv;
161 
162     aContext.mPrefix.SetLength(0);
163 
164     if (Get<Mle::MleRouter>().IsMeshLocalAddress(aAddress))
165     {
166         GetContextForMeshLocalPrefix(aContext);
167     }
168 
169     while ((prefixTlv = FindNextMatchingPrefixTlv(aAddress, prefixTlv)) != nullptr)
170     {
171         contextTlv = prefixTlv->FindSubTlv<ContextTlv>();
172 
173         if (contextTlv == nullptr)
174         {
175             continue;
176         }
177 
178         if (prefixTlv->GetPrefixLength() > aContext.mPrefix.GetLength())
179         {
180             prefixTlv->CopyPrefixTo(aContext.mPrefix);
181             aContext.mContextId    = contextTlv->GetContextId();
182             aContext.mCompressFlag = contextTlv->IsCompress();
183             aContext.mIsValid      = true;
184         }
185     }
186 
187     return (aContext.mPrefix.GetLength() > 0) ? kErrorNone : kErrorNotFound;
188 }
189 
GetContext(uint8_t aContextId,Lowpan::Context & aContext) const190 Error Leader::GetContext(uint8_t aContextId, Lowpan::Context &aContext) const
191 {
192     Error            error = kErrorNotFound;
193     TlvIterator      tlvIterator(GetTlvsStart(), GetTlvsEnd());
194     const PrefixTlv *prefixTlv;
195 
196     if (aContextId == Mle::kMeshLocalPrefixContextId)
197     {
198         GetContextForMeshLocalPrefix(aContext);
199         ExitNow(error = kErrorNone);
200     }
201 
202     while ((prefixTlv = tlvIterator.Iterate<PrefixTlv>()) != nullptr)
203     {
204         const ContextTlv *contextTlv = prefixTlv->FindSubTlv<ContextTlv>();
205 
206         if ((contextTlv == nullptr) || (contextTlv->GetContextId() != aContextId))
207         {
208             continue;
209         }
210 
211         prefixTlv->CopyPrefixTo(aContext.mPrefix);
212         aContext.mContextId    = contextTlv->GetContextId();
213         aContext.mCompressFlag = contextTlv->IsCompress();
214         aContext.mIsValid      = true;
215         ExitNow(error = kErrorNone);
216     }
217 
218 exit:
219     return error;
220 }
221 
GetContextForMeshLocalPrefix(Lowpan::Context & aContext) const222 void Leader::GetContextForMeshLocalPrefix(Lowpan::Context &aContext) const
223 {
224     aContext.mPrefix.Set(Get<Mle::MleRouter>().GetMeshLocalPrefix());
225     aContext.mContextId    = Mle::kMeshLocalPrefixContextId;
226     aContext.mCompressFlag = true;
227     aContext.mIsValid      = true;
228 }
229 
IsOnMesh(const Ip6::Address & aAddress) const230 bool Leader::IsOnMesh(const Ip6::Address &aAddress) const
231 {
232     const PrefixTlv *prefixTlv = nullptr;
233     bool             isOnMesh  = false;
234 
235     VerifyOrExit(!Get<Mle::MleRouter>().IsMeshLocalAddress(aAddress), isOnMesh = true);
236 
237     while ((prefixTlv = FindNextMatchingPrefixTlv(aAddress, prefixTlv)) != nullptr)
238     {
239         TlvIterator            subTlvIterator(*prefixTlv);
240         const BorderRouterTlv *brTlv;
241 
242         while ((brTlv = subTlvIterator.Iterate<BorderRouterTlv>()) != nullptr)
243         {
244             for (const BorderRouterEntry *entry = brTlv->GetFirstEntry(); entry <= brTlv->GetLastEntry();
245                  entry                          = entry->GetNext())
246             {
247                 if (entry->IsOnMesh())
248                 {
249                     ExitNow(isOnMesh = true);
250                 }
251             }
252         }
253     }
254 
255 exit:
256     return isOnMesh;
257 }
258 
RouteLookup(const Ip6::Address & aSource,const Ip6::Address & aDestination,uint16_t & aRloc16) const259 Error Leader::RouteLookup(const Ip6::Address &aSource, const Ip6::Address &aDestination, uint16_t &aRloc16) const
260 {
261     Error            error     = kErrorNoRoute;
262     const PrefixTlv *prefixTlv = nullptr;
263 
264     while ((prefixTlv = FindNextMatchingPrefixTlv(aSource, prefixTlv)) != nullptr)
265     {
266         if (prefixTlv->FindSubTlv<BorderRouterTlv>() == nullptr)
267         {
268             continue;
269         }
270 
271         if (ExternalRouteLookup(prefixTlv->GetDomainId(), aDestination, aRloc16) == kErrorNone)
272         {
273             ExitNow(error = kErrorNone);
274         }
275 
276         if (DefaultRouteLookup(*prefixTlv, aRloc16) == kErrorNone)
277         {
278             ExitNow(error = kErrorNone);
279         }
280     }
281 
282 exit:
283     return error;
284 }
285 
CompareRouteEntries(const EntryType & aFirst,const EntryType & aSecond) const286 template <typename EntryType> int Leader::CompareRouteEntries(const EntryType &aFirst, const EntryType &aSecond) const
287 {
288     // `EntryType` can be `HasRouteEntry` or `BorderRouterEntry`.
289 
290     return CompareRouteEntries(aFirst.GetPreference(), aFirst.GetRloc(), aSecond.GetPreference(), aSecond.GetRloc());
291 }
292 
CompareRouteEntries(int8_t aFirstPreference,uint16_t aFirstRloc,int8_t aSecondPreference,uint16_t aSecondRloc) const293 int Leader::CompareRouteEntries(int8_t   aFirstPreference,
294                                 uint16_t aFirstRloc,
295                                 int8_t   aSecondPreference,
296                                 uint16_t aSecondRloc) const
297 {
298     // Performs three-way comparison between two BR entries.
299 
300     int result;
301 
302     // Prefer the entry with higher preference.
303 
304     result = ThreeWayCompare(aFirstPreference, aSecondPreference);
305     VerifyOrExit(result == 0);
306 
307 #if OPENTHREAD_MTD
308     // On MTD, prefer the BR that is this device itself. This handles
309     // the uncommon case where an MTD itself may be acting as BR.
310 
311     result = ThreeWayCompare((aFirstRloc == Get<Mle::Mle>().GetRloc16()), (aSecondRloc == Get<Mle::Mle>().GetRloc16()));
312 #endif
313 
314 #if OPENTHREAD_FTD
315     // If all the same, prefer the one with lower mesh path cost.
316     // Lower cost is preferred so we pass the second entry's cost as
317     // the first argument in the call to `ThreeWayCompare()`, i.e.,
318     // if the second entry's cost is larger, we return 1 indicating
319     // that the first entry is preferred over the second one.
320 
321     result = ThreeWayCompare(Get<RouterTable>().GetPathCost(aSecondRloc), Get<RouterTable>().GetPathCost(aFirstRloc));
322     VerifyOrExit(result == 0);
323 
324     // If all the same, prefer the BR acting as a router over an
325     // end device.
326     result = ThreeWayCompare(Mle::IsActiveRouter(aFirstRloc), Mle::IsActiveRouter(aSecondRloc));
327 #endif
328 
329 exit:
330     return result;
331 }
332 
ExternalRouteLookup(uint8_t aDomainId,const Ip6::Address & aDestination,uint16_t & aRloc16) const333 Error Leader::ExternalRouteLookup(uint8_t aDomainId, const Ip6::Address &aDestination, uint16_t &aRloc16) const
334 {
335     Error                error           = kErrorNoRoute;
336     const PrefixTlv     *prefixTlv       = nullptr;
337     const HasRouteEntry *bestRouteEntry  = nullptr;
338     uint8_t              bestMatchLength = 0;
339 
340     while ((prefixTlv = FindNextMatchingPrefixTlv(aDestination, prefixTlv)) != nullptr)
341     {
342         const HasRouteTlv *hasRoute;
343         uint8_t            prefixLength = prefixTlv->GetPrefixLength();
344         TlvIterator        subTlvIterator(*prefixTlv);
345 
346         if (prefixTlv->GetDomainId() != aDomainId)
347         {
348             continue;
349         }
350 
351         if ((bestRouteEntry != nullptr) && (prefixLength <= bestMatchLength))
352         {
353             continue;
354         }
355 
356         while ((hasRoute = subTlvIterator.Iterate<HasRouteTlv>()) != nullptr)
357         {
358             for (const HasRouteEntry *entry = hasRoute->GetFirstEntry(); entry <= hasRoute->GetLastEntry();
359                  entry                      = entry->GetNext())
360             {
361                 if ((bestRouteEntry == nullptr) || (prefixLength > bestMatchLength) ||
362                     CompareRouteEntries(*entry, *bestRouteEntry) > 0)
363                 {
364                     bestRouteEntry  = entry;
365                     bestMatchLength = prefixLength;
366                 }
367             }
368         }
369     }
370 
371     if (bestRouteEntry != nullptr)
372     {
373         aRloc16 = bestRouteEntry->GetRloc();
374         error   = kErrorNone;
375     }
376 
377     return error;
378 }
379 
DefaultRouteLookup(const PrefixTlv & aPrefix,uint16_t & aRloc16) const380 Error Leader::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) const
381 {
382     Error                    error = kErrorNoRoute;
383     TlvIterator              subTlvIterator(aPrefix);
384     const BorderRouterTlv   *brTlv;
385     const BorderRouterEntry *route = nullptr;
386 
387     while ((brTlv = subTlvIterator.Iterate<BorderRouterTlv>()) != nullptr)
388     {
389         for (const BorderRouterEntry *entry = brTlv->GetFirstEntry(); entry <= brTlv->GetLastEntry();
390              entry                          = entry->GetNext())
391         {
392             if (!entry->IsDefaultRoute())
393             {
394                 continue;
395             }
396 
397             if (route == nullptr || CompareRouteEntries(*entry, *route) > 0)
398             {
399                 route = entry;
400             }
401         }
402     }
403 
404     if (route != nullptr)
405     {
406         aRloc16 = route->GetRloc();
407         error   = kErrorNone;
408     }
409 
410     return error;
411 }
412 
SetNetworkData(uint8_t aVersion,uint8_t aStableVersion,Type aType,const Message & aMessage,uint16_t aOffset,uint16_t aLength)413 Error Leader::SetNetworkData(uint8_t        aVersion,
414                              uint8_t        aStableVersion,
415                              Type           aType,
416                              const Message &aMessage,
417                              uint16_t       aOffset,
418                              uint16_t       aLength)
419 {
420     Error error = kErrorNone;
421 
422     VerifyOrExit(aLength <= kMaxSize, error = kErrorParse);
423     SuccessOrExit(error = aMessage.Read(aOffset, GetBytes(), aLength));
424 
425     SetLength(static_cast<uint8_t>(aLength));
426     mVersion       = aVersion;
427     mStableVersion = aStableVersion;
428 
429     if (aType == kStableSubset)
430     {
431         RemoveTemporaryData();
432     }
433 
434 #if OPENTHREAD_FTD
435     if (Get<Mle::MleRouter>().IsLeader())
436     {
437         Get<Leader>().HandleNetworkDataRestoredAfterReset();
438     }
439 #endif
440 
441     DumpDebg("SetNetworkData", GetBytes(), GetLength());
442 
443     SignalNetDataChanged();
444 
445 exit:
446     return error;
447 }
448 
FindCommissioningData(void) const449 const CommissioningDataTlv *Leader::FindCommissioningData(void) const
450 {
451     return NetworkDataTlv::Find<CommissioningDataTlv>(GetTlvsStart(), GetTlvsEnd());
452 }
453 
FindCommissioningDataSubTlv(uint8_t aType) const454 const MeshCoP::Tlv *Leader::FindCommissioningDataSubTlv(uint8_t aType) const
455 {
456     const MeshCoP::Tlv   *subTlv  = nullptr;
457     const NetworkDataTlv *dataTlv = FindCommissioningData();
458 
459     VerifyOrExit(dataTlv != nullptr);
460     subTlv = As<MeshCoP::Tlv>(Tlv::FindTlv(dataTlv->GetValue(), dataTlv->GetLength(), aType));
461 
462 exit:
463     return subTlv;
464 }
465 
ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::Type aType,uint16_t & aValue) const466 Error Leader::ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::Type aType, uint16_t &aValue) const
467 {
468     Error               error  = kErrorNone;
469     const MeshCoP::Tlv *subTlv = FindCommissioningDataSubTlv(aType);
470 
471     VerifyOrExit(subTlv != nullptr, error = kErrorNotFound);
472     VerifyOrExit(subTlv->GetLength() >= sizeof(uint16_t), error = kErrorParse);
473     aValue = BigEndian::ReadUint16(subTlv->GetValue());
474 
475 exit:
476     return error;
477 }
478 
GetCommissioningDataset(MeshCoP::CommissioningDataset & aDataset) const479 void Leader::GetCommissioningDataset(MeshCoP::CommissioningDataset &aDataset) const
480 {
481     const CommissioningDataTlv *dataTlv = FindCommissioningData();
482     const MeshCoP::Tlv         *subTlv;
483     const MeshCoP::Tlv         *endTlv;
484 
485     aDataset.Clear();
486 
487     VerifyOrExit(dataTlv != nullptr);
488 
489     aDataset.mIsLocatorSet       = (FindBorderAgentRloc(aDataset.mLocator) == kErrorNone);
490     aDataset.mIsSessionIdSet     = (FindCommissioningSessionId(aDataset.mSessionId) == kErrorNone);
491     aDataset.mIsJoinerUdpPortSet = (FindJoinerUdpPort(aDataset.mJoinerUdpPort) == kErrorNone);
492     aDataset.mIsSteeringDataSet  = (FindSteeringData(AsCoreType(&aDataset.mSteeringData)) == kErrorNone);
493 
494     // Determine if the Commissioning data has any extra unknown TLVs
495 
496     subTlv = reinterpret_cast<const MeshCoP::Tlv *>(dataTlv->GetValue());
497     endTlv = reinterpret_cast<const MeshCoP::Tlv *>(dataTlv->GetValue() + dataTlv->GetLength());
498 
499     for (; subTlv < endTlv; subTlv = subTlv->GetNext())
500     {
501         switch (subTlv->GetType())
502         {
503         case MeshCoP::Tlv::kBorderAgentLocator:
504         case MeshCoP::Tlv::kSteeringData:
505         case MeshCoP::Tlv::kJoinerUdpPort:
506         case MeshCoP::Tlv::kCommissionerSessionId:
507             break;
508         default:
509             ExitNow(aDataset.mHasExtraTlv = true);
510         }
511     }
512 
513 exit:
514     return;
515 }
516 
FindBorderAgentRloc(uint16_t & aRloc16) const517 Error Leader::FindBorderAgentRloc(uint16_t &aRloc16) const
518 {
519     return ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::kBorderAgentLocator, aRloc16);
520 }
521 
FindCommissioningSessionId(uint16_t & aSessionId) const522 Error Leader::FindCommissioningSessionId(uint16_t &aSessionId) const
523 {
524     return ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::kCommissionerSessionId, aSessionId);
525 }
526 
FindJoinerUdpPort(uint16_t & aPort) const527 Error Leader::FindJoinerUdpPort(uint16_t &aPort) const
528 {
529     return ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::kJoinerUdpPort, aPort);
530 }
531 
FindSteeringData(MeshCoP::SteeringData & aSteeringData) const532 Error Leader::FindSteeringData(MeshCoP::SteeringData &aSteeringData) const
533 {
534     Error                           error           = kErrorNone;
535     const MeshCoP::SteeringDataTlv *steeringDataTlv = FindInCommissioningData<MeshCoP::SteeringDataTlv>();
536 
537     VerifyOrExit(steeringDataTlv != nullptr, error = kErrorNotFound);
538     steeringDataTlv->CopyTo(aSteeringData);
539 
540 exit:
541     return error;
542 }
543 
IsJoiningAllowed(void) const544 bool Leader::IsJoiningAllowed(void) const
545 {
546     bool                  isAllowed = false;
547     MeshCoP::SteeringData steeringData;
548 
549     SuccessOrExit(FindSteeringData(steeringData));
550     isAllowed = !steeringData.IsEmpty();
551 
552 exit:
553     return isAllowed;
554 }
555 
SteeringDataCheck(const FilterIndexes & aFilterIndexes) const556 Error Leader::SteeringDataCheck(const FilterIndexes &aFilterIndexes) const
557 {
558     Error                 error = kErrorInvalidState;
559     MeshCoP::SteeringData steeringData;
560 
561     SuccessOrExit(FindSteeringData(steeringData));
562     error = steeringData.Contains(aFilterIndexes) ? kErrorNone : kErrorNotFound;
563 
564 exit:
565     return error;
566 }
567 
SteeringDataCheckJoiner(const Mac::ExtAddress & aEui64) const568 Error Leader::SteeringDataCheckJoiner(const Mac::ExtAddress &aEui64) const
569 {
570     FilterIndexes   filterIndexes;
571     Mac::ExtAddress joinerId;
572 
573     MeshCoP::ComputeJoinerId(aEui64, joinerId);
574     MeshCoP::SteeringData::CalculateHashBitIndexes(joinerId, filterIndexes);
575 
576     return SteeringDataCheck(filterIndexes);
577 }
578 
SteeringDataCheckJoiner(const MeshCoP::JoinerDiscerner & aDiscerner) const579 Error Leader::SteeringDataCheckJoiner(const MeshCoP::JoinerDiscerner &aDiscerner) const
580 {
581     FilterIndexes filterIndexes;
582 
583     MeshCoP::SteeringData::CalculateHashBitIndexes(aDiscerner, filterIndexes);
584 
585     return SteeringDataCheck(filterIndexes);
586 }
587 
SignalNetDataChanged(void)588 void Leader::SignalNetDataChanged(void)
589 {
590     mMaxLength = Max(mMaxLength, GetLength());
591     Get<ot::Notifier>().Signal(kEventThreadNetdataChanged);
592 }
593 
594 } // namespace NetworkData
595 } // namespace ot
596