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