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
HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent,void * aContext)130 void Commissioner::HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent, void *aContext)
131 {
132 static_cast<Commissioner *>(aContext)->HandleSecureAgentConnectEvent(aEvent);
133 }
134
HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent)135 void Commissioner::HandleSecureAgentConnectEvent(SecureTransport::ConnectEvent aEvent)
136 {
137 bool isConnected = (aEvent == SecureTransport::kConnected);
138 if (!isConnected)
139 {
140 mJoinerSessionTimer.Stop();
141 }
142
143 SignalJoinerEvent(isConnected ? kJoinerEventConnected : kJoinerEventEnd, mActiveJoiner);
144 }
145
GetUnusedJoinerEntry(void)146 Commissioner::Joiner *Commissioner::GetUnusedJoinerEntry(void)
147 {
148 Joiner *rval = nullptr;
149
150 for (Joiner &joiner : mJoiners)
151 {
152 if (joiner.mType == Joiner::kTypeUnused)
153 {
154 rval = &joiner;
155 break;
156 }
157 }
158
159 return rval;
160 }
161
FindJoinerEntry(const Mac::ExtAddress * aEui64)162 Commissioner::Joiner *Commissioner::FindJoinerEntry(const Mac::ExtAddress *aEui64)
163 {
164 Joiner *rval = nullptr;
165
166 for (Joiner &joiner : mJoiners)
167 {
168 switch (joiner.mType)
169 {
170 case Joiner::kTypeUnused:
171 case Joiner::kTypeDiscerner:
172 break;
173
174 case Joiner::kTypeAny:
175 if (aEui64 == nullptr)
176 {
177 ExitNow(rval = &joiner);
178 }
179 break;
180
181 case Joiner::kTypeEui64:
182 if ((aEui64 != nullptr) && (joiner.mSharedId.mEui64 == *aEui64))
183 {
184 ExitNow(rval = &joiner);
185 }
186 break;
187 }
188 }
189
190 exit:
191 return rval;
192 }
193
FindJoinerEntry(const JoinerDiscerner & aDiscerner)194 Commissioner::Joiner *Commissioner::FindJoinerEntry(const JoinerDiscerner &aDiscerner)
195 {
196 Joiner *rval = nullptr;
197
198 for (Joiner &joiner : mJoiners)
199 {
200 if ((joiner.mType == Joiner::kTypeDiscerner) && (aDiscerner == joiner.mSharedId.mDiscerner))
201 {
202 rval = &joiner;
203 break;
204 }
205 }
206
207 return rval;
208 }
209
FindBestMatchingJoinerEntry(const Mac::ExtAddress & aReceivedJoinerId)210 Commissioner::Joiner *Commissioner::FindBestMatchingJoinerEntry(const Mac::ExtAddress &aReceivedJoinerId)
211 {
212 Joiner *best = nullptr;
213 Mac::ExtAddress joinerId;
214
215 // Prefer a full Joiner ID match, if not found use the entry
216 // accepting any joiner.
217
218 for (Joiner &joiner : mJoiners)
219 {
220 switch (joiner.mType)
221 {
222 case Joiner::kTypeUnused:
223 break;
224
225 case Joiner::kTypeAny:
226 if (best == nullptr)
227 {
228 best = &joiner;
229 }
230 break;
231
232 case Joiner::kTypeEui64:
233 ComputeJoinerId(joiner.mSharedId.mEui64, joinerId);
234 if (joinerId == aReceivedJoinerId)
235 {
236 ExitNow(best = &joiner);
237 }
238 break;
239
240 case Joiner::kTypeDiscerner:
241 if (joiner.mSharedId.mDiscerner.Matches(aReceivedJoinerId))
242 {
243 if ((best == nullptr) ||
244 ((best->mType == Joiner::kTypeDiscerner) &&
245 (best->mSharedId.mDiscerner.GetLength() < joiner.mSharedId.mDiscerner.GetLength())))
246 {
247 best = &joiner;
248 }
249 }
250 break;
251 }
252 }
253
254 exit:
255 return best;
256 }
257
RemoveJoinerEntry(Commissioner::Joiner & aJoiner)258 void Commissioner::RemoveJoinerEntry(Commissioner::Joiner &aJoiner)
259 {
260 // Create a copy of `aJoiner` to use for signaling joiner event
261 // and logging after the entry is removed. This ensures the joiner
262 // event callback is invoked after all states are cleared.
263
264 Joiner joinerCopy = aJoiner;
265
266 aJoiner.mType = Joiner::kTypeUnused;
267
268 if (&aJoiner == mActiveJoiner)
269 {
270 mActiveJoiner = nullptr;
271 }
272
273 SendCommissionerSet();
274
275 LogJoinerEntry("Removed", joinerCopy);
276 SignalJoinerEvent(kJoinerEventRemoved, &joinerCopy);
277 }
278
Start(StateCallback aStateCallback,JoinerCallback aJoinerCallback,void * aCallbackContext)279 Error Commissioner::Start(StateCallback aStateCallback, JoinerCallback aJoinerCallback, void *aCallbackContext)
280 {
281 Error error = kErrorNone;
282
283 VerifyOrExit(Get<Mle::MleRouter>().IsAttached(), error = kErrorInvalidState);
284 VerifyOrExit(mState == kStateDisabled, error = kErrorAlready);
285
286 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
287 Get<BorderAgent>().Stop();
288 #endif
289
290 SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(SendRelayTransmit, this));
291 Get<Tmf::SecureAgent>().SetConnectEventCallback(&Commissioner::HandleSecureAgentConnectEvent, this);
292
293 mStateCallback.Set(aStateCallback, aCallbackContext);
294 mJoinerCallback.Set(aJoinerCallback, aCallbackContext);
295 mTransmitAttempts = 0;
296
297 SuccessOrExit(error = SendPetition());
298 SetState(kStatePetition);
299
300 LogInfo("start commissioner %s", mCommissionerId);
301
302 exit:
303 if ((error != kErrorNone) && (error != kErrorAlready))
304 {
305 Get<Tmf::SecureAgent>().Stop();
306 LogWarnOnError(error, "start commissioner");
307 }
308
309 return error;
310 }
311
Stop(ResignMode aResignMode)312 Error Commissioner::Stop(ResignMode aResignMode)
313 {
314 Error error = kErrorNone;
315 bool needResign = false;
316
317 VerifyOrExit(mState != kStateDisabled, error = kErrorAlready);
318
319 mJoinerSessionTimer.Stop();
320 Get<Tmf::SecureAgent>().Stop();
321
322 if (mState == kStateActive)
323 {
324 Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
325 ClearJoiners();
326 needResign = true;
327 }
328 else if (mState == kStatePetition)
329 {
330 mTransmitAttempts = 0;
331 }
332
333 mTimer.Stop();
334
335 SetState(kStateDisabled);
336
337 if (needResign && (aResignMode == kSendKeepAliveToResign))
338 {
339 SendKeepAlive();
340 }
341
342 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
343 Get<BorderAgent>().Start();
344 #endif
345
346 exit:
347 if (error != kErrorAlready)
348 {
349 LogWarnOnError(error, "stop commissioner");
350 }
351
352 return error;
353 }
354
SetId(const char * aId)355 Error Commissioner::SetId(const char *aId)
356 {
357 Error error = kErrorNone;
358
359 VerifyOrExit(IsDisabled(), error = kErrorInvalidState);
360 error = StringCopy(mCommissionerId, aId, kStringCheckUtf8Encoding);
361
362 exit:
363 return error;
364 }
365
ComputeBloomFilter(SteeringData & aSteeringData) const366 void Commissioner::ComputeBloomFilter(SteeringData &aSteeringData) const
367 {
368 Mac::ExtAddress joinerId;
369
370 aSteeringData.Init();
371
372 for (const Joiner &joiner : mJoiners)
373 {
374 switch (joiner.mType)
375 {
376 case Joiner::kTypeUnused:
377 break;
378
379 case Joiner::kTypeEui64:
380 ComputeJoinerId(joiner.mSharedId.mEui64, joinerId);
381 aSteeringData.UpdateBloomFilter(joinerId);
382 break;
383
384 case Joiner::kTypeDiscerner:
385 aSteeringData.UpdateBloomFilter(joiner.mSharedId.mDiscerner);
386 break;
387
388 case Joiner::kTypeAny:
389 aSteeringData.SetToPermitAllJoiners();
390 ExitNow();
391 }
392 }
393
394 exit:
395 return;
396 }
397
SendCommissionerSet(void)398 void Commissioner::SendCommissionerSet(void)
399 {
400 Error error = kErrorNone;
401 CommissioningDataset dataset;
402
403 VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
404
405 dataset.Clear();
406
407 dataset.SetSessionId(mSessionId);
408 ComputeBloomFilter(dataset.UpdateSteeringData());
409
410 error = SendMgmtCommissionerSetRequest(dataset, nullptr, 0);
411
412 exit:
413 LogWarnOnError(error, "send MGMT_COMMISSIONER_SET.req");
414 OT_UNUSED_VARIABLE(error);
415 }
416
ClearJoiners(void)417 void Commissioner::ClearJoiners(void)
418 {
419 for (Joiner &joiner : mJoiners)
420 {
421 joiner.mType = Joiner::kTypeUnused;
422 }
423
424 SendCommissionerSet();
425 }
426
AddJoiner(const Mac::ExtAddress * aEui64,const JoinerDiscerner * aDiscerner,const char * aPskd,uint32_t aTimeout)427 Error Commissioner::AddJoiner(const Mac::ExtAddress *aEui64,
428 const JoinerDiscerner *aDiscerner,
429 const char *aPskd,
430 uint32_t aTimeout)
431 {
432 Error error = kErrorNone;
433 Joiner *joiner;
434
435 VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
436
437 if (aDiscerner != nullptr)
438 {
439 VerifyOrExit(aDiscerner->IsValid(), error = kErrorInvalidArgs);
440 joiner = FindJoinerEntry(*aDiscerner);
441 }
442 else
443 {
444 joiner = FindJoinerEntry(aEui64);
445 }
446
447 if (joiner == nullptr)
448 {
449 joiner = GetUnusedJoinerEntry();
450 }
451
452 VerifyOrExit(joiner != nullptr, error = kErrorNoBufs);
453
454 SuccessOrExit(error = joiner->mPskd.SetFrom(aPskd));
455
456 if (aDiscerner != nullptr)
457 {
458 joiner->mType = Joiner::kTypeDiscerner;
459 joiner->mSharedId.mDiscerner = *aDiscerner;
460 }
461 else if (aEui64 != nullptr)
462 {
463 joiner->mType = Joiner::kTypeEui64;
464 joiner->mSharedId.mEui64 = *aEui64;
465 }
466 else
467 {
468 joiner->mType = Joiner::kTypeAny;
469 }
470
471 joiner->mExpirationTime = TimerMilli::GetNow() + Time::SecToMsec(aTimeout);
472
473 mJoinerExpirationTimer.FireAtIfEarlier(joiner->mExpirationTime);
474
475 SendCommissionerSet();
476
477 LogJoinerEntry("Added", *joiner);
478
479 exit:
480 return error;
481 }
482
CopyToJoinerInfo(otJoinerInfo & aJoiner) const483 void Commissioner::Joiner::CopyToJoinerInfo(otJoinerInfo &aJoiner) const
484 {
485 ClearAllBytes(aJoiner);
486
487 switch (mType)
488 {
489 case kTypeAny:
490 aJoiner.mType = OT_JOINER_INFO_TYPE_ANY;
491 break;
492
493 case kTypeEui64:
494 aJoiner.mType = OT_JOINER_INFO_TYPE_EUI64;
495 aJoiner.mSharedId.mEui64 = mSharedId.mEui64;
496 break;
497
498 case kTypeDiscerner:
499 aJoiner.mType = OT_JOINER_INFO_TYPE_DISCERNER;
500 aJoiner.mSharedId.mDiscerner = mSharedId.mDiscerner;
501 break;
502
503 case kTypeUnused:
504 ExitNow();
505 }
506
507 aJoiner.mPskd = mPskd;
508 aJoiner.mExpirationTime = mExpirationTime - TimerMilli::GetNow();
509
510 exit:
511 return;
512 }
513
GetNextJoinerInfo(uint16_t & aIterator,otJoinerInfo & aJoinerInfo) const514 Error Commissioner::GetNextJoinerInfo(uint16_t &aIterator, otJoinerInfo &aJoinerInfo) const
515 {
516 Error error = kErrorNone;
517
518 while (aIterator < GetArrayLength(mJoiners))
519 {
520 const Joiner &joiner = mJoiners[aIterator++];
521
522 if (joiner.mType != Joiner::kTypeUnused)
523 {
524 joiner.CopyToJoinerInfo(aJoinerInfo);
525 ExitNow();
526 }
527 }
528
529 error = kErrorNotFound;
530
531 exit:
532 return error;
533 }
534
RemoveJoiner(const Mac::ExtAddress * aEui64,const JoinerDiscerner * aDiscerner,uint32_t aDelay)535 Error Commissioner::RemoveJoiner(const Mac::ExtAddress *aEui64, const JoinerDiscerner *aDiscerner, uint32_t aDelay)
536 {
537 Error error = kErrorNone;
538 Joiner *joiner;
539
540 VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
541
542 if (aDiscerner != nullptr)
543 {
544 VerifyOrExit(aDiscerner->IsValid(), error = kErrorInvalidArgs);
545 joiner = FindJoinerEntry(*aDiscerner);
546 }
547 else
548 {
549 joiner = FindJoinerEntry(aEui64);
550 }
551
552 VerifyOrExit(joiner != nullptr, error = kErrorNotFound);
553
554 RemoveJoiner(*joiner, aDelay);
555
556 exit:
557 return error;
558 }
559
RemoveJoiner(Joiner & aJoiner,uint32_t aDelay)560 void Commissioner::RemoveJoiner(Joiner &aJoiner, uint32_t aDelay)
561 {
562 if (aDelay > 0)
563 {
564 TimeMilli newExpirationTime = TimerMilli::GetNow() + Time::SecToMsec(aDelay);
565
566 if (aJoiner.mExpirationTime > newExpirationTime)
567 {
568 aJoiner.mExpirationTime = newExpirationTime;
569 mJoinerExpirationTimer.FireAtIfEarlier(newExpirationTime);
570 }
571 }
572 else
573 {
574 RemoveJoinerEntry(aJoiner);
575 }
576 }
577
SetProvisioningUrl(const char * aProvisioningUrl)578 Error Commissioner::SetProvisioningUrl(const char *aProvisioningUrl)
579 {
580 return StringCopy(mProvisioningUrl, aProvisioningUrl, kStringCheckUtf8Encoding);
581 }
582
HandleTimer(void)583 void Commissioner::HandleTimer(void)
584 {
585 switch (mState)
586 {
587 case kStateDisabled:
588 break;
589
590 case kStatePetition:
591 IgnoreError(SendPetition());
592 break;
593
594 case kStateActive:
595 SendKeepAlive();
596 break;
597 }
598 }
599
HandleJoinerExpirationTimer(void)600 void Commissioner::HandleJoinerExpirationTimer(void)
601 {
602 NextFireTime nextTime;
603
604 for (Joiner &joiner : mJoiners)
605 {
606 if (joiner.mType == Joiner::kTypeUnused)
607 {
608 continue;
609 }
610
611 if (joiner.mExpirationTime <= nextTime.GetNow())
612 {
613 LogDebg("removing joiner due to timeout or successfully joined");
614 RemoveJoinerEntry(joiner);
615 }
616 else
617 {
618 nextTime.UpdateIfEarlier(joiner.mExpirationTime);
619 }
620 }
621
622 mJoinerExpirationTimer.FireAtIfEarlier(nextTime);
623 }
624
SendMgmtCommissionerGetRequest(const uint8_t * aTlvs,uint8_t aLength)625 Error Commissioner::SendMgmtCommissionerGetRequest(const uint8_t *aTlvs, uint8_t aLength)
626 {
627 Error error = kErrorNone;
628 Coap::Message *message;
629 Tmf::MessageInfo messageInfo(GetInstance());
630 Tlv tlv;
631
632 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriCommissionerGet);
633 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
634
635 if (aLength > 0)
636 {
637 tlv.SetType(Tlv::kGet);
638 tlv.SetLength(aLength);
639 SuccessOrExit(error = message->Append(tlv));
640 SuccessOrExit(error = message->AppendBytes(aTlvs, aLength));
641 }
642
643 messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc();
644 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
645 Commissioner::HandleMgmtCommissionerGetResponse, this));
646
647 LogInfo("Sent %s to leader", UriToString<kUriCommissionerGet>());
648
649 exit:
650 FreeMessageOnError(message, error);
651 return error;
652 }
653
HandleMgmtCommissionerGetResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)654 void Commissioner::HandleMgmtCommissionerGetResponse(void *aContext,
655 otMessage *aMessage,
656 const otMessageInfo *aMessageInfo,
657 Error aResult)
658 {
659 static_cast<Commissioner *>(aContext)->HandleMgmtCommissionerGetResponse(AsCoapMessagePtr(aMessage),
660 AsCoreTypePtr(aMessageInfo), aResult);
661 }
662
HandleMgmtCommissionerGetResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)663 void Commissioner::HandleMgmtCommissionerGetResponse(Coap::Message *aMessage,
664 const Ip6::MessageInfo *aMessageInfo,
665 Error aResult)
666 {
667 OT_UNUSED_VARIABLE(aMessageInfo);
668
669 VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged);
670 LogInfo("Received %s response", UriToString<kUriCommissionerGet>());
671
672 exit:
673 return;
674 }
675
SendMgmtCommissionerSetRequest(const CommissioningDataset & aDataset,const uint8_t * aTlvs,uint8_t aLength)676 Error Commissioner::SendMgmtCommissionerSetRequest(const CommissioningDataset &aDataset,
677 const uint8_t *aTlvs,
678 uint8_t aLength)
679 {
680 Error error = kErrorNone;
681 Coap::Message *message;
682 Tmf::MessageInfo messageInfo(GetInstance());
683
684 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriCommissionerSet);
685 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
686
687 if (aDataset.IsLocatorSet())
688 {
689 SuccessOrExit(error = Tlv::Append<BorderAgentLocatorTlv>(*message, aDataset.GetLocator()));
690 }
691
692 if (aDataset.IsSessionIdSet())
693 {
694 SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, aDataset.GetSessionId()));
695 }
696
697 if (aDataset.IsSteeringDataSet())
698 {
699 const SteeringData &steeringData = aDataset.GetSteeringData();
700
701 SuccessOrExit(error = Tlv::Append<SteeringDataTlv>(*message, steeringData.GetData(), steeringData.GetLength()));
702 }
703
704 if (aDataset.IsJoinerUdpPortSet())
705 {
706 SuccessOrExit(error = Tlv::Append<JoinerUdpPortTlv>(*message, aDataset.GetJoinerUdpPort()));
707 }
708
709 if (aLength > 0)
710 {
711 SuccessOrExit(error = message->AppendBytes(aTlvs, aLength));
712 }
713
714 messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc();
715 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
716 Commissioner::HandleMgmtCommissionerSetResponse, this));
717
718 LogInfo("Sent %s to leader", UriToString<kUriCommissionerSet>());
719
720 exit:
721 FreeMessageOnError(message, error);
722 return error;
723 }
724
HandleMgmtCommissionerSetResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)725 void Commissioner::HandleMgmtCommissionerSetResponse(void *aContext,
726 otMessage *aMessage,
727 const otMessageInfo *aMessageInfo,
728 Error aResult)
729 {
730 static_cast<Commissioner *>(aContext)->HandleMgmtCommissionerSetResponse(AsCoapMessagePtr(aMessage),
731 AsCoreTypePtr(aMessageInfo), aResult);
732 }
733
HandleMgmtCommissionerSetResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)734 void Commissioner::HandleMgmtCommissionerSetResponse(Coap::Message *aMessage,
735 const Ip6::MessageInfo *aMessageInfo,
736 Error aResult)
737 {
738 OT_UNUSED_VARIABLE(aMessageInfo);
739
740 Error error;
741 uint8_t state;
742
743 SuccessOrExit(error = aResult);
744 VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged && Tlv::Find<StateTlv>(*aMessage, state) == kErrorNone &&
745 state != StateTlv::kPending,
746 error = kErrorParse);
747
748 OT_UNUSED_VARIABLE(error);
749 exit:
750 LogInfo("Received %s response: %s", UriToString<kUriCommissionerSet>(),
751 error == kErrorNone ? StateTlv::StateToString(static_cast<StateTlv::State>(state)) : ErrorToString(error));
752 }
753
SendPetition(void)754 Error Commissioner::SendPetition(void)
755 {
756 Error error = kErrorNone;
757 Coap::Message *message = nullptr;
758 Tmf::MessageInfo messageInfo(GetInstance());
759
760 mTransmitAttempts++;
761
762 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriLeaderPetition);
763 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
764
765 SuccessOrExit(error = Tlv::Append<CommissionerIdTlv>(*message, mCommissionerId));
766
767 messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc();
768 SuccessOrExit(
769 error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, Commissioner::HandleLeaderPetitionResponse, this));
770
771 LogInfo("Sent %s", UriToString<kUriLeaderPetition>());
772
773 exit:
774 FreeMessageOnError(message, error);
775 return error;
776 }
777
HandleLeaderPetitionResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)778 void Commissioner::HandleLeaderPetitionResponse(void *aContext,
779 otMessage *aMessage,
780 const otMessageInfo *aMessageInfo,
781 Error aResult)
782 {
783 static_cast<Commissioner *>(aContext)->HandleLeaderPetitionResponse(AsCoapMessagePtr(aMessage),
784 AsCoreTypePtr(aMessageInfo), aResult);
785 }
786
HandleLeaderPetitionResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)787 void Commissioner::HandleLeaderPetitionResponse(Coap::Message *aMessage,
788 const Ip6::MessageInfo *aMessageInfo,
789 Error aResult)
790 {
791 OT_UNUSED_VARIABLE(aMessageInfo);
792
793 uint8_t state;
794 bool retransmit = false;
795
796 VerifyOrExit(mState != kStateActive);
797 VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged,
798 retransmit = (mState == kStatePetition));
799
800 LogInfo("Received %s response", UriToString<kUriLeaderPetition>());
801
802 SuccessOrExit(Tlv::Find<StateTlv>(*aMessage, state));
803 VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive)));
804
805 SuccessOrExit(Tlv::Find<CommissionerSessionIdTlv>(*aMessage, mSessionId));
806
807 // reject this session by sending KeepAlive reject if commissioner is in disabled state
808 // this could happen if commissioner is stopped by API during petitioning
809 if (mState == kStateDisabled)
810 {
811 SendKeepAlive(mSessionId);
812 ExitNow();
813 }
814
815 Get<Mle::Mle>().GetCommissionerAloc(mSessionId, mCommissionerAloc.GetAddress());
816 Get<ThreadNetif>().AddUnicastAddress(mCommissionerAloc);
817
818 SetState(kStateActive);
819
820 mTransmitAttempts = 0;
821 mTimer.Start(Time::SecToMsec(kKeepAliveTimeout) / 2);
822
823 exit:
824
825 if (retransmit)
826 {
827 if (mTransmitAttempts >= kPetitionRetryCount)
828 {
829 IgnoreError(Stop(kDoNotSendKeepAlive));
830 }
831 else
832 {
833 mTimer.Start(Time::SecToMsec(kPetitionRetryDelay));
834 }
835 }
836 }
837
SendKeepAlive(void)838 void Commissioner::SendKeepAlive(void) { SendKeepAlive(mSessionId); }
839
SendKeepAlive(uint16_t aSessionId)840 void Commissioner::SendKeepAlive(uint16_t aSessionId)
841 {
842 Error error = kErrorNone;
843 Coap::Message *message = nullptr;
844 Tmf::MessageInfo messageInfo(GetInstance());
845
846 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriLeaderKeepAlive);
847 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
848
849 SuccessOrExit(
850 error = Tlv::Append<StateTlv>(*message, (mState == kStateActive) ? StateTlv::kAccept : StateTlv::kReject));
851
852 SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, aSessionId));
853
854 messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc();
855 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
856 Commissioner::HandleLeaderKeepAliveResponse, this));
857
858 LogInfo("Sent %s", UriToString<kUriLeaderKeepAlive>());
859
860 exit:
861 FreeMessageOnError(message, error);
862 LogWarnOnError(error, "send keep alive");
863 }
864
HandleLeaderKeepAliveResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)865 void Commissioner::HandleLeaderKeepAliveResponse(void *aContext,
866 otMessage *aMessage,
867 const otMessageInfo *aMessageInfo,
868 Error aResult)
869 {
870 static_cast<Commissioner *>(aContext)->HandleLeaderKeepAliveResponse(AsCoapMessagePtr(aMessage),
871 AsCoreTypePtr(aMessageInfo), aResult);
872 }
873
HandleLeaderKeepAliveResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)874 void Commissioner::HandleLeaderKeepAliveResponse(Coap::Message *aMessage,
875 const Ip6::MessageInfo *aMessageInfo,
876 Error aResult)
877 {
878 OT_UNUSED_VARIABLE(aMessageInfo);
879
880 uint8_t state;
881
882 VerifyOrExit(mState == kStateActive);
883 VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged,
884 IgnoreError(Stop(kDoNotSendKeepAlive)));
885
886 LogInfo("Received %s response", UriToString<kUriLeaderKeepAlive>());
887
888 SuccessOrExit(Tlv::Find<StateTlv>(*aMessage, state));
889 VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive)));
890
891 mTimer.Start(Time::SecToMsec(kKeepAliveTimeout) / 2);
892
893 exit:
894 return;
895 }
896
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)897 template <> void Commissioner::HandleTmf<kUriRelayRx>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
898 {
899 OT_UNUSED_VARIABLE(aMessageInfo);
900
901 Error error;
902 uint16_t joinerPort;
903 Ip6::InterfaceIdentifier joinerIid;
904 uint16_t joinerRloc;
905 Ip6::MessageInfo joinerMessageInfo;
906 OffsetRange offsetRange;
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(error = Tlv::FindTlvValueOffsetRange(aMessage, Tlv::kJoinerDtlsEncapsulation, offsetRange));
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(offsetRange.GetOffset());
954 SuccessOrExit(error = aMessage.SetLength(offsetRange.GetEndOffset()));
955
956 joinerMessageInfo.SetPeerAddr(Get<Mle::MleRouter>().GetMeshLocalEid());
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>().GetMeshLocalEid());
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