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