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 Joiner role.
32 */
33
34 #include "joiner.hpp"
35
36 #if OPENTHREAD_CONFIG_JOINER_ENABLE
37
38 #include <stdio.h>
39
40 #include "common/array.hpp"
41 #include "common/as_core_type.hpp"
42 #include "common/code_utils.hpp"
43 #include "common/debug.hpp"
44 #include "common/encoding.hpp"
45 #include "common/locator_getters.hpp"
46 #include "common/log.hpp"
47 #include "common/string.hpp"
48 #include "instance/instance.hpp"
49 #include "meshcop/meshcop.hpp"
50 #include "radio/radio.hpp"
51 #include "thread/thread_netif.hpp"
52 #include "thread/uri_paths.hpp"
53 #include "utils/otns.hpp"
54
55 namespace ot {
56 namespace MeshCoP {
57
58 RegisterLogModule("Joiner");
59
Joiner(Instance & aInstance)60 Joiner::Joiner(Instance &aInstance)
61 : InstanceLocator(aInstance)
62 , mId()
63 , mDiscerner()
64 , mState(kStateIdle)
65 , mJoinerRouterIndex(0)
66 , mFinalizeMessage(nullptr)
67 , mTimer(aInstance)
68 {
69 SetIdFromIeeeEui64();
70 mDiscerner.Clear();
71 ClearAllBytes(mJoinerRouters);
72 }
73
SetIdFromIeeeEui64(void)74 void Joiner::SetIdFromIeeeEui64(void)
75 {
76 Mac::ExtAddress eui64;
77
78 Get<Radio>().GetIeeeEui64(eui64);
79 ComputeJoinerId(eui64, mId);
80 }
81
GetDiscerner(void) const82 const JoinerDiscerner *Joiner::GetDiscerner(void) const { return mDiscerner.IsEmpty() ? nullptr : &mDiscerner; }
83
SetDiscerner(const JoinerDiscerner & aDiscerner)84 Error Joiner::SetDiscerner(const JoinerDiscerner &aDiscerner)
85 {
86 Error error = kErrorNone;
87
88 VerifyOrExit(aDiscerner.IsValid(), error = kErrorInvalidArgs);
89 VerifyOrExit(mState == kStateIdle, error = kErrorInvalidState);
90
91 mDiscerner = aDiscerner;
92 mDiscerner.GenerateJoinerId(mId);
93
94 exit:
95 return error;
96 }
97
ClearDiscerner(void)98 Error Joiner::ClearDiscerner(void)
99 {
100 Error error = kErrorNone;
101
102 VerifyOrExit(mState == kStateIdle, error = kErrorInvalidState);
103 VerifyOrExit(!mDiscerner.IsEmpty());
104
105 mDiscerner.Clear();
106 SetIdFromIeeeEui64();
107
108 exit:
109 return error;
110 }
111
SetState(State aState)112 void Joiner::SetState(State aState)
113 {
114 State oldState = mState;
115 OT_UNUSED_VARIABLE(oldState);
116
117 SuccessOrExit(Get<Notifier>().Update(mState, aState, kEventJoinerStateChanged));
118
119 LogInfo("JoinerState: %s -> %s", StateToString(oldState), StateToString(aState));
120 exit:
121 return;
122 }
123
Start(const char * aPskd,const char * aProvisioningUrl,const char * aVendorName,const char * aVendorModel,const char * aVendorSwVersion,const char * aVendorData,otJoinerCallback aCallback,void * aContext)124 Error Joiner::Start(const char *aPskd,
125 const char *aProvisioningUrl,
126 const char *aVendorName,
127 const char *aVendorModel,
128 const char *aVendorSwVersion,
129 const char *aVendorData,
130 otJoinerCallback aCallback,
131 void *aContext)
132 {
133 Error error;
134 JoinerPskd joinerPskd;
135 Mac::ExtAddress randomAddress;
136 SteeringData::HashBitIndexes filterIndexes;
137
138 LogInfo("Joiner starting");
139
140 VerifyOrExit(aProvisioningUrl == nullptr || IsValidUtf8String(aProvisioningUrl), error = kErrorInvalidArgs);
141 VerifyOrExit(aVendorName == nullptr || IsValidUtf8String(aVendorName), error = kErrorInvalidArgs);
142 VerifyOrExit(aVendorSwVersion == nullptr || IsValidUtf8String(aVendorSwVersion), error = kErrorInvalidArgs);
143
144 VerifyOrExit(mState == kStateIdle, error = kErrorBusy);
145 VerifyOrExit(Get<ThreadNetif>().IsUp() && Get<Mle::Mle>().GetRole() == Mle::kRoleDisabled,
146 error = kErrorInvalidState);
147
148 SuccessOrExit(error = joinerPskd.SetFrom(aPskd));
149
150 // Use random-generated extended address.
151 randomAddress.GenerateRandom();
152 Get<Mac::Mac>().SetExtAddress(randomAddress);
153 Get<Mle::MleRouter>().UpdateLinkLocalAddress();
154
155 SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(kJoinerUdpPort));
156 Get<Tmf::SecureAgent>().SetPsk(joinerPskd);
157
158 for (JoinerRouter &router : mJoinerRouters)
159 {
160 router.mPriority = 0; // Priority zero means entry is not in-use.
161 }
162
163 SuccessOrExit(error = PrepareJoinerFinalizeMessage(aProvisioningUrl, aVendorName, aVendorModel, aVendorSwVersion,
164 aVendorData));
165
166 if (!mDiscerner.IsEmpty())
167 {
168 SteeringData::CalculateHashBitIndexes(mDiscerner, filterIndexes);
169 }
170 else
171 {
172 SteeringData::CalculateHashBitIndexes(mId, filterIndexes);
173 }
174
175 SuccessOrExit(error = Get<Mle::DiscoverScanner>().Discover(Mac::ChannelMask(0), Get<Mac::Mac>().GetPanId(),
176 /* aJoiner */ true, /* aEnableFiltering */ true,
177 &filterIndexes, HandleDiscoverResult, this));
178
179 mCallback.Set(aCallback, aContext);
180 SetState(kStateDiscover);
181
182 exit:
183 if (error != kErrorNone)
184 {
185 FreeJoinerFinalizeMessage();
186 }
187
188 LogWarnOnError(error, "start joiner");
189 return error;
190 }
191
Stop(void)192 void Joiner::Stop(void)
193 {
194 LogInfo("Joiner stopped");
195
196 // Callback is set to `nullptr` to skip calling it from `Finish()`
197 mCallback.Clear();
198 Finish(kErrorAbort);
199 }
200
Finish(Error aError)201 void Joiner::Finish(Error aError)
202 {
203 switch (mState)
204 {
205 case kStateIdle:
206 ExitNow();
207
208 case kStateConnect:
209 case kStateConnected:
210 case kStateEntrust:
211 case kStateJoined:
212 Get<Tmf::SecureAgent>().Disconnect();
213 IgnoreError(Get<Ip6::Filter>().RemoveUnsecurePort(kJoinerUdpPort));
214 mTimer.Stop();
215
216 OT_FALL_THROUGH;
217
218 case kStateDiscover:
219 Get<Tmf::SecureAgent>().Stop();
220 break;
221 }
222
223 SetState(kStateIdle);
224 FreeJoinerFinalizeMessage();
225
226 mCallback.InvokeIfSet(aError);
227
228 exit:
229 return;
230 }
231
CalculatePriority(int8_t aRssi,bool aSteeringDataAllowsAny)232 uint8_t Joiner::CalculatePriority(int8_t aRssi, bool aSteeringDataAllowsAny)
233 {
234 int16_t priority;
235
236 if (aRssi == Radio::kInvalidRssi)
237 {
238 aRssi = -127;
239 }
240
241 // Clamp the RSSI to range [-127, -1]
242 priority = Clamp<int8_t>(aRssi, -127, -1);
243
244 // Assign higher priority to networks with an exact match of Joiner
245 // ID in the Steering Data (128 < priority < 256) compared to ones
246 // that allow all Joiners (0 < priority < 128). Sub-prioritize
247 // based on signal strength. Priority 0 is reserved for unused
248 // entry.
249
250 priority += aSteeringDataAllowsAny ? 128 : 256;
251
252 return static_cast<uint8_t>(priority);
253 }
254
HandleDiscoverResult(Mle::DiscoverScanner::ScanResult * aResult,void * aContext)255 void Joiner::HandleDiscoverResult(Mle::DiscoverScanner::ScanResult *aResult, void *aContext)
256 {
257 static_cast<Joiner *>(aContext)->HandleDiscoverResult(aResult);
258 }
259
HandleDiscoverResult(Mle::DiscoverScanner::ScanResult * aResult)260 void Joiner::HandleDiscoverResult(Mle::DiscoverScanner::ScanResult *aResult)
261 {
262 VerifyOrExit(mState == kStateDiscover);
263
264 if (aResult != nullptr && aResult->mJoinerUdpPort > 0)
265 {
266 SaveDiscoveredJoinerRouter(*aResult);
267 }
268 else
269 {
270 Get<Mac::Mac>().SetExtAddress(mId);
271 Get<Mle::MleRouter>().UpdateLinkLocalAddress();
272
273 mJoinerRouterIndex = 0;
274 TryNextJoinerRouter(kErrorNone);
275 }
276
277 exit:
278 return;
279 }
280
SaveDiscoveredJoinerRouter(const Mle::DiscoverScanner::ScanResult & aResult)281 void Joiner::SaveDiscoveredJoinerRouter(const Mle::DiscoverScanner::ScanResult &aResult)
282 {
283 uint8_t priority;
284 bool doesAllowAny;
285 JoinerRouter *end = GetArrayEnd(mJoinerRouters);
286 JoinerRouter *entry;
287
288 doesAllowAny = AsCoreType(&aResult.mSteeringData).PermitsAllJoiners();
289
290 LogInfo("Joiner discover network: %s, pan:0x%04x, port:%d, chan:%d, rssi:%d, allow-any:%s",
291 AsCoreType(&aResult.mExtAddress).ToString().AsCString(), aResult.mPanId, aResult.mJoinerUdpPort,
292 aResult.mChannel, aResult.mRssi, ToYesNo(doesAllowAny));
293
294 priority = CalculatePriority(aResult.mRssi, doesAllowAny);
295
296 // We keep the list sorted based on priority. Find the place to
297 // add the new result.
298
299 for (entry = &mJoinerRouters[0]; entry < end; entry++)
300 {
301 if (priority > entry->mPriority)
302 {
303 break;
304 }
305 }
306
307 VerifyOrExit(entry < end);
308
309 // Shift elements in array to make room for the new one.
310 memmove(entry + 1, entry,
311 static_cast<size_t>(reinterpret_cast<uint8_t *>(end - 1) - reinterpret_cast<uint8_t *>(entry)));
312
313 entry->mExtAddr = AsCoreType(&aResult.mExtAddress);
314 entry->mPanId = aResult.mPanId;
315 entry->mJoinerUdpPort = aResult.mJoinerUdpPort;
316 entry->mChannel = aResult.mChannel;
317 entry->mPriority = priority;
318
319 exit:
320 return;
321 }
322
TryNextJoinerRouter(Error aPrevError)323 void Joiner::TryNextJoinerRouter(Error aPrevError)
324 {
325 for (; mJoinerRouterIndex < GetArrayLength(mJoinerRouters); mJoinerRouterIndex++)
326 {
327 JoinerRouter &router = mJoinerRouters[mJoinerRouterIndex];
328 Error error;
329
330 if (router.mPriority == 0)
331 {
332 break;
333 }
334
335 error = Connect(router);
336 VerifyOrExit(error != kErrorNone, mJoinerRouterIndex++);
337
338 // Save the error from `Connect` only if there is no previous
339 // error from earlier attempts. This ensures that if there has
340 // been a previous Joiner Router connect attempt where
341 // `Connect()` call itself was successful, the error status
342 // emitted from `Finish()` call corresponds to the error from
343 // that attempt.
344
345 if (aPrevError == kErrorNone)
346 {
347 aPrevError = error;
348 }
349 }
350
351 if (aPrevError == kErrorNone)
352 {
353 aPrevError = kErrorNotFound;
354 }
355
356 Finish(aPrevError);
357
358 exit:
359 return;
360 }
361
Connect(JoinerRouter & aRouter)362 Error Joiner::Connect(JoinerRouter &aRouter)
363 {
364 Error error = kErrorNotFound;
365 Ip6::SockAddr sockAddr(aRouter.mJoinerUdpPort);
366
367 LogInfo("Joiner connecting to %s, pan:0x%04x, chan:%d", aRouter.mExtAddr.ToString().AsCString(), aRouter.mPanId,
368 aRouter.mChannel);
369
370 Get<Mac::Mac>().SetPanId(aRouter.mPanId);
371 SuccessOrExit(error = Get<Mac::Mac>().SetPanChannel(aRouter.mChannel));
372 SuccessOrExit(error = Get<Ip6::Filter>().AddUnsecurePort(kJoinerUdpPort));
373
374 sockAddr.GetAddress().SetToLinkLocalAddress(aRouter.mExtAddr);
375
376 SuccessOrExit(error = Get<Tmf::SecureAgent>().Connect(sockAddr, Joiner::HandleSecureCoapClientConnect, this));
377
378 SetState(kStateConnect);
379
380 exit:
381 LogWarnOnError(error, "start secure joiner connection");
382 return error;
383 }
384
HandleSecureCoapClientConnect(bool aConnected,void * aContext)385 void Joiner::HandleSecureCoapClientConnect(bool aConnected, void *aContext)
386 {
387 static_cast<Joiner *>(aContext)->HandleSecureCoapClientConnect(aConnected);
388 }
389
HandleSecureCoapClientConnect(bool aConnected)390 void Joiner::HandleSecureCoapClientConnect(bool aConnected)
391 {
392 VerifyOrExit(mState == kStateConnect);
393
394 if (aConnected)
395 {
396 SetState(kStateConnected);
397 SendJoinerFinalize();
398 mTimer.Start(kResponseTimeout);
399 }
400 else
401 {
402 TryNextJoinerRouter(kErrorSecurity);
403 }
404
405 exit:
406 return;
407 }
408
PrepareJoinerFinalizeMessage(const char * aProvisioningUrl,const char * aVendorName,const char * aVendorModel,const char * aVendorSwVersion,const char * aVendorData)409 Error Joiner::PrepareJoinerFinalizeMessage(const char *aProvisioningUrl,
410 const char *aVendorName,
411 const char *aVendorModel,
412 const char *aVendorSwVersion,
413 const char *aVendorData)
414 {
415 Error error = kErrorNone;
416 VendorStackVersionTlv vendorStackVersionTlv;
417
418 mFinalizeMessage = Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriJoinerFinalize);
419 VerifyOrExit(mFinalizeMessage != nullptr, error = kErrorNoBufs);
420
421 mFinalizeMessage->SetOffset(mFinalizeMessage->GetLength());
422
423 SuccessOrExit(error = Tlv::Append<StateTlv>(*mFinalizeMessage, StateTlv::kAccept));
424
425 SuccessOrExit(error = Tlv::Append<VendorNameTlv>(*mFinalizeMessage, aVendorName));
426 SuccessOrExit(error = Tlv::Append<VendorModelTlv>(*mFinalizeMessage, aVendorModel));
427 SuccessOrExit(error = Tlv::Append<VendorSwVersionTlv>(*mFinalizeMessage, aVendorSwVersion));
428
429 vendorStackVersionTlv.Init();
430 vendorStackVersionTlv.SetOui(OPENTHREAD_CONFIG_STACK_VENDOR_OUI);
431 vendorStackVersionTlv.SetMajor(OPENTHREAD_CONFIG_STACK_VERSION_MAJOR);
432 vendorStackVersionTlv.SetMinor(OPENTHREAD_CONFIG_STACK_VERSION_MINOR);
433 vendorStackVersionTlv.SetRevision(OPENTHREAD_CONFIG_STACK_VERSION_REV);
434 SuccessOrExit(error = vendorStackVersionTlv.AppendTo(*mFinalizeMessage));
435
436 if (aVendorData != nullptr)
437 {
438 SuccessOrExit(error = Tlv::Append<VendorDataTlv>(*mFinalizeMessage, aVendorData));
439 }
440
441 if (aProvisioningUrl != nullptr)
442 {
443 SuccessOrExit(error = Tlv::Append<ProvisioningUrlTlv>(*mFinalizeMessage, aProvisioningUrl));
444 }
445
446 exit:
447 if (error != kErrorNone)
448 {
449 FreeJoinerFinalizeMessage();
450 }
451
452 return error;
453 }
454
FreeJoinerFinalizeMessage(void)455 void Joiner::FreeJoinerFinalizeMessage(void)
456 {
457 VerifyOrExit(mState == kStateIdle && mFinalizeMessage != nullptr);
458
459 mFinalizeMessage->Free();
460 mFinalizeMessage = nullptr;
461
462 exit:
463 return;
464 }
465
SendJoinerFinalize(void)466 void Joiner::SendJoinerFinalize(void)
467 {
468 OT_ASSERT(mFinalizeMessage != nullptr);
469
470 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
471 LogCertMessage("[THCI] direction=send | type=JOIN_FIN.req |", *mFinalizeMessage);
472 #endif
473
474 SuccessOrExit(Get<Tmf::SecureAgent>().SendMessage(*mFinalizeMessage, Joiner::HandleJoinerFinalizeResponse, this));
475 mFinalizeMessage = nullptr;
476
477 LogInfo("Sent %s", UriToString<kUriJoinerFinalize>());
478
479 exit:
480 return;
481 }
482
HandleJoinerFinalizeResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)483 void Joiner::HandleJoinerFinalizeResponse(void *aContext,
484 otMessage *aMessage,
485 const otMessageInfo *aMessageInfo,
486 Error aResult)
487 {
488 static_cast<Joiner *>(aContext)->HandleJoinerFinalizeResponse(AsCoapMessagePtr(aMessage), &AsCoreType(aMessageInfo),
489 aResult);
490 }
491
HandleJoinerFinalizeResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)492 void Joiner::HandleJoinerFinalizeResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
493 {
494 OT_UNUSED_VARIABLE(aMessageInfo);
495
496 uint8_t state;
497
498 VerifyOrExit(mState == kStateConnected && aResult == kErrorNone);
499 OT_ASSERT(aMessage != nullptr);
500
501 VerifyOrExit(aMessage->IsAck() && aMessage->GetCode() == Coap::kCodeChanged);
502
503 SuccessOrExit(Tlv::Find<StateTlv>(*aMessage, state));
504
505 SetState(kStateEntrust);
506 mTimer.Start(kResponseTimeout);
507
508 LogInfo("Received %s %d", UriToString<kUriJoinerFinalize>(), state);
509
510 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
511 LogCertMessage("[THCI] direction=recv | type=JOIN_FIN.rsp |", *aMessage);
512 #endif
513
514 exit:
515 Get<Tmf::SecureAgent>().Disconnect();
516 IgnoreError(Get<Ip6::Filter>().RemoveUnsecurePort(kJoinerUdpPort));
517 }
518
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)519 template <> void Joiner::HandleTmf<kUriJoinerEntrust>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
520 {
521 Error error;
522 Dataset::Info datasetInfo;
523
524 VerifyOrExit(mState == kStateEntrust && aMessage.IsConfirmablePostRequest(), error = kErrorDrop);
525
526 LogInfo("Received %s", UriToString<kUriJoinerEntrust>());
527 LogCert("[THCI] direction=recv | type=JOIN_ENT.ntf");
528
529 datasetInfo.Clear();
530
531 SuccessOrExit(error = Tlv::Find<NetworkKeyTlv>(aMessage, datasetInfo.Update<Dataset::kNetworkKey>()));
532
533 datasetInfo.Set<Dataset::kChannel>(Get<Mac::Mac>().GetPanChannel());
534 datasetInfo.Set<Dataset::kPanId>(Get<Mac::Mac>().GetPanId());
535
536 Get<ActiveDatasetManager>().SaveLocal(datasetInfo);
537
538 LogInfo("Joiner successful!");
539
540 SendJoinerEntrustResponse(aMessage, aMessageInfo);
541
542 // Delay extended address configuration to allow DTLS wrap up.
543 mTimer.Start(kConfigExtAddressDelay);
544
545 exit:
546 LogWarnOnError(error, "process joiner entrust");
547 }
548
SendJoinerEntrustResponse(const Coap::Message & aRequest,const Ip6::MessageInfo & aRequestInfo)549 void Joiner::SendJoinerEntrustResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aRequestInfo)
550 {
551 Error error = kErrorNone;
552 Coap::Message *message;
553 Ip6::MessageInfo responseInfo(aRequestInfo);
554
555 message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
556 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
557
558 message->SetSubType(Message::kSubTypeJoinerEntrust);
559
560 responseInfo.GetSockAddr().Clear();
561 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, responseInfo));
562
563 SetState(kStateJoined);
564
565 LogInfo("Sent %s response", UriToString<kUriJoinerEntrust>());
566 LogCert("[THCI] direction=send | type=JOIN_ENT.rsp");
567
568 exit:
569 FreeMessageOnError(message, error);
570 }
571
HandleTimer(void)572 void Joiner::HandleTimer(void)
573 {
574 Error error = kErrorNone;
575
576 switch (mState)
577 {
578 case kStateConnected:
579 case kStateEntrust:
580 error = kErrorResponseTimeout;
581 break;
582
583 case kStateJoined:
584 Mac::ExtAddress extAddress;
585
586 extAddress.GenerateRandom();
587 Get<Mac::Mac>().SetExtAddress(extAddress);
588 Get<Mle::MleRouter>().UpdateLinkLocalAddress();
589
590 error = kErrorNone;
591 break;
592
593 case kStateIdle:
594 case kStateDiscover:
595 case kStateConnect:
596 OT_ASSERT(false);
597 }
598
599 Finish(error);
600 }
601
602 // LCOV_EXCL_START
603
StateToString(State aState)604 const char *Joiner::StateToString(State aState)
605 {
606 static const char *const kStateStrings[] = {
607 "Idle", // (0) kStateIdle
608 "Discover", // (1) kStateDiscover
609 "Connecting", // (2) kStateConnect
610 "Connected", // (3) kStateConnected
611 "Entrust", // (4) kStateEntrust
612 "Joined", // (5) kStateJoined
613 };
614
615 static_assert(kStateIdle == 0, "kStateIdle value is incorrect");
616 static_assert(kStateDiscover == 1, "kStateDiscover value is incorrect");
617 static_assert(kStateConnect == 2, "kStateConnect value is incorrect");
618 static_assert(kStateConnected == 3, "kStateConnected value is incorrect");
619 static_assert(kStateEntrust == 4, "kStateEntrust value is incorrect");
620 static_assert(kStateJoined == 5, "kStateJoined value is incorrect");
621
622 return kStateStrings[aState];
623 }
624
625 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
LogCertMessage(const char * aText,const Coap::Message & aMessage) const626 void Joiner::LogCertMessage(const char *aText, const Coap::Message &aMessage) const
627 {
628 OT_UNUSED_VARIABLE(aText);
629
630 uint8_t buf[OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE];
631
632 VerifyOrExit(aMessage.GetLength() <= sizeof(buf));
633 aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset());
634
635 DumpCert(aText, buf, aMessage.GetLength() - aMessage.GetOffset());
636
637 exit:
638 return;
639 }
640 #endif
641
642 // LCOV_EXCL_STOP
643
644 } // namespace MeshCoP
645 } // namespace ot
646
647 #endif // OPENTHREAD_CONFIG_JOINER_ENABLE
648