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