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