• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements common methods for manipulating MeshCoP Datasets.
32  */
33 
34 #include "dataset.hpp"
35 
36 #include "instance/instance.hpp"
37 
38 namespace ot {
39 namespace MeshCoP {
40 
41 RegisterLogModule("Dataset");
42 
GenerateRandom(Instance & aInstance)43 Error Dataset::Info::GenerateRandom(Instance &aInstance)
44 {
45     Error            error;
46     Mac::ChannelMask supportedChannels = aInstance.Get<Mac::Mac>().GetSupportedChannelMask();
47     Mac::ChannelMask preferredChannels(aInstance.Get<Radio>().GetPreferredChannelMask());
48     StringWriter     nameWriter(mNetworkName.m8, sizeof(mNetworkName));
49 
50     // If the preferred channel mask is not empty, select a random
51     // channel from it, otherwise choose one from the supported
52     // channel mask.
53 
54     preferredChannels.Intersect(supportedChannels);
55 
56     if (preferredChannels.IsEmpty())
57     {
58         preferredChannels = supportedChannels;
59     }
60 
61     Clear();
62 
63     mActiveTimestamp.mSeconds       = 1;
64     mActiveTimestamp.mTicks         = 0;
65     mActiveTimestamp.mAuthoritative = false;
66     mChannel                        = preferredChannels.ChooseRandomChannel();
67     mChannelMask                    = supportedChannels.GetMask();
68     mWakeupChannel                  = supportedChannels.ChooseRandomChannel();
69     mPanId                          = Mac::GenerateRandomPanId();
70     AsCoreType(&mSecurityPolicy).SetToDefault();
71 
72     SuccessOrExit(error = AsCoreType(&mNetworkKey).GenerateRandom());
73     SuccessOrExit(error = AsCoreType(&mPskc).GenerateRandom());
74     SuccessOrExit(error = Random::Crypto::Fill(mExtendedPanId));
75     SuccessOrExit(error = AsCoreType(&mMeshLocalPrefix).GenerateRandomUla());
76 
77     nameWriter.Append("%s-%04x", NetworkName::kNetworkNameInit, mPanId);
78 
79     mComponents.mIsActiveTimestampPresent = true;
80     mComponents.mIsNetworkKeyPresent      = true;
81     mComponents.mIsNetworkNamePresent     = true;
82     mComponents.mIsExtendedPanIdPresent   = true;
83     mComponents.mIsMeshLocalPrefixPresent = true;
84     mComponents.mIsPanIdPresent           = true;
85     mComponents.mIsChannelPresent         = true;
86     mComponents.mIsWakeupChannelPresent   = true;
87     mComponents.mIsPskcPresent            = true;
88     mComponents.mIsSecurityPolicyPresent  = true;
89     mComponents.mIsChannelMaskPresent     = true;
90 
91 exit:
92     return error;
93 }
94 
Dataset(void)95 Dataset::Dataset(void)
96     : mLength(0)
97     , mUpdateTime(0)
98 {
99     ClearAllBytes(mTlvs);
100 }
101 
ValidateTlvs(void) const102 Error Dataset::ValidateTlvs(void) const
103 {
104     Error      error = kErrorParse;
105     const Tlv *end   = GetTlvsEnd();
106     uint16_t   validatedLength;
107 
108     VerifyOrExit(mLength <= kMaxLength);
109 
110     for (const Tlv *tlv = GetTlvsStart(); tlv < end; tlv = tlv->GetNext())
111     {
112         VerifyOrExit(!tlv->IsExtended() && ((tlv + 1) <= end) && (tlv->GetNext() <= end));
113         VerifyOrExit(IsTlvValid(*tlv));
114 
115         // Ensure there are no duplicate TLVs.
116         validatedLength = static_cast<uint16_t>(reinterpret_cast<const uint8_t *>(tlv) - mTlvs);
117         VerifyOrExit(Tlv::FindTlv(mTlvs, validatedLength, tlv->GetType()) == nullptr);
118     }
119 
120     error = kErrorNone;
121 
122 exit:
123     return error;
124 }
125 
IsTlvValid(const Tlv & aTlv)126 bool Dataset::IsTlvValid(const Tlv &aTlv)
127 {
128     bool    isValid   = true;
129     uint8_t minLength = 0;
130 
131     switch (aTlv.GetType())
132     {
133     case Tlv::kActiveTimestamp:
134         minLength = sizeof(ActiveTimestampTlv::ValueType);
135         break;
136     case Tlv::kPendingTimestamp:
137         minLength = sizeof(PendingTimestampTlv::ValueType);
138         break;
139     case Tlv::kDelayTimer:
140         minLength = sizeof(DelayTimerTlv::UintValueType);
141         break;
142     case Tlv::kPanId:
143         minLength = sizeof(PanIdTlv::UintValueType);
144         break;
145     case Tlv::kExtendedPanId:
146         minLength = sizeof(ExtendedPanIdTlv::ValueType);
147         break;
148     case Tlv::kPskc:
149         minLength = sizeof(PskcTlv::ValueType);
150         break;
151     case Tlv::kNetworkKey:
152         minLength = sizeof(NetworkKeyTlv::ValueType);
153         break;
154     case Tlv::kMeshLocalPrefix:
155         minLength = sizeof(MeshLocalPrefixTlv::ValueType);
156         break;
157     case Tlv::kChannel:
158         VerifyOrExit(aTlv.GetLength() >= sizeof(ChannelTlvValue), isValid = false);
159         isValid = aTlv.ReadValueAs<ChannelTlv>().IsValid();
160         break;
161     case Tlv::kWakeupChannel:
162         VerifyOrExit(aTlv.GetLength() >= sizeof(ChannelTlvValue), isValid = false);
163         isValid = aTlv.ReadValueAs<WakeupChannelTlv>().IsValid();
164         break;
165     case Tlv::kNetworkName:
166         isValid = As<NetworkNameTlv>(aTlv).IsValid();
167         break;
168 
169     case Tlv::kSecurityPolicy:
170         isValid = As<SecurityPolicyTlv>(aTlv).IsValid();
171         break;
172 
173     case Tlv::kChannelMask:
174         isValid = As<ChannelMaskTlv>(aTlv).IsValid();
175         break;
176 
177     default:
178         break;
179     }
180 
181     if (minLength > 0)
182     {
183         isValid = (aTlv.GetLength() >= minLength);
184     }
185 
186 exit:
187     return isValid;
188 }
189 
ContainsAllTlvs(const Tlv::Type aTlvTypes[],uint8_t aLength) const190 bool Dataset::ContainsAllTlvs(const Tlv::Type aTlvTypes[], uint8_t aLength) const
191 {
192     bool containsAll = true;
193 
194     for (uint8_t index = 0; index < aLength; index++)
195     {
196         if (!ContainsTlv(aTlvTypes[index]))
197         {
198             containsAll = false;
199             break;
200         }
201     }
202 
203     return containsAll;
204 }
205 
ContainsAllRequiredTlvsFor(Type aType) const206 bool Dataset::ContainsAllRequiredTlvsFor(Type aType) const
207 {
208     static const Tlv::Type kDatasetTlvs[] = {
209         Tlv::kActiveTimestamp,
210         Tlv::kChannel,
211         Tlv::kChannelMask,
212         Tlv::kExtendedPanId,
213         Tlv::kMeshLocalPrefix,
214         Tlv::kNetworkKey,
215         Tlv::kNetworkName,
216         Tlv::kPanId,
217         Tlv::kPskc,
218         Tlv::kSecurityPolicy,
219         // The last two TLVs are for Pending Dataset
220         Tlv::kPendingTimestamp,
221         Tlv::kDelayTimer,
222     };
223 
224     uint8_t length = sizeof(kDatasetTlvs);
225 
226     if (aType == kActive)
227     {
228         length -= 2;
229     }
230 
231     return ContainsAllTlvs(kDatasetTlvs, length);
232 }
233 
FindTlv(Tlv::Type aType) const234 const Tlv *Dataset::FindTlv(Tlv::Type aType) const { return As<Tlv>(Tlv::FindTlv(mTlvs, mLength, aType)); }
235 
ConvertTo(Info & aDatasetInfo) const236 void Dataset::ConvertTo(Info &aDatasetInfo) const
237 {
238     aDatasetInfo.Clear();
239 
240     for (const Tlv *cur = GetTlvsStart(); cur < GetTlvsEnd(); cur = cur->GetNext())
241     {
242         switch (cur->GetType())
243         {
244         case Tlv::kActiveTimestamp:
245             aDatasetInfo.Set<kActiveTimestamp>(cur->ReadValueAs<ActiveTimestampTlv>());
246             break;
247 
248         case Tlv::kChannel:
249             aDatasetInfo.Set<kChannel>(cur->ReadValueAs<ChannelTlv>().GetChannel());
250             break;
251 
252         case Tlv::kWakeupChannel:
253             aDatasetInfo.Set<kWakeupChannel>(cur->ReadValueAs<WakeupChannelTlv>().GetChannel());
254             break;
255 
256         case Tlv::kChannelMask:
257         {
258             uint32_t mask;
259 
260             if (As<ChannelMaskTlv>(cur)->ReadChannelMask(mask) == kErrorNone)
261             {
262                 aDatasetInfo.Set<kChannelMask>(mask);
263             }
264 
265             break;
266         }
267 
268         case Tlv::kDelayTimer:
269             aDatasetInfo.Set<kDelay>(cur->ReadValueAs<DelayTimerTlv>());
270             break;
271 
272         case Tlv::kExtendedPanId:
273             aDatasetInfo.Set<kExtendedPanId>(cur->ReadValueAs<ExtendedPanIdTlv>());
274             break;
275 
276         case Tlv::kMeshLocalPrefix:
277             aDatasetInfo.Set<kMeshLocalPrefix>(cur->ReadValueAs<MeshLocalPrefixTlv>());
278             break;
279 
280         case Tlv::kNetworkKey:
281             aDatasetInfo.Set<kNetworkKey>(cur->ReadValueAs<NetworkKeyTlv>());
282             break;
283 
284         case Tlv::kNetworkName:
285             IgnoreError(aDatasetInfo.Update<kNetworkName>().Set(As<NetworkNameTlv>(cur)->GetNetworkName()));
286             break;
287 
288         case Tlv::kPanId:
289             aDatasetInfo.Set<kPanId>(cur->ReadValueAs<PanIdTlv>());
290             break;
291 
292         case Tlv::kPendingTimestamp:
293             aDatasetInfo.Set<kPendingTimestamp>(cur->ReadValueAs<PendingTimestampTlv>());
294             break;
295 
296         case Tlv::kPskc:
297             aDatasetInfo.Set<kPskc>(cur->ReadValueAs<PskcTlv>());
298             break;
299 
300         case Tlv::kSecurityPolicy:
301             aDatasetInfo.Set<kSecurityPolicy>(As<SecurityPolicyTlv>(cur)->GetSecurityPolicy());
302             break;
303 
304         default:
305             break;
306         }
307     }
308 }
309 
ConvertTo(Tlvs & aTlvs) const310 void Dataset::ConvertTo(Tlvs &aTlvs) const
311 {
312     memcpy(aTlvs.mTlvs, mTlvs, mLength);
313     aTlvs.mLength = static_cast<uint8_t>(mLength);
314 }
315 
SetFrom(const Dataset & aDataset)316 void Dataset::SetFrom(const Dataset &aDataset)
317 {
318     memcpy(mTlvs, aDataset.mTlvs, aDataset.mLength);
319     mLength     = aDataset.mLength;
320     mUpdateTime = aDataset.GetUpdateTime();
321 }
322 
SetFrom(const Tlvs & aTlvs)323 Error Dataset::SetFrom(const Tlvs &aTlvs) { return SetFrom(aTlvs.mTlvs, aTlvs.mLength); }
324 
SetFrom(const uint8_t * aTlvs,uint8_t aLength)325 Error Dataset::SetFrom(const uint8_t *aTlvs, uint8_t aLength)
326 {
327     Error error = kErrorNone;
328 
329     VerifyOrExit(aLength <= kMaxLength, error = kErrorInvalidArgs);
330 
331     mLength = aLength;
332     memcpy(mTlvs, aTlvs, mLength);
333 
334     mUpdateTime = TimerMilli::GetNow();
335 
336 exit:
337     return error;
338 }
339 
SetFrom(const Info & aDatasetInfo)340 void Dataset::SetFrom(const Info &aDatasetInfo)
341 {
342     Clear();
343     IgnoreError(WriteTlvsFrom(aDatasetInfo));
344 
345     // `mUpdateTime` is already set by `WriteTlvsFrom()`.
346 }
347 
SetFrom(const Message & aMessage,const OffsetRange & aOffsetRange)348 Error Dataset::SetFrom(const Message &aMessage, const OffsetRange &aOffsetRange)
349 {
350     Error error = kErrorNone;
351 
352     VerifyOrExit(aOffsetRange.GetLength() <= kMaxLength, error = kErrorInvalidArgs);
353 
354     SuccessOrExit(error = aMessage.Read(aOffsetRange, mTlvs, aOffsetRange.GetLength()));
355     mLength = static_cast<uint8_t>(aOffsetRange.GetLength());
356 
357     mUpdateTime = TimerMilli::GetNow();
358 
359 exit:
360     return error;
361 }
362 
WriteTlv(Tlv::Type aType,const void * aValue,uint8_t aLength)363 Error Dataset::WriteTlv(Tlv::Type aType, const void *aValue, uint8_t aLength)
364 {
365     Error    error          = kErrorNone;
366     uint16_t bytesAvailable = sizeof(mTlvs) - mLength;
367     Tlv     *oldTlv         = FindTlv(aType);
368     Tlv     *newTlv;
369 
370     if (oldTlv != nullptr)
371     {
372         bytesAvailable += sizeof(Tlv) + oldTlv->GetLength();
373     }
374 
375     VerifyOrExit(sizeof(Tlv) + aLength <= bytesAvailable, error = kErrorNoBufs);
376 
377     RemoveTlv(oldTlv);
378 
379     newTlv = GetTlvsEnd();
380     mLength += sizeof(Tlv) + aLength;
381 
382     newTlv->SetType(aType);
383     newTlv->SetLength(aLength);
384     memcpy(newTlv->GetValue(), aValue, aLength);
385 
386     mUpdateTime = TimerMilli::GetNow();
387 
388 exit:
389     return error;
390 }
391 
WriteTlv(const Tlv & aTlv)392 Error Dataset::WriteTlv(const Tlv &aTlv) { return WriteTlv(aTlv.GetType(), aTlv.GetValue(), aTlv.GetLength()); }
393 
WriteTlvsFrom(const Dataset & aDataset)394 Error Dataset::WriteTlvsFrom(const Dataset &aDataset)
395 {
396     Error error;
397 
398     SuccessOrExit(error = aDataset.ValidateTlvs());
399 
400     for (const Tlv *tlv = aDataset.GetTlvsStart(); tlv < aDataset.GetTlvsEnd(); tlv = tlv->GetNext())
401     {
402         SuccessOrExit(error = WriteTlv(*tlv));
403     }
404 
405 exit:
406     return error;
407 }
408 
WriteTlvsFrom(const uint8_t * aTlvs,uint8_t aLength)409 Error Dataset::WriteTlvsFrom(const uint8_t *aTlvs, uint8_t aLength)
410 {
411     Error   error;
412     Dataset dataset;
413 
414     SuccessOrExit(error = dataset.SetFrom(aTlvs, aLength));
415     error = WriteTlvsFrom(dataset);
416 
417 exit:
418     return error;
419 }
420 
WriteTlvsFrom(const Dataset::Info & aDatasetInfo)421 Error Dataset::WriteTlvsFrom(const Dataset::Info &aDatasetInfo)
422 {
423     Error error = kErrorNone;
424 
425     if (aDatasetInfo.IsPresent<kActiveTimestamp>())
426     {
427         Timestamp activeTimestamp;
428 
429         aDatasetInfo.Get<kActiveTimestamp>(activeTimestamp);
430         SuccessOrExit(error = Write<ActiveTimestampTlv>(activeTimestamp));
431     }
432 
433     if (aDatasetInfo.IsPresent<kPendingTimestamp>())
434     {
435         Timestamp pendingTimestamp;
436 
437         aDatasetInfo.Get<kPendingTimestamp>(pendingTimestamp);
438         SuccessOrExit(error = Write<PendingTimestampTlv>(pendingTimestamp));
439     }
440 
441     if (aDatasetInfo.IsPresent<kDelay>())
442     {
443         SuccessOrExit(error = Write<DelayTimerTlv>(aDatasetInfo.Get<kDelay>()));
444     }
445 
446     if (aDatasetInfo.IsPresent<kChannel>())
447     {
448         ChannelTlvValue channelValue;
449 
450         channelValue.SetChannelAndPage(aDatasetInfo.Get<kChannel>());
451         SuccessOrExit(error = Write<ChannelTlv>(channelValue));
452     }
453 
454     if (aDatasetInfo.IsPresent<kWakeupChannel>())
455     {
456         ChannelTlvValue channelValue;
457 
458         channelValue.SetChannelAndPage(aDatasetInfo.Get<kWakeupChannel>());
459         SuccessOrExit(error = Write<WakeupChannelTlv>(channelValue));
460     }
461 
462     if (aDatasetInfo.IsPresent<kChannelMask>())
463     {
464         ChannelMaskTlv::Value value;
465 
466         ChannelMaskTlv::PrepareValue(value, aDatasetInfo.Get<kChannelMask>());
467         SuccessOrExit(error = WriteTlv(Tlv::kChannelMask, value.mData, value.mLength));
468     }
469 
470     if (aDatasetInfo.IsPresent<kExtendedPanId>())
471     {
472         SuccessOrExit(error = Write<ExtendedPanIdTlv>(aDatasetInfo.Get<kExtendedPanId>()));
473     }
474 
475     if (aDatasetInfo.IsPresent<kMeshLocalPrefix>())
476     {
477         SuccessOrExit(error = Write<MeshLocalPrefixTlv>(aDatasetInfo.Get<kMeshLocalPrefix>()));
478     }
479 
480     if (aDatasetInfo.IsPresent<kNetworkKey>())
481     {
482         SuccessOrExit(error = Write<NetworkKeyTlv>(aDatasetInfo.Get<kNetworkKey>()));
483     }
484 
485     if (aDatasetInfo.IsPresent<kNetworkName>())
486     {
487         NameData nameData = aDatasetInfo.Get<kNetworkName>().GetAsData();
488 
489         SuccessOrExit(error = WriteTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength()));
490     }
491 
492     if (aDatasetInfo.IsPresent<kPanId>())
493     {
494         SuccessOrExit(error = Write<PanIdTlv>(aDatasetInfo.Get<kPanId>()));
495     }
496 
497     if (aDatasetInfo.IsPresent<kPskc>())
498     {
499         SuccessOrExit(error = Write<PskcTlv>(aDatasetInfo.Get<kPskc>()));
500     }
501 
502     if (aDatasetInfo.IsPresent<kSecurityPolicy>())
503     {
504         SecurityPolicyTlv tlv;
505 
506         tlv.Init();
507         tlv.SetSecurityPolicy(aDatasetInfo.Get<kSecurityPolicy>());
508         SuccessOrExit(error = WriteTlv(tlv));
509     }
510 
511 exit:
512     return error;
513 }
514 
AppendTlvsFrom(const uint8_t * aTlvs,uint8_t aLength)515 Error Dataset::AppendTlvsFrom(const uint8_t *aTlvs, uint8_t aLength)
516 {
517     Error    error     = kErrorNone;
518     uint16_t newLength = mLength;
519 
520     newLength += aLength;
521     VerifyOrExit(newLength <= kMaxLength, error = kErrorNoBufs);
522 
523     memcpy(mTlvs + mLength, aTlvs, aLength);
524     mLength += aLength;
525 
526 exit:
527     return error;
528 }
529 
RemoveTlv(Tlv::Type aType)530 void Dataset::RemoveTlv(Tlv::Type aType) { RemoveTlv(FindTlv(aType)); }
531 
RemoveTlv(Tlv * aTlv)532 void Dataset::RemoveTlv(Tlv *aTlv)
533 {
534     if (aTlv != nullptr)
535     {
536         uint8_t *start  = reinterpret_cast<uint8_t *>(aTlv);
537         uint16_t length = sizeof(Tlv) + aTlv->GetLength();
538 
539         memmove(start, start + length, mLength - (static_cast<uint8_t>(start - mTlvs) + length));
540         mLength -= length;
541     }
542 }
543 
ReadTimestamp(Type aType,Timestamp & aTimestamp) const544 Error Dataset::ReadTimestamp(Type aType, Timestamp &aTimestamp) const
545 {
546     Error      error = kErrorNone;
547     const Tlv *tlv   = FindTlv(TimestampTlvFor(aType));
548 
549     VerifyOrExit(tlv != nullptr, error = kErrorNotFound);
550 
551     // Since both `ActiveTimestampTlv` and `PendingTimestampTlv` use
552     // `Timestamp` as their TLV value format, we can safely use
553     // `ReadValueAs<ActiveTimestampTlv>()` for both.
554 
555     aTimestamp = tlv->ReadValueAs<ActiveTimestampTlv>();
556 
557 exit:
558     return error;
559 }
560 
WriteTimestamp(Type aType,const Timestamp & aTimestamp)561 Error Dataset::WriteTimestamp(Type aType, const Timestamp &aTimestamp)
562 {
563     return WriteTlv(TimestampTlvFor(aType), &aTimestamp, sizeof(Timestamp));
564 }
565 
RemoveTimestamp(Type aType)566 void Dataset::RemoveTimestamp(Type aType) { RemoveTlv(TimestampTlvFor(aType)); }
567 
IsSubsetOf(const Dataset & aOther) const568 bool Dataset::IsSubsetOf(const Dataset &aOther) const
569 {
570     bool isSubset = false;
571 
572     for (const Tlv *tlv = GetTlvsStart(); tlv < GetTlvsEnd(); tlv = tlv->GetNext())
573     {
574         const Tlv *otherTlv;
575 
576         if ((tlv->GetType() == Tlv::kActiveTimestamp) || (tlv->GetType() == Tlv::kPendingTimestamp) ||
577             (tlv->GetType() == Tlv::kDelayTimer))
578         {
579             continue;
580         }
581 
582         otherTlv = aOther.FindTlv(tlv->GetType());
583         VerifyOrExit(otherTlv != nullptr);
584         VerifyOrExit(memcmp(tlv, otherTlv, tlv->GetSize()) == 0);
585     }
586 
587     isSubset = true;
588 
589 exit:
590     return isSubset;
591 }
592 
TypeToString(Type aType)593 const char *Dataset::TypeToString(Type aType) { return (aType == kActive) ? "Active" : "Pending"; }
594 
595 } // namespace MeshCoP
596 } // namespace ot
597