| /* |
| * 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 the Joiner role. |
| */ |
| |
| #define WPP_NAME "joiner.tmh" |
| |
| #include "joiner.hpp" |
| |
| #include <stdio.h> |
| |
| #include <openthread/platform/radio.h> |
| #include <openthread/platform/random.h> |
| |
| #include "common/code_utils.hpp" |
| #include "common/crc16.hpp" |
| #include "common/debug.hpp" |
| #include "common/encoding.hpp" |
| #include "common/instance.hpp" |
| #include "common/logging.hpp" |
| #include "common/owner-locator.hpp" |
| #include "mac/mac_frame.hpp" |
| #include "meshcop/meshcop.hpp" |
| #include "thread/thread_netif.hpp" |
| #include "thread/thread_uri_paths.hpp" |
| |
| #if OPENTHREAD_ENABLE_JOINER |
| |
| using ot::Encoding::BigEndian::HostSwap16; |
| |
| namespace ot { |
| namespace MeshCoP { |
| |
| Joiner::Joiner(Instance &aInstance) |
| : InstanceLocator(aInstance) |
| , mState(OT_JOINER_STATE_IDLE) |
| , mCallback(NULL) |
| , mContext(NULL) |
| , mCcitt(0) |
| , mAnsi(0) |
| , mVendorName(NULL) |
| , mVendorModel(NULL) |
| , mVendorSwVersion(NULL) |
| , mVendorData(NULL) |
| , mTimer(aInstance, &Joiner::HandleTimer, this) |
| , mJoinerEntrust(OT_URI_PATH_JOINER_ENTRUST, &Joiner::HandleJoinerEntrust, this) |
| { |
| memset(mJoinerRouters, 0, sizeof(mJoinerRouters)); |
| GetNetif().GetCoap().AddResource(mJoinerEntrust); |
| } |
| |
| void Joiner::GetJoinerId(Mac::ExtAddress &aJoinerId) const |
| { |
| otPlatRadioGetIeeeEui64(&GetInstance(), aJoinerId.m8); |
| ComputeJoinerId(aJoinerId, aJoinerId); |
| } |
| |
| otError Joiner::Start(const char * aPSKd, |
| const char * aProvisioningUrl, |
| const char * aVendorName, |
| const char * aVendorModel, |
| const char * aVendorSwVersion, |
| const char * aVendorData, |
| otJoinerCallback aCallback, |
| void * aContext) |
| { |
| ThreadNetif & netif = GetNetif(); |
| otError error; |
| Mac::ExtAddress joinerId; |
| Crc16 ccitt(Crc16::kCcitt); |
| Crc16 ansi(Crc16::kAnsi); |
| |
| VerifyOrExit(mState == OT_JOINER_STATE_IDLE, error = OT_ERROR_BUSY); |
| |
| GetNotifier().Signal(OT_CHANGED_JOINER_STATE); |
| |
| // use extended address based on factory-assigned IEEE EUI-64 |
| GetJoinerId(joinerId); |
| netif.GetMac().SetExtAddress(joinerId); |
| netif.GetMle().UpdateLinkLocalAddress(); |
| |
| for (size_t i = 0; i < sizeof(joinerId); i++) |
| { |
| ccitt.Update(joinerId.m8[i]); |
| ansi.Update(joinerId.m8[i]); |
| } |
| |
| mCcitt = ccitt.Get(); |
| mAnsi = ansi.Get(); |
| |
| error = netif.GetCoapSecure().Start(OPENTHREAD_CONFIG_JOINER_UDP_PORT); |
| SuccessOrExit(error); |
| |
| error = netif.GetCoapSecure().SetPsk(reinterpret_cast<const uint8_t *>(aPSKd), static_cast<uint8_t>(strlen(aPSKd))); |
| SuccessOrExit(error); |
| |
| error = netif.GetCoapSecure().GetDtls().mProvisioningUrl.SetProvisioningUrl(aProvisioningUrl); |
| SuccessOrExit(error); |
| |
| memset(mJoinerRouters, 0, sizeof(mJoinerRouters)); |
| |
| SuccessOrExit(error = |
| netif.GetMle().Discover(0, netif.GetMac().GetPanId(), true, false, HandleDiscoverResult, this)); |
| |
| mVendorName = aVendorName; |
| mVendorModel = aVendorModel; |
| mVendorSwVersion = aVendorSwVersion; |
| mVendorData = aVendorData; |
| mCallback = aCallback; |
| mContext = aContext; |
| mState = OT_JOINER_STATE_DISCOVER; |
| |
| exit: |
| return error; |
| } |
| |
| otError Joiner::Stop(void) |
| { |
| Close(); |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otJoinerState Joiner::GetState(void) const |
| { |
| return mState; |
| } |
| |
| void Joiner::Close(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| netif.GetCoapSecure().Disconnect(); |
| netif.GetIp6Filter().RemoveUnsecurePort(netif.GetCoapSecure().GetPort()); |
| } |
| |
| void Joiner::Complete(otError aError) |
| { |
| ThreadNetif &netif = GetNetif(); |
| mState = OT_JOINER_STATE_IDLE; |
| otError error = OT_ERROR_NOT_FOUND; |
| GetNotifier().Signal(OT_CHANGED_JOINER_STATE); |
| |
| netif.GetCoapSecure().Disconnect(); |
| |
| if (aError != OT_ERROR_NONE && aError != OT_ERROR_NOT_FOUND) |
| { |
| error = TryNextJoin(); |
| } |
| |
| if (error == OT_ERROR_NOT_FOUND && mCallback) |
| { |
| netif.GetCoapSecure().Stop(); |
| otJoinerCallback callback = mCallback; |
| mCallback = NULL; |
| callback(aError, mContext); |
| } |
| } |
| |
| void Joiner::HandleDiscoverResult(otActiveScanResult *aResult, void *aContext) |
| { |
| static_cast<Joiner *>(aContext)->HandleDiscoverResult(aResult); |
| } |
| |
| void Joiner::HandleDiscoverResult(otActiveScanResult *aResult) |
| { |
| if (aResult != NULL) |
| { |
| JoinerRouter joinerRouter; |
| |
| otLogDebgMeshCoP(GetInstance(), "Received Discovery Response (%s)", |
| static_cast<Mac::ExtAddress &>(aResult->mExtAddress).ToString().AsCString()); |
| |
| // Joining is disabled if the Steering Data is not included |
| if (aResult->mSteeringData.mLength == 0) |
| { |
| otLogDebgMeshCoP(GetInstance(), "No steering data, joining disabled"); |
| ExitNow(); |
| } |
| |
| SteeringDataTlv steeringData; |
| steeringData.SetLength(aResult->mSteeringData.mLength); |
| memcpy(steeringData.GetValue(), aResult->mSteeringData.m8, steeringData.GetLength()); |
| |
| if (!steeringData.GetBit(mCcitt % steeringData.GetNumBits()) || |
| !steeringData.GetBit(mAnsi % steeringData.GetNumBits())) |
| { |
| otLogDebgMeshCoP(GetInstance(), "Steering data does not include this device"); |
| ExitNow(); |
| } |
| |
| joinerRouter.mPriority = static_cast<uint16_t>(aResult->mRssi) + 0x80; |
| |
| if (!steeringData.DoesAllowAny()) |
| { |
| joinerRouter.mPriority += kSpecificPriorityBonus; |
| } |
| |
| joinerRouter.mJoinerUdpPort = aResult->mJoinerUdpPort; |
| joinerRouter.mPanId = aResult->mPanId; |
| joinerRouter.mChannel = aResult->mChannel; |
| joinerRouter.mExtAddr = static_cast<Mac::ExtAddress &>(aResult->mExtAddress); |
| AddJoinerRouter(joinerRouter); |
| } |
| else |
| { |
| otError error = TryNextJoin(); |
| |
| if (error != OT_ERROR_NONE) |
| { |
| Complete(error); |
| } |
| } |
| |
| exit: |
| return; |
| } |
| |
| void Joiner::AddJoinerRouter(JoinerRouter &aJoinerRouter) |
| { |
| JoinerRouter *joinerRouter = &mJoinerRouters[0]; |
| |
| // Find the lowest priority |
| for (size_t i = 1; i < OPENTHREAD_CONFIG_MAX_JOINER_ROUTER_ENTRIES; i++) |
| { |
| if (mJoinerRouters[i].mPriority < joinerRouter->mPriority) |
| { |
| joinerRouter = &mJoinerRouters[i]; |
| } |
| |
| if (joinerRouter->mPriority == 0) |
| { |
| break; |
| } |
| } |
| |
| if (aJoinerRouter.mPriority > joinerRouter->mPriority) |
| { |
| memcpy(joinerRouter, &aJoinerRouter, sizeof(aJoinerRouter)); |
| } |
| } |
| |
| otError Joiner::TryNextJoin() |
| { |
| ThreadNetif & netif = GetNetif(); |
| otError error = OT_ERROR_NOT_FOUND; |
| JoinerRouter *joinerRouter = &mJoinerRouters[0]; |
| |
| for (size_t i = 1; i < OPENTHREAD_CONFIG_MAX_JOINER_ROUTER_ENTRIES; i++) |
| { |
| if (mJoinerRouters[i].mPriority > joinerRouter->mPriority) |
| { |
| joinerRouter = &mJoinerRouters[i]; |
| } |
| } |
| |
| if (joinerRouter->mPriority > 0) |
| { |
| Ip6::MessageInfo messageInfo; |
| |
| joinerRouter->mPriority = 0; |
| |
| netif.GetMac().SetPanId(joinerRouter->mPanId); |
| netif.GetMac().SetPanChannel(joinerRouter->mChannel); |
| netif.GetIp6Filter().AddUnsecurePort(netif.GetCoapSecure().GetPort()); |
| |
| messageInfo.GetPeerAddr().mFields.m16[0] = HostSwap16(0xfe80); |
| messageInfo.GetPeerAddr().SetIid(joinerRouter->mExtAddr); |
| messageInfo.mPeerPort = joinerRouter->mJoinerUdpPort; |
| messageInfo.mInterfaceId = OT_NETIF_INTERFACE_ID_THREAD; |
| |
| netif.GetCoapSecure().Connect(messageInfo, Joiner::HandleSecureCoapClientConnect, this); |
| mState = OT_JOINER_STATE_CONNECT; |
| error = OT_ERROR_NONE; |
| } |
| else |
| { |
| otLogDebgMeshCoP(GetInstance(), "No joinable networks remaining to try"); |
| } |
| |
| return error; |
| } |
| |
| void Joiner::HandleSecureCoapClientConnect(bool aConnected, void *aContext) |
| { |
| static_cast<Joiner *>(aContext)->HandleSecureCoapClientConnect(aConnected); |
| } |
| |
| void Joiner::HandleSecureCoapClientConnect(bool aConnected) |
| { |
| switch (mState) |
| { |
| case OT_JOINER_STATE_CONNECT: |
| if (aConnected) |
| { |
| mState = OT_JOINER_STATE_CONNECTED; |
| SendJoinerFinalize(); |
| mTimer.Start(kTimeout); |
| } |
| else |
| { |
| Complete(OT_ERROR_SECURITY); |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| void Joiner::SendJoinerFinalize(void) |
| { |
| ThreadNetif & netif = GetNetif(); |
| Coap::Header header; |
| otError error = OT_ERROR_NONE; |
| Message * message = NULL; |
| StateTlv stateTlv; |
| VendorNameTlv vendorNameTlv; |
| VendorModelTlv vendorModelTlv; |
| VendorSwVersionTlv vendorSwVersionTlv; |
| VendorStackVersionTlv vendorStackVersionTlv; |
| |
| header.Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST); |
| header.AppendUriPathOptions(OT_URI_PATH_JOINER_FINALIZE); |
| header.SetPayloadMarker(); |
| |
| VerifyOrExit((message = NewMeshCoPMessage(netif.GetCoapSecure(), header)) != NULL, error = OT_ERROR_NO_BUFS); |
| |
| stateTlv.Init(); |
| stateTlv.SetState(MeshCoP::StateTlv::kAccept); |
| SuccessOrExit(error = message->Append(&stateTlv, sizeof(stateTlv))); |
| |
| vendorNameTlv.Init(); |
| vendorNameTlv.SetVendorName(mVendorName); |
| SuccessOrExit(error = message->Append(&vendorNameTlv, vendorNameTlv.GetSize())); |
| |
| vendorModelTlv.Init(); |
| vendorModelTlv.SetVendorModel(mVendorModel); |
| SuccessOrExit(error = message->Append(&vendorModelTlv, vendorModelTlv.GetSize())); |
| |
| vendorSwVersionTlv.Init(); |
| vendorSwVersionTlv.SetVendorSwVersion(mVendorSwVersion); |
| SuccessOrExit(error = message->Append(&vendorSwVersionTlv, vendorSwVersionTlv.GetSize())); |
| |
| vendorStackVersionTlv.Init(); |
| vendorStackVersionTlv.SetOui(OPENTHREAD_CONFIG_STACK_VENDOR_OUI); |
| vendorStackVersionTlv.SetMajor(OPENTHREAD_CONFIG_STACK_VERSION_MAJOR); |
| vendorStackVersionTlv.SetMinor(OPENTHREAD_CONFIG_STACK_VERSION_MINOR); |
| vendorStackVersionTlv.SetRevision(OPENTHREAD_CONFIG_STACK_VERSION_REV); |
| SuccessOrExit(error = message->Append(&vendorStackVersionTlv, vendorStackVersionTlv.GetSize())); |
| |
| if (mVendorData != NULL) |
| { |
| VendorDataTlv vendorDataTlv; |
| vendorDataTlv.Init(); |
| vendorDataTlv.SetVendorData(mVendorData); |
| SuccessOrExit(error = message->Append(&vendorDataTlv, vendorDataTlv.GetSize())); |
| } |
| |
| if (netif.GetCoapSecure().GetDtls().mProvisioningUrl.GetLength() > 0) |
| { |
| SuccessOrExit(error = message->Append(&netif.GetCoapSecure().GetDtls().mProvisioningUrl, |
| netif.GetCoapSecure().GetDtls().mProvisioningUrl.GetSize())); |
| } |
| |
| #if OPENTHREAD_ENABLE_CERT_LOG |
| uint8_t buf[OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE]; |
| VerifyOrExit(message->GetLength() <= sizeof(buf)); |
| message->Read(header.GetLength(), message->GetLength() - header.GetLength(), buf); |
| otDumpCertMeshCoP(GetInstance(), "[THCI] direction=send | type=JOIN_FIN.req |", buf, |
| message->GetLength() - header.GetLength()); |
| #endif |
| |
| SuccessOrExit(error = netif.GetCoapSecure().SendMessage(*message, Joiner::HandleJoinerFinalizeResponse, this)); |
| |
| otLogInfoMeshCoP(GetInstance(), "Sent joiner finalize"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| } |
| |
| void Joiner::HandleJoinerFinalizeResponse(void * aContext, |
| otCoapHeader * aHeader, |
| otMessage * aMessage, |
| const otMessageInfo *aMessageInfo, |
| otError aResult) |
| { |
| static_cast<Joiner *>(aContext)->HandleJoinerFinalizeResponse( |
| static_cast<Coap::Header *>(aHeader), static_cast<Message *>(aMessage), |
| static_cast<const Ip6::MessageInfo *>(aMessageInfo), aResult); |
| } |
| |
| void Joiner::HandleJoinerFinalizeResponse(Coap::Header * aHeader, |
| Message * aMessage, |
| const Ip6::MessageInfo *aMessageInfo, |
| otError aResult) |
| { |
| (void)aMessageInfo; |
| StateTlv state; |
| |
| VerifyOrExit(mState == OT_JOINER_STATE_CONNECTED && aResult == OT_ERROR_NONE && |
| aHeader->GetType() == OT_COAP_TYPE_ACKNOWLEDGMENT && aHeader->GetCode() == OT_COAP_CODE_CHANGED); |
| |
| SuccessOrExit(Tlv::GetTlv(*aMessage, Tlv::kState, sizeof(state), state)); |
| VerifyOrExit(state.IsValid()); |
| |
| mState = OT_JOINER_STATE_ENTRUST; |
| mTimer.Start(kTimeout); |
| |
| otLogInfoMeshCoP(GetInstance(), "received joiner finalize response %d", static_cast<uint8_t>(state.GetState())); |
| #if OPENTHREAD_ENABLE_CERT_LOG |
| uint8_t buf[OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE]; |
| VerifyOrExit(aMessage->GetLength() <= sizeof(buf)); |
| aMessage->Read(aHeader->GetLength(), aMessage->GetLength() - aHeader->GetLength(), buf); |
| otDumpCertMeshCoP(GetInstance(), "[THCI] direction=recv | type=JOIN_FIN.rsp |", buf, |
| aMessage->GetLength() - aHeader->GetLength()); |
| #endif |
| |
| exit: |
| Close(); |
| } |
| |
| void Joiner::HandleJoinerEntrust(void * aContext, |
| otCoapHeader * aHeader, |
| otMessage * aMessage, |
| const otMessageInfo *aMessageInfo) |
| { |
| static_cast<Joiner *>(aContext)->HandleJoinerEntrust(*static_cast<Coap::Header *>(aHeader), |
| *static_cast<Message *>(aMessage), |
| *static_cast<const Ip6::MessageInfo *>(aMessageInfo)); |
| } |
| |
| void Joiner::HandleJoinerEntrust(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadNetif & netif = GetNetif(); |
| otError error; |
| NetworkMasterKeyTlv masterKey; |
| MeshLocalPrefixTlv meshLocalPrefix; |
| ExtendedPanIdTlv extendedPanId; |
| NetworkNameTlv networkName; |
| ActiveTimestampTlv activeTimestamp; |
| NetworkKeySequenceTlv networkKeySeq; |
| |
| VerifyOrExit(mState == OT_JOINER_STATE_ENTRUST && aHeader.GetType() == OT_COAP_TYPE_CONFIRMABLE && |
| aHeader.GetCode() == OT_COAP_CODE_POST, |
| error = OT_ERROR_DROP); |
| |
| otLogInfoMeshCoP(GetInstance(), "Received joiner entrust"); |
| otLogCertMeshCoP(GetInstance(), "[THCI] direction=recv | type=JOIN_ENT.ntf"); |
| |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kNetworkMasterKey, sizeof(masterKey), masterKey)); |
| VerifyOrExit(masterKey.IsValid(), error = OT_ERROR_PARSE); |
| |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kMeshLocalPrefix, sizeof(meshLocalPrefix), meshLocalPrefix)); |
| VerifyOrExit(meshLocalPrefix.IsValid(), error = OT_ERROR_PARSE); |
| |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kExtendedPanId, sizeof(extendedPanId), extendedPanId)); |
| VerifyOrExit(extendedPanId.IsValid(), error = OT_ERROR_PARSE); |
| |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kNetworkName, sizeof(networkName), networkName)); |
| VerifyOrExit(networkName.IsValid(), error = OT_ERROR_PARSE); |
| |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(activeTimestamp), activeTimestamp)); |
| VerifyOrExit(activeTimestamp.IsValid(), error = OT_ERROR_PARSE); |
| |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kNetworkKeySequence, sizeof(networkKeySeq), networkKeySeq)); |
| VerifyOrExit(networkKeySeq.IsValid(), error = OT_ERROR_PARSE); |
| |
| netif.GetKeyManager().SetMasterKey(masterKey.GetNetworkMasterKey()); |
| netif.GetKeyManager().SetCurrentKeySequence(networkKeySeq.GetNetworkKeySequence()); |
| netif.GetMle().SetMeshLocalPrefix(meshLocalPrefix.GetMeshLocalPrefix()); |
| netif.GetMac().SetExtendedPanId(extendedPanId.GetExtendedPanId()); |
| |
| { |
| otNetworkName name; |
| memcpy(name.m8, networkName.GetNetworkName(), networkName.GetLength()); |
| name.m8[networkName.GetLength()] = '\0'; |
| netif.GetMac().SetNetworkName(name.m8); |
| } |
| |
| otLogInfoMeshCoP(GetInstance(), "join success!"); |
| |
| // Send dummy response. |
| SendJoinerEntrustResponse(aHeader, aMessageInfo); |
| |
| // Delay extended address configuration to allow DTLS wrap up. |
| mTimer.Start(kConfigExtAddressDelay); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE) |
| { |
| otLogWarnMeshCoP(GetInstance(), "Error while processing joiner entrust: %s", otThreadErrorToString(error)); |
| } |
| } |
| |
| void Joiner::SendJoinerEntrustResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aRequestInfo) |
| { |
| ThreadNetif & netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Message * message; |
| Coap::Header responseHeader; |
| Ip6::MessageInfo responseInfo(aRequestInfo); |
| |
| responseHeader.SetDefaultResponseHeader(aRequestHeader); |
| |
| VerifyOrExit((message = NewMeshCoPMessage(netif.GetCoap(), responseHeader)) != NULL, error = OT_ERROR_NO_BUFS); |
| message->SetSubType(Message::kSubTypeJoinerEntrust); |
| |
| memset(&responseInfo.mSockAddr, 0, sizeof(responseInfo.mSockAddr)); |
| SuccessOrExit(error = netif.GetCoap().SendMessage(*message, responseInfo)); |
| |
| mState = OT_JOINER_STATE_JOINED; |
| |
| otLogInfoArp(GetInstance(), "Sent Joiner Entrust response"); |
| |
| otLogInfoMeshCoP(GetInstance(), "Sent joiner entrust response length = %d", message->GetLength()); |
| otLogCertMeshCoP(GetInstance(), "[THCI] direction=send | type=JOIN_ENT.rsp"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| } |
| |
| void Joiner::HandleTimer(Timer &aTimer) |
| { |
| aTimer.GetOwner<Joiner>().HandleTimer(); |
| } |
| |
| void Joiner::HandleTimer(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| |
| switch (mState) |
| { |
| case OT_JOINER_STATE_IDLE: |
| case OT_JOINER_STATE_DISCOVER: |
| case OT_JOINER_STATE_CONNECT: |
| assert(false); |
| break; |
| |
| case OT_JOINER_STATE_CONNECTED: |
| case OT_JOINER_STATE_ENTRUST: |
| error = OT_ERROR_RESPONSE_TIMEOUT; |
| break; |
| |
| case OT_JOINER_STATE_JOINED: |
| Mac::ExtAddress extAddress; |
| |
| netif.GetMac().GenerateExtAddress(&extAddress); |
| netif.GetMac().SetExtAddress(extAddress); |
| netif.GetMle().UpdateLinkLocalAddress(); |
| |
| error = OT_ERROR_NONE; |
| break; |
| } |
| |
| Complete(error); |
| } |
| |
| } // namespace MeshCoP |
| } // namespace ot |
| |
| #endif // OPENTHREAD_ENABLE_JOINER |