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