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 a Commissioner role.
32 */
33
34 #include "commissioner.hpp"
35
36 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
37
38 #include "instance/instance.hpp"
39
40 namespace ot {
41 namespace MeshCoP {
42
43 RegisterLogModule("Commissioner");
44
Commissioner(Instance & aInstance)45 Commissioner::Commissioner(Instance &aInstance)
46 : InstanceLocator(aInstance)
47 , mActiveJoiner(nullptr)
48 , mJoinerPort(0)
49 , mJoinerRloc(0)
50 , mSessionId(0)
51 , mTransmitAttempts(0)
52 , mJoinerExpirationTimer(aInstance)
53 , mTimer(aInstance)
54 , mJoinerSessionTimer(aInstance)
55 , mAnnounceBegin(aInstance)
56 , mEnergyScan(aInstance)
57 , mPanIdQuery(aInstance)
58 , mState(kStateDisabled)
59 {
60 ClearAllBytes(mJoiners);
61
62 mCommissionerAloc.InitAsThreadOriginMeshLocal();
63 mCommissionerAloc.mPreferred = true;
64
65 IgnoreError(SetId("OpenThread Commissioner"));
66
67 mProvisioningUrl[0] = '\0';
68 }
69
SetState(State aState)70 void Commissioner::SetState(State aState)
71 {
72 State oldState = mState;
73
74 OT_UNUSED_VARIABLE(oldState);
75
76 SuccessOrExit(Get<Notifier>().Update(mState, aState, kEventCommissionerStateChanged));
77
78 LogInfo("State: %s -> %s", StateToString(oldState), StateToString(aState));
79
80 mStateCallback.InvokeIfSet(MapEnum(mState));
81
82 exit:
83 return;
84 }
85
SignalJoinerEvent(JoinerEvent aEvent,const Joiner * aJoiner) const86 void Commissioner::SignalJoinerEvent(JoinerEvent aEvent, const Joiner *aJoiner) const
87 {
88 otJoinerInfo joinerInfo;
89 Mac::ExtAddress joinerId;
90 bool noJoinerId = false;
91
92 VerifyOrExit(mJoinerCallback.IsSet() && (aJoiner != nullptr));
93
94 aJoiner->CopyToJoinerInfo(joinerInfo);
95
96 if (aJoiner->mType == Joiner::kTypeEui64)
97 {
98 ComputeJoinerId(aJoiner->mSharedId.mEui64, joinerId);
99 }
100 else if (aJoiner == mActiveJoiner)
101 {
102 mJoinerIid.ConvertToExtAddress(joinerId);
103 }
104 else
105 {
106 noJoinerId = true;
107 }
108
109 mJoinerCallback.Invoke(MapEnum(aEvent), &joinerInfo, noJoinerId ? nullptr : &joinerId);
110
111 exit:
112 return;
113 }
114
HandleSecureAgentConnectEvent(Dtls::Session::ConnectEvent aEvent,void * aContext)115 void Commissioner::HandleSecureAgentConnectEvent(Dtls::Session::ConnectEvent aEvent, void *aContext)
116 {
117 static_cast<Commissioner *>(aContext)->HandleSecureAgentConnectEvent(aEvent);
118 }
119
HandleSecureAgentConnectEvent(Dtls::Session::ConnectEvent aEvent)120 void Commissioner::HandleSecureAgentConnectEvent(Dtls::Session::ConnectEvent aEvent)
121 {
122 bool isConnected = (aEvent == Dtls::Session::kConnected);
123 if (!isConnected)
124 {
125 mJoinerSessionTimer.Stop();
126 }
127
128 SignalJoinerEvent(isConnected ? kJoinerEventConnected : kJoinerEventEnd, mActiveJoiner);
129 }
130
GetUnusedJoinerEntry(void)131 Commissioner::Joiner *Commissioner::GetUnusedJoinerEntry(void)
132 {
133 Joiner *rval = nullptr;
134
135 for (Joiner &joiner : mJoiners)
136 {
137 if (joiner.mType == Joiner::kTypeUnused)
138 {
139 rval = &joiner;
140 break;
141 }
142 }
143
144 return rval;
145 }
146
FindJoinerEntry(const Mac::ExtAddress * aEui64)147 Commissioner::Joiner *Commissioner::FindJoinerEntry(const Mac::ExtAddress *aEui64)
148 {
149 Joiner *rval = nullptr;
150
151 for (Joiner &joiner : mJoiners)
152 {
153 switch (joiner.mType)
154 {
155 case Joiner::kTypeUnused:
156 case Joiner::kTypeDiscerner:
157 break;
158
159 case Joiner::kTypeAny:
160 if (aEui64 == nullptr)
161 {
162 ExitNow(rval = &joiner);
163 }
164 break;
165
166 case Joiner::kTypeEui64:
167 if ((aEui64 != nullptr) && (joiner.mSharedId.mEui64 == *aEui64))
168 {
169 ExitNow(rval = &joiner);
170 }
171 break;
172 }
173 }
174
175 exit:
176 return rval;
177 }
178
FindJoinerEntry(const JoinerDiscerner & aDiscerner)179 Commissioner::Joiner *Commissioner::FindJoinerEntry(const JoinerDiscerner &aDiscerner)
180 {
181 Joiner *rval = nullptr;
182
183 for (Joiner &joiner : mJoiners)
184 {
185 if ((joiner.mType == Joiner::kTypeDiscerner) && (aDiscerner == joiner.mSharedId.mDiscerner))
186 {
187 rval = &joiner;
188 break;
189 }
190 }
191
192 return rval;
193 }
194
FindBestMatchingJoinerEntry(const Mac::ExtAddress & aReceivedJoinerId)195 Commissioner::Joiner *Commissioner::FindBestMatchingJoinerEntry(const Mac::ExtAddress &aReceivedJoinerId)
196 {
197 Joiner *best = nullptr;
198 Mac::ExtAddress joinerId;
199
200 // Prefer a full Joiner ID match, if not found use the entry
201 // accepting any joiner.
202
203 for (Joiner &joiner : mJoiners)
204 {
205 switch (joiner.mType)
206 {
207 case Joiner::kTypeUnused:
208 break;
209
210 case Joiner::kTypeAny:
211 if (best == nullptr)
212 {
213 best = &joiner;
214 }
215 break;
216
217 case Joiner::kTypeEui64:
218 ComputeJoinerId(joiner.mSharedId.mEui64, joinerId);
219 if (joinerId == aReceivedJoinerId)
220 {
221 ExitNow(best = &joiner);
222 }
223 break;
224
225 case Joiner::kTypeDiscerner:
226 if (joiner.mSharedId.mDiscerner.Matches(aReceivedJoinerId))
227 {
228 if ((best == nullptr) ||
229 ((best->mType == Joiner::kTypeDiscerner) &&
230 (best->mSharedId.mDiscerner.GetLength() < joiner.mSharedId.mDiscerner.GetLength())))
231 {
232 best = &joiner;
233 }
234 }
235 break;
236 }
237 }
238
239 exit:
240 return best;
241 }
242
RemoveJoinerEntry(Commissioner::Joiner & aJoiner)243 void Commissioner::RemoveJoinerEntry(Commissioner::Joiner &aJoiner)
244 {
245 // Create a copy of `aJoiner` to use for signaling joiner event
246 // and logging after the entry is removed. This ensures the joiner
247 // event callback is invoked after all states are cleared.
248
249 Joiner joinerCopy = aJoiner;
250
251 aJoiner.mType = Joiner::kTypeUnused;
252
253 if (&aJoiner == mActiveJoiner)
254 {
255 mActiveJoiner = nullptr;
256 }
257
258 SendCommissionerSet();
259
260 LogJoinerEntry("Removed", joinerCopy);
261 SignalJoinerEvent(kJoinerEventRemoved, &joinerCopy);
262 }
263
Start(StateCallback aStateCallback,JoinerCallback aJoinerCallback,void * aCallbackContext)264 Error Commissioner::Start(StateCallback aStateCallback, JoinerCallback aJoinerCallback, void *aCallbackContext)
265 {
266 Error error = kErrorNone;
267
268 VerifyOrExit(Get<Mle::MleRouter>().IsAttached(), error = kErrorInvalidState);
269 VerifyOrExit(mState == kStateDisabled, error = kErrorAlready);
270
271 SuccessOrExit(error = Get<Tmf::SecureAgent>().Open());
272 SuccessOrExit(error = Get<Tmf::SecureAgent>().Bind(SendRelayTransmit, this));
273
274 Get<Tmf::SecureAgent>().SetConnectCallback(HandleSecureAgentConnectEvent, this);
275
276 mStateCallback.Set(aStateCallback, aCallbackContext);
277 mJoinerCallback.Set(aJoinerCallback, aCallbackContext);
278 mTransmitAttempts = 0;
279
280 SuccessOrExit(error = SendPetition());
281 SetState(kStatePetition);
282
283 LogInfo("start commissioner %s", mCommissionerId);
284
285 exit:
286 if ((error != kErrorNone) && (error != kErrorAlready))
287 {
288 Get<Tmf::SecureAgent>().Close();
289 LogWarnOnError(error, "start commissioner");
290 }
291
292 return error;
293 }
294
Stop(ResignMode aResignMode)295 Error Commissioner::Stop(ResignMode aResignMode)
296 {
297 Error error = kErrorNone;
298 bool needResign = false;
299
300 VerifyOrExit(mState != kStateDisabled, error = kErrorAlready);
301
302 mJoinerSessionTimer.Stop();
303 Get<Tmf::SecureAgent>().Close();
304
305 if (mState == kStateActive)
306 {
307 Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
308 ClearJoiners();
309 needResign = true;
310 }
311 else if (mState == kStatePetition)
312 {
313 mTransmitAttempts = 0;
314 }
315
316 mTimer.Stop();
317
318 SetState(kStateDisabled);
319
320 if (needResign && (aResignMode == kSendKeepAliveToResign))
321 {
322 SendKeepAlive();
323 }
324
325 exit:
326 if (error != kErrorAlready)
327 {
328 LogWarnOnError(error, "stop commissioner");
329 }
330
331 return error;
332 }
333
SetId(const char * aId)334 Error Commissioner::SetId(const char *aId)
335 {
336 Error error = kErrorNone;
337
338 VerifyOrExit(IsDisabled(), error = kErrorInvalidState);
339 error = StringCopy(mCommissionerId, aId, kStringCheckUtf8Encoding);
340
341 exit:
342 return error;
343 }
344
ComputeBloomFilter(SteeringData & aSteeringData) const345 void Commissioner::ComputeBloomFilter(SteeringData &aSteeringData) const
346 {
347 Mac::ExtAddress joinerId;
348
349 aSteeringData.Init();
350
351 for (const Joiner &joiner : mJoiners)
352 {
353 switch (joiner.mType)
354 {
355 case Joiner::kTypeUnused:
356 break;
357
358 case Joiner::kTypeEui64:
359 ComputeJoinerId(joiner.mSharedId.mEui64, joinerId);
360 aSteeringData.UpdateBloomFilter(joinerId);
361 break;
362
363 case Joiner::kTypeDiscerner:
364 aSteeringData.UpdateBloomFilter(joiner.mSharedId.mDiscerner);
365 break;
366
367 case Joiner::kTypeAny:
368 aSteeringData.SetToPermitAllJoiners();
369 ExitNow();
370 }
371 }
372
373 exit:
374 return;
375 }
376
SendCommissionerSet(void)377 void Commissioner::SendCommissionerSet(void)
378 {
379 Error error = kErrorNone;
380 CommissioningDataset dataset;
381
382 VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
383
384 dataset.Clear();
385
386 dataset.SetSessionId(mSessionId);
387 ComputeBloomFilter(dataset.UpdateSteeringData());
388
389 error = SendMgmtCommissionerSetRequest(dataset, nullptr, 0);
390
391 exit:
392 LogWarnOnError(error, "send MGMT_COMMISSIONER_SET.req");
393 OT_UNUSED_VARIABLE(error);
394 }
395
ClearJoiners(void)396 void Commissioner::ClearJoiners(void)
397 {
398 for (Joiner &joiner : mJoiners)
399 {
400 joiner.mType = Joiner::kTypeUnused;
401 }
402
403 SendCommissionerSet();
404 }
405
AddJoiner(const Mac::ExtAddress * aEui64,const JoinerDiscerner * aDiscerner,const char * aPskd,uint32_t aTimeout)406 Error Commissioner::AddJoiner(const Mac::ExtAddress *aEui64,
407 const JoinerDiscerner *aDiscerner,
408 const char *aPskd,
409 uint32_t aTimeout)
410 {
411 Error error = kErrorNone;
412 Joiner *joiner;
413
414 VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
415
416 if (aDiscerner != nullptr)
417 {
418 VerifyOrExit(aDiscerner->IsValid(), error = kErrorInvalidArgs);
419 joiner = FindJoinerEntry(*aDiscerner);
420 }
421 else
422 {
423 joiner = FindJoinerEntry(aEui64);
424 }
425
426 if (joiner == nullptr)
427 {
428 joiner = GetUnusedJoinerEntry();
429 }
430
431 VerifyOrExit(joiner != nullptr, error = kErrorNoBufs);
432
433 SuccessOrExit(error = joiner->mPskd.SetFrom(aPskd));
434
435 if (aDiscerner != nullptr)
436 {
437 joiner->mType = Joiner::kTypeDiscerner;
438 joiner->mSharedId.mDiscerner = *aDiscerner;
439 }
440 else if (aEui64 != nullptr)
441 {
442 joiner->mType = Joiner::kTypeEui64;
443 joiner->mSharedId.mEui64 = *aEui64;
444 }
445 else
446 {
447 joiner->mType = Joiner::kTypeAny;
448 }
449
450 joiner->mExpirationTime = TimerMilli::GetNow() + Time::SecToMsec(aTimeout);
451
452 mJoinerExpirationTimer.FireAtIfEarlier(joiner->mExpirationTime);
453
454 SendCommissionerSet();
455
456 LogJoinerEntry("Added", *joiner);
457
458 exit:
459 return error;
460 }
461
CopyToJoinerInfo(otJoinerInfo & aJoiner) const462 void Commissioner::Joiner::CopyToJoinerInfo(otJoinerInfo &aJoiner) const
463 {
464 ClearAllBytes(aJoiner);
465
466 switch (mType)
467 {
468 case kTypeAny:
469 aJoiner.mType = OT_JOINER_INFO_TYPE_ANY;
470 break;
471
472 case kTypeEui64:
473 aJoiner.mType = OT_JOINER_INFO_TYPE_EUI64;
474 aJoiner.mSharedId.mEui64 = mSharedId.mEui64;
475 break;
476
477 case kTypeDiscerner:
478 aJoiner.mType = OT_JOINER_INFO_TYPE_DISCERNER;
479 aJoiner.mSharedId.mDiscerner = mSharedId.mDiscerner;
480 break;
481
482 case kTypeUnused:
483 ExitNow();
484 }
485
486 aJoiner.mPskd = mPskd;
487 aJoiner.mExpirationTime = mExpirationTime - TimerMilli::GetNow();
488
489 exit:
490 return;
491 }
492
GetNextJoinerInfo(uint16_t & aIterator,otJoinerInfo & aJoinerInfo) const493 Error Commissioner::GetNextJoinerInfo(uint16_t &aIterator, otJoinerInfo &aJoinerInfo) const
494 {
495 Error error = kErrorNone;
496
497 while (aIterator < GetArrayLength(mJoiners))
498 {
499 const Joiner &joiner = mJoiners[aIterator++];
500
501 if (joiner.mType != Joiner::kTypeUnused)
502 {
503 joiner.CopyToJoinerInfo(aJoinerInfo);
504 ExitNow();
505 }
506 }
507
508 error = kErrorNotFound;
509
510 exit:
511 return error;
512 }
513
RemoveJoiner(const Mac::ExtAddress * aEui64,const JoinerDiscerner * aDiscerner,uint32_t aDelay)514 Error Commissioner::RemoveJoiner(const Mac::ExtAddress *aEui64, const JoinerDiscerner *aDiscerner, uint32_t aDelay)
515 {
516 Error error = kErrorNone;
517 Joiner *joiner;
518
519 VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
520
521 if (aDiscerner != nullptr)
522 {
523 VerifyOrExit(aDiscerner->IsValid(), error = kErrorInvalidArgs);
524 joiner = FindJoinerEntry(*aDiscerner);
525 }
526 else
527 {
528 joiner = FindJoinerEntry(aEui64);
529 }
530
531 VerifyOrExit(joiner != nullptr, error = kErrorNotFound);
532
533 RemoveJoiner(*joiner, aDelay);
534
535 exit:
536 return error;
537 }
538
RemoveJoiner(Joiner & aJoiner,uint32_t aDelay)539 void Commissioner::RemoveJoiner(Joiner &aJoiner, uint32_t aDelay)
540 {
541 if (aDelay > 0)
542 {
543 TimeMilli newExpirationTime = TimerMilli::GetNow() + Time::SecToMsec(aDelay);
544
545 if (aJoiner.mExpirationTime > newExpirationTime)
546 {
547 aJoiner.mExpirationTime = newExpirationTime;
548 mJoinerExpirationTimer.FireAtIfEarlier(newExpirationTime);
549 }
550 }
551 else
552 {
553 RemoveJoinerEntry(aJoiner);
554 }
555 }
556
SetProvisioningUrl(const char * aProvisioningUrl)557 Error Commissioner::SetProvisioningUrl(const char *aProvisioningUrl)
558 {
559 return StringCopy(mProvisioningUrl, aProvisioningUrl, kStringCheckUtf8Encoding);
560 }
561
HandleTimer(void)562 void Commissioner::HandleTimer(void)
563 {
564 switch (mState)
565 {
566 case kStateDisabled:
567 break;
568
569 case kStatePetition:
570 IgnoreError(SendPetition());
571 break;
572
573 case kStateActive:
574 SendKeepAlive();
575 break;
576 }
577 }
578
HandleJoinerExpirationTimer(void)579 void Commissioner::HandleJoinerExpirationTimer(void)
580 {
581 NextFireTime nextTime;
582
583 for (Joiner &joiner : mJoiners)
584 {
585 if (joiner.mType == Joiner::kTypeUnused)
586 {
587 continue;
588 }
589
590 if (joiner.mExpirationTime <= nextTime.GetNow())
591 {
592 LogDebg("removing joiner due to timeout or successfully joined");
593 RemoveJoinerEntry(joiner);
594 }
595 else
596 {
597 nextTime.UpdateIfEarlier(joiner.mExpirationTime);
598 }
599 }
600
601 mJoinerExpirationTimer.FireAtIfEarlier(nextTime);
602 }
603
SendMgmtCommissionerGetRequest(const uint8_t * aTlvs,uint8_t aLength)604 Error Commissioner::SendMgmtCommissionerGetRequest(const uint8_t *aTlvs, uint8_t aLength)
605 {
606 Error error = kErrorNone;
607 Coap::Message *message;
608 Tmf::MessageInfo messageInfo(GetInstance());
609 Tlv tlv;
610
611 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriCommissionerGet);
612 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
613
614 if (aLength > 0)
615 {
616 tlv.SetType(Tlv::kGet);
617 tlv.SetLength(aLength);
618 SuccessOrExit(error = message->Append(tlv));
619 SuccessOrExit(error = message->AppendBytes(aTlvs, aLength));
620 }
621
622 messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc();
623 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
624 Commissioner::HandleMgmtCommissionerGetResponse, this));
625
626 LogInfo("Sent %s to leader", UriToString<kUriCommissionerGet>());
627
628 exit:
629 FreeMessageOnError(message, error);
630 return error;
631 }
632
HandleMgmtCommissionerGetResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aResult)633 void Commissioner::HandleMgmtCommissionerGetResponse(void *aContext,
634 otMessage *aMessage,
635 const otMessageInfo *aMessageInfo,
636 otError aResult)
637 {
638 static_cast<Commissioner *>(aContext)->HandleMgmtCommissionerGetResponse(AsCoapMessagePtr(aMessage),
639 AsCoreTypePtr(aMessageInfo), aResult);
640 }
641
HandleMgmtCommissionerGetResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)642 void Commissioner::HandleMgmtCommissionerGetResponse(Coap::Message *aMessage,
643 const Ip6::MessageInfo *aMessageInfo,
644 Error aResult)
645 {
646 OT_UNUSED_VARIABLE(aMessageInfo);
647
648 VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged);
649 LogInfo("Received %s response", UriToString<kUriCommissionerGet>());
650
651 exit:
652 return;
653 }
654
SendMgmtCommissionerSetRequest(const CommissioningDataset & aDataset,const uint8_t * aTlvs,uint8_t aLength)655 Error Commissioner::SendMgmtCommissionerSetRequest(const CommissioningDataset &aDataset,
656 const uint8_t *aTlvs,
657 uint8_t aLength)
658 {
659 Error error = kErrorNone;
660 Coap::Message *message;
661 Tmf::MessageInfo messageInfo(GetInstance());
662
663 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriCommissionerSet);
664 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
665
666 if (aDataset.IsLocatorSet())
667 {
668 SuccessOrExit(error = Tlv::Append<BorderAgentLocatorTlv>(*message, aDataset.GetLocator()));
669 }
670
671 if (aDataset.IsSessionIdSet())
672 {
673 SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, aDataset.GetSessionId()));
674 }
675
676 if (aDataset.IsSteeringDataSet())
677 {
678 const SteeringData &steeringData = aDataset.GetSteeringData();
679
680 SuccessOrExit(error = Tlv::Append<SteeringDataTlv>(*message, steeringData.GetData(), steeringData.GetLength()));
681 }
682
683 if (aDataset.IsJoinerUdpPortSet())
684 {
685 SuccessOrExit(error = Tlv::Append<JoinerUdpPortTlv>(*message, aDataset.GetJoinerUdpPort()));
686 }
687
688 if (aLength > 0)
689 {
690 SuccessOrExit(error = message->AppendBytes(aTlvs, aLength));
691 }
692
693 messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc();
694 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
695 Commissioner::HandleMgmtCommissionerSetResponse, this));
696
697 LogInfo("Sent %s to leader", UriToString<kUriCommissionerSet>());
698
699 exit:
700 FreeMessageOnError(message, error);
701 return error;
702 }
703
HandleMgmtCommissionerSetResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aResult)704 void Commissioner::HandleMgmtCommissionerSetResponse(void *aContext,
705 otMessage *aMessage,
706 const otMessageInfo *aMessageInfo,
707 otError aResult)
708 {
709 static_cast<Commissioner *>(aContext)->HandleMgmtCommissionerSetResponse(AsCoapMessagePtr(aMessage),
710 AsCoreTypePtr(aMessageInfo), aResult);
711 }
712
HandleMgmtCommissionerSetResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)713 void Commissioner::HandleMgmtCommissionerSetResponse(Coap::Message *aMessage,
714 const Ip6::MessageInfo *aMessageInfo,
715 Error aResult)
716 {
717 OT_UNUSED_VARIABLE(aMessageInfo);
718
719 Error error;
720 uint8_t state;
721
722 SuccessOrExit(error = aResult);
723 VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged && Tlv::Find<StateTlv>(*aMessage, state) == kErrorNone &&
724 state != StateTlv::kPending,
725 error = kErrorParse);
726
727 OT_UNUSED_VARIABLE(error);
728 exit:
729 LogInfo("Received %s response: %s", UriToString<kUriCommissionerSet>(),
730 error == kErrorNone ? StateTlv::StateToString(static_cast<StateTlv::State>(state)) : ErrorToString(error));
731 }
732
SendPetition(void)733 Error Commissioner::SendPetition(void)
734 {
735 Error error = kErrorNone;
736 Coap::Message *message = nullptr;
737 Tmf::MessageInfo messageInfo(GetInstance());
738
739 mTransmitAttempts++;
740
741 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriLeaderPetition);
742 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
743
744 SuccessOrExit(error = Tlv::Append<CommissionerIdTlv>(*message, mCommissionerId));
745
746 messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc();
747 SuccessOrExit(
748 error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, Commissioner::HandleLeaderPetitionResponse, this));
749
750 LogInfo("Sent %s", UriToString<kUriLeaderPetition>());
751
752 exit:
753 FreeMessageOnError(message, error);
754 return error;
755 }
756
HandleLeaderPetitionResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aResult)757 void Commissioner::HandleLeaderPetitionResponse(void *aContext,
758 otMessage *aMessage,
759 const otMessageInfo *aMessageInfo,
760 otError aResult)
761 {
762 static_cast<Commissioner *>(aContext)->HandleLeaderPetitionResponse(AsCoapMessagePtr(aMessage),
763 AsCoreTypePtr(aMessageInfo), aResult);
764 }
765
HandleLeaderPetitionResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)766 void Commissioner::HandleLeaderPetitionResponse(Coap::Message *aMessage,
767 const Ip6::MessageInfo *aMessageInfo,
768 Error aResult)
769 {
770 OT_UNUSED_VARIABLE(aMessageInfo);
771
772 uint8_t state;
773 bool retransmit = false;
774
775 VerifyOrExit(mState != kStateActive);
776 VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged,
777 retransmit = (mState == kStatePetition));
778
779 LogInfo("Received %s response", UriToString<kUriLeaderPetition>());
780
781 SuccessOrExit(Tlv::Find<StateTlv>(*aMessage, state));
782 VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive)));
783
784 SuccessOrExit(Tlv::Find<CommissionerSessionIdTlv>(*aMessage, mSessionId));
785
786 // reject this session by sending KeepAlive reject if commissioner is in disabled state
787 // this could happen if commissioner is stopped by API during petitioning
788 if (mState == kStateDisabled)
789 {
790 SendKeepAlive(mSessionId);
791 ExitNow();
792 }
793
794 Get<Mle::Mle>().GetCommissionerAloc(mSessionId, mCommissionerAloc.GetAddress());
795 Get<ThreadNetif>().AddUnicastAddress(mCommissionerAloc);
796
797 SetState(kStateActive);
798
799 mTransmitAttempts = 0;
800 mTimer.Start(Time::SecToMsec(kKeepAliveTimeout) / 2);
801
802 exit:
803
804 if (retransmit)
805 {
806 if (mTransmitAttempts >= kPetitionRetryCount)
807 {
808 IgnoreError(Stop(kDoNotSendKeepAlive));
809 }
810 else
811 {
812 mTimer.Start(Time::SecToMsec(kPetitionRetryDelay));
813 }
814 }
815 }
816
SendKeepAlive(void)817 void Commissioner::SendKeepAlive(void) { SendKeepAlive(mSessionId); }
818
SendKeepAlive(uint16_t aSessionId)819 void Commissioner::SendKeepAlive(uint16_t aSessionId)
820 {
821 Error error = kErrorNone;
822 Coap::Message *message = nullptr;
823 Tmf::MessageInfo messageInfo(GetInstance());
824
825 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriLeaderKeepAlive);
826 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
827
828 SuccessOrExit(
829 error = Tlv::Append<StateTlv>(*message, (mState == kStateActive) ? StateTlv::kAccept : StateTlv::kReject));
830
831 SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, aSessionId));
832
833 messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc();
834 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
835 Commissioner::HandleLeaderKeepAliveResponse, this));
836
837 LogInfo("Sent %s", UriToString<kUriLeaderKeepAlive>());
838
839 exit:
840 FreeMessageOnError(message, error);
841 LogWarnOnError(error, "send keep alive");
842 }
843
HandleLeaderKeepAliveResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aResult)844 void Commissioner::HandleLeaderKeepAliveResponse(void *aContext,
845 otMessage *aMessage,
846 const otMessageInfo *aMessageInfo,
847 otError aResult)
848 {
849 static_cast<Commissioner *>(aContext)->HandleLeaderKeepAliveResponse(AsCoapMessagePtr(aMessage),
850 AsCoreTypePtr(aMessageInfo), aResult);
851 }
852
HandleLeaderKeepAliveResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)853 void Commissioner::HandleLeaderKeepAliveResponse(Coap::Message *aMessage,
854 const Ip6::MessageInfo *aMessageInfo,
855 Error aResult)
856 {
857 OT_UNUSED_VARIABLE(aMessageInfo);
858
859 uint8_t state;
860
861 VerifyOrExit(mState == kStateActive);
862 VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged,
863 IgnoreError(Stop(kDoNotSendKeepAlive)));
864
865 LogInfo("Received %s response", UriToString<kUriLeaderKeepAlive>());
866
867 SuccessOrExit(Tlv::Find<StateTlv>(*aMessage, state));
868 VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive)));
869
870 mTimer.Start(Time::SecToMsec(kKeepAliveTimeout) / 2);
871
872 exit:
873 return;
874 }
875
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)876 template <> void Commissioner::HandleTmf<kUriRelayRx>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
877 {
878 OT_UNUSED_VARIABLE(aMessageInfo);
879
880 Error error;
881 uint16_t joinerPort;
882 Ip6::InterfaceIdentifier joinerIid;
883 uint16_t joinerRloc;
884 Ip6::MessageInfo joinerMessageInfo;
885 OffsetRange offsetRange;
886
887 VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
888
889 VerifyOrExit(aMessage.IsNonConfirmablePostRequest());
890
891 SuccessOrExit(error = Tlv::Find<JoinerUdpPortTlv>(aMessage, joinerPort));
892 SuccessOrExit(error = Tlv::Find<JoinerIidTlv>(aMessage, joinerIid));
893 SuccessOrExit(error = Tlv::Find<JoinerRouterLocatorTlv>(aMessage, joinerRloc));
894
895 SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, Tlv::kJoinerDtlsEncapsulation, offsetRange));
896
897 if (!Get<Tmf::SecureAgent>().IsConnectionActive())
898 {
899 Mac::ExtAddress receivedId;
900 Joiner *joiner;
901
902 mJoinerIid = joinerIid;
903 mJoinerIid.ConvertToExtAddress(receivedId);
904
905 joiner = FindBestMatchingJoinerEntry(receivedId);
906 VerifyOrExit(joiner != nullptr);
907
908 Get<Tmf::SecureAgent>().SetPsk(joiner->mPskd);
909 mActiveJoiner = joiner;
910
911 mJoinerSessionTimer.Start(kJoinerSessionTimeoutMillis);
912
913 LogJoinerEntry("Starting new session with", *joiner);
914 SignalJoinerEvent(kJoinerEventStart, joiner);
915 }
916 else
917 {
918 if (mJoinerIid != joinerIid)
919 {
920 LogNote("Ignore %s (%s, 0x%04x), session in progress with (%s, 0x%04x)", UriToString<kUriRelayRx>(),
921 joinerIid.ToString().AsCString(), joinerRloc, mJoinerIid.ToString().AsCString(), mJoinerRloc);
922
923 ExitNow();
924 }
925 }
926
927 mJoinerPort = joinerPort;
928 mJoinerRloc = joinerRloc;
929
930 LogInfo("Received %s (%s, 0x%04x)", UriToString<kUriRelayRx>(), mJoinerIid.ToString().AsCString(), mJoinerRloc);
931
932 aMessage.SetOffset(offsetRange.GetOffset());
933 SuccessOrExit(error = aMessage.SetLength(offsetRange.GetEndOffset()));
934
935 joinerMessageInfo.SetPeerAddr(Get<Mle::MleRouter>().GetMeshLocalEid());
936 joinerMessageInfo.GetPeerAddr().SetIid(mJoinerIid);
937 joinerMessageInfo.SetPeerPort(mJoinerPort);
938
939 Get<Tmf::SecureAgent>().HandleReceive(aMessage, joinerMessageInfo);
940
941 exit:
942 return;
943 }
944
HandleJoinerSessionTimer(void)945 void Commissioner::HandleJoinerSessionTimer(void)
946 {
947 if (mActiveJoiner != nullptr)
948 {
949 LogJoinerEntry("Timed out session with", *mActiveJoiner);
950 }
951
952 Get<Tmf::SecureAgent>().Disconnect();
953 }
954
955 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)956 void Commissioner::HandleTmf<kUriDatasetChanged>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
957 {
958 VerifyOrExit(mState == kStateActive);
959 VerifyOrExit(aMessage.IsConfirmablePostRequest());
960
961 LogInfo("Received %s", UriToString<kUriDatasetChanged>());
962
963 SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
964
965 LogInfo("Sent %s ack", UriToString<kUriDatasetChanged>());
966
967 exit:
968 return;
969 }
970
971 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)972 void Commissioner::HandleTmf<kUriJoinerFinalize>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
973 {
974 OT_UNUSED_VARIABLE(aMessageInfo);
975
976 StateTlv::State state = StateTlv::kAccept;
977 ProvisioningUrlTlv::StringType provisioningUrl;
978
979 VerifyOrExit(mState == kStateActive);
980
981 LogInfo("Received %s", UriToString<kUriJoinerFinalize>());
982
983 switch (Tlv::Find<ProvisioningUrlTlv>(aMessage, provisioningUrl))
984 {
985 case kErrorNone:
986 if (!StringMatch(provisioningUrl, mProvisioningUrl))
987 {
988 state = StateTlv::kReject;
989 }
990 break;
991
992 case kErrorNotFound:
993 break;
994
995 default:
996 ExitNow();
997 }
998
999 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1000 LogCertMessage("[THCI] direction=recv | type=JOIN_FIN.req |", aMessage);
1001 #endif
1002
1003 SendJoinFinalizeResponse(aMessage, state);
1004
1005 exit:
1006 return;
1007 }
1008
SendJoinFinalizeResponse(const Coap::Message & aRequest,StateTlv::State aState)1009 void Commissioner::SendJoinFinalizeResponse(const Coap::Message &aRequest, StateTlv::State aState)
1010 {
1011 Error error = kErrorNone;
1012 Coap::Message *message;
1013
1014 message = Get<Tmf::SecureAgent>().NewPriorityResponseMessage(aRequest);
1015 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
1016
1017 message->SetOffset(message->GetLength());
1018 message->SetSubType(Message::kSubTypeJoinerFinalizeResponse);
1019
1020 SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
1021
1022 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1023 LogCertMessage("[THCI] direction=send | type=JOIN_FIN.rsp |", *message);
1024 #endif
1025
1026 SuccessOrExit(error = Get<Tmf::SecureAgent>().SendMessage(*message));
1027
1028 SignalJoinerEvent(kJoinerEventFinalize, mActiveJoiner);
1029
1030 if ((mActiveJoiner != nullptr) && (mActiveJoiner->mType != Joiner::kTypeAny))
1031 {
1032 // Remove after kRemoveJoinerDelay (seconds)
1033 RemoveJoiner(*mActiveJoiner, kRemoveJoinerDelay);
1034 }
1035
1036 LogInfo("Sent %s response", UriToString<kUriJoinerFinalize>());
1037
1038 exit:
1039 FreeMessageOnError(message, error);
1040 }
1041
SendRelayTransmit(void * aContext,Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1042 Error Commissioner::SendRelayTransmit(void *aContext, Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1043 {
1044 return static_cast<Commissioner *>(aContext)->SendRelayTransmit(aMessage, aMessageInfo);
1045 }
1046
SendRelayTransmit(Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1047 Error Commissioner::SendRelayTransmit(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1048 {
1049 OT_UNUSED_VARIABLE(aMessageInfo);
1050
1051 Error error = kErrorNone;
1052 ExtendedTlv tlv;
1053 Coap::Message *message;
1054 Tmf::MessageInfo messageInfo(GetInstance());
1055 Kek kek;
1056
1057 Get<KeyManager>().ExtractKek(kek);
1058
1059 message = Get<Tmf::Agent>().NewPriorityNonConfirmablePostMessage(kUriRelayTx);
1060 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
1061
1062 SuccessOrExit(error = Tlv::Append<JoinerUdpPortTlv>(*message, mJoinerPort));
1063 SuccessOrExit(error = Tlv::Append<JoinerIidTlv>(*message, mJoinerIid));
1064 SuccessOrExit(error = Tlv::Append<JoinerRouterLocatorTlv>(*message, mJoinerRloc));
1065
1066 if (aMessage.GetSubType() == Message::kSubTypeJoinerFinalizeResponse)
1067 {
1068 SuccessOrExit(error = Tlv::Append<JoinerRouterKekTlv>(*message, kek));
1069 }
1070
1071 tlv.SetType(Tlv::kJoinerDtlsEncapsulation);
1072 tlv.SetLength(aMessage.GetLength());
1073 SuccessOrExit(error = message->Append(tlv));
1074 SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, 0, aMessage.GetLength()));
1075
1076 messageInfo.SetSockAddrToRlocPeerAddrTo(mJoinerRloc);
1077
1078 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
1079
1080 aMessage.Free();
1081
1082 exit:
1083 FreeMessageOnError(message, error);
1084 return error;
1085 }
1086
1087 // LCOV_EXCL_START
1088
1089 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1090
StateToString(State aState)1091 const char *Commissioner::StateToString(State aState)
1092 {
1093 static const char *const kStateStrings[] = {
1094 "disabled", // (0) kStateDisabled
1095 "petition", // (1) kStatePetition
1096 "active", // (2) kStateActive
1097 };
1098
1099 struct EnumCheck
1100 {
1101 InitEnumValidatorCounter();
1102 ValidateNextEnum(kStateDisabled);
1103 ValidateNextEnum(kStatePetition);
1104 ValidateNextEnum(kStateActive);
1105 };
1106
1107 return kStateStrings[aState];
1108 }
1109
LogJoinerEntry(const char * aAction,const Joiner & aJoiner) const1110 void Commissioner::LogJoinerEntry(const char *aAction, const Joiner &aJoiner) const
1111 {
1112 switch (aJoiner.mType)
1113 {
1114 case Joiner::kTypeUnused:
1115 break;
1116
1117 case Joiner::kTypeAny:
1118 LogInfo("%s Joiner (any, %s)", aAction, aJoiner.mPskd.GetAsCString());
1119 break;
1120
1121 case Joiner::kTypeEui64:
1122 LogInfo("%s Joiner (eui64:%s, %s)", aAction, aJoiner.mSharedId.mEui64.ToString().AsCString(),
1123 aJoiner.mPskd.GetAsCString());
1124 break;
1125
1126 case Joiner::kTypeDiscerner:
1127 LogInfo("%s Joiner (disc:%s, %s)", aAction, aJoiner.mSharedId.mDiscerner.ToString().AsCString(),
1128 aJoiner.mPskd.GetAsCString());
1129 break;
1130 }
1131 }
1132
1133 #else
1134
LogJoinerEntry(const char *,const Joiner &) const1135 void Commissioner::LogJoinerEntry(const char *, const Joiner &) const {}
1136
1137 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1138
1139 // LCOV_EXCL_STOP
1140
1141 } // namespace MeshCoP
1142 } // namespace ot
1143
1144 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
1145