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