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 memset(reinterpret_cast<void *>(mJoiners), 0, sizeof(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 }
306
307 LogError("start commissioner", error);
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 LogError("stop commissioner", error);
347 return error;
348 }
349
SetId(const char * aId)350 Error Commissioner::SetId(const char *aId)
351 {
352 Error error = kErrorNone;
353
354 VerifyOrExit(IsDisabled(), error = kErrorInvalidState);
355 error = StringCopy(mCommissionerId, aId, kStringCheckUtf8Encoding);
356
357 exit:
358 return error;
359 }
360
ComputeBloomFilter(SteeringData & aSteeringData) const361 void Commissioner::ComputeBloomFilter(SteeringData &aSteeringData) const
362 {
363 Mac::ExtAddress joinerId;
364
365 aSteeringData.Init();
366
367 for (const Joiner &joiner : mJoiners)
368 {
369 switch (joiner.mType)
370 {
371 case Joiner::kTypeUnused:
372 break;
373
374 case Joiner::kTypeEui64:
375 ComputeJoinerId(joiner.mSharedId.mEui64, joinerId);
376 aSteeringData.UpdateBloomFilter(joinerId);
377 break;
378
379 case Joiner::kTypeDiscerner:
380 aSteeringData.UpdateBloomFilter(joiner.mSharedId.mDiscerner);
381 break;
382
383 case Joiner::kTypeAny:
384 aSteeringData.SetToPermitAllJoiners();
385 ExitNow();
386 }
387 }
388
389 exit:
390 return;
391 }
392
SendCommissionerSet(void)393 void Commissioner::SendCommissionerSet(void)
394 {
395 Error error = kErrorNone;
396 CommissioningDataset dataset;
397
398 VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
399
400 dataset.Clear();
401
402 dataset.SetSessionId(mSessionId);
403 ComputeBloomFilter(dataset.UpdateSteeringData());
404
405 error = SendMgmtCommissionerSetRequest(dataset, nullptr, 0);
406
407 exit:
408 LogError("send MGMT_COMMISSIONER_SET.req", error);
409 }
410
ClearJoiners(void)411 void Commissioner::ClearJoiners(void)
412 {
413 for (Joiner &joiner : mJoiners)
414 {
415 joiner.mType = Joiner::kTypeUnused;
416 }
417
418 SendCommissionerSet();
419 }
420
AddJoiner(const Mac::ExtAddress * aEui64,const JoinerDiscerner * aDiscerner,const char * aPskd,uint32_t aTimeout)421 Error Commissioner::AddJoiner(const Mac::ExtAddress *aEui64,
422 const JoinerDiscerner *aDiscerner,
423 const char *aPskd,
424 uint32_t aTimeout)
425 {
426 Error error = kErrorNone;
427 Joiner *joiner;
428
429 VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
430
431 if (aDiscerner != nullptr)
432 {
433 VerifyOrExit(aDiscerner->IsValid(), error = kErrorInvalidArgs);
434 joiner = FindJoinerEntry(*aDiscerner);
435 }
436 else
437 {
438 joiner = FindJoinerEntry(aEui64);
439 }
440
441 if (joiner == nullptr)
442 {
443 joiner = GetUnusedJoinerEntry();
444 }
445
446 VerifyOrExit(joiner != nullptr, error = kErrorNoBufs);
447
448 SuccessOrExit(error = joiner->mPskd.SetFrom(aPskd));
449
450 if (aDiscerner != nullptr)
451 {
452 joiner->mType = Joiner::kTypeDiscerner;
453 joiner->mSharedId.mDiscerner = *aDiscerner;
454 }
455 else if (aEui64 != nullptr)
456 {
457 joiner->mType = Joiner::kTypeEui64;
458 joiner->mSharedId.mEui64 = *aEui64;
459 }
460 else
461 {
462 joiner->mType = Joiner::kTypeAny;
463 }
464
465 joiner->mExpirationTime = TimerMilli::GetNow() + Time::SecToMsec(aTimeout);
466
467 mJoinerExpirationTimer.FireAtIfEarlier(joiner->mExpirationTime);
468
469 SendCommissionerSet();
470
471 LogJoinerEntry("Added", *joiner);
472
473 exit:
474 return error;
475 }
476
CopyToJoinerInfo(otJoinerInfo & aJoiner) const477 void Commissioner::Joiner::CopyToJoinerInfo(otJoinerInfo &aJoiner) const
478 {
479 memset(&aJoiner, 0, sizeof(aJoiner));
480
481 switch (mType)
482 {
483 case kTypeAny:
484 aJoiner.mType = OT_JOINER_INFO_TYPE_ANY;
485 break;
486
487 case kTypeEui64:
488 aJoiner.mType = OT_JOINER_INFO_TYPE_EUI64;
489 aJoiner.mSharedId.mEui64 = mSharedId.mEui64;
490 break;
491
492 case kTypeDiscerner:
493 aJoiner.mType = OT_JOINER_INFO_TYPE_DISCERNER;
494 aJoiner.mSharedId.mDiscerner = mSharedId.mDiscerner;
495 break;
496
497 case kTypeUnused:
498 ExitNow();
499 }
500
501 aJoiner.mPskd = mPskd;
502 aJoiner.mExpirationTime = mExpirationTime - TimerMilli::GetNow();
503
504 exit:
505 return;
506 }
507
GetNextJoinerInfo(uint16_t & aIterator,otJoinerInfo & aJoinerInfo) const508 Error Commissioner::GetNextJoinerInfo(uint16_t &aIterator, otJoinerInfo &aJoinerInfo) const
509 {
510 Error error = kErrorNone;
511
512 while (aIterator < GetArrayLength(mJoiners))
513 {
514 const Joiner &joiner = mJoiners[aIterator++];
515
516 if (joiner.mType != Joiner::kTypeUnused)
517 {
518 joiner.CopyToJoinerInfo(aJoinerInfo);
519 ExitNow();
520 }
521 }
522
523 error = kErrorNotFound;
524
525 exit:
526 return error;
527 }
528
RemoveJoiner(const Mac::ExtAddress * aEui64,const JoinerDiscerner * aDiscerner,uint32_t aDelay)529 Error Commissioner::RemoveJoiner(const Mac::ExtAddress *aEui64, const JoinerDiscerner *aDiscerner, uint32_t aDelay)
530 {
531 Error error = kErrorNone;
532 Joiner *joiner;
533
534 VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
535
536 if (aDiscerner != nullptr)
537 {
538 VerifyOrExit(aDiscerner->IsValid(), error = kErrorInvalidArgs);
539 joiner = FindJoinerEntry(*aDiscerner);
540 }
541 else
542 {
543 joiner = FindJoinerEntry(aEui64);
544 }
545
546 VerifyOrExit(joiner != nullptr, error = kErrorNotFound);
547
548 RemoveJoiner(*joiner, aDelay);
549
550 exit:
551 return error;
552 }
553
RemoveJoiner(Joiner & aJoiner,uint32_t aDelay)554 void Commissioner::RemoveJoiner(Joiner &aJoiner, uint32_t aDelay)
555 {
556 if (aDelay > 0)
557 {
558 TimeMilli newExpirationTime = TimerMilli::GetNow() + Time::SecToMsec(aDelay);
559
560 if (aJoiner.mExpirationTime > newExpirationTime)
561 {
562 aJoiner.mExpirationTime = newExpirationTime;
563 mJoinerExpirationTimer.FireAtIfEarlier(newExpirationTime);
564 }
565 }
566 else
567 {
568 RemoveJoinerEntry(aJoiner);
569 }
570 }
571
SetProvisioningUrl(const char * aProvisioningUrl)572 Error Commissioner::SetProvisioningUrl(const char *aProvisioningUrl)
573 {
574 return StringCopy(mProvisioningUrl, aProvisioningUrl, kStringCheckUtf8Encoding);
575 }
576
HandleTimer(void)577 void Commissioner::HandleTimer(void)
578 {
579 switch (mState)
580 {
581 case kStateDisabled:
582 break;
583
584 case kStatePetition:
585 IgnoreError(SendPetition());
586 break;
587
588 case kStateActive:
589 SendKeepAlive();
590 break;
591 }
592 }
593
HandleJoinerExpirationTimer(void)594 void Commissioner::HandleJoinerExpirationTimer(void)
595 {
596 TimeMilli now = TimerMilli::GetNow();
597 TimeMilli next = now.GetDistantFuture();
598
599 for (Joiner &joiner : mJoiners)
600 {
601 if (joiner.mType == Joiner::kTypeUnused)
602 {
603 continue;
604 }
605
606 if (joiner.mExpirationTime <= now)
607 {
608 LogDebg("removing joiner due to timeout or successfully joined");
609 RemoveJoinerEntry(joiner);
610 }
611 else
612 {
613 next = Min(joiner.mExpirationTime, next);
614 }
615 }
616
617 if (next != now.GetDistantFuture())
618 {
619 mJoinerExpirationTimer.FireAtIfEarlier(next);
620 }
621 }
622
SendMgmtCommissionerGetRequest(const uint8_t * aTlvs,uint8_t aLength)623 Error Commissioner::SendMgmtCommissionerGetRequest(const uint8_t *aTlvs, uint8_t aLength)
624 {
625 Error error = kErrorNone;
626 Coap::Message *message;
627 Tmf::MessageInfo messageInfo(GetInstance());
628 Tlv tlv;
629
630 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriCommissionerGet);
631 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
632
633 if (aLength > 0)
634 {
635 tlv.SetType(Tlv::kGet);
636 tlv.SetLength(aLength);
637 SuccessOrExit(error = message->Append(tlv));
638 SuccessOrExit(error = message->AppendBytes(aTlvs, aLength));
639 }
640
641 SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
642 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
643 Commissioner::HandleMgmtCommissionerGetResponse, this));
644
645 LogInfo("Sent %s to leader", UriToString<kUriCommissionerGet>());
646
647 exit:
648 FreeMessageOnError(message, error);
649 return error;
650 }
651
HandleMgmtCommissionerGetResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)652 void Commissioner::HandleMgmtCommissionerGetResponse(void *aContext,
653 otMessage *aMessage,
654 const otMessageInfo *aMessageInfo,
655 Error aResult)
656 {
657 static_cast<Commissioner *>(aContext)->HandleMgmtCommissionerGetResponse(AsCoapMessagePtr(aMessage),
658 AsCoreTypePtr(aMessageInfo), aResult);
659 }
660
HandleMgmtCommissionerGetResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)661 void Commissioner::HandleMgmtCommissionerGetResponse(Coap::Message *aMessage,
662 const Ip6::MessageInfo *aMessageInfo,
663 Error aResult)
664 {
665 OT_UNUSED_VARIABLE(aMessageInfo);
666
667 VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged);
668 LogInfo("Received %s response", UriToString<kUriCommissionerGet>());
669
670 exit:
671 return;
672 }
673
SendMgmtCommissionerSetRequest(const CommissioningDataset & aDataset,const uint8_t * aTlvs,uint8_t aLength)674 Error Commissioner::SendMgmtCommissionerSetRequest(const CommissioningDataset &aDataset,
675 const uint8_t *aTlvs,
676 uint8_t aLength)
677 {
678 Error error = kErrorNone;
679 Coap::Message *message;
680 Tmf::MessageInfo messageInfo(GetInstance());
681
682 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriCommissionerSet);
683 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
684
685 if (aDataset.IsLocatorSet())
686 {
687 SuccessOrExit(error = Tlv::Append<BorderAgentLocatorTlv>(*message, aDataset.GetLocator()));
688 }
689
690 if (aDataset.IsSessionIdSet())
691 {
692 SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, aDataset.GetSessionId()));
693 }
694
695 if (aDataset.IsSteeringDataSet())
696 {
697 const SteeringData &steeringData = aDataset.GetSteeringData();
698
699 SuccessOrExit(error = Tlv::Append<SteeringDataTlv>(*message, steeringData.GetData(), steeringData.GetLength()));
700 }
701
702 if (aDataset.IsJoinerUdpPortSet())
703 {
704 SuccessOrExit(error = Tlv::Append<JoinerUdpPortTlv>(*message, aDataset.GetJoinerUdpPort()));
705 }
706
707 if (aLength > 0)
708 {
709 SuccessOrExit(error = message->AppendBytes(aTlvs, aLength));
710 }
711
712 SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
713 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
714 Commissioner::HandleMgmtCommissionerSetResponse, this));
715
716 LogInfo("Sent %s to leader", UriToString<kUriCommissionerSet>());
717
718 exit:
719 FreeMessageOnError(message, error);
720 return error;
721 }
722
HandleMgmtCommissionerSetResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)723 void Commissioner::HandleMgmtCommissionerSetResponse(void *aContext,
724 otMessage *aMessage,
725 const otMessageInfo *aMessageInfo,
726 Error aResult)
727 {
728 static_cast<Commissioner *>(aContext)->HandleMgmtCommissionerSetResponse(AsCoapMessagePtr(aMessage),
729 AsCoreTypePtr(aMessageInfo), aResult);
730 }
731
HandleMgmtCommissionerSetResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)732 void Commissioner::HandleMgmtCommissionerSetResponse(Coap::Message *aMessage,
733 const Ip6::MessageInfo *aMessageInfo,
734 Error aResult)
735 {
736 OT_UNUSED_VARIABLE(aMessageInfo);
737
738 Error error;
739 uint8_t state;
740
741 SuccessOrExit(error = aResult);
742 VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged && Tlv::Find<StateTlv>(*aMessage, state) == kErrorNone &&
743 state != StateTlv::kPending,
744 error = kErrorParse);
745
746 OT_UNUSED_VARIABLE(error);
747 exit:
748 LogInfo("Received %s response: %s", UriToString<kUriCommissionerSet>(),
749 error == kErrorNone ? StateTlv::StateToString(static_cast<StateTlv::State>(state)) : ErrorToString(error));
750 }
751
SendPetition(void)752 Error Commissioner::SendPetition(void)
753 {
754 Error error = kErrorNone;
755 Coap::Message *message = nullptr;
756 Tmf::MessageInfo messageInfo(GetInstance());
757
758 mTransmitAttempts++;
759
760 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriLeaderPetition);
761 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
762
763 SuccessOrExit(error = Tlv::Append<CommissionerIdTlv>(*message, mCommissionerId));
764
765 SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
766 SuccessOrExit(
767 error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, Commissioner::HandleLeaderPetitionResponse, this));
768
769 LogInfo("Sent %s", UriToString<kUriLeaderPetition>());
770
771 exit:
772 FreeMessageOnError(message, error);
773 return error;
774 }
775
HandleLeaderPetitionResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)776 void Commissioner::HandleLeaderPetitionResponse(void *aContext,
777 otMessage *aMessage,
778 const otMessageInfo *aMessageInfo,
779 Error aResult)
780 {
781 static_cast<Commissioner *>(aContext)->HandleLeaderPetitionResponse(AsCoapMessagePtr(aMessage),
782 AsCoreTypePtr(aMessageInfo), aResult);
783 }
784
HandleLeaderPetitionResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)785 void Commissioner::HandleLeaderPetitionResponse(Coap::Message *aMessage,
786 const Ip6::MessageInfo *aMessageInfo,
787 Error aResult)
788 {
789 OT_UNUSED_VARIABLE(aMessageInfo);
790
791 uint8_t state;
792 bool retransmit = false;
793
794 VerifyOrExit(mState != kStateActive);
795 VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged,
796 retransmit = (mState == kStatePetition));
797
798 LogInfo("Received %s response", UriToString<kUriLeaderPetition>());
799
800 SuccessOrExit(Tlv::Find<StateTlv>(*aMessage, state));
801 VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive)));
802
803 SuccessOrExit(Tlv::Find<CommissionerSessionIdTlv>(*aMessage, mSessionId));
804
805 // reject this session by sending KeepAlive reject if commissioner is in disabled state
806 // this could happen if commissioner is stopped by API during petitioning
807 if (mState == kStateDisabled)
808 {
809 SendKeepAlive(mSessionId);
810 ExitNow();
811 }
812
813 IgnoreError(Get<Mle::MleRouter>().GetCommissionerAloc(mCommissionerAloc.GetAddress(), mSessionId));
814 Get<ThreadNetif>().AddUnicastAddress(mCommissionerAloc);
815
816 SetState(kStateActive);
817
818 mTransmitAttempts = 0;
819 mTimer.Start(Time::SecToMsec(kKeepAliveTimeout) / 2);
820
821 exit:
822
823 if (retransmit)
824 {
825 if (mTransmitAttempts >= kPetitionRetryCount)
826 {
827 IgnoreError(Stop(kDoNotSendKeepAlive));
828 }
829 else
830 {
831 mTimer.Start(Time::SecToMsec(kPetitionRetryDelay));
832 }
833 }
834 }
835
SendKeepAlive(void)836 void Commissioner::SendKeepAlive(void) { SendKeepAlive(mSessionId); }
837
SendKeepAlive(uint16_t aSessionId)838 void Commissioner::SendKeepAlive(uint16_t aSessionId)
839 {
840 Error error = kErrorNone;
841 Coap::Message *message = nullptr;
842 Tmf::MessageInfo messageInfo(GetInstance());
843
844 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriLeaderKeepAlive);
845 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
846
847 SuccessOrExit(
848 error = Tlv::Append<StateTlv>(*message, (mState == kStateActive) ? StateTlv::kAccept : StateTlv::kReject));
849
850 SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, aSessionId));
851
852 SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
853 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
854 Commissioner::HandleLeaderKeepAliveResponse, this));
855
856 LogInfo("Sent %s", UriToString<kUriLeaderKeepAlive>());
857
858 exit:
859 FreeMessageOnError(message, error);
860 LogError("send keep alive", error);
861 }
862
HandleLeaderKeepAliveResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)863 void Commissioner::HandleLeaderKeepAliveResponse(void *aContext,
864 otMessage *aMessage,
865 const otMessageInfo *aMessageInfo,
866 Error aResult)
867 {
868 static_cast<Commissioner *>(aContext)->HandleLeaderKeepAliveResponse(AsCoapMessagePtr(aMessage),
869 AsCoreTypePtr(aMessageInfo), aResult);
870 }
871
HandleLeaderKeepAliveResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)872 void Commissioner::HandleLeaderKeepAliveResponse(Coap::Message *aMessage,
873 const Ip6::MessageInfo *aMessageInfo,
874 Error aResult)
875 {
876 OT_UNUSED_VARIABLE(aMessageInfo);
877
878 uint8_t state;
879
880 VerifyOrExit(mState == kStateActive);
881 VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged,
882 IgnoreError(Stop(kDoNotSendKeepAlive)));
883
884 LogInfo("Received %s response", UriToString<kUriLeaderKeepAlive>());
885
886 SuccessOrExit(Tlv::Find<StateTlv>(*aMessage, state));
887 VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive)));
888
889 mTimer.Start(Time::SecToMsec(kKeepAliveTimeout) / 2);
890
891 exit:
892 return;
893 }
894
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)895 template <> void Commissioner::HandleTmf<kUriRelayRx>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
896 {
897 OT_UNUSED_VARIABLE(aMessageInfo);
898
899 Error error;
900 uint16_t joinerPort;
901 Ip6::InterfaceIdentifier joinerIid;
902 uint16_t joinerRloc;
903 Ip6::MessageInfo joinerMessageInfo;
904 uint16_t startOffset;
905 uint16_t endOffset;
906
907 VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
908
909 VerifyOrExit(aMessage.IsNonConfirmablePostRequest());
910
911 SuccessOrExit(error = Tlv::Find<JoinerUdpPortTlv>(aMessage, joinerPort));
912 SuccessOrExit(error = Tlv::Find<JoinerIidTlv>(aMessage, joinerIid));
913 SuccessOrExit(error = Tlv::Find<JoinerRouterLocatorTlv>(aMessage, joinerRloc));
914
915 SuccessOrExit(
916 error = Tlv::FindTlvValueStartEndOffsets(aMessage, Tlv::kJoinerDtlsEncapsulation, startOffset, endOffset));
917
918 if (!Get<Tmf::SecureAgent>().IsConnectionActive())
919 {
920 Mac::ExtAddress receivedId;
921 Joiner *joiner;
922
923 mJoinerIid = joinerIid;
924 mJoinerIid.ConvertToExtAddress(receivedId);
925
926 joiner = FindBestMatchingJoinerEntry(receivedId);
927 VerifyOrExit(joiner != nullptr);
928
929 Get<Tmf::SecureAgent>().SetPsk(joiner->mPskd);
930 mActiveJoiner = joiner;
931
932 mJoinerSessionTimer.Start(kJoinerSessionTimeoutMillis);
933
934 LogJoinerEntry("Starting new session with", *joiner);
935 SignalJoinerEvent(kJoinerEventStart, joiner);
936 }
937 else
938 {
939 if (mJoinerIid != joinerIid)
940 {
941 LogNote("Ignore %s (%s, 0x%04x), session in progress with (%s, 0x%04x)", UriToString<kUriRelayRx>(),
942 joinerIid.ToString().AsCString(), joinerRloc, mJoinerIid.ToString().AsCString(), mJoinerRloc);
943
944 ExitNow();
945 }
946 }
947
948 mJoinerPort = joinerPort;
949 mJoinerRloc = joinerRloc;
950
951 LogInfo("Received %s (%s, 0x%04x)", UriToString<kUriRelayRx>(), mJoinerIid.ToString().AsCString(), mJoinerRloc);
952
953 aMessage.SetOffset(startOffset);
954 SuccessOrExit(error = aMessage.SetLength(endOffset));
955
956 joinerMessageInfo.SetPeerAddr(Get<Mle::MleRouter>().GetMeshLocal64());
957 joinerMessageInfo.GetPeerAddr().SetIid(mJoinerIid);
958 joinerMessageInfo.SetPeerPort(mJoinerPort);
959
960 Get<Tmf::SecureAgent>().HandleUdpReceive(aMessage, joinerMessageInfo);
961
962 exit:
963 return;
964 }
965
HandleJoinerSessionTimer(void)966 void Commissioner::HandleJoinerSessionTimer(void)
967 {
968 if (mActiveJoiner != nullptr)
969 {
970 LogJoinerEntry("Timed out session with", *mActiveJoiner);
971 }
972
973 Get<Tmf::SecureAgent>().Disconnect();
974 }
975
976 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)977 void Commissioner::HandleTmf<kUriDatasetChanged>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
978 {
979 VerifyOrExit(mState == kStateActive);
980 VerifyOrExit(aMessage.IsConfirmablePostRequest());
981
982 LogInfo("Received %s", UriToString<kUriDatasetChanged>());
983
984 SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
985
986 LogInfo("Sent %s ack", UriToString<kUriDatasetChanged>());
987
988 exit:
989 return;
990 }
991
992 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)993 void Commissioner::HandleTmf<kUriJoinerFinalize>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
994 {
995 OT_UNUSED_VARIABLE(aMessageInfo);
996
997 StateTlv::State state = StateTlv::kAccept;
998 ProvisioningUrlTlv::StringType provisioningUrl;
999
1000 VerifyOrExit(mState == kStateActive);
1001
1002 LogInfo("Received %s", UriToString<kUriJoinerFinalize>());
1003
1004 switch (Tlv::Find<ProvisioningUrlTlv>(aMessage, provisioningUrl))
1005 {
1006 case kErrorNone:
1007 if (!StringMatch(provisioningUrl, mProvisioningUrl))
1008 {
1009 state = StateTlv::kReject;
1010 }
1011 break;
1012
1013 case kErrorNotFound:
1014 break;
1015
1016 default:
1017 ExitNow();
1018 }
1019
1020 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1021 if (aMessage.GetLength() <= OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE)
1022 {
1023 uint8_t buf[OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE];
1024
1025 aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset());
1026 DumpCert("[THCI] direction=recv | type=JOIN_FIN.req |", buf, aMessage.GetLength() - aMessage.GetOffset());
1027 }
1028 #endif
1029
1030 SendJoinFinalizeResponse(aMessage, state);
1031
1032 exit:
1033 return;
1034 }
1035
SendJoinFinalizeResponse(const Coap::Message & aRequest,StateTlv::State aState)1036 void Commissioner::SendJoinFinalizeResponse(const Coap::Message &aRequest, StateTlv::State aState)
1037 {
1038 Error error = kErrorNone;
1039 Ip6::MessageInfo joinerMessageInfo;
1040 Coap::Message *message;
1041
1042 message = Get<Tmf::SecureAgent>().NewPriorityResponseMessage(aRequest);
1043 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
1044
1045 message->SetOffset(message->GetLength());
1046 message->SetSubType(Message::kSubTypeJoinerFinalizeResponse);
1047
1048 SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
1049
1050 joinerMessageInfo.SetPeerAddr(Get<Mle::MleRouter>().GetMeshLocal64());
1051 joinerMessageInfo.GetPeerAddr().SetIid(mJoinerIid);
1052 joinerMessageInfo.SetPeerPort(mJoinerPort);
1053
1054 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1055 uint8_t buf[OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE];
1056
1057 VerifyOrExit(message->GetLength() <= sizeof(buf));
1058 message->ReadBytes(message->GetOffset(), buf, message->GetLength() - message->GetOffset());
1059 DumpCert("[THCI] direction=send | type=JOIN_FIN.rsp |", buf, message->GetLength() - message->GetOffset());
1060 #endif
1061
1062 SuccessOrExit(error = Get<Tmf::SecureAgent>().SendMessage(*message, joinerMessageInfo));
1063
1064 SignalJoinerEvent(kJoinerEventFinalize, mActiveJoiner);
1065
1066 if ((mActiveJoiner != nullptr) && (mActiveJoiner->mType != Joiner::kTypeAny))
1067 {
1068 // Remove after kRemoveJoinerDelay (seconds)
1069 RemoveJoiner(*mActiveJoiner, kRemoveJoinerDelay);
1070 }
1071
1072 LogInfo("Sent %s response", UriToString<kUriJoinerFinalize>());
1073
1074 exit:
1075 FreeMessageOnError(message, error);
1076 }
1077
SendRelayTransmit(void * aContext,Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1078 Error Commissioner::SendRelayTransmit(void *aContext, Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1079 {
1080 return static_cast<Commissioner *>(aContext)->SendRelayTransmit(aMessage, aMessageInfo);
1081 }
1082
SendRelayTransmit(Message & aMessage,const Ip6::MessageInfo & aMessageInfo)1083 Error Commissioner::SendRelayTransmit(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
1084 {
1085 OT_UNUSED_VARIABLE(aMessageInfo);
1086
1087 Error error = kErrorNone;
1088 ExtendedTlv tlv;
1089 Coap::Message *message;
1090 Tmf::MessageInfo messageInfo(GetInstance());
1091 Kek kek;
1092
1093 Get<KeyManager>().ExtractKek(kek);
1094
1095 message = Get<Tmf::Agent>().NewPriorityNonConfirmablePostMessage(kUriRelayTx);
1096 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
1097
1098 SuccessOrExit(error = Tlv::Append<JoinerUdpPortTlv>(*message, mJoinerPort));
1099 SuccessOrExit(error = Tlv::Append<JoinerIidTlv>(*message, mJoinerIid));
1100 SuccessOrExit(error = Tlv::Append<JoinerRouterLocatorTlv>(*message, mJoinerRloc));
1101
1102 if (aMessage.GetSubType() == Message::kSubTypeJoinerFinalizeResponse)
1103 {
1104 SuccessOrExit(error = Tlv::Append<JoinerRouterKekTlv>(*message, kek));
1105 }
1106
1107 tlv.SetType(Tlv::kJoinerDtlsEncapsulation);
1108 tlv.SetLength(aMessage.GetLength());
1109 SuccessOrExit(error = message->Append(tlv));
1110 SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, 0, aMessage.GetLength()));
1111
1112 messageInfo.SetSockAddrToRlocPeerAddrTo(mJoinerRloc);
1113
1114 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
1115
1116 aMessage.Free();
1117
1118 exit:
1119 FreeMessageOnError(message, error);
1120 return error;
1121 }
1122
1123 // LCOV_EXCL_START
1124
1125 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1126
StateToString(State aState)1127 const char *Commissioner::StateToString(State aState)
1128 {
1129 static const char *const kStateStrings[] = {
1130 "disabled", // (0) kStateDisabled
1131 "petition", // (1) kStatePetition
1132 "active", // (2) kStateActive
1133 };
1134
1135 static_assert(kStateDisabled == 0, "kStateDisabled value is incorrect");
1136 static_assert(kStatePetition == 1, "kStatePetition value is incorrect");
1137 static_assert(kStateActive == 2, "kStateActive value is incorrect");
1138
1139 return kStateStrings[aState];
1140 }
1141
LogJoinerEntry(const char * aAction,const Joiner & aJoiner) const1142 void Commissioner::LogJoinerEntry(const char *aAction, const Joiner &aJoiner) const
1143 {
1144 switch (aJoiner.mType)
1145 {
1146 case Joiner::kTypeUnused:
1147 break;
1148
1149 case Joiner::kTypeAny:
1150 LogInfo("%s Joiner (any, %s)", aAction, aJoiner.mPskd.GetAsCString());
1151 break;
1152
1153 case Joiner::kTypeEui64:
1154 LogInfo("%s Joiner (eui64:%s, %s)", aAction, aJoiner.mSharedId.mEui64.ToString().AsCString(),
1155 aJoiner.mPskd.GetAsCString());
1156 break;
1157
1158 case Joiner::kTypeDiscerner:
1159 LogInfo("%s Joiner (disc:%s, %s)", aAction, aJoiner.mSharedId.mDiscerner.ToString().AsCString(),
1160 aJoiner.mPskd.GetAsCString());
1161 break;
1162 }
1163 }
1164
1165 #else
1166
LogJoinerEntry(const char *,const Joiner &) const1167 void Commissioner::LogJoinerEntry(const char *, const Joiner &) const {}
1168
1169 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1170
1171 // LCOV_EXCL_STOP
1172
1173 } // namespace MeshCoP
1174 } // namespace ot
1175
1176 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
1177