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