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