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