/* * 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 MLE functionality required for the Thread Child, Router and Leader roles. */ #define WPP_NAME "mle.tmh" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Thread::Encoding::BigEndian::HostSwap16; namespace Thread { namespace Mle { Mle::Mle(ThreadNetif &aThreadNetif) : mNetif(aThreadNetif), mAddressResolver(aThreadNetif.GetAddressResolver()), mKeyManager(aThreadNetif.GetKeyManager()), mMac(aThreadNetif.GetMac()), mMesh(aThreadNetif.GetMeshForwarder()), mMleRouter(aThreadNetif.GetMle()), mNetworkData(aThreadNetif.GetNetworkDataLeader()), mJoinerRouter(aThreadNetif.GetJoinerRouter()), mParentRequestTimer(aThreadNetif.GetIp6().mTimerScheduler, &Mle::HandleParentRequestTimer, this), mSocket(aThreadNetif.GetIp6().mUdp), mSendChildUpdateRequest(aThreadNetif.GetIp6().mTaskletScheduler, &Mle::HandleSendChildUpdateRequest, this) { mDeviceState = kDeviceStateDisabled; mDeviceMode = ModeTlv::kModeRxOnWhenIdle | ModeTlv::kModeSecureDataRequest | ModeTlv::kModeFFD | ModeTlv::kModeFullNetworkData; mParentRequestState = kParentIdle; mParentRequestMode = kMleAttachAnyPartition; mParentLinkQuality = 0; mParentPriority = 0; mParentLinkQuality3 = 0; mParentLinkQuality2 = 0; mParentLinkQuality1 = 0; mRetrieveNewNetworkData = false; mTimeout = kMleEndDeviceTimeout; mReattachState = kReattachStop; memset(&mLeaderData, 0, sizeof(mLeaderData)); memset(&mParent, 0, sizeof(mParent)); memset(&mChildIdRequest, 0, sizeof(mChildIdRequest)); memset(&mLinkLocal64, 0, sizeof(mLinkLocal64)); memset(&mMeshLocal64, 0, sizeof(mMeshLocal64)); memset(&mMeshLocal16, 0, sizeof(mMeshLocal16)); memset(&mLinkLocalAllThreadNodes, 0, sizeof(mLinkLocalAllThreadNodes)); memset(&mRealmLocalAllThreadNodes, 0, sizeof(mRealmLocalAllThreadNodes)); // link-local 64 memset(&mLinkLocal64, 0, sizeof(mLinkLocal64)); mLinkLocal64.GetAddress().mFields.m16[0] = HostSwap16(0xfe80); mLinkLocal64.GetAddress().SetIid(*mMac.GetExtAddress()); mLinkLocal64.mPrefixLength = 64; mLinkLocal64.mPreferredLifetime = 0xffffffff; mLinkLocal64.mValidLifetime = 0xffffffff; mNetif.AddUnicastAddress(mLinkLocal64); // Leader Aloc memset(&mLeaderAloc, 0, sizeof(mLeaderAloc)); mLeaderAloc.mPrefixLength = 128; mLeaderAloc.mPreferredLifetime = 0xffffffff; mLeaderAloc.mValidLifetime = 0xffffffff; mLeaderAloc.mScopeOverride = Ip6::Address::kRealmLocalScope; mLeaderAloc.mScopeOverrideValid = true; // initialize Mesh Local Prefix mMeshLocal64.GetAddress().mFields.m8[0] = 0xfd; memcpy(mMeshLocal64.GetAddress().mFields.m8 + 1, mMac.GetExtendedPanId(), 5); mMeshLocal64.GetAddress().mFields.m8[6] = 0x00; mMeshLocal64.GetAddress().mFields.m8[7] = 0x00; mMeshLocal64.mScopeOverride = Ip6::Address::kRealmLocalScope; mMeshLocal64.mScopeOverrideValid = true; // mesh-local 64 for (int i = 8; i < 16; i++) { mMeshLocal64.GetAddress().mFields.m8[i] = static_cast(otPlatRandomGet()); } mMeshLocal64.mPrefixLength = 64; mMeshLocal64.mPreferredLifetime = 0xffffffff; mMeshLocal64.mValidLifetime = 0xffffffff; SetMeshLocalPrefix(mMeshLocal64.GetAddress().mFields.m8); // Also calls AddUnicastAddress // mesh-local 16 mMeshLocal16.GetAddress().mFields.m16[4] = HostSwap16(0x0000); mMeshLocal16.GetAddress().mFields.m16[5] = HostSwap16(0x00ff); mMeshLocal16.GetAddress().mFields.m16[6] = HostSwap16(0xfe00); mMeshLocal16.mPrefixLength = 64; mMeshLocal16.mPreferredLifetime = 0xffffffff; mMeshLocal16.mValidLifetime = 0xffffffff; mMeshLocal16.mScopeOverride = Ip6::Address::kRealmLocalScope; mMeshLocal16.mScopeOverrideValid = true; // Store RLOC address reference in MPL module. mNetif.GetIp6().mMpl.SetMatchingAddress(mMeshLocal16.GetAddress()); // link-local all thread nodes mLinkLocalAllThreadNodes.GetAddress().mFields.m16[0] = HostSwap16(0xff32); mLinkLocalAllThreadNodes.GetAddress().mFields.m16[6] = HostSwap16(0x0000); mLinkLocalAllThreadNodes.GetAddress().mFields.m16[7] = HostSwap16(0x0001); mNetif.SubscribeMulticast(mLinkLocalAllThreadNodes); // realm-local all thread nodes mRealmLocalAllThreadNodes.GetAddress().mFields.m16[0] = HostSwap16(0xff33); mRealmLocalAllThreadNodes.GetAddress().mFields.m16[6] = HostSwap16(0x0000); mRealmLocalAllThreadNodes.GetAddress().mFields.m16[7] = HostSwap16(0x0001); mNetif.SubscribeMulticast(mRealmLocalAllThreadNodes); mNetifCallback.Set(&Mle::HandleNetifStateChanged, this); mNetif.RegisterCallback(mNetifCallback); isAssignLinkQuality = false; mAssignLinkQuality = 0; mAssignLinkMargin = 0; memset(&mAddr64, 0, sizeof(mAddr64)); mIsDiscoverInProgress = false; mRouterSelectionJitterTimeout = 0; mRouterSelectionJitter = kRouterSelectionJitter; mAnnounceChannel = kPhyMinChannel; mPreviousPanId = Mac::kPanIdBroadcast; } ThreadError Mle::Enable(void) { ThreadError error = kThreadError_None; Ip6::SockAddr sockaddr; // memcpy(&sockaddr.mAddr, &mLinkLocal64.GetAddress(), sizeof(sockaddr.mAddr)); sockaddr.mPort = kUdpPort; SuccessOrExit(error = mSocket.Open(&Mle::HandleUdpReceive, this)); SuccessOrExit(error = mSocket.Bind(sockaddr)); exit: return error; } ThreadError Mle::Disable(void) { ThreadError error = kThreadError_None; SuccessOrExit(error = Stop(false)); SuccessOrExit(error = mSocket.Close()); exit: return error; } ThreadError Mle::Start(bool aEnableReattach) { ThreadError error = kThreadError_None; otLogFuncEntry(); // cannot bring up the interface if IEEE 802.15.4 promiscuous mode is enabled VerifyOrExit(otPlatRadioGetPromiscuous(mNetif.GetInstance()) == false, error = kThreadError_InvalidState); VerifyOrExit(mNetif.IsUp(), error = kThreadError_InvalidState); mDeviceState = kDeviceStateDetached; mNetif.SetStateChangedFlags(OT_NET_ROLE); SetStateDetached(); mKeyManager.Start(); if (aEnableReattach) { mReattachState = kReattachStart; } if (GetRloc16() == Mac::kShortAddrInvalid) { BecomeChild(kMleAttachAnyPartition); } else if (IsActiveRouter(GetRloc16())) { mMleRouter.BecomeRouter(ThreadStatusTlv::kTooFewRouters); } else { SendChildUpdateRequest(); mParentRequestState = kParentSynchronize; mParentRequestTimer.Start(kParentRequestRouterTimeout); } exit: otLogFuncExitErr(error); return error; } ThreadError Mle::Stop(bool aClearNetworkDatasets) { otLogFuncEntry(); mKeyManager.Stop(); SetStateDetached(); mNetif.RemoveUnicastAddress(mMeshLocal16); if (aClearNetworkDatasets) { mNetif.GetActiveDataset().Clear(true); mNetif.GetPendingDataset().Clear(true); } mDeviceState = kDeviceStateDisabled; otLogFuncExit(); return kThreadError_None; } ThreadError Mle::Discover(uint32_t aScanChannels, uint16_t aScanDuration, uint16_t aPanId, DiscoverHandler aCallback, void *aContext) { ThreadError error = kThreadError_None; Message *message = NULL; Ip6::Address destination; Tlv tlv; MeshCoP::DiscoveryRequestTlv discoveryRequest; uint16_t startOffset; VerifyOrExit(!mIsDiscoverInProgress, error = kThreadError_Busy); mDiscoverHandler = aCallback; mDiscoverContext = aContext; mMesh.SetDiscoverParameters(aScanChannels, aScanDuration); VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); message->SetLinkSecurityEnabled(false); message->SetSubType(Message::kSubTypeMleDiscoverRequest); message->SetPanId(aPanId); SuccessOrExit(error = AppendHeader(*message, Header::kCommandDiscoveryRequest)); // Discovery TLV tlv.SetType(Tlv::kDiscovery); SuccessOrExit(error = message->Append(&tlv, sizeof(tlv))); startOffset = message->GetLength(); // Discovery Request TLV discoveryRequest.Init(); discoveryRequest.SetVersion(kVersion); SuccessOrExit(error = message->Append(&discoveryRequest, sizeof(discoveryRequest))); tlv.SetLength(static_cast(message->GetLength() - startOffset)); message->Write(startOffset - sizeof(tlv), sizeof(tlv), &tlv); memset(&destination, 0, sizeof(destination)); destination.mFields.m16[0] = HostSwap16(0xff02); destination.mFields.m16[7] = HostSwap16(0x0002); SuccessOrExit(error = SendMessage(*message, destination)); mIsDiscoverInProgress = true; otLogInfoMle("Sent discovery request"); exit: if (error != kThreadError_None && message != NULL) { message->Free(); } return error; } bool Mle::IsDiscoverInProgress(void) { return mIsDiscoverInProgress; } void Mle::HandleDiscoverComplete(void) { mIsDiscoverInProgress = false; mDiscoverHandler(NULL, mDiscoverContext); } ThreadError Mle::BecomeDetached(void) { ThreadError error = kThreadError_None; otLogFuncEntry(); VerifyOrExit(mDeviceState != kDeviceStateDisabled, error = kThreadError_InvalidState); if (mReattachState == kReattachStop) { mNetif.GetPendingDataset().UpdateDelayTimer(); mNetif.GetPendingDataset().Set(mNetif.GetPendingDataset().GetLocal()); } SetStateDetached(); SetRloc16(Mac::kShortAddrInvalid); BecomeChild(kMleAttachAnyPartition); exit: otLogFuncExitErr(error); return error; } ThreadError Mle::BecomeChild(otMleAttachFilter aFilter) { ThreadError error = kThreadError_None; otLogFuncEntry(); VerifyOrExit(mDeviceState != kDeviceStateDisabled, error = kThreadError_InvalidState); VerifyOrExit(mParentRequestState == kParentIdle, error = kThreadError_Busy); if (mReattachState == kReattachStart) { if (mNetif.GetActiveDataset().Restore() == kThreadError_None) { mReattachState = kReattachActive; } else { mReattachState = kReattachStop; } } mParentRequestState = kParentRequestStart; mParentRequestMode = aFilter; memset(&mParent, 0, sizeof(mParent)); if (aFilter == kMleAttachAnyPartition) { mParent.mState = Neighbor::kStateInvalid; } mParentRequestTimer.Start(kParentRequestRouterTimeout); exit: otLogFuncExitErr(error); return error; } bool Mle::IsAttached(void) const { return (mDeviceState == kDeviceStateChild || mDeviceState == kDeviceStateRouter || mDeviceState == kDeviceStateLeader); } DeviceState Mle::GetDeviceState(void) const { return mDeviceState; } ThreadError Mle::SetStateDetached(void) { if (mDeviceState != kDeviceStateDetached) { mNetif.SetStateChangedFlags(OT_NET_ROLE); } if (mDeviceState == kDeviceStateLeader) { mNetif.RemoveUnicastAddress(mLeaderAloc); } mAddressResolver.Clear(); mDeviceState = kDeviceStateDetached; mParentRequestState = kParentIdle; mParentRequestTimer.Stop(); mMesh.SetRxOnWhenIdle(true); mMleRouter.HandleDetachStart(); mNetif.GetIp6().SetForwardingEnabled(false); mNetif.GetIp6().mMpl.SetTimerExpirations(0); otLogInfoMle("Mode -> Detached"); return kThreadError_None; } ThreadError Mle::SetStateChild(uint16_t aRloc16) { if (mDeviceState != kDeviceStateChild) { mNetif.SetStateChangedFlags(OT_NET_ROLE); } if (mDeviceState == kDeviceStateLeader) { mNetif.RemoveUnicastAddress(mLeaderAloc); } SetRloc16(aRloc16); mDeviceState = kDeviceStateChild; mParentRequestState = kParentIdle; if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) != 0) { mParentRequestTimer.Start(Timer::SecToMsec(mTimeout / kMaxChildKeepAliveAttempts)); } if ((mDeviceMode & ModeTlv::kModeFFD) != 0) { mMleRouter.HandleChildStart(mParentRequestMode); } mNetif.GetNetworkDataLocal().ClearResubmitDelayTimer(); mNetif.GetIp6().SetForwardingEnabled(false); mNetif.GetIp6().mMpl.SetTimerExpirations(kMplChildDataMessageTimerExpirations); if (mPreviousPanId != Mac::kPanIdBroadcast && (mDeviceMode & ModeTlv::kModeFFD)) { mPreviousPanId = Mac::kPanIdBroadcast; mNetif.GetAnnounceBeginServer().SendAnnounce(1 << mPreviousChannel); } otLogInfoMle("Mode -> Child"); return kThreadError_None; } uint32_t Mle::GetTimeout(void) const { return mTimeout; } ThreadError Mle::SetTimeout(uint32_t aTimeout) { if (aTimeout < 4) { aTimeout = 4; } mTimeout = aTimeout; if (mDeviceState == kDeviceStateChild) { SendChildUpdateRequest(); if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) != 0) { mParentRequestTimer.Start(Timer::SecToMsec(mTimeout / kMaxChildKeepAliveAttempts)); } } return kThreadError_None; } uint8_t Mle::GetDeviceMode(void) const { return mDeviceMode; } ThreadError Mle::SetDeviceMode(uint8_t aDeviceMode) { ThreadError error = kThreadError_None; uint8_t oldMode = mDeviceMode; VerifyOrExit((aDeviceMode & ModeTlv::kModeFFD) == 0 || (aDeviceMode & ModeTlv::kModeRxOnWhenIdle) != 0, error = kThreadError_InvalidArgs); mDeviceMode = aDeviceMode; switch (mDeviceState) { case kDeviceStateDisabled: case kDeviceStateDetached: break; case kDeviceStateChild: SetStateChild(GetRloc16()); SendChildUpdateRequest(); break; case kDeviceStateRouter: case kDeviceStateLeader: if ((oldMode & ModeTlv::kModeFFD) != 0 && (aDeviceMode & ModeTlv::kModeFFD) == 0) { BecomeDetached(); } break; } exit: return error; } ThreadError Mle::UpdateLinkLocalAddress(void) { mNetif.RemoveUnicastAddress(mLinkLocal64); mLinkLocal64.GetAddress().SetIid(*mMac.GetExtAddress()); mNetif.AddUnicastAddress(mLinkLocal64); mNetif.SetStateChangedFlags(OT_IP6_LL_ADDR_CHANGED); return kThreadError_None; } const uint8_t *Mle::GetMeshLocalPrefix(void) const { return mMeshLocal16.GetAddress().mFields.m8; } ThreadError Mle::SetMeshLocalPrefix(const uint8_t *aMeshLocalPrefix) { // We must remove the old address before adding the new one. mNetif.RemoveUnicastAddress(mMeshLocal64); mNetif.RemoveUnicastAddress(mMeshLocal16); memcpy(mMeshLocal64.GetAddress().mFields.m8, aMeshLocalPrefix, 8); memcpy(mMeshLocal16.GetAddress().mFields.m8, mMeshLocal64.GetAddress().mFields.m8, 8); mLinkLocalAllThreadNodes.GetAddress().mFields.m8[3] = 64; memcpy(mLinkLocalAllThreadNodes.GetAddress().mFields.m8 + 4, mMeshLocal64.GetAddress().mFields.m8, 8); mRealmLocalAllThreadNodes.GetAddress().mFields.m8[3] = 64; memcpy(mRealmLocalAllThreadNodes.GetAddress().mFields.m8 + 4, mMeshLocal64.GetAddress().mFields.m8, 8); // Add the address back into the table. mNetif.AddUnicastAddress(mMeshLocal64); if (mDeviceState >= kDeviceStateChild) { mNetif.AddUnicastAddress(mMeshLocal16); } // update Leader ALOC if (mDeviceState == kDeviceStateLeader) { mNetif.RemoveUnicastAddress(mLeaderAloc); AddLeaderAloc(); } // Changing the prefix also causes the mesh local address to be different. mNetif.SetStateChangedFlags(OT_IP6_ML_ADDR_CHANGED); return kThreadError_None; } const Ip6::Address *Mle::GetLinkLocalAllThreadNodesAddress(void) const { return &mLinkLocalAllThreadNodes.GetAddress(); } const Ip6::Address *Mle::GetRealmLocalAllThreadNodesAddress(void) const { return &mRealmLocalAllThreadNodes.GetAddress(); } uint16_t Mle::GetRloc16(void) const { return mMac.GetShortAddress(); } ThreadError Mle::SetRloc16(uint16_t aRloc16) { mNetif.RemoveUnicastAddress(mMeshLocal16); if (aRloc16 != Mac::kShortAddrInvalid) { // mesh-local 16 mMeshLocal16.GetAddress().mFields.m16[7] = HostSwap16(aRloc16); mNetif.AddUnicastAddress(mMeshLocal16); } mMac.SetShortAddress(aRloc16); mNetif.GetIp6().mMpl.SetSeedId(aRloc16); return kThreadError_None; } uint8_t Mle::GetLeaderId(void) const { return mLeaderData.GetLeaderRouterId(); } void Mle::SetLeaderData(uint32_t aPartitionId, uint8_t aWeighting, uint8_t aLeaderRouterId) { if (mLeaderData.GetPartitionId() != aPartitionId) { mNetif.SetStateChangedFlags(OT_NET_PARTITION_ID); } mLeaderData.SetPartitionId(aPartitionId); mLeaderData.SetWeighting(aWeighting); mLeaderData.SetLeaderRouterId(aLeaderRouterId); } const Ip6::Address &Mle::GetMeshLocal16(void) const { return mMeshLocal16.GetAddress(); } const Ip6::Address &Mle::GetMeshLocal64(void) const { return mMeshLocal64.GetAddress(); } ThreadError Mle::GetLeaderAddress(Ip6::Address &aAddress) const { ThreadError error = kThreadError_None; VerifyOrExit(GetRloc16() != Mac::kShortAddrInvalid, error = kThreadError_Detached); memcpy(&aAddress, &mMeshLocal16.GetAddress(), 8); aAddress.mFields.m16[4] = HostSwap16(0x0000); aAddress.mFields.m16[5] = HostSwap16(0x00ff); aAddress.mFields.m16[6] = HostSwap16(0xfe00); aAddress.mFields.m16[7] = HostSwap16(GetRloc16(mLeaderData.GetLeaderRouterId())); exit: return error; } ThreadError Mle::GetLeaderAloc(Ip6::Address &aAddress) const { ThreadError error = kThreadError_None; VerifyOrExit(GetRloc16() != Mac::kShortAddrInvalid, error = kThreadError_Detached); memcpy(&aAddress, &mMeshLocal16.GetAddress(), 8); aAddress.mFields.m16[4] = HostSwap16(0x0000); aAddress.mFields.m16[5] = HostSwap16(0x00ff); aAddress.mFields.m16[6] = HostSwap16(0xfe00); aAddress.mFields.m16[7] = HostSwap16(kAloc16Leader); exit: return error; } ThreadError Mle::AddLeaderAloc(void) { ThreadError error = kThreadError_None; VerifyOrExit(mDeviceState == kDeviceStateLeader, error = kThreadError_InvalidState); SuccessOrExit(error = GetLeaderAloc(mLeaderAloc.GetAddress())); error = mNetif.AddUnicastAddress(mLeaderAloc); exit: return error; } const LeaderDataTlv &Mle::GetLeaderDataTlv(void) { mLeaderData.SetDataVersion(mNetworkData.GetVersion()); mLeaderData.SetStableDataVersion(mNetworkData.GetStableVersion()); return mLeaderData; } ThreadError Mle::GetLeaderData(otLeaderData &aLeaderData) { const LeaderDataTlv &leaderData(GetLeaderDataTlv()); ThreadError error = kThreadError_None; VerifyOrExit(mDeviceState != kDeviceStateDisabled && mDeviceState != kDeviceStateDetached, error = kThreadError_Detached); aLeaderData.mPartitionId = leaderData.GetPartitionId(); aLeaderData.mWeighting = leaderData.GetWeighting(); aLeaderData.mDataVersion = leaderData.GetDataVersion(); aLeaderData.mStableDataVersion = leaderData.GetStableDataVersion(); aLeaderData.mLeaderRouterId = leaderData.GetLeaderRouterId(); exit: return error; } ThreadError Mle::GetAssignLinkQuality(const Mac::ExtAddress aMacAddr, uint8_t &aLinkQuality) { ThreadError error; VerifyOrExit((memcmp(aMacAddr.m8, mAddr64.m8, OT_EXT_ADDRESS_SIZE)) == 0, error = kThreadError_InvalidArgs); aLinkQuality = mAssignLinkQuality; return kThreadError_None; exit: return error; } void Mle::SetAssignLinkQuality(const Mac::ExtAddress aMacAddr, uint8_t aLinkQuality) { isAssignLinkQuality = true; mAddr64 = aMacAddr; mAssignLinkQuality = aLinkQuality; switch (aLinkQuality) { case 3: mAssignLinkMargin = kMinAssignedLinkMargin3; break; case 2: mAssignLinkMargin = kMinAssignedLinkMargin2; break; case 1: mAssignLinkMargin = kMinAssignedLinkMargin1; break; case 0: mAssignLinkMargin = kMinAssignedLinkMargin0; default: break; } } uint8_t Mle::GetRouterSelectionJitter(void) const { return mRouterSelectionJitter; } void Mle::SetRouterSelectionJitter(uint8_t aRouterJitter) { mRouterSelectionJitter = aRouterJitter; } void Mle::GenerateNonce(const Mac::ExtAddress &aMacAddr, uint32_t aFrameCounter, uint8_t aSecurityLevel, uint8_t *aNonce) { // source address memcpy(aNonce, aMacAddr.m8, sizeof(aMacAddr)); aNonce += sizeof(aMacAddr); // frame counter aNonce[0] = (aFrameCounter >> 24) & 0xff; aNonce[1] = (aFrameCounter >> 16) & 0xff; aNonce[2] = (aFrameCounter >> 8) & 0xff; aNonce[3] = aFrameCounter & 0xff; aNonce += 4; // security level aNonce[0] = aSecurityLevel; } ThreadError Mle::AppendHeader(Message &aMessage, Header::Command aCommand) { ThreadError error = kThreadError_None; Header header; header.Init(); if (aCommand == Header::kCommandDiscoveryRequest || aCommand == Header::kCommandDiscoveryResponse) { header.SetSecuritySuite(Header::kNoSecurity); } else { header.SetKeyIdMode2(); } header.SetCommand(aCommand); SuccessOrExit(error = aMessage.Append(&header, header.GetLength())); exit: return error; } ThreadError Mle::AppendSourceAddress(Message &aMessage) { SourceAddressTlv tlv; tlv.Init(); tlv.SetRloc16(GetRloc16()); return aMessage.Append(&tlv, sizeof(tlv)); } ThreadError Mle::AppendStatus(Message &aMessage, StatusTlv::Status aStatus) { StatusTlv tlv; tlv.Init(); tlv.SetStatus(aStatus); return aMessage.Append(&tlv, sizeof(tlv)); } ThreadError Mle::AppendMode(Message &aMessage, uint8_t aMode) { ModeTlv tlv; tlv.Init(); tlv.SetMode(aMode); return aMessage.Append(&tlv, sizeof(tlv)); } ThreadError Mle::AppendTimeout(Message &aMessage, uint32_t aTimeout) { TimeoutTlv tlv; tlv.Init(); tlv.SetTimeout(aTimeout); return aMessage.Append(&tlv, sizeof(tlv)); } ThreadError Mle::AppendChallenge(Message &aMessage, const uint8_t *aChallenge, uint8_t aChallengeLength) { ThreadError error; Tlv tlv; tlv.SetType(Tlv::kChallenge); tlv.SetLength(aChallengeLength); SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); SuccessOrExit(error = aMessage.Append(aChallenge, aChallengeLength)); exit: return error; } ThreadError Mle::AppendResponse(Message &aMessage, const uint8_t *aResponse, uint8_t aResponseLength) { ThreadError error; Tlv tlv; tlv.SetType(Tlv::kResponse); tlv.SetLength(aResponseLength); SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); SuccessOrExit(error = aMessage.Append(aResponse, aResponseLength)); exit: return error; } ThreadError Mle::AppendLinkFrameCounter(Message &aMessage) { LinkFrameCounterTlv tlv; tlv.Init(); tlv.SetFrameCounter(mKeyManager.GetMacFrameCounter()); return aMessage.Append(&tlv, sizeof(tlv)); } ThreadError Mle::AppendMleFrameCounter(Message &aMessage) { MleFrameCounterTlv tlv; tlv.Init(); tlv.SetFrameCounter(mKeyManager.GetMleFrameCounter()); return aMessage.Append(&tlv, sizeof(tlv)); } ThreadError Mle::AppendAddress16(Message &aMessage, uint16_t aRloc16) { Address16Tlv tlv; tlv.Init(); tlv.SetRloc16(aRloc16); return aMessage.Append(&tlv, sizeof(tlv)); } ThreadError Mle::AppendLeaderData(Message &aMessage) { mLeaderData.Init(); mLeaderData.SetDataVersion(mNetworkData.GetVersion()); mLeaderData.SetStableDataVersion(mNetworkData.GetStableVersion()); return aMessage.Append(&mLeaderData, sizeof(mLeaderData)); } void Mle::FillNetworkDataTlv(NetworkDataTlv &aTlv, bool aStableOnly) { uint8_t length; mNetworkData.GetNetworkData(aStableOnly, aTlv.GetNetworkData(), length); aTlv.SetLength(length); } ThreadError Mle::AppendNetworkData(Message &aMessage, bool aStableOnly) { ThreadError error = kThreadError_None; NetworkDataTlv tlv; tlv.Init(); FillNetworkDataTlv(tlv, aStableOnly); SuccessOrExit(error = aMessage.Append(&tlv, sizeof(Tlv) + tlv.GetLength())); exit: return error; } ThreadError Mle::AppendTlvRequest(Message &aMessage, const uint8_t *aTlvs, uint8_t aTlvsLength) { ThreadError error; Tlv tlv; tlv.SetType(Tlv::kTlvRequest); tlv.SetLength(aTlvsLength); SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); SuccessOrExit(error = aMessage.Append(aTlvs, aTlvsLength)); exit: return error; } ThreadError Mle::AppendScanMask(Message &aMessage, uint8_t aScanMask) { ScanMaskTlv tlv; tlv.Init(); tlv.SetMask(aScanMask); return aMessage.Append(&tlv, sizeof(tlv)); } ThreadError Mle::AppendLinkMargin(Message &aMessage, uint8_t aLinkMargin) { LinkMarginTlv tlv; tlv.Init(); tlv.SetLinkMargin(aLinkMargin); return aMessage.Append(&tlv, sizeof(tlv)); } ThreadError Mle::AppendVersion(Message &aMessage) { VersionTlv tlv; tlv.Init(); tlv.SetVersion(kVersion); return aMessage.Append(&tlv, sizeof(tlv)); } ThreadError Mle::AppendAddressRegistration(Message &aMessage) { ThreadError error; Tlv tlv; AddressRegistrationEntry entry; Lowpan::Context context; uint8_t length = 0; uint16_t startOffset = aMessage.GetLength(); tlv.SetType(Tlv::kAddressRegistration); SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); // write entries to message for (const Ip6::NetifUnicastAddress *addr = mNetif.GetUnicastAddresses(); addr; addr = addr->GetNext()) { if (addr->GetAddress().IsLinkLocal() || addr->GetAddress() == mMeshLocal16.GetAddress()) { continue; } if (mNetworkData.GetContext(addr->GetAddress(), context) == kThreadError_None) { // compressed entry entry.SetContextId(context.mContextId); entry.SetIid(addr->GetAddress().GetIid()); } else { // uncompressed entry entry.SetUncompressed(); entry.SetIp6Address(addr->GetAddress()); } SuccessOrExit(error = aMessage.Append(&entry, entry.GetLength())); length += entry.GetLength(); } tlv.SetLength(length); aMessage.Write(startOffset, sizeof(tlv), &tlv); exit: return error; } ThreadError Mle::AppendActiveTimestamp(Message &aMessage, bool aCouldUseLocal) { ThreadError error; ActiveTimestampTlv timestampTlv; const MeshCoP::Timestamp *timestamp; if ((timestamp = mNetif.GetActiveDataset().GetNetwork().GetTimestamp()) == NULL && aCouldUseLocal) { timestamp = mNetif.GetActiveDataset().GetLocal().GetTimestamp(); } VerifyOrExit(timestamp, error = kThreadError_None); timestampTlv.Init(); *static_cast(×tampTlv) = *timestamp; error = aMessage.Append(×tampTlv, sizeof(timestampTlv)); exit: return error; } ThreadError Mle::AppendPendingTimestamp(Message &aMessage, bool aCouldUseLocal) { ThreadError error; PendingTimestampTlv timestampTlv; const MeshCoP::Timestamp *timestamp; if ((timestamp = mNetif.GetPendingDataset().GetNetwork().GetTimestamp()) == NULL && aCouldUseLocal) { timestamp = mNetif.GetPendingDataset().GetLocal().GetTimestamp(); } VerifyOrExit(timestamp && timestamp->GetSeconds() != 0, error = kThreadError_None); timestampTlv.Init(); *static_cast(×tampTlv) = *timestamp; error = aMessage.Append(×tampTlv, sizeof(timestampTlv)); exit: return error; } void Mle::HandleNetifStateChanged(uint32_t aFlags, void *aContext) { static_cast(aContext)->HandleNetifStateChanged(aFlags); } void Mle::HandleNetifStateChanged(uint32_t aFlags) { if ((aFlags & (OT_IP6_ADDRESS_ADDED | OT_IP6_ADDRESS_REMOVED)) != 0) { if (!mNetif.IsUnicastAddress(mMeshLocal64.GetAddress())) { // Mesh Local EID was removed, choose a new one and add it back for (int i = 8; i < 16; i++) { mMeshLocal64.GetAddress().mFields.m8[i] = static_cast(otPlatRandomGet()); } mNetif.AddUnicastAddress(mMeshLocal64); mNetif.SetStateChangedFlags(OT_IP6_ML_ADDR_CHANGED); } if (mDeviceState == kDeviceStateChild && (mDeviceMode & ModeTlv::kModeFFD) == 0) { mSendChildUpdateRequest.Post(); } } if ((aFlags & OT_THREAD_NETDATA_UPDATED) != 0) { if (mDeviceMode & ModeTlv::kModeFFD) { mMleRouter.HandleNetworkDataUpdateRouter(); } else { mSendChildUpdateRequest.Post(); } mNetif.GetNetworkDataLocal().SendServerDataNotification(); } } void Mle::HandleParentRequestTimer(void *aContext) { static_cast(aContext)->HandleParentRequestTimer(); } void Mle::HandleParentRequestTimer(void) { switch (mParentRequestState) { case kParentIdle: if (mParent.mState == Neighbor::kStateValid) { if (mDeviceMode & ModeTlv::kModeRxOnWhenIdle) { SendChildUpdateRequest(); mParentRequestTimer.Start(Timer::SecToMsec(mTimeout / kMaxChildKeepAliveAttempts)); } } else { BecomeDetached(); } break; case kParentSynchronize: mParentRequestState = kParentIdle; BecomeChild(kMleAttachAnyPartition); break; case kParentRequestStart: mParentRequestState = kParentRequestRouter; mParent.mState = Neighbor::kStateInvalid; SendParentRequest(); mParentRequestTimer.Start(kParentRequestRouterTimeout); break; case kParentRequestRouter: mParentRequestState = kParentRequestChild; if (mParent.mState == Neighbor::kStateValid) { SendChildIdRequest(); mParentRequestState = kChildIdRequest; } else { SendParentRequest(); } mParentRequestTimer.Start(kParentRequestChildTimeout); break; case kParentRequestChild: mParentRequestState = kParentRequestChild; if (mParent.mState == Neighbor::kStateValid) { SendChildIdRequest(); mParentRequestState = kChildIdRequest; mParentRequestTimer.Start(kParentRequestChildTimeout); } else { if (mReattachState == kReattachActive) { if (mNetif.GetPendingDataset().Restore() == kThreadError_None) { mNetif.GetPendingDataset().ApplyConfiguration(); mReattachState = kReattachPending; mParentRequestState = kParentRequestStart; mParentRequestTimer.Start(kParentRequestRouterTimeout); } else { mReattachState = kReattachStop; } } else if (mReattachState == kReattachPending) { mReattachState = kReattachStop; mNetif.GetActiveDataset().Restore(); mNetif.GetPendingDataset().Set(mNetif.GetPendingDataset().GetLocal()); } if (mReattachState == kReattachStop) { switch (mParentRequestMode) { case kMleAttachAnyPartition: if (mPreviousPanId != Mac::kPanIdBroadcast) { mMac.SetChannel(mPreviousChannel); mMac.SetPanId(mPreviousPanId); mPreviousPanId = Mac::kPanIdBroadcast; BecomeDetached(); } else if ((mDeviceMode & ModeTlv::kModeFFD) == 0) { SendOrphanAnnounce(); BecomeDetached(); } else if (mMleRouter.BecomeLeader() != kThreadError_None) { mParentRequestState = kParentIdle; BecomeDetached(); } break; case kMleAttachSamePartition: mParentRequestState = kParentIdle; BecomeChild(kMleAttachAnyPartition); break; case kMleAttachBetterPartition: mParentRequestState = kParentIdle; break; } } } break; case kChildIdRequest: mParentRequestState = kParentIdle; if (mDeviceState != kDeviceStateRouter && mDeviceState != kDeviceStateLeader) { BecomeDetached(); } break; } } ThreadError Mle::SendParentRequest(void) { ThreadError error = kThreadError_None; Message *message; uint8_t scanMask = 0; Ip6::Address destination; for (uint8_t i = 0; i < sizeof(mParentRequest.mChallenge); i++) { mParentRequest.mChallenge[i] = static_cast(otPlatRandomGet()); } VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandParentRequest)); SuccessOrExit(error = AppendMode(*message, mDeviceMode)); SuccessOrExit(error = AppendChallenge(*message, mParentRequest.mChallenge, sizeof(mParentRequest.mChallenge))); switch (mParentRequestState) { case kParentRequestRouter: scanMask = ScanMaskTlv::kRouterFlag; if (mParentRequestMode == kMleAttachSamePartition) { scanMask |= ScanMaskTlv::kEndDeviceFlag; } break; case kParentRequestChild: scanMask = ScanMaskTlv::kRouterFlag | ScanMaskTlv::kEndDeviceFlag; break; default: assert(false); break; } SuccessOrExit(error = AppendScanMask(*message, scanMask)); SuccessOrExit(error = AppendVersion(*message)); memset(&destination, 0, sizeof(destination)); destination.mFields.m16[0] = HostSwap16(0xff02); destination.mFields.m16[7] = HostSwap16(0x0002); SuccessOrExit(error = SendMessage(*message, destination)); switch (mParentRequestState) { case kParentRequestRouter: otLogInfoMle("Sent parent request to routers"); break; case kParentRequestChild: otLogInfoMle("Sent parent request to all devices"); break; default: assert(false); break; } exit: if (error != kThreadError_None && message != NULL) { message->Free(); } return kThreadError_None; } ThreadError Mle::SendChildIdRequest(void) { ThreadError error = kThreadError_None; uint8_t tlvs[] = {Tlv::kAddress16, Tlv::kNetworkData, Tlv::kRoute}; Message *message; Ip6::Address destination; VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandChildIdRequest)); SuccessOrExit(error = AppendResponse(*message, mChildIdRequest.mChallenge, mChildIdRequest.mChallengeLength)); SuccessOrExit(error = AppendLinkFrameCounter(*message)); SuccessOrExit(error = AppendMleFrameCounter(*message)); SuccessOrExit(error = AppendMode(*message, mDeviceMode)); SuccessOrExit(error = AppendTimeout(*message, mTimeout)); SuccessOrExit(error = AppendVersion(*message)); if ((mDeviceMode & ModeTlv::kModeFFD) == 0) { SuccessOrExit(error = AppendAddressRegistration(*message)); } SuccessOrExit(error = AppendTlvRequest(*message, tlvs, sizeof(tlvs))); SuccessOrExit(error = AppendActiveTimestamp(*message, true)); // SuccessOrExit(error = AppendPendingTimestamp(*message, false)); // we should not include the Local Pending Timestamp TLV in Child ID Request, this is a workaround for // Certification test 9.2.15 and 9.2.16 (https://github.com/openthread/openthread/issues/918) SuccessOrExit(error = AppendPendingTimestamp(*message, true)); memset(&destination, 0, sizeof(destination)); destination.mFields.m16[0] = HostSwap16(0xfe80); destination.SetIid(mParent.mMacAddr); SuccessOrExit(error = SendMessage(*message, destination)); otLogInfoMle("Sent Child ID Request"); if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) { mMesh.SetPollPeriod(kAttachDataPollPeriod); mMesh.SetRxOnWhenIdle(false); } exit: if (error != kThreadError_None && message != NULL) { message->Free(); } return error; } ThreadError Mle::SendDataRequest(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength) { ThreadError error = kThreadError_None; Message *message; VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandDataRequest)); SuccessOrExit(error = AppendTlvRequest(*message, aTlvs, aTlvsLength)); SuccessOrExit(error = AppendActiveTimestamp(*message, false)); SuccessOrExit(error = AppendPendingTimestamp(*message, false)); SuccessOrExit(error = SendMessage(*message, aDestination)); otLogInfoMle("Sent Data Request"); exit: if (error != kThreadError_None && message != NULL) { message->Free(); } return error; } void Mle::HandleSendChildUpdateRequest(void *aContext) { static_cast(aContext)->HandleSendChildUpdateRequest(); } void Mle::HandleSendChildUpdateRequest(void) { // a Network Data udpate can cause a change to the IPv6 address configuration // only send a Child Update Request after we know there are no more pending changes if (mNetif.IsStateChangedCallbackPending()) { mSendChildUpdateRequest.Post(); } else { SendChildUpdateRequest(); } } ThreadError Mle::SendChildUpdateRequest(void) { ThreadError error = kThreadError_None; Ip6::Address destination; Message *message; VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); message->SetLinkSecurityEnabled(false); SuccessOrExit(error = AppendHeader(*message, Header::kCommandChildUpdateRequest)); SuccessOrExit(error = AppendMode(*message, mDeviceMode)); if ((mDeviceMode & ModeTlv::kModeFFD) == 0) { SuccessOrExit(error = AppendAddressRegistration(*message)); } switch (mDeviceState) { case kDeviceStateDetached: for (uint8_t i = 0; i < sizeof(mParentRequest.mChallenge); i++) { mParentRequest.mChallenge[i] = static_cast(otPlatRandomGet()); } SuccessOrExit(error = AppendChallenge(*message, mParentRequest.mChallenge, sizeof(mParentRequest.mChallenge))); break; case kDeviceStateChild: SuccessOrExit(error = AppendSourceAddress(*message)); SuccessOrExit(error = AppendLeaderData(*message)); SuccessOrExit(error = AppendTimeout(*message, mTimeout)); break; case kDeviceStateDisabled: case kDeviceStateRouter: case kDeviceStateLeader: assert(false); break; } memset(&destination, 0, sizeof(destination)); destination.mFields.m16[0] = HostSwap16(0xfe80); destination.SetIid(mParent.mMacAddr); SuccessOrExit(error = SendMessage(*message, destination)); otLogInfoMle("Sent Child Update Request"); if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) { mMesh.SetPollPeriod(kAttachDataPollPeriod); mMesh.SetRxOnWhenIdle(false); } exit: if (error != kThreadError_None && message != NULL) { message->Free(); } return error; } ThreadError Mle::SendAnnounce(uint8_t aChannel, bool aOrphanAnnounce) { ThreadError error = kThreadError_None; ChannelTlv channel; PanIdTlv panid; ActiveTimestampTlv activeTimestamp; Ip6::Address destination; Message *message; VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); message->SetSubType(Message::kSubTypeMleAnnounce); message->SetChannel(aChannel); SuccessOrExit(error = AppendHeader(*message, Header::kCommandAnnounce)); channel.Init(); channel.SetChannelPage(0); channel.SetChannel(mMac.GetChannel()); SuccessOrExit(error = message->Append(&channel, sizeof(channel))); if (aOrphanAnnounce) { activeTimestamp.Init(); activeTimestamp.SetSeconds(0); activeTimestamp.SetTicks(0); activeTimestamp.SetAuthoritative(true); SuccessOrExit(error = message->Append(&activeTimestamp, sizeof(activeTimestamp))); } else { SuccessOrExit(error = AppendActiveTimestamp(*message, false)); } panid.Init(); panid.SetPanId(mMac.GetPanId()); SuccessOrExit(error = message->Append(&panid, sizeof(panid))); memset(&destination, 0, sizeof(destination)); destination.mFields.m16[0] = HostSwap16(0xff02); destination.mFields.m16[7] = HostSwap16(0x0001); SuccessOrExit(error = SendMessage(*message, destination)); otLogInfoMle("sent announce on channel %d", aChannel); exit: if (error != kThreadError_None && message != NULL) { message->Free(); } return error; } void Mle::SendOrphanAnnounce(void) { MeshCoP::ChannelMask0Tlv *channelMask; uint8_t channel; channelMask = static_cast(mNetif.GetActiveDataset().GetNetwork().Get( MeshCoP::Tlv::kChannelMask)); VerifyOrExit(channelMask != NULL,); // find next channel in the Active Operational Dataset Channel Mask channel = mAnnounceChannel; while (!channelMask->IsChannelSet(channel)) { channel++; if (channel > kPhyMaxChannel) { channel = kPhyMinChannel; } VerifyOrExit(channel != mAnnounceChannel,); } // Send Annuonce message SendAnnounce(channel, true); // Move to next channel mAnnounceChannel++; if (mAnnounceChannel > kPhyMaxChannel) { mAnnounceChannel = kPhyMinChannel; } exit: return; } ThreadError Mle::SendMessage(Message &aMessage, const Ip6::Address &aDestination) { ThreadError error = kThreadError_None; Header header; uint32_t keySequence; uint8_t nonce[13]; uint8_t tag[4]; uint8_t tagLength; Crypto::AesCcm aesCcm; uint8_t buf[64]; uint16_t length; Ip6::MessageInfo messageInfo; aMessage.Read(0, sizeof(header), &header); if (header.GetSecuritySuite() == Header::k154Security) { header.SetFrameCounter(mKeyManager.GetMleFrameCounter()); keySequence = mKeyManager.GetCurrentKeySequence(); header.SetKeyId(keySequence); aMessage.Write(0, header.GetLength(), &header); GenerateNonce(*mMac.GetExtAddress(), mKeyManager.GetMleFrameCounter(), Mac::Frame::kSecEncMic32, nonce); aesCcm.SetKey(mKeyManager.GetCurrentMleKey(), 16); aesCcm.Init(16 + 16 + header.GetHeaderLength(), aMessage.GetLength() - (header.GetLength() - 1), sizeof(tag), nonce, sizeof(nonce)); aesCcm.Header(&mLinkLocal64.GetAddress(), sizeof(mLinkLocal64.GetAddress())); aesCcm.Header(&aDestination, sizeof(aDestination)); aesCcm.Header(header.GetBytes() + 1, header.GetHeaderLength()); aMessage.SetOffset(header.GetLength() - 1); while (aMessage.GetOffset() < aMessage.GetLength()) { length = aMessage.Read(aMessage.GetOffset(), sizeof(buf), buf); aesCcm.Payload(buf, buf, length, true); aMessage.Write(aMessage.GetOffset(), length, buf); aMessage.MoveOffset(length); } tagLength = sizeof(tag); aesCcm.Finalize(tag, &tagLength); SuccessOrExit(error = aMessage.Append(tag, tagLength)); mKeyManager.IncrementMleFrameCounter(); } messageInfo.SetPeerAddr(aDestination); messageInfo.SetSockAddr(mLinkLocal64.GetAddress()); messageInfo.SetPeerPort(kUdpPort); messageInfo.SetInterfaceId(mNetif.GetInterfaceId()); messageInfo.SetHopLimit(255); SuccessOrExit(error = mSocket.SendTo(aMessage, messageInfo)); exit: return error; } void Mle::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo) { static_cast(aContext)->HandleUdpReceive(*static_cast(aMessage), *static_cast(aMessageInfo)); } void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Header header; uint32_t keySequence; const uint8_t *mleKey; uint32_t frameCounter; uint8_t messageTag[4]; uint16_t messageTagLength; uint8_t nonce[13]; Mac::ExtAddress macAddr; Crypto::AesCcm aesCcm; uint16_t mleOffset; uint8_t buf[64]; uint16_t length; uint8_t tag[4]; uint8_t tagLength; uint8_t command; Neighbor *neighbor; aMessage.Read(aMessage.GetOffset(), sizeof(header), &header); VerifyOrExit(header.IsValid(),); assert(aMessageInfo.GetLinkInfo() != NULL); if (header.GetSecuritySuite() == Header::kNoSecurity) { aMessage.MoveOffset(header.GetLength()); switch (header.GetCommand()) { case Header::kCommandDiscoveryRequest: mMleRouter.HandleDiscoveryRequest(aMessage, aMessageInfo); break; case Header::kCommandDiscoveryResponse: HandleDiscoveryResponse(aMessage, aMessageInfo); break; default: break; } ExitNow(); } VerifyOrExit(mDeviceState != kDeviceStateDisabled && header.GetSecuritySuite() == Header::k154Security, ;); keySequence = header.GetKeyId(); if (keySequence == mKeyManager.GetCurrentKeySequence()) { mleKey = mKeyManager.GetCurrentMleKey(); } else { mleKey = mKeyManager.GetTemporaryMleKey(keySequence); } aMessage.MoveOffset(header.GetLength() - 1); frameCounter = header.GetFrameCounter(); messageTagLength = aMessage.Read(aMessage.GetLength() - sizeof(messageTag), sizeof(messageTag), messageTag); VerifyOrExit(messageTagLength == sizeof(messageTag), ;); SuccessOrExit(aMessage.SetLength(aMessage.GetLength() - sizeof(messageTag))); macAddr.Set(aMessageInfo.GetPeerAddr()); GenerateNonce(macAddr, frameCounter, Mac::Frame::kSecEncMic32, nonce); aesCcm.SetKey(mleKey, 16); aesCcm.Init(sizeof(aMessageInfo.GetPeerAddr()) + sizeof(aMessageInfo.GetSockAddr()) + header.GetHeaderLength(), aMessage.GetLength() - aMessage.GetOffset(), sizeof(messageTag), nonce, sizeof(nonce)); aesCcm.Header(&aMessageInfo.GetPeerAddr(), sizeof(aMessageInfo.GetPeerAddr())); aesCcm.Header(&aMessageInfo.GetSockAddr(), sizeof(aMessageInfo.GetSockAddr())); aesCcm.Header(header.GetBytes() + 1, header.GetHeaderLength()); mleOffset = aMessage.GetOffset(); while (aMessage.GetOffset() < aMessage.GetLength()) { length = aMessage.Read(aMessage.GetOffset(), sizeof(buf), buf); aesCcm.Payload(buf, buf, length, false); aMessage.Write(aMessage.GetOffset(), length, buf); aMessage.MoveOffset(length); } tagLength = sizeof(tag); aesCcm.Finalize(tag, &tagLength); VerifyOrExit(messageTagLength == tagLength && memcmp(messageTag, tag, tagLength) == 0, ;); if (keySequence > mKeyManager.GetCurrentKeySequence()) { mKeyManager.SetCurrentKeySequence(keySequence); } aMessage.SetOffset(mleOffset); aMessage.Read(aMessage.GetOffset(), sizeof(command), &command); aMessage.MoveOffset(sizeof(command)); switch (mDeviceState) { case kDeviceStateDetached: case kDeviceStateChild: neighbor = GetNeighbor(macAddr); break; case kDeviceStateRouter: case kDeviceStateLeader: if (command == Header::kCommandChildIdResponse) { neighbor = GetNeighbor(macAddr); } else { neighbor = mMleRouter.GetNeighbor(macAddr); } break; default: neighbor = NULL; break; } if (neighbor != NULL && neighbor->mState == Neighbor::kStateValid) { if (keySequence == neighbor->mKeySequence) { if (frameCounter < neighbor->mValid.mMleFrameCounter) { otLogDebgMle("mle frame reject 1"); ExitNow(); } } else { if (keySequence <= neighbor->mKeySequence) { otLogDebgMle("mle frame reject 2"); ExitNow(); } neighbor->mKeySequence = keySequence; neighbor->mValid.mLinkFrameCounter = 0; } neighbor->mValid.mMleFrameCounter = frameCounter + 1; } else { if (!(command == Header::kCommandLinkRequest || command == Header::kCommandLinkAccept || command == Header::kCommandLinkAcceptAndRequest || command == Header::kCommandAdvertisement || command == Header::kCommandParentRequest || command == Header::kCommandParentResponse || command == Header::kCommandChildIdRequest || command == Header::kCommandChildUpdateRequest || command == Header::kCommandAnnounce)) { otLogDebgMle("mle sequence unknown! %d", command); ExitNow(); } } switch (command) { case Header::kCommandLinkRequest: mMleRouter.HandleLinkRequest(aMessage, aMessageInfo); break; case Header::kCommandLinkAccept: mMleRouter.HandleLinkAccept(aMessage, aMessageInfo, keySequence); break; case Header::kCommandLinkAcceptAndRequest: mMleRouter.HandleLinkAcceptAndRequest(aMessage, aMessageInfo, keySequence); break; case Header::kCommandAdvertisement: HandleAdvertisement(aMessage, aMessageInfo); break; case Header::kCommandDataRequest: mMleRouter.HandleDataRequest(aMessage, aMessageInfo); break; case Header::kCommandDataResponse: HandleDataResponse(aMessage, aMessageInfo); break; case Header::kCommandParentRequest: mMleRouter.HandleParentRequest(aMessage, aMessageInfo); break; case Header::kCommandParentResponse: HandleParentResponse(aMessage, aMessageInfo, keySequence); break; case Header::kCommandChildIdRequest: mMleRouter.HandleChildIdRequest(aMessage, aMessageInfo, keySequence); break; case Header::kCommandChildIdResponse: HandleChildIdResponse(aMessage, aMessageInfo); break; case Header::kCommandChildUpdateRequest: mMleRouter.HandleChildUpdateRequest(aMessage, aMessageInfo); break; case Header::kCommandChildUpdateResponse: HandleChildUpdateResponse(aMessage, aMessageInfo); break; case Header::kCommandAnnounce: HandleAnnounce(aMessage, aMessageInfo); break; } exit: {} } ThreadError Mle::HandleAdvertisement(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { ThreadError error = kThreadError_None; Mac::ExtAddress macAddr; bool isNeighbor; Neighbor *neighbor; SourceAddressTlv sourceAddress; LeaderDataTlv leaderData; uint8_t tlvs[] = {Tlv::kNetworkData}; // Source Address SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); VerifyOrExit(sourceAddress.IsValid(), error = kThreadError_Parse); // Leader Data SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); VerifyOrExit(leaderData.IsValid(), error = kThreadError_Parse); otLogInfoMle("Received advertisement from %04x", sourceAddress.GetRloc16()); if (mDeviceState != kDeviceStateDetached) { SuccessOrExit(error = mMleRouter.HandleAdvertisement(aMessage, aMessageInfo)); } macAddr.Set(aMessageInfo.GetPeerAddr()); isNeighbor = false; switch (mDeviceState) { case kDeviceStateDisabled: case kDeviceStateDetached: break; case kDeviceStateChild: if (memcmp(&mParent.mMacAddr, &macAddr, sizeof(mParent.mMacAddr))) { break; } if ((mParent.mValid.mRloc16 == sourceAddress.GetRloc16()) && (leaderData.GetPartitionId() != mLeaderData.GetPartitionId() || leaderData.GetLeaderRouterId() != GetLeaderId())) { SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId()); mRetrieveNewNetworkData = true; } isNeighbor = true; mParent.mLastHeard = mParentRequestTimer.GetNow(); break; case kDeviceStateRouter: case kDeviceStateLeader: if ((neighbor = mMleRouter.GetNeighbor(macAddr)) != NULL && neighbor->mState == Neighbor::kStateValid) { isNeighbor = true; } break; } if (isNeighbor) { if (mRetrieveNewNetworkData || (static_cast(leaderData.GetDataVersion() - mNetworkData.GetVersion()) > 0)) { SendDataRequest(aMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs)); } } exit: if (error != kThreadError_None) { otLogWarnMleErr(error, "Failed to process Advertisement"); } return error; } ThreadError Mle::HandleDataResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { ThreadError error = kThreadError_None; LeaderDataTlv leaderData; NetworkDataTlv networkData; ActiveTimestampTlv activeTimestamp; PendingTimestampTlv pendingTimestamp; uint16_t activeDatasetOffset = 0; uint16_t pendingDatasetOffset = 0; bool dataRequest = false; Tlv tlv; otLogInfoMle("Received Data Response"); // Leader Data SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); VerifyOrExit(leaderData.IsValid(), error = kThreadError_Parse); if ((leaderData.GetPartitionId() != mLeaderData.GetPartitionId()) || (leaderData.GetLeaderRouterId() != GetLeaderId())) { if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) { SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId()); } else { ExitNow(error = kThreadError_Drop); } } else if (!mRetrieveNewNetworkData) { VerifyOrExit(static_cast(leaderData.GetDataVersion() - mNetworkData.GetVersion()) > 0, ;); } // Network Data SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kNetworkData, sizeof(networkData), networkData)); VerifyOrExit(networkData.IsValid(), error = kThreadError_Parse); // Active Timestamp if (Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(activeTimestamp), activeTimestamp) == kThreadError_None) { const MeshCoP::Timestamp *timestamp; VerifyOrExit(activeTimestamp.IsValid(), error = kThreadError_Parse); timestamp = mNetif.GetActiveDataset().GetNetwork().GetTimestamp(); // if received timestamp does not match the local value and message does not contain the dataset, // send MLE Data Request if ((timestamp == NULL || timestamp->Compare(activeTimestamp) != 0) && (Tlv::GetOffset(aMessage, Tlv::kActiveDataset, activeDatasetOffset) != kThreadError_None)) { ExitNow(dataRequest = true); } } else { activeTimestamp.SetLength(0); } // Pending Timestamp if (Tlv::GetTlv(aMessage, Tlv::kPendingTimestamp, sizeof(pendingTimestamp), pendingTimestamp) == kThreadError_None) { const MeshCoP::Timestamp *timestamp; VerifyOrExit(pendingTimestamp.IsValid(), error = kThreadError_Parse); timestamp = mNetif.GetPendingDataset().GetNetwork().GetTimestamp(); // if received timestamp does not match the local value and message does not contain the dataset, // send MLE Data Request if ((timestamp == NULL || timestamp->Compare(pendingTimestamp) != 0) && (Tlv::GetOffset(aMessage, Tlv::kPendingDataset, pendingDatasetOffset) != kThreadError_None)) { ExitNow(dataRequest = true); } } else { pendingTimestamp.SetLength(0); } // Network Data mNetworkData.SetNetworkData(leaderData.GetDataVersion(), leaderData.GetStableDataVersion(), (mDeviceMode & ModeTlv::kModeFullNetworkData) == 0, networkData.GetNetworkData(), networkData.GetLength()); // Active Dataset if (activeTimestamp.GetLength() > 0) { if (activeDatasetOffset > 0) { aMessage.Read(activeDatasetOffset, sizeof(tlv), &tlv); mNetif.GetActiveDataset().Set(activeTimestamp, aMessage, activeDatasetOffset + sizeof(tlv), tlv.GetLength()); } } else { mNetif.GetActiveDataset().Clear(false); } // Pending Dataset if (pendingTimestamp.GetLength() > 0) { if (pendingDatasetOffset > 0) { aMessage.Read(pendingDatasetOffset, sizeof(tlv), &tlv); mNetif.GetPendingDataset().Set(pendingTimestamp, aMessage, pendingDatasetOffset + sizeof(tlv), tlv.GetLength()); } } else { mNetif.GetPendingDataset().Clear(false); } if (mPreviousPanId != Mac::kPanIdBroadcast && ((mDeviceMode & ModeTlv::kModeFFD) == 0)) { mPreviousPanId = Mac::kPanIdBroadcast; mNetif.GetAnnounceBeginServer().SendAnnounce(1 << mPreviousChannel); } mRetrieveNewNetworkData = false; exit: if (error != kThreadError_None) { otLogWarnMleErr(error, "Failed to process Data Response"); } (void)aMessageInfo; if (dataRequest) { static const uint8_t tlvs[] = {Tlv::kNetworkData}; SendDataRequest(aMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs)); } return error; } bool Mle::IsBetterParent(uint16_t aRloc16, uint8_t aLinkQuality, ConnectivityTlv &aConnectivityTlv) const { bool rval = false; if (aLinkQuality != mParentLinkQuality) { ExitNow(rval = (aLinkQuality > mParentLinkQuality)); } if (IsActiveRouter(aRloc16) != IsActiveRouter(mParent.mValid.mRloc16)) { ExitNow(rval = IsActiveRouter(aRloc16)); } if (aConnectivityTlv.GetParentPriority() != mParentPriority) { ExitNow(rval = (aConnectivityTlv.GetParentPriority() > mParentPriority)); } if (aConnectivityTlv.GetLinkQuality3() != mParentLinkQuality3) { ExitNow(rval = (aConnectivityTlv.GetLinkQuality3() > mParentLinkQuality3)); } if (aConnectivityTlv.GetLinkQuality2() != mParentLinkQuality2) { ExitNow(rval = (aConnectivityTlv.GetLinkQuality2() > mParentLinkQuality2)); } if (aConnectivityTlv.GetLinkQuality1() != mParentLinkQuality1) { ExitNow(rval = (aConnectivityTlv.GetLinkQuality1() > mParentLinkQuality1)); } exit: return rval; } ThreadError Mle::HandleParentResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint32_t aKeySequence) { ThreadError error = kThreadError_None; const ThreadMessageInfo *threadMessageInfo = static_cast(aMessageInfo.GetLinkInfo()); ResponseTlv response; SourceAddressTlv sourceAddress; LeaderDataTlv leaderData; LinkMarginTlv linkMarginTlv; uint8_t linkMargin; uint8_t linkQuality; ConnectivityTlv connectivity; LinkFrameCounterTlv linkFrameCounter; MleFrameCounterTlv mleFrameCounter; ChallengeTlv challenge; int8_t diff; otLogInfoMle("Received Parent Response"); // Response SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kResponse, sizeof(response), response)); VerifyOrExit(response.IsValid() && memcmp(response.GetResponse(), mParentRequest.mChallenge, response.GetLength()) == 0, error = kThreadError_Parse); // Source Address SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); VerifyOrExit(sourceAddress.IsValid(), error = kThreadError_Parse); // Leader Data SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); VerifyOrExit(leaderData.IsValid(), error = kThreadError_Parse); // Link Quality SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLinkMargin, sizeof(linkMarginTlv), linkMarginTlv)); VerifyOrExit(linkMarginTlv.IsValid(), error = kThreadError_Parse); linkMargin = LinkQualityInfo::ConvertRssToLinkMargin(mMac.GetNoiseFloor(), threadMessageInfo->mRss); if (linkMargin > linkMarginTlv.GetLinkMargin()) { linkMargin = linkMarginTlv.GetLinkMargin(); } linkQuality = LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMargin); VerifyOrExit(mParentRequestState != kParentRequestRouter || linkQuality == 3, ;); // Connectivity SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kConnectivity, sizeof(connectivity), connectivity)); VerifyOrExit(connectivity.IsValid(), error = kThreadError_Parse); if ((mDeviceMode & ModeTlv::kModeFFD) && (mDeviceState != kDeviceStateDetached)) { switch (mParentRequestMode) { case kMleAttachAnyPartition: VerifyOrExit(leaderData.GetPartitionId() != mLeaderData.GetPartitionId() || static_cast(connectivity.GetIdSequence() - mMleRouter.GetRouterIdSequence()) > 0,); break; case kMleAttachSamePartition: VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId(), ;); diff = static_cast(connectivity.GetIdSequence() - mMleRouter.GetRouterIdSequence()); VerifyOrExit(diff > 0 || (diff == 0 && mMleRouter.GetLeaderAge() < mMleRouter.GetNetworkIdTimeout()), ;); break; case kMleAttachBetterPartition: VerifyOrExit(leaderData.GetPartitionId() != mLeaderData.GetPartitionId(), ;); VerifyOrExit(mMleRouter.ComparePartitions(connectivity.GetActiveRouters() <= 1, leaderData, mMleRouter.IsSingleton(), mLeaderData) > 0, ;); break; } } // if already have a candidate parent, only seek a better parent if (mParent.mState == Neighbor::kStateValid) { int compare = 0; if (mDeviceMode & ModeTlv::kModeFFD) { compare = mMleRouter.ComparePartitions(connectivity.GetActiveRouters() <= 1, leaderData, mParentIsSingleton, mParentLeaderData); } // only consider partitions that are the same or better VerifyOrExit(compare >= 0, ;); // only consider better parents if the partitions are the same VerifyOrExit(compare != 0 || IsBetterParent(sourceAddress.GetRloc16(), linkQuality, connectivity), ;); } // Link Frame Counter SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLinkFrameCounter, sizeof(linkFrameCounter), linkFrameCounter)); VerifyOrExit(linkFrameCounter.IsValid(), error = kThreadError_Parse); // Mle Frame Counter if (Tlv::GetTlv(aMessage, Tlv::kMleFrameCounter, sizeof(mleFrameCounter), mleFrameCounter) == kThreadError_None) { VerifyOrExit(mleFrameCounter.IsValid(), ;); } else { mleFrameCounter.SetFrameCounter(linkFrameCounter.GetFrameCounter()); } // Challenge SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kChallenge, sizeof(challenge), challenge)); VerifyOrExit(challenge.IsValid(), error = kThreadError_Parse); memcpy(mChildIdRequest.mChallenge, challenge.GetChallenge(), challenge.GetLength()); mChildIdRequest.mChallengeLength = challenge.GetLength(); mParent.mMacAddr.Set(aMessageInfo.GetPeerAddr()); mParent.mValid.mRloc16 = sourceAddress.GetRloc16(); mParent.mValid.mLinkFrameCounter = linkFrameCounter.GetFrameCounter(); mParent.mValid.mMleFrameCounter = mleFrameCounter.GetFrameCounter(); mParent.mMode = ModeTlv::kModeFFD | ModeTlv::kModeRxOnWhenIdle | ModeTlv::kModeFullNetworkData; mParent.mLinkInfo.Clear(); mParent.mLinkInfo.AddRss(mMac.GetNoiseFloor(), threadMessageInfo->mRss); mParent.mLinkFailures = 0; mParent.mState = Neighbor::kStateValid; mParent.mKeySequence = aKeySequence; mParentLinkQuality = linkQuality; mParentPriority = connectivity.GetParentPriority(); mParentLinkQuality3 = connectivity.GetLinkQuality3(); mParentLinkQuality2 = connectivity.GetLinkQuality2(); mParentLinkQuality1 = connectivity.GetLinkQuality1(); mParentLeaderData = leaderData; mParentIsSingleton = connectivity.GetActiveRouters() <= 1; exit: if (error != kThreadError_None) { otLogWarnMleErr(error, "Failed to process Parent Response"); } return error; } ThreadError Mle::HandleChildIdResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { ThreadError error = kThreadError_None; LeaderDataTlv leaderData; SourceAddressTlv sourceAddress; Address16Tlv shortAddress; NetworkDataTlv networkData; RouteTlv route; ActiveTimestampTlv activeTimestamp; PendingTimestampTlv pendingTimestamp; Tlv tlv; uint16_t offset; uint8_t numRouters = 0; otLogInfoMle("Received Child ID Response"); VerifyOrExit(mParentRequestState == kChildIdRequest, ;); // Leader Data SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); VerifyOrExit(leaderData.IsValid(), error = kThreadError_Parse); // Source Address SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); VerifyOrExit(sourceAddress.IsValid(), error = kThreadError_Parse); // ShortAddress SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kAddress16, sizeof(shortAddress), shortAddress)); VerifyOrExit(shortAddress.IsValid(), error = kThreadError_Parse); // Network Data SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kNetworkData, sizeof(networkData), networkData)); // Active Timestamp if (Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(activeTimestamp), activeTimestamp) == kThreadError_None) { VerifyOrExit(activeTimestamp.IsValid(), error = kThreadError_Parse); // Active Dataset if (Tlv::GetOffset(aMessage, Tlv::kActiveDataset, offset) == kThreadError_None) { aMessage.Read(offset, sizeof(tlv), &tlv); mNetif.GetActiveDataset().Set(activeTimestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()); } else if (mNetif.GetActiveDataset().GetNetwork().GetTimestamp() == NULL && mNetif.GetActiveDataset().GetLocal().GetTimestamp() != NULL) { mNetif.GetActiveDataset().Set(mNetif.GetActiveDataset().GetLocal()); } } // Pending Timestamp if (Tlv::GetTlv(aMessage, Tlv::kPendingTimestamp, sizeof(pendingTimestamp), pendingTimestamp) == kThreadError_None) { VerifyOrExit(pendingTimestamp.IsValid(), error = kThreadError_Parse); // Pending Dataset if (Tlv::GetOffset(aMessage, Tlv::kPendingDataset, offset) == kThreadError_None) { aMessage.Read(offset, sizeof(tlv), &tlv); mNetif.GetPendingDataset().Set(pendingTimestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()); } // this is a workaround for Certification test 9.2.15 and 9.2.16 (https://github.com/openthread/openthread/issues/918) else if (mNetif.GetPendingDataset().GetNetwork().GetTimestamp() == NULL && mNetif.GetPendingDataset().GetLocal().GetTimestamp() != NULL) { mNetif.GetPendingDataset().Set(mNetif.GetPendingDataset().GetLocal()); } } else { mNetif.GetPendingDataset().Clear(true); } // Parent Attach Success mParentRequestTimer.Stop(); mReattachState = kReattachStop; SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId()); if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) { mMesh.SetPollPeriod(Timer::SecToMsec(mTimeout / kMaxChildKeepAliveAttempts)); mMesh.SetRxOnWhenIdle(false); } else { mMesh.SetRxOnWhenIdle(true); } mParent.mValid.mRloc16 = sourceAddress.GetRloc16(); SuccessOrExit(error = SetStateChild(shortAddress.GetRloc16())); mNetworkData.SetNetworkData(leaderData.GetDataVersion(), leaderData.GetStableDataVersion(), (mDeviceMode & ModeTlv::kModeFullNetworkData) == 0, networkData.GetNetworkData(), networkData.GetLength()); mNetif.GetActiveDataset().ApplyConfiguration(); // Route if ((Tlv::GetTlv(aMessage, Tlv::kRoute, sizeof(route), route) == kThreadError_None) && (mDeviceMode & ModeTlv::kModeFFD)) { SuccessOrExit(error = mMleRouter.ProcessRouteTlv(route)); for (uint8_t i = 0; i <= kMaxRouterId; i++) { if (route.IsRouterIdSet(i)) { numRouters++; } } if (mRouterSelectionJitterTimeout == 0 && (mDeviceMode & ModeTlv::kModeFFD) && (numRouters < mMleRouter.GetRouterUpgradeThreshold())) { mRouterSelectionJitterTimeout = (otPlatRandomGet() % mRouterSelectionJitter) + 1; } } exit: if (error != kThreadError_None) { otLogWarnMleErr(error, "Failed to process Child ID Response"); } (void)aMessageInfo; return error; } ThreadError Mle::HandleChildUpdateResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { ThreadError error = kThreadError_None; StatusTlv status; ModeTlv mode; ResponseTlv response; LeaderDataTlv leaderData; SourceAddressTlv sourceAddress; TimeoutTlv timeout; NetworkDataTlv networkData; uint8_t tlvs[] = {Tlv::kNetworkData}; otLogInfoMle("Received Child Update Response"); // Status if (Tlv::GetTlv(aMessage, Tlv::kStatus, sizeof(status), status) == kThreadError_None) { BecomeDetached(); ExitNow(); } // Mode SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kMode, sizeof(mode), mode)); VerifyOrExit(mode.IsValid(), error = kThreadError_Parse); VerifyOrExit(mode.GetMode() == mDeviceMode, error = kThreadError_Drop); switch (mDeviceState) { case kDeviceStateDetached: // Response SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kResponse, sizeof(response), response)); VerifyOrExit(response.IsValid(), error = kThreadError_Parse); VerifyOrExit(memcmp(response.GetResponse(), mParentRequest.mChallenge, sizeof(mParentRequest.mChallenge)) == 0, error = kThreadError_Drop); SetStateChild(GetRloc16()); // fall through case kDeviceStateChild: // Leader Data SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); VerifyOrExit(leaderData.IsValid(), error = kThreadError_Parse); // Source Address SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); VerifyOrExit(sourceAddress.IsValid(), error = kThreadError_Parse); if (GetRouterId(sourceAddress.GetRloc16()) != GetRouterId(GetRloc16())) { BecomeDetached(); ExitNow(); } // Timeout optional if (Tlv::GetTlv(aMessage, Tlv::kTimeout, sizeof(timeout), timeout) == kThreadError_None) { VerifyOrExit(timeout.IsValid(), error = kThreadError_Parse); mTimeout = timeout.GetTimeout(); } // Network Data optional if (Tlv::GetTlv(aMessage, Tlv::kNetworkData, sizeof(networkData), networkData) == kThreadError_None) { VerifyOrExit(networkData.IsValid(), error = kThreadError_Parse); mNetworkData.SetNetworkData(leaderData.GetDataVersion(), leaderData.GetStableDataVersion(), (mDeviceMode & ModeTlv::kModeFullNetworkData) == 0, networkData.GetNetworkData(), networkData.GetLength()); } if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) { mMesh.SetPollPeriod(Timer::SecToMsec(mTimeout / kMaxChildKeepAliveAttempts)); mMesh.SetRxOnWhenIdle(false); } else { mMesh.SetRxOnWhenIdle(true); } if (mDeviceMode & ModeTlv::kModeFullNetworkData) { // full network data if (leaderData.GetDataVersion() != mNetworkData.GetVersion()) { SendDataRequest(aMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs)); } } else { // stable network data if (leaderData.GetStableDataVersion() != mNetworkData.GetStableVersion()) { SendDataRequest(aMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs)); } } break; default: assert(false); break; } exit: if (error != kThreadError_None) { otLogWarnMleErr(error, "Failed to process Child Update Response"); } return error; } ThreadError Mle::HandleAnnounce(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { ThreadError error = kThreadError_None; ChannelTlv channel; ActiveTimestampTlv timestamp; const MeshCoP::Timestamp *localTimestamp; PanIdTlv panid; otLogInfoMle("Received announce"); SuccessOrExit(Tlv::GetTlv(aMessage, Tlv::kChannel, sizeof(channel), channel)); VerifyOrExit(channel.IsValid(),); SuccessOrExit(Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(timestamp), timestamp)); VerifyOrExit(timestamp.IsValid(),); SuccessOrExit(Tlv::GetTlv(aMessage, Tlv::kPanId, sizeof(panid), panid)); VerifyOrExit(panid.IsValid(),); localTimestamp = mNetif.GetActiveDataset().GetNetwork().GetTimestamp(); if (localTimestamp == NULL || localTimestamp->Compare(timestamp) > 0) { if ((mDeviceMode & ModeTlv::kModeFFD) == 0) { mRetrieveNewNetworkData = true; } Stop(false); mPreviousChannel = mMac.GetChannel(); mPreviousPanId = mMac.GetPanId(); mMac.SetChannel(static_cast(channel.GetChannel())); mMac.SetPanId(panid.GetPanId()); Start(false); } else { SendAnnounce(static_cast(channel.GetChannel()), false); } exit: (void)aMessageInfo; return error; } ThreadError Mle::HandleDiscoveryResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { ThreadError error = kThreadError_None; const ThreadMessageInfo *threadMessageInfo = static_cast(aMessageInfo.GetLinkInfo()); Tlv tlv; MeshCoP::Tlv meshcopTlv; MeshCoP::DiscoveryResponseTlv discoveryResponse; MeshCoP::ExtendedPanIdTlv extPanId; MeshCoP::NetworkNameTlv networkName; MeshCoP::SteeringDataTlv steeringData; MeshCoP::JoinerUdpPortTlv JoinerUdpPort; otActiveScanResult result; uint16_t offset; uint16_t end; otLogInfoMle("Handle discovery response"); VerifyOrExit(mIsDiscoverInProgress, error = kThreadError_Drop); offset = aMessage.GetOffset(); end = aMessage.GetLength(); // find MLE Discovery TLV while (offset < end) { aMessage.Read(offset, sizeof(tlv), &tlv); if (tlv.GetType() == Tlv::kDiscovery) { break; } offset += sizeof(tlv) + tlv.GetLength(); } VerifyOrExit(offset < end, error = kThreadError_Parse); offset += sizeof(tlv); end = offset + sizeof(tlv) + tlv.GetLength(); memset(&result, 0, sizeof(result)); result.mPanId = threadMessageInfo->mPanId; result.mChannel = threadMessageInfo->mChannel; result.mRssi = threadMessageInfo->mRss; result.mLqi = threadMessageInfo->mLqi; static_cast(&result.mExtAddress)->Set(aMessageInfo.GetPeerAddr()); // process MeshCoP TLVs while (offset < end) { aMessage.Read(offset, sizeof(meshcopTlv), &meshcopTlv); switch (meshcopTlv.GetType()) { case MeshCoP::Tlv::kDiscoveryResponse: aMessage.Read(offset, sizeof(discoveryResponse), &discoveryResponse); VerifyOrExit(discoveryResponse.IsValid(), error = kThreadError_Parse); result.mVersion = discoveryResponse.GetVersion(); result.mIsNative = discoveryResponse.IsNativeCommissioner(); break; case MeshCoP::Tlv::kExtendedPanId: aMessage.Read(offset, sizeof(extPanId), &extPanId); VerifyOrExit(extPanId.IsValid(), error = kThreadError_Parse); memcpy(&result.mExtendedPanId, extPanId.GetExtendedPanId(), sizeof(result.mExtendedPanId)); break; case MeshCoP::Tlv::kNetworkName: aMessage.Read(offset, sizeof(networkName), &networkName); VerifyOrExit(networkName.IsValid(), error = kThreadError_Parse); memcpy(&result.mNetworkName, networkName.GetNetworkName(), networkName.GetLength()); break; case MeshCoP::Tlv::kSteeringData: aMessage.Read(offset, sizeof(steeringData), &steeringData); VerifyOrExit(steeringData.IsValid(), error = kThreadError_Parse); result.mSteeringData.mLength = steeringData.GetLength(); memcpy(result.mSteeringData.m8, steeringData.GetValue(), result.mSteeringData.mLength); break; case MeshCoP::Tlv::kJoinerUdpPort: aMessage.Read(offset, sizeof(JoinerUdpPort), &JoinerUdpPort); VerifyOrExit(JoinerUdpPort.IsValid(), error = kThreadError_Parse); result.mJoinerUdpPort = JoinerUdpPort.GetUdpPort(); break; default: break; } offset += sizeof(meshcopTlv) + meshcopTlv.GetLength(); } // signal callback mDiscoverHandler(&result, mDiscoverContext); exit: if (error != kThreadError_None) { otLogWarnMleErr(error, "Failed to process Discovery Response"); } return error; } Neighbor *Mle::GetNeighbor(uint16_t aAddress) { return (mParent.mState == Neighbor::kStateValid && mParent.mValid.mRloc16 == aAddress) ? &mParent : NULL; } Neighbor *Mle::GetNeighbor(const Mac::ExtAddress &aAddress) { return (mParent.mState == Neighbor::kStateValid && memcmp(&mParent.mMacAddr, &aAddress, sizeof(mParent.mMacAddr)) == 0) ? &mParent : NULL; } Neighbor *Mle::GetNeighbor(const Mac::Address &aAddress) { Neighbor *neighbor = NULL; switch (aAddress.mLength) { case 2: neighbor = GetNeighbor(aAddress.mShortAddress); break; case 8: neighbor = GetNeighbor(aAddress.mExtAddress); break; } return neighbor; } Neighbor *Mle::GetNeighbor(const Ip6::Address &aAddress) { (void)aAddress; return NULL; } uint16_t Mle::GetNextHop(uint16_t aDestination) const { (void)aDestination; return (mParent.mState == Neighbor::kStateValid) ? mParent.mValid.mRloc16 : static_cast (Mac::kShortAddrInvalid); } bool Mle::IsRoutingLocator(const Ip6::Address &aAddress) const { return memcmp(&mMeshLocal16, &aAddress, kRlocPrefixLength) == 0 && aAddress.mFields.m8[14] != kAloc16Mask; } bool Mle::IsAnycastLocator(const Ip6::Address &aAddress) const { return memcmp(&mMeshLocal16, &aAddress, kRlocPrefixLength) == 0 && aAddress.mFields.m8[14] == kAloc16Mask; } Router *Mle::GetParent() { return &mParent; } ThreadError Mle::CheckReachability(uint16_t aMeshSource, uint16_t aMeshDest, Ip6::Header &aIp6Header) { ThreadError error = kThreadError_Drop; Ip6::Address dst; if (aMeshDest != GetRloc16()) { ExitNow(error = kThreadError_None); } if (mNetif.IsUnicastAddress(aIp6Header.GetDestination())) { ExitNow(error = kThreadError_None); } dst = GetMeshLocal16(); dst.mFields.m16[7] = HostSwap16(aMeshSource); mNetif.GetIp6().mIcmp.SendError(dst, Ip6::IcmpHeader::kTypeDstUnreach, Ip6::IcmpHeader::kCodeDstUnreachNoRoute, aIp6Header); exit: return error; } } // namespace Mle } // namespace Thread