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