mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
3d7b9fb686
This commit renames several methods in the `Mle` class that construct an IPv6 address from the mesh-local prefix and an RLOC16/ALOC16 from `Get...()` to `Compose...()` to better reflect their behavior. The affected methods are: - `GetLeaderRloc()` -> `ComposeLeaderRloc()` - `GetLeaderAloc()` -> `ComposeLeaderAloc()` - `GetCommissionerAloc()` -> `ComposeCommissionerAloc()` - `GetServiceAloc()` -> `ComposeServiceAloc()`
1200 lines
34 KiB
C++
1200 lines
34 KiB
C++
/*
|
|
* Copyright (c) 2016, The OpenThread Authors.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* This file implements a Commissioner role.
|
|
*/
|
|
|
|
#include "commissioner.hpp"
|
|
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
|
|
|
#include "instance/instance.hpp"
|
|
|
|
namespace ot {
|
|
namespace MeshCoP {
|
|
|
|
RegisterLogModule("Commissioner");
|
|
|
|
Commissioner::Commissioner(Instance &aInstance)
|
|
: InstanceLocator(aInstance)
|
|
, mActiveJoiner(nullptr)
|
|
, mJoinerPort(0)
|
|
, mJoinerRloc(0)
|
|
, mSessionId(0)
|
|
, mTransmitAttempts(0)
|
|
, mState(kStateDisabled)
|
|
, mJoinerExpirationTimer(aInstance)
|
|
, mTimer(aInstance)
|
|
, mJoinerSessionTimer(aInstance)
|
|
{
|
|
ClearAllBytes(mJoiners);
|
|
|
|
mCommissionerAloc.InitAsThreadOriginMeshLocal();
|
|
mCommissionerAloc.mPreferred = true;
|
|
|
|
IgnoreError(SetId("OpenThread Commissioner"));
|
|
|
|
mProvisioningUrl[0] = kNullChar;
|
|
}
|
|
|
|
void Commissioner::SetState(State aState)
|
|
{
|
|
State oldState = mState;
|
|
|
|
OT_UNUSED_VARIABLE(oldState);
|
|
|
|
SuccessOrExit(Get<Notifier>().Update(mState, aState, kEventCommissionerStateChanged));
|
|
|
|
LogInfo("State: %s -> %s", StateToString(oldState), StateToString(aState));
|
|
|
|
mStateCallback.InvokeIfSet(MapEnum(mState));
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Commissioner::SignalJoinerEvent(JoinerEvent aEvent, const Joiner *aJoiner) const
|
|
{
|
|
otJoinerInfo joinerInfo;
|
|
Mac::ExtAddress joinerId;
|
|
bool noJoinerId = false;
|
|
|
|
VerifyOrExit(mJoinerCallback.IsSet() && (aJoiner != nullptr));
|
|
|
|
aJoiner->CopyToJoinerInfo(joinerInfo);
|
|
|
|
if (aJoiner->mType == Joiner::kTypeEui64)
|
|
{
|
|
ComputeJoinerId(aJoiner->mSharedId.mEui64, joinerId);
|
|
}
|
|
else if (aJoiner == mActiveJoiner)
|
|
{
|
|
joinerId.SetFromIid(mJoinerIid);
|
|
}
|
|
else
|
|
{
|
|
noJoinerId = true;
|
|
}
|
|
|
|
mJoinerCallback.Invoke(MapEnum(aEvent), &joinerInfo, noJoinerId ? nullptr : &joinerId);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Commissioner::HandleSecureAgentConnectEvent(Dtls::Session::ConnectEvent aEvent, void *aContext)
|
|
{
|
|
static_cast<Commissioner *>(aContext)->HandleSecureAgentConnectEvent(aEvent);
|
|
}
|
|
|
|
void Commissioner::HandleSecureAgentConnectEvent(Dtls::Session::ConnectEvent aEvent)
|
|
{
|
|
bool isConnected = (aEvent == Dtls::Session::kConnected);
|
|
if (!isConnected)
|
|
{
|
|
mJoinerSessionTimer.Stop();
|
|
}
|
|
|
|
SignalJoinerEvent(isConnected ? kJoinerEventConnected : kJoinerEventEnd, mActiveJoiner);
|
|
}
|
|
|
|
Commissioner::Joiner *Commissioner::GetUnusedJoinerEntry(void)
|
|
{
|
|
Joiner *rval = nullptr;
|
|
|
|
for (Joiner &joiner : mJoiners)
|
|
{
|
|
if (joiner.mType == Joiner::kTypeUnused)
|
|
{
|
|
rval = &joiner;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
Commissioner::Joiner *Commissioner::FindJoinerEntry(const Mac::ExtAddress *aEui64)
|
|
{
|
|
Joiner *rval = nullptr;
|
|
|
|
for (Joiner &joiner : mJoiners)
|
|
{
|
|
switch (joiner.mType)
|
|
{
|
|
case Joiner::kTypeUnused:
|
|
case Joiner::kTypeDiscerner:
|
|
break;
|
|
|
|
case Joiner::kTypeAny:
|
|
if (aEui64 == nullptr)
|
|
{
|
|
ExitNow(rval = &joiner);
|
|
}
|
|
break;
|
|
|
|
case Joiner::kTypeEui64:
|
|
if ((aEui64 != nullptr) && (joiner.mSharedId.mEui64 == *aEui64))
|
|
{
|
|
ExitNow(rval = &joiner);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return rval;
|
|
}
|
|
|
|
Commissioner::Joiner *Commissioner::FindJoinerEntry(const JoinerDiscerner &aDiscerner)
|
|
{
|
|
Joiner *rval = nullptr;
|
|
|
|
for (Joiner &joiner : mJoiners)
|
|
{
|
|
if ((joiner.mType == Joiner::kTypeDiscerner) && (aDiscerner == joiner.mSharedId.mDiscerner))
|
|
{
|
|
rval = &joiner;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
Commissioner::Joiner *Commissioner::FindBestMatchingJoinerEntry(const Mac::ExtAddress &aReceivedJoinerId)
|
|
{
|
|
Joiner *best = nullptr;
|
|
Mac::ExtAddress joinerId;
|
|
|
|
// Prefer a full Joiner ID match, if not found use the entry
|
|
// accepting any joiner.
|
|
|
|
for (Joiner &joiner : mJoiners)
|
|
{
|
|
switch (joiner.mType)
|
|
{
|
|
case Joiner::kTypeUnused:
|
|
break;
|
|
|
|
case Joiner::kTypeAny:
|
|
if (best == nullptr)
|
|
{
|
|
best = &joiner;
|
|
}
|
|
break;
|
|
|
|
case Joiner::kTypeEui64:
|
|
ComputeJoinerId(joiner.mSharedId.mEui64, joinerId);
|
|
if (joinerId == aReceivedJoinerId)
|
|
{
|
|
ExitNow(best = &joiner);
|
|
}
|
|
break;
|
|
|
|
case Joiner::kTypeDiscerner:
|
|
if (joiner.mSharedId.mDiscerner.Matches(aReceivedJoinerId))
|
|
{
|
|
if ((best == nullptr) ||
|
|
((best->mType == Joiner::kTypeDiscerner) &&
|
|
(best->mSharedId.mDiscerner.GetLength() < joiner.mSharedId.mDiscerner.GetLength())))
|
|
{
|
|
best = &joiner;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return best;
|
|
}
|
|
|
|
void Commissioner::RemoveJoinerEntry(Commissioner::Joiner &aJoiner)
|
|
{
|
|
// Create a copy of `aJoiner` to use for signaling joiner event
|
|
// and logging after the entry is removed. This ensures the joiner
|
|
// event callback is invoked after all states are cleared.
|
|
|
|
Joiner joinerCopy = aJoiner;
|
|
|
|
aJoiner.mType = Joiner::kTypeUnused;
|
|
|
|
if (&aJoiner == mActiveJoiner)
|
|
{
|
|
mActiveJoiner = nullptr;
|
|
}
|
|
|
|
SendCommissionerSet();
|
|
|
|
LogJoinerEntry("Removed", joinerCopy);
|
|
SignalJoinerEvent(kJoinerEventRemoved, &joinerCopy);
|
|
}
|
|
|
|
Error Commissioner::Start(StateCallback aStateCallback, JoinerCallback aJoinerCallback, void *aCallbackContext)
|
|
{
|
|
Error error = kErrorNone;
|
|
|
|
VerifyOrExit(Get<Mle::Mle>().IsAttached(), error = kErrorInvalidState);
|
|
VerifyOrExit(mState == kStateDisabled, error = kErrorAlready);
|
|
|
|
SuccessOrExit(error = Get<Tmf::SecureAgent>().Open(SendRelayTransmit, this));
|
|
|
|
Get<Tmf::SecureAgent>().SetConnectCallback(HandleSecureAgentConnectEvent, this);
|
|
|
|
mStateCallback.Set(aStateCallback, aCallbackContext);
|
|
mJoinerCallback.Set(aJoinerCallback, aCallbackContext);
|
|
mTransmitAttempts = 0;
|
|
|
|
SuccessOrExit(error = SendPetition());
|
|
SetState(kStatePetition);
|
|
|
|
LogInfo("start commissioner %s", mCommissionerId);
|
|
|
|
exit:
|
|
if ((error != kErrorNone) && (error != kErrorAlready))
|
|
{
|
|
Get<Tmf::SecureAgent>().Close();
|
|
LogWarnOnError(error, "start commissioner");
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
Error Commissioner::Stop(ResignMode aResignMode)
|
|
{
|
|
Error error = kErrorNone;
|
|
bool needResign = false;
|
|
|
|
VerifyOrExit(mState != kStateDisabled, error = kErrorAlready);
|
|
|
|
mJoinerSessionTimer.Stop();
|
|
Get<Tmf::SecureAgent>().Close();
|
|
|
|
if (mState == kStateActive)
|
|
{
|
|
Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
|
|
ClearJoiners();
|
|
needResign = true;
|
|
}
|
|
else if (mState == kStatePetition)
|
|
{
|
|
mTransmitAttempts = 0;
|
|
}
|
|
|
|
mTimer.Stop();
|
|
|
|
SetState(kStateDisabled);
|
|
|
|
if (needResign && (aResignMode == kSendKeepAliveToResign))
|
|
{
|
|
SendKeepAlive();
|
|
}
|
|
|
|
exit:
|
|
if (error != kErrorAlready)
|
|
{
|
|
LogWarnOnError(error, "stop commissioner");
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
Error Commissioner::SetId(const char *aId)
|
|
{
|
|
Error error = kErrorNone;
|
|
|
|
VerifyOrExit(IsDisabled(), error = kErrorInvalidState);
|
|
error = StringCopy(mCommissionerId, aId, kStringCheckUtf8Encoding);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Commissioner::ComputeBloomFilter(SteeringData &aSteeringData) const
|
|
{
|
|
Mac::ExtAddress joinerId;
|
|
|
|
IgnoreError(aSteeringData.Init(SteeringData::kMaxLength));
|
|
|
|
for (const Joiner &joiner : mJoiners)
|
|
{
|
|
switch (joiner.mType)
|
|
{
|
|
case Joiner::kTypeUnused:
|
|
break;
|
|
|
|
case Joiner::kTypeEui64:
|
|
ComputeJoinerId(joiner.mSharedId.mEui64, joinerId);
|
|
IgnoreError(aSteeringData.UpdateBloomFilter(joinerId));
|
|
break;
|
|
|
|
case Joiner::kTypeDiscerner:
|
|
IgnoreError(aSteeringData.UpdateBloomFilter(joiner.mSharedId.mDiscerner));
|
|
break;
|
|
|
|
case Joiner::kTypeAny:
|
|
aSteeringData.SetToPermitAllJoiners();
|
|
ExitNow();
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Commissioner::SendCommissionerSet(void)
|
|
{
|
|
Error error = kErrorNone;
|
|
CommissioningDataset dataset;
|
|
|
|
VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
|
|
|
|
dataset.Clear();
|
|
|
|
dataset.SetSessionId(mSessionId);
|
|
ComputeBloomFilter(dataset.UpdateSteeringData());
|
|
|
|
error = SendMgmtCommissionerSetRequest(dataset, nullptr, 0);
|
|
|
|
exit:
|
|
LogWarnOnError(error, "send MGMT_COMMISSIONER_SET.req");
|
|
OT_UNUSED_VARIABLE(error);
|
|
}
|
|
|
|
void Commissioner::ClearJoiners(void)
|
|
{
|
|
for (Joiner &joiner : mJoiners)
|
|
{
|
|
joiner.mType = Joiner::kTypeUnused;
|
|
}
|
|
|
|
SendCommissionerSet();
|
|
}
|
|
|
|
Error Commissioner::AddJoiner(const Mac::ExtAddress *aEui64,
|
|
const JoinerDiscerner *aDiscerner,
|
|
const char *aPskd,
|
|
uint32_t aTimeout)
|
|
{
|
|
Error error = kErrorNone;
|
|
Joiner *joiner;
|
|
|
|
VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
|
|
|
|
if (aDiscerner != nullptr)
|
|
{
|
|
VerifyOrExit(aDiscerner->IsValid(), error = kErrorInvalidArgs);
|
|
joiner = FindJoinerEntry(*aDiscerner);
|
|
}
|
|
else
|
|
{
|
|
joiner = FindJoinerEntry(aEui64);
|
|
}
|
|
|
|
if (joiner == nullptr)
|
|
{
|
|
joiner = GetUnusedJoinerEntry();
|
|
}
|
|
|
|
VerifyOrExit(joiner != nullptr, error = kErrorNoBufs);
|
|
|
|
SuccessOrExit(error = joiner->mPskd.SetFrom(aPskd));
|
|
|
|
if (aDiscerner != nullptr)
|
|
{
|
|
joiner->mType = Joiner::kTypeDiscerner;
|
|
joiner->mSharedId.mDiscerner = *aDiscerner;
|
|
}
|
|
else if (aEui64 != nullptr)
|
|
{
|
|
joiner->mType = Joiner::kTypeEui64;
|
|
joiner->mSharedId.mEui64 = *aEui64;
|
|
}
|
|
else
|
|
{
|
|
joiner->mType = Joiner::kTypeAny;
|
|
}
|
|
|
|
joiner->mExpirationTime = TimerMilli::GetNow() + Time::SecToMsec(aTimeout);
|
|
|
|
mJoinerExpirationTimer.FireAtIfEarlier(joiner->mExpirationTime);
|
|
|
|
SendCommissionerSet();
|
|
|
|
LogJoinerEntry("Added", *joiner);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Commissioner::Joiner::CopyToJoinerInfo(otJoinerInfo &aJoiner) const
|
|
{
|
|
ClearAllBytes(aJoiner);
|
|
|
|
switch (mType)
|
|
{
|
|
case kTypeAny:
|
|
aJoiner.mType = OT_JOINER_INFO_TYPE_ANY;
|
|
break;
|
|
|
|
case kTypeEui64:
|
|
aJoiner.mType = OT_JOINER_INFO_TYPE_EUI64;
|
|
aJoiner.mSharedId.mEui64 = mSharedId.mEui64;
|
|
break;
|
|
|
|
case kTypeDiscerner:
|
|
aJoiner.mType = OT_JOINER_INFO_TYPE_DISCERNER;
|
|
aJoiner.mSharedId.mDiscerner = mSharedId.mDiscerner;
|
|
break;
|
|
|
|
case kTypeUnused:
|
|
ExitNow();
|
|
}
|
|
|
|
aJoiner.mPskd = mPskd;
|
|
aJoiner.mExpirationTime = mExpirationTime - TimerMilli::GetNow();
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
Error Commissioner::GetNextJoinerInfo(uint16_t &aIterator, otJoinerInfo &aJoinerInfo) const
|
|
{
|
|
Error error = kErrorNone;
|
|
|
|
while (aIterator < GetArrayLength(mJoiners))
|
|
{
|
|
const Joiner &joiner = mJoiners[aIterator++];
|
|
|
|
if (joiner.mType != Joiner::kTypeUnused)
|
|
{
|
|
joiner.CopyToJoinerInfo(aJoinerInfo);
|
|
ExitNow();
|
|
}
|
|
}
|
|
|
|
error = kErrorNotFound;
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error Commissioner::RemoveJoiner(const Mac::ExtAddress *aEui64, const JoinerDiscerner *aDiscerner, uint32_t aDelay)
|
|
{
|
|
Error error = kErrorNone;
|
|
Joiner *joiner;
|
|
|
|
VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
|
|
|
|
if (aDiscerner != nullptr)
|
|
{
|
|
VerifyOrExit(aDiscerner->IsValid(), error = kErrorInvalidArgs);
|
|
joiner = FindJoinerEntry(*aDiscerner);
|
|
}
|
|
else
|
|
{
|
|
joiner = FindJoinerEntry(aEui64);
|
|
}
|
|
|
|
VerifyOrExit(joiner != nullptr, error = kErrorNotFound);
|
|
|
|
RemoveJoiner(*joiner, aDelay);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Commissioner::RemoveJoiner(Joiner &aJoiner, uint32_t aDelay)
|
|
{
|
|
if (aDelay > 0)
|
|
{
|
|
TimeMilli newExpirationTime = TimerMilli::GetNow() + Time::SecToMsec(aDelay);
|
|
|
|
if (aJoiner.mExpirationTime > newExpirationTime)
|
|
{
|
|
aJoiner.mExpirationTime = newExpirationTime;
|
|
mJoinerExpirationTimer.FireAtIfEarlier(newExpirationTime);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RemoveJoinerEntry(aJoiner);
|
|
}
|
|
}
|
|
|
|
Error Commissioner::SetProvisioningUrl(const char *aProvisioningUrl)
|
|
{
|
|
return StringCopy(mProvisioningUrl, aProvisioningUrl, kStringCheckUtf8Encoding);
|
|
}
|
|
|
|
void Commissioner::HandleTimer(void)
|
|
{
|
|
switch (mState)
|
|
{
|
|
case kStateDisabled:
|
|
break;
|
|
|
|
case kStatePetition:
|
|
IgnoreError(SendPetition());
|
|
break;
|
|
|
|
case kStateActive:
|
|
SendKeepAlive();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Commissioner::HandleJoinerExpirationTimer(void)
|
|
{
|
|
NextFireTime nextTime;
|
|
|
|
for (Joiner &joiner : mJoiners)
|
|
{
|
|
if (joiner.mType == Joiner::kTypeUnused)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (joiner.mExpirationTime <= nextTime.GetNow())
|
|
{
|
|
LogDebg("removing joiner due to timeout or successfully joined");
|
|
RemoveJoinerEntry(joiner);
|
|
}
|
|
else
|
|
{
|
|
nextTime.UpdateIfEarlier(joiner.mExpirationTime);
|
|
}
|
|
}
|
|
|
|
mJoinerExpirationTimer.FireAtIfEarlier(nextTime);
|
|
}
|
|
|
|
Error Commissioner::SendMgmtCommissionerGetRequest(const uint8_t *aTlvs, uint8_t aLength)
|
|
{
|
|
Error error = kErrorNone;
|
|
OwnedPtr<Coap::Message> message;
|
|
|
|
message.Reset(Get<Tmf::Agent>().AllocateAndInitPriorityConfirmablePostMessage(kUriCommissionerGet));
|
|
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
|
|
|
if (aLength > 0)
|
|
{
|
|
SuccessOrExit(error = Tlv::AppendTlv(*message, Tlv::kGet, aTlvs, aLength));
|
|
}
|
|
|
|
SuccessOrExit(error = Get<Tmf::Agent>().SendMessageToLeaderAloc(*message, HandleMgmtCommissionerGetResponse, this));
|
|
message.Release();
|
|
|
|
LogInfo("Sent %s to leader", UriToString<kUriCommissionerGet>());
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Commissioner::HandleMgmtCommissionerGetResponse(Coap::Msg *aMsg, Error aResult)
|
|
{
|
|
VerifyOrExit(aResult == kErrorNone && aMsg->GetCode() == Coap::kCodeChanged);
|
|
LogInfo("Received %s response", UriToString<kUriCommissionerGet>());
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
Error Commissioner::SendMgmtCommissionerSetRequest(const CommissioningDataset &aDataset,
|
|
const uint8_t *aTlvs,
|
|
uint8_t aLength)
|
|
{
|
|
Error error = kErrorNone;
|
|
OwnedPtr<Coap::Message> message;
|
|
|
|
message.Reset(Get<Tmf::Agent>().AllocateAndInitPriorityConfirmablePostMessage(kUriCommissionerSet));
|
|
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
|
|
|
if (aDataset.IsLocatorSet())
|
|
{
|
|
SuccessOrExit(error = Tlv::Append<BorderAgentLocatorTlv>(*message, aDataset.GetLocator()));
|
|
}
|
|
|
|
if (aDataset.IsSessionIdSet())
|
|
{
|
|
SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, aDataset.GetSessionId()));
|
|
}
|
|
|
|
if (aDataset.IsSteeringDataSet())
|
|
{
|
|
const SteeringData &steeringData = aDataset.GetSteeringData();
|
|
|
|
SuccessOrExit(error = SteeringDataTlv::AppendTo(*message, steeringData));
|
|
}
|
|
|
|
if (aDataset.IsJoinerUdpPortSet())
|
|
{
|
|
SuccessOrExit(error = Tlv::Append<JoinerUdpPortTlv>(*message, aDataset.GetJoinerUdpPort()));
|
|
}
|
|
|
|
if (aLength > 0)
|
|
{
|
|
SuccessOrExit(error = message->AppendBytes(aTlvs, aLength));
|
|
}
|
|
|
|
SuccessOrExit(error = Get<Tmf::Agent>().SendMessageToLeaderAloc(*message, HandleMgmtCommissionerSetResponse, this));
|
|
message.Release();
|
|
|
|
LogInfo("Sent %s to leader", UriToString<kUriCommissionerSet>());
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Commissioner::HandleMgmtCommissionerSetResponse(Coap::Msg *aMsg, Error aResult)
|
|
{
|
|
Error error;
|
|
uint8_t state;
|
|
|
|
SuccessOrExit(error = aResult);
|
|
VerifyOrExit(aMsg->GetCode() == Coap::kCodeChanged && Tlv::Find<StateTlv>(aMsg->mMessage, state) == kErrorNone &&
|
|
state != StateTlv::kPending,
|
|
error = kErrorParse);
|
|
|
|
OT_UNUSED_VARIABLE(error);
|
|
exit:
|
|
LogInfo("Received %s response: %s", UriToString<kUriCommissionerSet>(),
|
|
error == kErrorNone ? StateTlv::StateToString(static_cast<StateTlv::State>(state)) : ErrorToString(error));
|
|
}
|
|
|
|
Error Commissioner::SendPetition(void)
|
|
{
|
|
Error error = kErrorNone;
|
|
OwnedPtr<Coap::Message> message;
|
|
|
|
mTransmitAttempts++;
|
|
|
|
message.Reset(Get<Tmf::Agent>().AllocateAndInitPriorityConfirmablePostMessage(kUriLeaderPetition));
|
|
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
|
|
|
SuccessOrExit(error = Tlv::Append<CommissionerIdTlv>(*message, mCommissionerId));
|
|
|
|
SuccessOrExit(error = Get<Tmf::Agent>().SendMessageToLeaderAloc(*message, HandleLeaderPetitionResponse, this));
|
|
message.Release();
|
|
|
|
LogInfo("Sent %s", UriToString<kUriLeaderPetition>());
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Commissioner::HandleLeaderPetitionResponse(Coap::Msg *aMsg, Error aResult)
|
|
{
|
|
uint8_t state;
|
|
bool retransmit = false;
|
|
|
|
VerifyOrExit(mState != kStateActive);
|
|
VerifyOrExit(aResult == kErrorNone && aMsg->GetCode() == Coap::kCodeChanged,
|
|
retransmit = (mState == kStatePetition));
|
|
|
|
LogInfo("Received %s response", UriToString<kUriLeaderPetition>());
|
|
|
|
SuccessOrExit(Tlv::Find<StateTlv>(aMsg->mMessage, state));
|
|
VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive)));
|
|
|
|
SuccessOrExit(Tlv::Find<CommissionerSessionIdTlv>(aMsg->mMessage, mSessionId));
|
|
|
|
// reject this session by sending KeepAlive reject if commissioner is in disabled state
|
|
// this could happen if commissioner is stopped by API during petitioning
|
|
if (mState == kStateDisabled)
|
|
{
|
|
SendKeepAlive(mSessionId);
|
|
ExitNow();
|
|
}
|
|
|
|
Get<Mle::Mle>().ComposeCommissionerAloc(mSessionId, mCommissionerAloc.GetAddress());
|
|
Get<ThreadNetif>().AddUnicastAddress(mCommissionerAloc);
|
|
|
|
SetState(kStateActive);
|
|
|
|
mTransmitAttempts = 0;
|
|
mTimer.Start(Time::SecToMsec(kKeepAliveTimeout) / 2);
|
|
|
|
exit:
|
|
|
|
if (retransmit)
|
|
{
|
|
if (mTransmitAttempts >= kPetitionRetryCount)
|
|
{
|
|
IgnoreError(Stop(kDoNotSendKeepAlive));
|
|
}
|
|
else
|
|
{
|
|
mTimer.Start(Time::SecToMsec(kPetitionRetryDelay));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Commissioner::SendKeepAlive(void) { SendKeepAlive(mSessionId); }
|
|
|
|
void Commissioner::SendKeepAlive(uint16_t aSessionId)
|
|
{
|
|
Error error = kErrorNone;
|
|
OwnedPtr<Coap::Message> message;
|
|
|
|
message.Reset(Get<Tmf::Agent>().AllocateAndInitPriorityConfirmablePostMessage(kUriLeaderKeepAlive));
|
|
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
|
|
|
SuccessOrExit(
|
|
error = Tlv::Append<StateTlv>(*message, (mState == kStateActive) ? StateTlv::kAccept : StateTlv::kReject));
|
|
|
|
SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, aSessionId));
|
|
|
|
SuccessOrExit(error = Get<Tmf::Agent>().SendMessageToLeaderAloc(*message, HandleLeaderKeepAliveResponse, this));
|
|
message.Release();
|
|
|
|
LogInfo("Sent %s", UriToString<kUriLeaderKeepAlive>());
|
|
|
|
exit:
|
|
LogWarnOnError(error, "send keep alive");
|
|
}
|
|
|
|
void Commissioner::HandleLeaderKeepAliveResponse(Coap::Msg *aMsg, Error aResult)
|
|
{
|
|
uint8_t state;
|
|
|
|
VerifyOrExit(mState == kStateActive);
|
|
VerifyOrExit(aResult == kErrorNone && aMsg->GetCode() == Coap::kCodeChanged,
|
|
IgnoreError(Stop(kDoNotSendKeepAlive)));
|
|
|
|
LogInfo("Received %s response", UriToString<kUriLeaderKeepAlive>());
|
|
|
|
SuccessOrExit(Tlv::Find<StateTlv>(aMsg->mMessage, state));
|
|
VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive)));
|
|
|
|
mTimer.Start(Time::SecToMsec(kKeepAliveTimeout) / 2);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
template <> void Commissioner::HandleTmf<kUriRelayRx>(Coap::Msg &aMsg)
|
|
{
|
|
Error error;
|
|
uint16_t joinerPort;
|
|
Ip6::InterfaceIdentifier joinerIid;
|
|
uint16_t joinerRloc;
|
|
Ip6::MessageInfo joinerMessageInfo;
|
|
OffsetRange offsetRange;
|
|
|
|
VerifyOrExit(mState == kStateActive, error = kErrorInvalidState);
|
|
|
|
VerifyOrExit(aMsg.IsNonConfirmable());
|
|
|
|
SuccessOrExit(error = Tlv::Find<JoinerUdpPortTlv>(aMsg.mMessage, joinerPort));
|
|
SuccessOrExit(error = Tlv::Find<JoinerIidTlv>(aMsg.mMessage, joinerIid));
|
|
SuccessOrExit(error = Tlv::Find<JoinerRouterLocatorTlv>(aMsg.mMessage, joinerRloc));
|
|
|
|
SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMsg.mMessage, Tlv::kJoinerDtlsEncapsulation, offsetRange));
|
|
|
|
if (!Get<Tmf::SecureAgent>().IsConnectionActive())
|
|
{
|
|
Mac::ExtAddress receivedId;
|
|
Joiner *joiner;
|
|
|
|
mJoinerIid = joinerIid;
|
|
receivedId.SetFromIid(mJoinerIid);
|
|
|
|
joiner = FindBestMatchingJoinerEntry(receivedId);
|
|
VerifyOrExit(joiner != nullptr);
|
|
|
|
Get<Tmf::SecureAgent>().SetPsk(joiner->mPskd);
|
|
mActiveJoiner = joiner;
|
|
|
|
mJoinerSessionTimer.Start(kJoinerSessionTimeoutMillis);
|
|
|
|
LogJoinerEntry("Starting new session with", *joiner);
|
|
SignalJoinerEvent(kJoinerEventStart, joiner);
|
|
}
|
|
else
|
|
{
|
|
if (mJoinerIid != joinerIid)
|
|
{
|
|
LogNote("Ignore %s (%s, 0x%04x), session in progress with (%s, 0x%04x)", UriToString<kUriRelayRx>(),
|
|
joinerIid.ToString().AsCString(), joinerRloc, mJoinerIid.ToString().AsCString(), mJoinerRloc);
|
|
|
|
ExitNow();
|
|
}
|
|
}
|
|
|
|
mJoinerPort = joinerPort;
|
|
mJoinerRloc = joinerRloc;
|
|
|
|
LogInfo("Received %s (%s, 0x%04x)", UriToString<kUriRelayRx>(), mJoinerIid.ToString().AsCString(), mJoinerRloc);
|
|
|
|
aMsg.mMessage.SetOffset(offsetRange.GetOffset());
|
|
SuccessOrExit(error = aMsg.mMessage.SetLength(offsetRange.GetEndOffset()));
|
|
|
|
joinerMessageInfo.SetPeerAddr(Get<Mle::Mle>().GetMeshLocalEid());
|
|
joinerMessageInfo.GetPeerAddr().SetIid(mJoinerIid);
|
|
joinerMessageInfo.SetPeerPort(mJoinerPort);
|
|
|
|
Get<Tmf::SecureAgent>().HandleReceive(aMsg.mMessage, joinerMessageInfo);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Commissioner::HandleJoinerSessionTimer(void)
|
|
{
|
|
if (mActiveJoiner != nullptr)
|
|
{
|
|
LogJoinerEntry("Timed out session with", *mActiveJoiner);
|
|
}
|
|
|
|
Get<Tmf::SecureAgent>().Disconnect();
|
|
}
|
|
|
|
template <> void Commissioner::HandleTmf<kUriDatasetChanged>(Coap::Msg &aMsg)
|
|
{
|
|
VerifyOrExit(mState == kStateActive);
|
|
VerifyOrExit(aMsg.IsConfirmable());
|
|
|
|
LogInfo("Received %s", UriToString<kUriDatasetChanged>());
|
|
|
|
SuccessOrExit(Get<Tmf::Agent>().SendAckResponse(aMsg));
|
|
|
|
LogInfo("Sent %s ack", UriToString<kUriDatasetChanged>());
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
template <> void Commissioner::HandleTmf<kUriJoinerFinalize>(Coap::Msg &aMsg)
|
|
{
|
|
StateTlv::State state = StateTlv::kAccept;
|
|
ProvisioningUrlTlv::StringType provisioningUrl;
|
|
|
|
VerifyOrExit(mState == kStateActive);
|
|
|
|
LogInfo("Received %s", UriToString<kUriJoinerFinalize>());
|
|
|
|
switch (Tlv::Find<ProvisioningUrlTlv>(aMsg.mMessage, provisioningUrl))
|
|
{
|
|
case kErrorNone:
|
|
if (!StringMatch(provisioningUrl, mProvisioningUrl))
|
|
{
|
|
state = StateTlv::kReject;
|
|
}
|
|
break;
|
|
|
|
case kErrorNotFound:
|
|
break;
|
|
|
|
default:
|
|
ExitNow();
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
LogCertMessage("[THCI] direction=recv | type=JOIN_FIN.req |", aMsg.mMessage);
|
|
#endif
|
|
|
|
SendJoinFinalizeResponse(aMsg.mMessage, state);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Commissioner::SendJoinFinalizeResponse(const Coap::Message &aRequest, StateTlv::State aState)
|
|
{
|
|
Error error = kErrorNone;
|
|
Coap::Message *message;
|
|
|
|
message = Get<Tmf::SecureAgent>().AllocateAndInitPriorityResponseFor(aRequest);
|
|
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
|
|
|
message->SetOffset(message->GetLength());
|
|
message->SetSubType(Message::kSubTypeJoinerFinalizeResponse);
|
|
|
|
SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
|
|
|
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
LogCertMessage("[THCI] direction=send | type=JOIN_FIN.rsp |", *message);
|
|
#endif
|
|
|
|
SuccessOrExit(error = Get<Tmf::SecureAgent>().SendMessage(*message));
|
|
|
|
SignalJoinerEvent(kJoinerEventFinalize, mActiveJoiner);
|
|
|
|
if ((mActiveJoiner != nullptr) && (mActiveJoiner->mType != Joiner::kTypeAny))
|
|
{
|
|
// Remove after kRemoveJoinerDelay (seconds)
|
|
RemoveJoiner(*mActiveJoiner, kRemoveJoinerDelay);
|
|
}
|
|
|
|
LogInfo("Sent %s response", UriToString<kUriJoinerFinalize>());
|
|
|
|
exit:
|
|
FreeMessageOnError(message, error);
|
|
}
|
|
|
|
Error Commissioner::SendRelayTransmit(void *aContext, Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
|
{
|
|
return static_cast<Commissioner *>(aContext)->SendRelayTransmit(aMessage, aMessageInfo);
|
|
}
|
|
|
|
Error Commissioner::SendRelayTransmit(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
|
{
|
|
OT_UNUSED_VARIABLE(aMessageInfo);
|
|
|
|
Error error = kErrorNone;
|
|
OffsetRange offsetRange;
|
|
OwnedPtr<Coap::Message> message;
|
|
Kek kek;
|
|
|
|
Get<KeyManager>().ExtractKek(kek);
|
|
|
|
message.Reset(Get<Tmf::Agent>().AllocateAndInitPriorityNonConfirmablePostMessage(kUriRelayTx));
|
|
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
|
|
|
SuccessOrExit(error = Tlv::Append<JoinerUdpPortTlv>(*message, mJoinerPort));
|
|
SuccessOrExit(error = Tlv::Append<JoinerIidTlv>(*message, mJoinerIid));
|
|
SuccessOrExit(error = Tlv::Append<JoinerRouterLocatorTlv>(*message, mJoinerRloc));
|
|
|
|
if (aMessage.GetSubType() == Message::kSubTypeJoinerFinalizeResponse)
|
|
{
|
|
SuccessOrExit(error = Tlv::Append<JoinerRouterKekTlv>(*message, kek));
|
|
}
|
|
|
|
offsetRange.InitFromMessageFullLength(aMessage);
|
|
SuccessOrExit(
|
|
error = Tlv::AppendTlvWithValueFromMessage(*message, Tlv::kJoinerDtlsEncapsulation, aMessage, offsetRange));
|
|
|
|
SuccessOrExit(error = Get<Tmf::Agent>().SendMessageToRloc(*message, mJoinerRloc));
|
|
message.Release();
|
|
|
|
aMessage.Free();
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error Commissioner::SendAnnounceBeginRequest(uint32_t aChannelMask,
|
|
uint8_t aCount,
|
|
uint16_t aPeriod,
|
|
const Ip6::Address &aAddress)
|
|
{
|
|
Error error = kErrorNone;
|
|
Coap::Message *message = nullptr;
|
|
|
|
VerifyOrExit(IsActive(), error = kErrorInvalidState);
|
|
|
|
message = Get<Tmf::Agent>().AllocateAndInitPriorityPostMessageTo(kUriAnnounceBegin, aAddress);
|
|
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
|
|
|
SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, GetSessionId()));
|
|
|
|
SuccessOrExit(error = ChannelMaskTlv::AppendTo(*message, aChannelMask));
|
|
|
|
SuccessOrExit(error = Tlv::Append<CountTlv>(*message, aCount));
|
|
SuccessOrExit(error = Tlv::Append<PeriodTlv>(*message, aPeriod));
|
|
|
|
SuccessOrExit(error = Get<Tmf::Agent>().SendMessageTo(*message, aAddress));
|
|
|
|
LogInfo("Sent %s", UriToString<kUriAnnounceBegin>());
|
|
|
|
exit:
|
|
FreeMessageOnError(message, error);
|
|
return error;
|
|
}
|
|
|
|
Error Commissioner::SendEnergyScanQuery(uint32_t aChannelMask,
|
|
uint8_t aCount,
|
|
uint16_t aPeriod,
|
|
uint16_t aScanDuration,
|
|
const Ip6::Address &aAddress,
|
|
EnergyReportCallback aCallback,
|
|
void *aContext)
|
|
{
|
|
Error error = kErrorNone;
|
|
Coap::Message *message = nullptr;
|
|
|
|
VerifyOrExit(IsActive(), error = kErrorInvalidState);
|
|
|
|
message = Get<Tmf::Agent>().AllocateAndInitPriorityPostMessageTo(kUriEnergyScan, aAddress);
|
|
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
|
|
|
SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, GetSessionId()));
|
|
|
|
SuccessOrExit(error = ChannelMaskTlv::AppendTo(*message, aChannelMask));
|
|
|
|
SuccessOrExit(error = Tlv::Append<CountTlv>(*message, aCount));
|
|
SuccessOrExit(error = Tlv::Append<PeriodTlv>(*message, aPeriod));
|
|
SuccessOrExit(error = Tlv::Append<ScanDurationTlv>(*message, aScanDuration));
|
|
|
|
SuccessOrExit(error = Get<Tmf::Agent>().SendMessageTo(*message, aAddress));
|
|
|
|
LogInfo("Sent %s", UriToString<kUriEnergyScan>());
|
|
|
|
mEnergyReportCallback.Set(aCallback, aContext);
|
|
|
|
exit:
|
|
FreeMessageOnError(message, error);
|
|
return error;
|
|
}
|
|
|
|
template <> void Commissioner::HandleTmf<kUriEnergyReport>(Coap::Msg &aMsg)
|
|
{
|
|
uint32_t mask;
|
|
OffsetRange valueOffsetRange;
|
|
uint8_t results[kMaxEnergyScanResults];
|
|
|
|
VerifyOrExit(aMsg.IsConfirmable());
|
|
|
|
LogInfo("Received %s", UriToString<kUriEnergyReport>());
|
|
|
|
SuccessOrExit(ChannelMaskTlv::FindIn(aMsg.mMessage, mask));
|
|
|
|
SuccessOrExit(Tlv::FindTlvValueOffsetRange(aMsg.mMessage, Tlv::kEnergyList, valueOffsetRange));
|
|
valueOffsetRange.ShrinkLength(sizeof(results));
|
|
aMsg.mMessage.ReadBytes(valueOffsetRange, results);
|
|
|
|
mEnergyReportCallback.InvokeIfSet(mask, results, static_cast<uint8_t>(valueOffsetRange.GetLength()));
|
|
|
|
SuccessOrExit(Get<Tmf::Agent>().SendAckResponse(aMsg));
|
|
|
|
LogInfo("Sent %s ack", UriToString<kUriEnergyReport>());
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
Error Commissioner::SendPanIdQuery(uint16_t aPanId,
|
|
uint32_t aChannelMask,
|
|
const Ip6::Address &aAddress,
|
|
PanIdConflictCallback aCallback,
|
|
void *aContext)
|
|
{
|
|
Error error = kErrorNone;
|
|
Coap::Message *message = nullptr;
|
|
|
|
VerifyOrExit(IsActive(), error = kErrorInvalidState);
|
|
|
|
message = Get<Tmf::Agent>().AllocateAndInitPriorityPostMessageTo(kUriPanIdQuery, aAddress);
|
|
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
|
|
|
SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, GetSessionId()));
|
|
|
|
SuccessOrExit(error = ChannelMaskTlv::AppendTo(*message, aChannelMask));
|
|
|
|
SuccessOrExit(error = Tlv::Append<PanIdTlv>(*message, aPanId));
|
|
|
|
SuccessOrExit(error = Get<Tmf::Agent>().SendMessageTo(*message, aAddress));
|
|
|
|
LogInfo("Sent %s", UriToString<kUriPanIdQuery>());
|
|
|
|
mPanIdConflictCallback.Set(aCallback, aContext);
|
|
|
|
exit:
|
|
FreeMessageOnError(message, error);
|
|
return error;
|
|
}
|
|
|
|
template <> void Commissioner::HandleTmf<kUriPanIdConflict>(Coap::Msg &aMsg)
|
|
{
|
|
uint16_t panId;
|
|
uint32_t mask;
|
|
|
|
VerifyOrExit(aMsg.IsConfirmable());
|
|
|
|
LogInfo("Received %s", UriToString<kUriPanIdConflict>());
|
|
|
|
SuccessOrExit(Tlv::Find<PanIdTlv>(aMsg.mMessage, panId));
|
|
|
|
SuccessOrExit(ChannelMaskTlv::FindIn(aMsg.mMessage, mask));
|
|
|
|
mPanIdConflictCallback.InvokeIfSet(panId, mask);
|
|
|
|
SuccessOrExit(Get<Tmf::Agent>().SendAckResponse(aMsg));
|
|
|
|
LogInfo("Sent %s response", UriToString<kUriPanIdConflict>());
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
// LCOV_EXCL_START
|
|
|
|
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
|
|
|
|
const char *Commissioner::StateToString(State aState)
|
|
{
|
|
#define StateMapList(_) \
|
|
_(kStateDisabled, "disabled") \
|
|
_(kStatePetition, "petition") \
|
|
_(kStateActive, "active")
|
|
|
|
DefineEnumStringArray(StateMapList);
|
|
|
|
return kStrings[aState];
|
|
}
|
|
|
|
void Commissioner::LogJoinerEntry(const char *aAction, const Joiner &aJoiner) const
|
|
{
|
|
switch (aJoiner.mType)
|
|
{
|
|
case Joiner::kTypeUnused:
|
|
break;
|
|
|
|
case Joiner::kTypeAny:
|
|
LogInfo("%s Joiner (any, %s)", aAction, aJoiner.mPskd.GetAsCString());
|
|
break;
|
|
|
|
case Joiner::kTypeEui64:
|
|
LogInfo("%s Joiner (eui64:%s, %s)", aAction, aJoiner.mSharedId.mEui64.ToString().AsCString(),
|
|
aJoiner.mPskd.GetAsCString());
|
|
break;
|
|
|
|
case Joiner::kTypeDiscerner:
|
|
LogInfo("%s Joiner (disc:%s, %s)", aAction, aJoiner.mSharedId.mDiscerner.ToString().AsCString(),
|
|
aJoiner.mPskd.GetAsCString());
|
|
break;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
void Commissioner::LogJoinerEntry(const char *, const Joiner &) const {}
|
|
|
|
#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
|
|
|
|
// LCOV_EXCL_STOP
|
|
|
|
} // namespace MeshCoP
|
|
} // namespace ot
|
|
|
|
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|