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 #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
283 {
284 // The `Slaac` module keeps track of the associated Domain IDs
285 // for deprecating SLAAC prefixes, even if the related
286 // Prefix TLV has already been removed from the Network
287 // Data.
288
289 uint8_t domainId;
290
291 if (Get<Utils::Slaac>().FindDomainIdFor(aSource, domainId) == kErrorNone)
292 {
293 error = ExternalRouteLookup(domainId, aDestination, aRloc16);
294 }
295 }
296 #endif
297
298 exit:
299 return error;
300 }
301
CompareRouteEntries(const EntryType & aFirst,const EntryType & aSecond) const302 template <typename EntryType> int Leader::CompareRouteEntries(const EntryType &aFirst, const EntryType &aSecond) const
303 {
304 // `EntryType` can be `HasRouteEntry` or `BorderRouterEntry`.
305
306 return CompareRouteEntries(aFirst.GetPreference(), aFirst.GetRloc(), aSecond.GetPreference(), aSecond.GetRloc());
307 }
308
CompareRouteEntries(int8_t aFirstPreference,uint16_t aFirstRloc,int8_t aSecondPreference,uint16_t aSecondRloc) const309 int Leader::CompareRouteEntries(int8_t aFirstPreference,
310 uint16_t aFirstRloc,
311 int8_t aSecondPreference,
312 uint16_t aSecondRloc) const
313 {
314 // Performs three-way comparison between two BR entries.
315
316 int result;
317
318 // Prefer the entry with higher preference.
319
320 result = ThreeWayCompare(aFirstPreference, aSecondPreference);
321 VerifyOrExit(result == 0);
322
323 #if OPENTHREAD_MTD
324 // On MTD, prefer the BR that is this device itself. This handles
325 // the uncommon case where an MTD itself may be acting as BR.
326
327 result = ThreeWayCompare((aFirstRloc == Get<Mle::Mle>().GetRloc16()), (aSecondRloc == Get<Mle::Mle>().GetRloc16()));
328 #endif
329
330 #if OPENTHREAD_FTD
331 // If all the same, prefer the one with lower mesh path cost.
332 // Lower cost is preferred so we pass the second entry's cost as
333 // the first argument in the call to `ThreeWayCompare()`, i.e.,
334 // if the second entry's cost is larger, we return 1 indicating
335 // that the first entry is preferred over the second one.
336
337 result = ThreeWayCompare(Get<RouterTable>().GetPathCost(aSecondRloc), Get<RouterTable>().GetPathCost(aFirstRloc));
338 VerifyOrExit(result == 0);
339
340 // If all the same, prefer the BR acting as a router over an
341 // end device.
342 result = ThreeWayCompare(Mle::IsActiveRouter(aFirstRloc), Mle::IsActiveRouter(aSecondRloc));
343 #endif
344
345 exit:
346 return result;
347 }
348
ExternalRouteLookup(uint8_t aDomainId,const Ip6::Address & aDestination,uint16_t & aRloc16) const349 Error Leader::ExternalRouteLookup(uint8_t aDomainId, const Ip6::Address &aDestination, uint16_t &aRloc16) const
350 {
351 Error error = kErrorNoRoute;
352 const PrefixTlv *prefixTlv = nullptr;
353 const HasRouteEntry *bestRouteEntry = nullptr;
354 uint8_t bestMatchLength = 0;
355
356 while ((prefixTlv = FindNextMatchingPrefixTlv(aDestination, prefixTlv)) != nullptr)
357 {
358 const HasRouteTlv *hasRoute;
359 uint8_t prefixLength = prefixTlv->GetPrefixLength();
360 TlvIterator subTlvIterator(*prefixTlv);
361
362 if (prefixTlv->GetDomainId() != aDomainId)
363 {
364 continue;
365 }
366
367 if ((bestRouteEntry != nullptr) && (prefixLength <= bestMatchLength))
368 {
369 continue;
370 }
371
372 while ((hasRoute = subTlvIterator.Iterate<HasRouteTlv>()) != nullptr)
373 {
374 for (const HasRouteEntry *entry = hasRoute->GetFirstEntry(); entry <= hasRoute->GetLastEntry();
375 entry = entry->GetNext())
376 {
377 if ((bestRouteEntry == nullptr) || (prefixLength > bestMatchLength) ||
378 CompareRouteEntries(*entry, *bestRouteEntry) > 0)
379 {
380 bestRouteEntry = entry;
381 bestMatchLength = prefixLength;
382 }
383 }
384 }
385 }
386
387 if (bestRouteEntry != nullptr)
388 {
389 aRloc16 = bestRouteEntry->GetRloc();
390 error = kErrorNone;
391 }
392
393 return error;
394 }
395
DefaultRouteLookup(const PrefixTlv & aPrefix,uint16_t & aRloc16) const396 Error Leader::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) const
397 {
398 Error error = kErrorNoRoute;
399 TlvIterator subTlvIterator(aPrefix);
400 const BorderRouterTlv *brTlv;
401 const BorderRouterEntry *route = nullptr;
402
403 while ((brTlv = subTlvIterator.Iterate<BorderRouterTlv>()) != nullptr)
404 {
405 for (const BorderRouterEntry *entry = brTlv->GetFirstEntry(); entry <= brTlv->GetLastEntry();
406 entry = entry->GetNext())
407 {
408 if (!entry->IsDefaultRoute())
409 {
410 continue;
411 }
412
413 if (route == nullptr || CompareRouteEntries(*entry, *route) > 0)
414 {
415 route = entry;
416 }
417 }
418 }
419
420 if (route != nullptr)
421 {
422 aRloc16 = route->GetRloc();
423 error = kErrorNone;
424 }
425
426 return error;
427 }
428
SetNetworkData(uint8_t aVersion,uint8_t aStableVersion,Type aType,const Message & aMessage,uint16_t aOffset,uint16_t aLength)429 Error Leader::SetNetworkData(uint8_t aVersion,
430 uint8_t aStableVersion,
431 Type aType,
432 const Message &aMessage,
433 uint16_t aOffset,
434 uint16_t aLength)
435 {
436 Error error = kErrorNone;
437
438 VerifyOrExit(aLength <= kMaxSize, error = kErrorParse);
439 SuccessOrExit(error = aMessage.Read(aOffset, GetBytes(), aLength));
440
441 SetLength(static_cast<uint8_t>(aLength));
442 mVersion = aVersion;
443 mStableVersion = aStableVersion;
444
445 if (aType == kStableSubset)
446 {
447 RemoveTemporaryData();
448 }
449
450 #if OPENTHREAD_FTD
451 if (Get<Mle::MleRouter>().IsLeader())
452 {
453 Get<Leader>().HandleNetworkDataRestoredAfterReset();
454 }
455 #endif
456
457 DumpDebg("SetNetworkData", GetBytes(), GetLength());
458
459 SignalNetDataChanged();
460
461 exit:
462 return error;
463 }
464
FindCommissioningData(void) const465 const CommissioningDataTlv *Leader::FindCommissioningData(void) const
466 {
467 return NetworkDataTlv::Find<CommissioningDataTlv>(GetTlvsStart(), GetTlvsEnd());
468 }
469
FindCommissioningDataSubTlv(uint8_t aType) const470 const MeshCoP::Tlv *Leader::FindCommissioningDataSubTlv(uint8_t aType) const
471 {
472 const MeshCoP::Tlv *subTlv = nullptr;
473 const NetworkDataTlv *dataTlv = FindCommissioningData();
474
475 VerifyOrExit(dataTlv != nullptr);
476 subTlv = As<MeshCoP::Tlv>(Tlv::FindTlv(dataTlv->GetValue(), dataTlv->GetLength(), aType));
477
478 exit:
479 return subTlv;
480 }
481
ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::Type aType,uint16_t & aValue) const482 Error Leader::ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::Type aType, uint16_t &aValue) const
483 {
484 Error error = kErrorNone;
485 const MeshCoP::Tlv *subTlv = FindCommissioningDataSubTlv(aType);
486
487 VerifyOrExit(subTlv != nullptr, error = kErrorNotFound);
488 VerifyOrExit(subTlv->GetLength() >= sizeof(uint16_t), error = kErrorParse);
489 aValue = BigEndian::ReadUint16(subTlv->GetValue());
490
491 exit:
492 return error;
493 }
494
GetCommissioningDataset(MeshCoP::CommissioningDataset & aDataset) const495 void Leader::GetCommissioningDataset(MeshCoP::CommissioningDataset &aDataset) const
496 {
497 const CommissioningDataTlv *dataTlv = FindCommissioningData();
498 const MeshCoP::Tlv *subTlv;
499 const MeshCoP::Tlv *endTlv;
500
501 aDataset.Clear();
502
503 VerifyOrExit(dataTlv != nullptr);
504
505 aDataset.mIsLocatorSet = (FindBorderAgentRloc(aDataset.mLocator) == kErrorNone);
506 aDataset.mIsSessionIdSet = (FindCommissioningSessionId(aDataset.mSessionId) == kErrorNone);
507 aDataset.mIsJoinerUdpPortSet = (FindJoinerUdpPort(aDataset.mJoinerUdpPort) == kErrorNone);
508 aDataset.mIsSteeringDataSet = (FindSteeringData(AsCoreType(&aDataset.mSteeringData)) == kErrorNone);
509
510 // Determine if the Commissioning data has any extra unknown TLVs
511
512 subTlv = reinterpret_cast<const MeshCoP::Tlv *>(dataTlv->GetValue());
513 endTlv = reinterpret_cast<const MeshCoP::Tlv *>(dataTlv->GetValue() + dataTlv->GetLength());
514
515 for (; subTlv < endTlv; subTlv = subTlv->GetNext())
516 {
517 switch (subTlv->GetType())
518 {
519 case MeshCoP::Tlv::kBorderAgentLocator:
520 case MeshCoP::Tlv::kSteeringData:
521 case MeshCoP::Tlv::kJoinerUdpPort:
522 case MeshCoP::Tlv::kCommissionerSessionId:
523 break;
524 default:
525 ExitNow(aDataset.mHasExtraTlv = true);
526 }
527 }
528
529 exit:
530 return;
531 }
532
FindBorderAgentRloc(uint16_t & aRloc16) const533 Error Leader::FindBorderAgentRloc(uint16_t &aRloc16) const
534 {
535 return ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::kBorderAgentLocator, aRloc16);
536 }
537
FindCommissioningSessionId(uint16_t & aSessionId) const538 Error Leader::FindCommissioningSessionId(uint16_t &aSessionId) const
539 {
540 return ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::kCommissionerSessionId, aSessionId);
541 }
542
FindJoinerUdpPort(uint16_t & aPort) const543 Error Leader::FindJoinerUdpPort(uint16_t &aPort) const
544 {
545 return ReadCommissioningDataUint16SubTlv(MeshCoP::Tlv::kJoinerUdpPort, aPort);
546 }
547
FindSteeringData(MeshCoP::SteeringData & aSteeringData) const548 Error Leader::FindSteeringData(MeshCoP::SteeringData &aSteeringData) const
549 {
550 Error error = kErrorNone;
551 const MeshCoP::SteeringDataTlv *steeringDataTlv = FindInCommissioningData<MeshCoP::SteeringDataTlv>();
552
553 VerifyOrExit(steeringDataTlv != nullptr, error = kErrorNotFound);
554 steeringDataTlv->CopyTo(aSteeringData);
555
556 exit:
557 return error;
558 }
559
IsJoiningAllowed(void) const560 bool Leader::IsJoiningAllowed(void) const
561 {
562 bool isAllowed = false;
563 MeshCoP::SteeringData steeringData;
564
565 SuccessOrExit(FindSteeringData(steeringData));
566 isAllowed = !steeringData.IsEmpty();
567
568 exit:
569 return isAllowed;
570 }
571
SteeringDataCheck(const FilterIndexes & aFilterIndexes) const572 Error Leader::SteeringDataCheck(const FilterIndexes &aFilterIndexes) const
573 {
574 Error error = kErrorInvalidState;
575 MeshCoP::SteeringData steeringData;
576
577 SuccessOrExit(FindSteeringData(steeringData));
578 error = steeringData.Contains(aFilterIndexes) ? kErrorNone : kErrorNotFound;
579
580 exit:
581 return error;
582 }
583
SteeringDataCheckJoiner(const Mac::ExtAddress & aEui64) const584 Error Leader::SteeringDataCheckJoiner(const Mac::ExtAddress &aEui64) const
585 {
586 FilterIndexes filterIndexes;
587 Mac::ExtAddress joinerId;
588
589 MeshCoP::ComputeJoinerId(aEui64, joinerId);
590 MeshCoP::SteeringData::CalculateHashBitIndexes(joinerId, filterIndexes);
591
592 return SteeringDataCheck(filterIndexes);
593 }
594
SteeringDataCheckJoiner(const MeshCoP::JoinerDiscerner & aDiscerner) const595 Error Leader::SteeringDataCheckJoiner(const MeshCoP::JoinerDiscerner &aDiscerner) const
596 {
597 FilterIndexes filterIndexes;
598
599 MeshCoP::SteeringData::CalculateHashBitIndexes(aDiscerner, filterIndexes);
600
601 return SteeringDataCheck(filterIndexes);
602 }
603
SignalNetDataChanged(void)604 void Leader::SignalNetDataChanged(void)
605 {
606 mMaxLength = Max(mMaxLength, GetLength());
607 Get<ot::Notifier>().Signal(kEventThreadNetdataChanged);
608 }
609
610 } // namespace NetworkData
611 } // namespace ot
612