| /* |
| * 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 <openthread/config.h> |
| #include "mle.hpp" |
| #include <openthread/platform/radio.h> |
| #include <openthread/platform/random.h> |
| #include <openthread/platform/settings.h> |
| |
| #include "openthread-instance.h" |
| #include "common/code_utils.hpp" |
| #include "common/debug.hpp" |
| #include "common/encoding.hpp" |
| #include "common/logging.hpp" |
| #include "common/settings.hpp" |
| #include "crypto/aes_ccm.hpp" |
| #include "mac/mac_frame.hpp" |
| #include "meshcop/meshcop_tlvs.hpp" |
| #include "net/netif.hpp" |
| #include "net/udp6.hpp" |
| #include "thread/address_resolver.hpp" |
| #include "thread/key_manager.hpp" |
| #include "thread/mle_router.hpp" |
| #include "thread/thread_netif.hpp" |
| |
| using ot::Encoding::BigEndian::HostSwap16; |
| |
| namespace ot { |
| namespace Mle { |
| |
| Mle::Mle(ThreadNetif &aThreadNetif) : |
| ThreadNetifLocator(aThreadNetif), |
| mRetrieveNewNetworkData(false), |
| mRole(OT_DEVICE_ROLE_DISABLED), |
| mDeviceMode(ModeTlv::kModeRxOnWhenIdle | ModeTlv::kModeSecureDataRequest), |
| isAssignLinkQuality(false), |
| mAssignLinkQuality(0), |
| mAssignLinkMargin(0), |
| mParentRequestState(kParentIdle), |
| mReattachState(kReattachStop), |
| mParentRequestTimer(aThreadNetif.GetIp6().mTimerScheduler, &Mle::HandleParentRequestTimer, this), |
| mDelayedResponseTimer(aThreadNetif.GetIp6().mTimerScheduler, &Mle::HandleDelayedResponseTimer, this), |
| mLastPartitionRouterIdSequence(0), |
| mLastPartitionId(0), |
| mParentLeaderCost(0), |
| mParentRequestMode(kAttachAny), |
| mParentPriority(0), |
| mParentLinkQuality3(0), |
| mParentLinkQuality2(0), |
| mParentLinkQuality1(0), |
| mChildUpdateAttempts(0), |
| mParentIsSingleton(false), |
| mSocket(aThreadNetif.GetIp6().mUdp), |
| mTimeout(kMleEndDeviceTimeout), |
| mSendChildUpdateRequest(aThreadNetif.GetIp6().mTaskletScheduler, &Mle::HandleSendChildUpdateRequest, this), |
| mDiscoverHandler(NULL), |
| mDiscoverContext(NULL), |
| mIsDiscoverInProgress(false), |
| mEnableEui64Filtering(false), |
| mAnnounceChannel(OT_RADIO_CHANNEL_MIN), |
| mPreviousChannel(0), |
| mPreviousPanId(Mac::kPanIdBroadcast) |
| { |
| uint8_t meshLocalPrefix[8]; |
| |
| memset(&mLeaderData, 0, sizeof(mLeaderData)); |
| memset(&mParentLeaderData, 0, sizeof(mParentLeaderData)); |
| 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)); |
| memset(&mLeaderAloc, 0, sizeof(mLeaderAloc)); |
| memset(&mParentCandidate, 0, sizeof(mParentCandidate)); |
| |
| // link-local 64 |
| mLinkLocal64.GetAddress().mFields.m16[0] = HostSwap16(0xfe80); |
| mLinkLocal64.GetAddress().SetIid(*aThreadNetif.GetMac().GetExtAddress()); |
| mLinkLocal64.mPrefixLength = 64; |
| mLinkLocal64.mPreferred = true; |
| mLinkLocal64.mValid = true; |
| aThreadNetif.AddUnicastAddress(mLinkLocal64); |
| |
| // Leader Aloc |
| mLeaderAloc.mPrefixLength = 128; |
| mLeaderAloc.mPreferred = true; |
| mLeaderAloc.mValid = true; |
| mLeaderAloc.mScopeOverride = Ip6::Address::kRealmLocalScope; |
| mLeaderAloc.mScopeOverrideValid = true; |
| |
| // initialize Mesh Local Prefix |
| meshLocalPrefix[0] = 0xfd; |
| memcpy(meshLocalPrefix + 1, aThreadNetif.GetMac().GetExtendedPanId(), 5); |
| meshLocalPrefix[6] = 0x00; |
| meshLocalPrefix[7] = 0x00; |
| |
| // mesh-local 64 |
| for (int i = OT_IP6_PREFIX_SIZE; i < OT_IP6_ADDRESS_SIZE; i++) |
| { |
| mMeshLocal64.GetAddress().mFields.m8[i] = static_cast<uint8_t>(otPlatRandomGet()); |
| } |
| |
| mMeshLocal64.mPrefixLength = 64; |
| mMeshLocal64.mPreferred = true; |
| mMeshLocal64.mValid = true; |
| mMeshLocal64.mScopeOverride = Ip6::Address::kRealmLocalScope; |
| mMeshLocal64.mScopeOverrideValid = true; |
| SetMeshLocalPrefix(meshLocalPrefix); // 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.mPreferred = true; |
| mMeshLocal16.mValid = true; |
| mMeshLocal16.mScopeOverride = Ip6::Address::kRealmLocalScope; |
| mMeshLocal16.mScopeOverrideValid = true; |
| mMeshLocal16.mRloc = true; |
| |
| // Store RLOC address reference in MPL module. |
| aThreadNetif.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); |
| aThreadNetif.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); |
| aThreadNetif.SubscribeMulticast(mRealmLocalAllThreadNodes); |
| |
| mNetifCallback.Set(&Mle::HandleNetifStateChanged, this); |
| aThreadNetif.RegisterCallback(mNetifCallback); |
| |
| memset(&mAddr64, 0, sizeof(mAddr64)); |
| } |
| |
| otError Mle::Enable(void) |
| { |
| otError error = OT_ERROR_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; |
| } |
| |
| otError Mle::Disable(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = Stop(false)); |
| SuccessOrExit(error = mSocket.Close()); |
| |
| exit: |
| return error; |
| } |
| |
| otError Mle::Start(bool aEnableReattach, bool aAnnounceAttach) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| |
| otLogFuncEntry(); |
| |
| // cannot bring up the interface if IEEE 802.15.4 promiscuous mode is enabled |
| VerifyOrExit(otPlatRadioGetPromiscuous(netif.GetInstance()) == false, error = OT_ERROR_INVALID_STATE); |
| VerifyOrExit(netif.IsUp(), error = OT_ERROR_INVALID_STATE); |
| |
| mRole = OT_DEVICE_ROLE_DETACHED; |
| netif.SetStateChangedFlags(OT_CHANGED_THREAD_ROLE); |
| SetStateDetached(); |
| |
| netif.GetKeyManager().Start(); |
| |
| if (aEnableReattach) |
| { |
| mReattachState = kReattachStart; |
| } |
| |
| if (aAnnounceAttach || (GetRloc16() == Mac::kShortAddrInvalid)) |
| { |
| BecomeChild(kAttachAny); |
| } |
| else if (IsActiveRouter(GetRloc16())) |
| { |
| if (netif.GetMle().BecomeRouter(ThreadStatusTlv::kTooFewRouters) != OT_ERROR_NONE) |
| { |
| BecomeChild(kAttachAny); |
| } |
| } |
| else |
| { |
| mParentRequestState = kParentSynchronize; |
| mChildUpdateAttempts = 0; |
| SendChildUpdateRequest(); |
| } |
| |
| exit: |
| otLogFuncExitErr(error); |
| return error; |
| } |
| |
| otError Mle::Stop(bool aClearNetworkDatasets) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| otLogFuncEntry(); |
| netif.GetKeyManager().Stop(); |
| SetStateDetached(); |
| netif.RemoveUnicastAddress(mMeshLocal16); |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| netif.GetNetworkDataLocal().Clear(); |
| #endif |
| netif.GetNetworkDataLeader().Clear(); |
| memset(&mLeaderData, 0, sizeof(mLeaderData)); |
| |
| if (aClearNetworkDatasets) |
| { |
| netif.GetActiveDataset().HandleDetach(); |
| netif.GetPendingDataset().HandleDetach(); |
| } |
| |
| mRole = OT_DEVICE_ROLE_DISABLED; |
| otLogFuncExit(); |
| return OT_ERROR_NONE; |
| } |
| |
| otError Mle::Restore(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Settings::NetworkInfo networkInfo; |
| Settings::ParentInfo parentInfo; |
| uint16_t length; |
| |
| netif.GetActiveDataset().Restore(); |
| netif.GetPendingDataset().Restore(); |
| |
| length = sizeof(networkInfo); |
| SuccessOrExit(error = otPlatSettingsGet(netif.GetInstance(), Settings::kKeyNetworkInfo, 0, |
| reinterpret_cast<uint8_t *>(&networkInfo), &length)); |
| VerifyOrExit(length == sizeof(networkInfo), error = OT_ERROR_NOT_FOUND); |
| VerifyOrExit(networkInfo.mRole >= OT_DEVICE_ROLE_CHILD, error = OT_ERROR_NOT_FOUND); |
| |
| mDeviceMode = networkInfo.mDeviceMode; |
| SetRloc16(networkInfo.mRloc16); |
| netif.GetKeyManager().SetCurrentKeySequence(networkInfo.mKeySequence); |
| netif.GetKeyManager().SetMleFrameCounter(networkInfo.mMleFrameCounter); |
| netif.GetKeyManager().SetMacFrameCounter(networkInfo.mMacFrameCounter); |
| netif.GetMac().SetExtAddress(networkInfo.mExtAddress); |
| UpdateLinkLocalAddress(); |
| |
| memcpy(&mMeshLocal64.GetAddress().mFields.m8[OT_IP6_PREFIX_SIZE], |
| networkInfo.mMlIid, |
| OT_IP6_ADDRESS_SIZE - OT_IP6_PREFIX_SIZE); |
| |
| if (networkInfo.mRloc16 == Mac::kShortAddrInvalid) |
| { |
| ExitNow(); |
| } |
| |
| if (!IsActiveRouter(networkInfo.mRloc16)) |
| { |
| length = sizeof(parentInfo); |
| SuccessOrExit(error = otPlatSettingsGet(netif.GetInstance(), Settings::kKeyParentInfo, 0, |
| reinterpret_cast<uint8_t *>(&parentInfo), &length)); |
| VerifyOrExit(length >= sizeof(parentInfo), error = OT_ERROR_PARSE); |
| |
| memset(&mParent, 0, sizeof(mParent)); |
| mParent.SetExtAddress(*static_cast<Mac::ExtAddress *>(&parentInfo.mExtAddress)); |
| mParent.SetDeviceMode(ModeTlv::kModeFFD | ModeTlv::kModeRxOnWhenIdle | ModeTlv::kModeFullNetworkData | |
| ModeTlv::kModeSecureDataRequest); |
| mParent.SetRloc16(GetRloc16(GetRouterId(networkInfo.mRloc16))); |
| mParent.SetState(Neighbor::kStateRestored); |
| } |
| else |
| { |
| netif.GetMle().SetRouterId(GetRouterId(GetRloc16())); |
| netif.GetMle().SetPreviousPartitionId(networkInfo.mPreviousPartitionId); |
| |
| switch (netif.GetMle().RestoreChildren()) |
| { |
| // If there are more saved children in non-volatile settings |
| // than could be restored or the values in the settings are |
| // invalid, erase all the children info in the settings and |
| // refresh the info to ensure that the non-volatile settings |
| // stay in sync with the child table. |
| |
| case OT_ERROR_FAILED: |
| case OT_ERROR_NO_BUFS: |
| netif.GetMle().RefreshStoredChildren(); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError Mle::Store(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Settings::NetworkInfo networkInfo; |
| Settings::ParentInfo parentInfo; |
| |
| VerifyOrExit(IsAttached(), error = OT_ERROR_INVALID_STATE); |
| |
| if (netif.GetActiveDataset().GetTimestamp() == NULL) |
| { |
| netif.GetActiveDataset().GenerateLocal(); |
| } |
| |
| memset(&networkInfo, 0, sizeof(networkInfo)); |
| |
| networkInfo.mDeviceMode = mDeviceMode; |
| networkInfo.mRole = mRole; |
| networkInfo.mRloc16 = GetRloc16(); |
| networkInfo.mKeySequence = netif.GetKeyManager().GetCurrentKeySequence(); |
| networkInfo.mMleFrameCounter = netif.GetKeyManager().GetMleFrameCounter() + |
| OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD; |
| networkInfo.mMacFrameCounter = netif.GetKeyManager().GetMacFrameCounter() + |
| OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD; |
| networkInfo.mPreviousPartitionId = mLeaderData.GetPartitionId(); |
| memcpy(networkInfo.mExtAddress.m8, netif.GetMac().GetExtAddress(), sizeof(networkInfo.mExtAddress)); |
| memcpy(networkInfo.mMlIid, |
| &mMeshLocal64.GetAddress().mFields.m8[OT_IP6_PREFIX_SIZE], |
| OT_IP6_ADDRESS_SIZE - OT_IP6_PREFIX_SIZE); |
| |
| |
| if (mRole == OT_DEVICE_ROLE_CHILD) |
| { |
| memset(&parentInfo, 0, sizeof(parentInfo)); |
| memcpy(&parentInfo.mExtAddress, &mParent.GetExtAddress(), sizeof(parentInfo.mExtAddress)); |
| |
| SuccessOrExit(error = otPlatSettingsSet(netif.GetInstance(), Settings::kKeyParentInfo, |
| reinterpret_cast<uint8_t *>(&parentInfo), sizeof(parentInfo))); |
| } |
| |
| SuccessOrExit(error = otPlatSettingsSet(netif.GetInstance(), Settings::kKeyNetworkInfo, |
| reinterpret_cast<uint8_t *>(&networkInfo), sizeof(networkInfo))); |
| |
| netif.GetKeyManager().SetStoredMleFrameCounter(networkInfo.mMleFrameCounter); |
| netif.GetKeyManager().SetStoredMacFrameCounter(networkInfo.mMacFrameCounter); |
| |
| otLogDebgMle(GetInstance(), "Store Network Information"); |
| |
| exit: |
| return error; |
| } |
| |
| otError Mle::Discover(uint32_t aScanChannels, uint16_t aPanId, bool aJoiner, bool aEnableEui64Filtering, |
| DiscoverHandler aCallback, void *aContext) |
| { |
| otError error = OT_ERROR_NONE; |
| Message *message = NULL; |
| Ip6::Address destination; |
| Tlv tlv; |
| MeshCoP::DiscoveryRequestTlv discoveryRequest; |
| uint16_t startOffset; |
| |
| VerifyOrExit(!mIsDiscoverInProgress, error = OT_ERROR_BUSY); |
| |
| mEnableEui64Filtering = aEnableEui64Filtering; |
| |
| mDiscoverHandler = aCallback; |
| mDiscoverContext = aContext; |
| GetNetif().GetMeshForwarder().SetDiscoverParameters(aScanChannels); |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| 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); |
| discoveryRequest.SetJoiner(aJoiner); |
| SuccessOrExit(error = message->Append(&discoveryRequest, sizeof(discoveryRequest))); |
| |
| tlv.SetLength(static_cast<uint8_t>(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(GetInstance(), "Sent discovery request"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| bool Mle::IsDiscoverInProgress(void) |
| { |
| return mIsDiscoverInProgress; |
| } |
| |
| void Mle::HandleDiscoverComplete(void) |
| { |
| mIsDiscoverInProgress = false; |
| mEnableEui64Filtering = false; |
| mDiscoverHandler(NULL, mDiscoverContext); |
| } |
| |
| otError Mle::BecomeDetached(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| |
| otLogFuncEntry(); |
| |
| VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE); |
| |
| netif.GetPendingDataset().HandleDetach(); |
| |
| SetStateDetached(); |
| SetRloc16(Mac::kShortAddrInvalid); |
| BecomeChild(kAttachAny); |
| |
| exit: |
| otLogFuncExitErr(error); |
| return error; |
| } |
| |
| otError Mle::BecomeChild(AttachMode aMode) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| |
| otLogFuncEntry(); |
| |
| VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE); |
| VerifyOrExit(mParentRequestState == kParentIdle, error = OT_ERROR_BUSY); |
| |
| if (mReattachState == kReattachStart) |
| { |
| if (netif.GetActiveDataset().Restore() == OT_ERROR_NONE) |
| { |
| mReattachState = kReattachActive; |
| } |
| else |
| { |
| mReattachState = kReattachStop; |
| } |
| } |
| |
| ResetParentCandidate(); |
| mParentRequestState = kParentRequestStart; |
| mParentRequestMode = aMode; |
| |
| if (aMode != kAttachBetter) |
| { |
| memset(&mParent, 0, sizeof(mParent)); |
| |
| if (mDeviceMode & ModeTlv::kModeFFD) |
| { |
| netif.GetMle().StopAdvertiseTimer(); |
| } |
| } |
| |
| if (aMode == kAttachAny) |
| { |
| mParent.SetState(Neighbor::kStateInvalid); |
| mLastPartitionId = netif.GetMle().GetPreviousPartitionId(); |
| mLastPartitionRouterIdSequence = netif.GetMle().GetRouterIdSequence(); |
| } |
| |
| netif.GetMeshForwarder().SetRxOnWhenIdle(true); |
| |
| mParentRequestTimer.Start((otPlatRandomGet() % kParentRequestRouterTimeout) + 1); |
| |
| exit: |
| otLogFuncExitErr(error); |
| return error; |
| } |
| |
| bool Mle::IsAttached(void) const |
| { |
| return (mRole == OT_DEVICE_ROLE_CHILD || mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER); |
| } |
| |
| otError Mle::SetStateDetached(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| if (mRole != OT_DEVICE_ROLE_DETACHED) |
| { |
| netif.SetStateChangedFlags(OT_CHANGED_THREAD_ROLE); |
| } |
| |
| if (mRole == OT_DEVICE_ROLE_LEADER) |
| { |
| netif.RemoveUnicastAddress(mLeaderAloc); |
| } |
| |
| mRole = OT_DEVICE_ROLE_DETACHED; |
| mParentRequestState = kParentIdle; |
| mParentRequestTimer.Stop(); |
| netif.GetMeshForwarder().SetRxOff(); |
| netif.GetMac().SetBeaconEnabled(false); |
| netif.GetMle().HandleDetachStart(); |
| netif.GetIp6().SetForwardingEnabled(false); |
| netif.GetIp6().mMpl.SetTimerExpirations(0); |
| |
| otLogInfoMle(GetInstance(), "Mode -> Detached"); |
| return OT_ERROR_NONE; |
| } |
| |
| otError Mle::SetStateChild(uint16_t aRloc16) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| if (mRole != OT_DEVICE_ROLE_CHILD) |
| { |
| netif.SetStateChangedFlags(OT_CHANGED_THREAD_ROLE); |
| } |
| |
| if (mRole == OT_DEVICE_ROLE_LEADER) |
| { |
| netif.RemoveUnicastAddress(mLeaderAloc); |
| } |
| |
| SetRloc16(aRloc16); |
| mRole = OT_DEVICE_ROLE_CHILD; |
| mParentRequestState = kParentIdle; |
| mChildUpdateAttempts = 0; |
| netif.GetMac().SetBeaconEnabled(false); |
| |
| if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) != 0) |
| { |
| mParentRequestTimer.Start(Timer::SecToMsec(mTimeout) - |
| static_cast<uint32_t>(kUnicastRetransmissionDelay) * kMaxChildKeepAliveAttempts); |
| } |
| |
| if ((mDeviceMode & ModeTlv::kModeFFD) != 0) |
| { |
| netif.GetMle().HandleChildStart(mParentRequestMode); |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| netif.GetNetworkDataLocal().ClearResubmitDelayTimer(); |
| #endif |
| netif.GetIp6().SetForwardingEnabled(false); |
| netif.GetIp6().mMpl.SetTimerExpirations(kMplChildDataMessageTimerExpirations); |
| |
| // Once the Thread device receives the new Active Commissioning Dataset, the device MUST |
| // transmit its own Announce messages on the channel it was on prior to the attachment. |
| if (mPreviousPanId != Mac::kPanIdBroadcast) |
| { |
| mPreviousPanId = Mac::kPanIdBroadcast; |
| netif.GetAnnounceBeginServer().SendAnnounce(1 << mPreviousChannel); |
| } |
| |
| otLogInfoMle(GetInstance(), "Mode -> Child"); |
| return OT_ERROR_NONE; |
| } |
| |
| otError Mle::SetTimeout(uint32_t aTimeout) |
| { |
| VerifyOrExit(mTimeout != aTimeout); |
| |
| if (aTimeout < kMinTimeout) |
| { |
| aTimeout = kMinTimeout; |
| } |
| |
| mTimeout = aTimeout; |
| |
| GetNetif().GetMeshForwarder().GetDataPollManager().RecalculatePollPeriod(); |
| |
| if (mRole == OT_DEVICE_ROLE_CHILD) |
| { |
| SendChildUpdateRequest(); |
| } |
| |
| exit: |
| return OT_ERROR_NONE; |
| } |
| |
| otError Mle::SetDeviceMode(uint8_t aDeviceMode) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t oldMode = mDeviceMode; |
| |
| VerifyOrExit((aDeviceMode & ModeTlv::kModeFFD) == 0 || (aDeviceMode & ModeTlv::kModeRxOnWhenIdle) != 0, |
| error = OT_ERROR_INVALID_ARGS); |
| VerifyOrExit(mDeviceMode != aDeviceMode); |
| |
| mDeviceMode = aDeviceMode; |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| SetStateChild(GetRloc16()); |
| SendChildUpdateRequest(); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| if ((oldMode & ModeTlv::kModeFFD) != 0 && (aDeviceMode & ModeTlv::kModeFFD) == 0) |
| { |
| BecomeDetached(); |
| } |
| |
| break; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| const Ip6::Address &Mle::GetLinkLocalAddress(void) const |
| { |
| return mLinkLocal64.GetAddress(); |
| } |
| |
| otError Mle::UpdateLinkLocalAddress(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| netif.RemoveUnicastAddress(mLinkLocal64); |
| mLinkLocal64.GetAddress().SetIid(*netif.GetMac().GetExtAddress()); |
| netif.AddUnicastAddress(mLinkLocal64); |
| |
| netif.SetStateChangedFlags(OT_CHANGED_THREAD_LL_ADDR); |
| |
| return OT_ERROR_NONE; |
| } |
| |
| const uint8_t *Mle::GetMeshLocalPrefix(void) const |
| { |
| return mMeshLocal16.GetAddress().mFields.m8; |
| } |
| |
| otError Mle::SetMeshLocalPrefix(const uint8_t *aMeshLocalPrefix) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| if (memcmp(mMeshLocal64.GetAddress().mFields.m8, aMeshLocalPrefix, 8) == 0) |
| { |
| ExitNow(); |
| } |
| |
| // We must remove the old address before adding the new one. |
| netif.RemoveUnicastAddress(mMeshLocal64); |
| netif.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. |
| netif.AddUnicastAddress(mMeshLocal64); |
| |
| if (mRole >= OT_DEVICE_ROLE_CHILD) |
| { |
| netif.AddUnicastAddress(mMeshLocal16); |
| } |
| |
| // update Leader ALOC |
| if (mRole == OT_DEVICE_ROLE_LEADER) |
| { |
| netif.RemoveUnicastAddress(mLeaderAloc); |
| AddLeaderAloc(); |
| } |
| |
| // Changing the prefix also causes the mesh local address to be different. |
| netif.SetStateChangedFlags(OT_CHANGED_THREAD_ML_ADDR); |
| |
| exit: |
| return OT_ERROR_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 GetNetif().GetMac().GetShortAddress(); |
| } |
| |
| otError Mle::SetRloc16(uint16_t aRloc16) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| netif.RemoveUnicastAddress(mMeshLocal16); |
| |
| if (aRloc16 != Mac::kShortAddrInvalid) |
| { |
| // mesh-local 16 |
| mMeshLocal16.GetAddress().mFields.m16[7] = HostSwap16(aRloc16); |
| netif.AddUnicastAddress(mMeshLocal16); |
| } |
| |
| netif.GetMac().SetShortAddress(aRloc16); |
| netif.GetIp6().mMpl.SetSeedId(aRloc16); |
| |
| return OT_ERROR_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) |
| { |
| GetNetif().SetStateChangedFlags(OT_CHANGED_THREAD_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(); |
| } |
| |
| otError Mle::GetLeaderAddress(Ip6::Address &aAddress) const |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(GetRloc16() != Mac::kShortAddrInvalid, error = OT_ERROR_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; |
| } |
| |
| otError Mle::GetLeaderAloc(Ip6::Address &aAddress) const |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(GetRloc16() != Mac::kShortAddrInvalid, error = OT_ERROR_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; |
| } |
| |
| otError Mle::AddLeaderAloc(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(mRole == OT_DEVICE_ROLE_LEADER, error = OT_ERROR_INVALID_STATE); |
| |
| SuccessOrExit(error = GetLeaderAloc(mLeaderAloc.GetAddress())); |
| |
| error = GetNetif().AddUnicastAddress(mLeaderAloc); |
| |
| exit: |
| return error; |
| } |
| |
| const LeaderDataTlv &Mle::GetLeaderDataTlv(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| mLeaderData.SetDataVersion(netif.GetNetworkDataLeader().GetVersion()); |
| mLeaderData.SetStableDataVersion(netif.GetNetworkDataLeader().GetStableVersion()); |
| return mLeaderData; |
| } |
| |
| otError Mle::GetLeaderData(otLeaderData &aLeaderData) |
| { |
| const LeaderDataTlv &leaderData(GetLeaderDataTlv()); |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED && mRole != OT_DEVICE_ROLE_DETACHED, error = OT_ERROR_DETACHED); |
| |
| aLeaderData.mPartitionId = leaderData.GetPartitionId(); |
| aLeaderData.mWeighting = leaderData.GetWeighting(); |
| aLeaderData.mDataVersion = leaderData.GetDataVersion(); |
| aLeaderData.mStableDataVersion = leaderData.GetStableDataVersion(); |
| aLeaderData.mLeaderRouterId = leaderData.GetLeaderRouterId(); |
| |
| exit: |
| return error; |
| } |
| |
| otError Mle::GetAssignLinkQuality(const Mac::ExtAddress aMacAddr, uint8_t &aLinkQuality) |
| { |
| otError error; |
| |
| VerifyOrExit((memcmp(aMacAddr.m8, mAddr64.m8, OT_EXT_ADDRESS_SIZE)) == 0, error = OT_ERROR_INVALID_ARGS); |
| |
| aLinkQuality = mAssignLinkQuality; |
| |
| return OT_ERROR_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; |
| } |
| } |
| |
| 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; |
| } |
| |
| Message *Mle::NewMleMessage(void) |
| { |
| Message *message; |
| |
| message = mSocket.NewMessage(0); |
| VerifyOrExit(message != NULL); |
| |
| message->SetSubType(Message::kSubTypeMleGeneral); |
| message->SetLinkSecurityEnabled(false); |
| message->SetPriority(kMleMessagePriority); |
| |
| exit: |
| return message; |
| } |
| |
| otError Mle::AppendHeader(Message &aMessage, Header::Command aCommand) |
| { |
| otError error = OT_ERROR_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; |
| } |
| |
| otError Mle::AppendSourceAddress(Message &aMessage) |
| { |
| SourceAddressTlv tlv; |
| |
| tlv.Init(); |
| tlv.SetRloc16(GetRloc16()); |
| |
| return aMessage.Append(&tlv, sizeof(tlv)); |
| } |
| |
| otError Mle::AppendStatus(Message &aMessage, StatusTlv::Status aStatus) |
| { |
| StatusTlv tlv; |
| |
| tlv.Init(); |
| tlv.SetStatus(aStatus); |
| |
| return aMessage.Append(&tlv, sizeof(tlv)); |
| } |
| |
| otError Mle::AppendMode(Message &aMessage, uint8_t aMode) |
| { |
| ModeTlv tlv; |
| |
| tlv.Init(); |
| tlv.SetMode(aMode); |
| |
| return aMessage.Append(&tlv, sizeof(tlv)); |
| } |
| |
| otError Mle::AppendTimeout(Message &aMessage, uint32_t aTimeout) |
| { |
| TimeoutTlv tlv; |
| |
| tlv.Init(); |
| tlv.SetTimeout(aTimeout); |
| |
| return aMessage.Append(&tlv, sizeof(tlv)); |
| } |
| |
| otError Mle::AppendChallenge(Message &aMessage, const uint8_t *aChallenge, uint8_t aChallengeLength) |
| { |
| otError 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; |
| } |
| |
| otError Mle::AppendResponse(Message &aMessage, const uint8_t *aResponse, uint8_t aResponseLength) |
| { |
| otError 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; |
| } |
| |
| otError Mle::AppendLinkFrameCounter(Message &aMessage) |
| { |
| LinkFrameCounterTlv tlv; |
| |
| tlv.Init(); |
| tlv.SetFrameCounter(GetNetif().GetKeyManager().GetMacFrameCounter()); |
| |
| return aMessage.Append(&tlv, sizeof(tlv)); |
| } |
| |
| otError Mle::AppendMleFrameCounter(Message &aMessage) |
| { |
| MleFrameCounterTlv tlv; |
| |
| tlv.Init(); |
| tlv.SetFrameCounter(GetNetif().GetKeyManager().GetMleFrameCounter()); |
| |
| return aMessage.Append(&tlv, sizeof(tlv)); |
| } |
| |
| otError Mle::AppendAddress16(Message &aMessage, uint16_t aRloc16) |
| { |
| Address16Tlv tlv; |
| |
| tlv.Init(); |
| tlv.SetRloc16(aRloc16); |
| |
| return aMessage.Append(&tlv, sizeof(tlv)); |
| } |
| |
| otError Mle::AppendLeaderData(Message &aMessage) |
| { |
| mLeaderData.Init(); |
| mLeaderData.SetDataVersion(GetNetif().GetNetworkDataLeader().GetVersion()); |
| mLeaderData.SetStableDataVersion(GetNetif().GetNetworkDataLeader().GetStableVersion()); |
| |
| return aMessage.Append(&mLeaderData, sizeof(mLeaderData)); |
| } |
| |
| void Mle::FillNetworkDataTlv(NetworkDataTlv &aTlv, bool aStableOnly) |
| { |
| uint8_t length; |
| GetNetif().GetNetworkDataLeader().GetNetworkData(aStableOnly, aTlv.GetNetworkData(), length); |
| aTlv.SetLength(length); |
| } |
| |
| otError Mle::AppendNetworkData(Message &aMessage, bool aStableOnly) |
| { |
| otError error = OT_ERROR_NONE; |
| NetworkDataTlv tlv; |
| |
| tlv.Init(); |
| FillNetworkDataTlv(tlv, aStableOnly); |
| |
| SuccessOrExit(error = aMessage.Append(&tlv, sizeof(Tlv) + tlv.GetLength())); |
| |
| exit: |
| return error; |
| } |
| |
| otError Mle::AppendTlvRequest(Message &aMessage, const uint8_t *aTlvs, uint8_t aTlvsLength) |
| { |
| otError 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; |
| } |
| |
| otError Mle::AppendScanMask(Message &aMessage, uint8_t aScanMask) |
| { |
| ScanMaskTlv tlv; |
| |
| tlv.Init(); |
| tlv.SetMask(aScanMask); |
| |
| return aMessage.Append(&tlv, sizeof(tlv)); |
| } |
| |
| otError Mle::AppendLinkMargin(Message &aMessage, uint8_t aLinkMargin) |
| { |
| LinkMarginTlv tlv; |
| |
| tlv.Init(); |
| tlv.SetLinkMargin(aLinkMargin); |
| |
| return aMessage.Append(&tlv, sizeof(tlv)); |
| } |
| |
| otError Mle::AppendVersion(Message &aMessage) |
| { |
| VersionTlv tlv; |
| |
| tlv.Init(); |
| tlv.SetVersion(kVersion); |
| |
| return aMessage.Append(&tlv, sizeof(tlv)); |
| } |
| |
| otError Mle::AppendAddressRegistration(Message &aMessage) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError 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 = netif.GetUnicastAddresses(); addr; addr = addr->GetNext()) |
| { |
| if (addr->GetAddress().IsLinkLocal() || addr->GetAddress() == mMeshLocal16.GetAddress()) |
| { |
| continue; |
| } |
| |
| if (netif.GetNetworkDataLeader().GetContext(addr->GetAddress(), context) == OT_ERROR_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; |
| } |
| |
| otError Mle::AppendActiveTimestamp(Message &aMessage) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error; |
| ActiveTimestampTlv timestampTlv; |
| const MeshCoP::Timestamp *timestamp; |
| |
| timestamp = netif.GetActiveDataset().GetTimestamp(); |
| VerifyOrExit(timestamp, error = OT_ERROR_NONE); |
| |
| timestampTlv.Init(); |
| *static_cast<MeshCoP::Timestamp *>(×tampTlv) = *timestamp; |
| error = aMessage.Append(×tampTlv, sizeof(timestampTlv)); |
| |
| exit: |
| return error; |
| } |
| |
| otError Mle::AppendPendingTimestamp(Message &aMessage) |
| { |
| otError error; |
| PendingTimestampTlv timestampTlv; |
| const MeshCoP::Timestamp *timestamp; |
| |
| timestamp = GetNetif().GetPendingDataset().GetTimestamp(); |
| VerifyOrExit(timestamp && timestamp->GetSeconds() != 0, error = OT_ERROR_NONE); |
| |
| timestampTlv.Init(); |
| *static_cast<MeshCoP::Timestamp *>(×tampTlv) = *timestamp; |
| error = aMessage.Append(×tampTlv, sizeof(timestampTlv)); |
| |
| exit: |
| return error; |
| } |
| |
| void Mle::HandleNetifStateChanged(uint32_t aFlags, void *aContext) |
| { |
| static_cast<Mle *>(aContext)->HandleNetifStateChanged(aFlags); |
| } |
| |
| void Mle::HandleNetifStateChanged(uint32_t aFlags) |
| { |
| ThreadNetif &netif = GetNetif(); |
| VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED); |
| |
| if ((aFlags & (OT_CHANGED_IP6_ADDRESS_ADDED | OT_CHANGED_IP6_ADDRESS_REMOVED)) != 0) |
| { |
| if (!netif.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<uint8_t>(otPlatRandomGet()); |
| } |
| |
| netif.AddUnicastAddress(mMeshLocal64); |
| netif.SetStateChangedFlags(OT_CHANGED_THREAD_ML_ADDR); |
| } |
| |
| if (mRole == OT_DEVICE_ROLE_CHILD && (mDeviceMode & ModeTlv::kModeFFD) == 0) |
| { |
| mSendChildUpdateRequest.Post(); |
| } |
| } |
| |
| if ((aFlags & OT_CHANGED_THREAD_NETDATA) != 0) |
| { |
| if (mDeviceMode & ModeTlv::kModeFFD) |
| { |
| netif.GetMle().HandleNetworkDataUpdateRouter(); |
| } |
| else if ((aFlags & OT_CHANGED_THREAD_ROLE) == 0) |
| { |
| mSendChildUpdateRequest.Post(); |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| netif.GetNetworkDataLocal().SendServerDataNotification(); |
| #endif |
| } |
| |
| if (aFlags & (OT_CHANGED_THREAD_ROLE | OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER)) |
| { |
| Store(); |
| } |
| |
| exit: |
| return; |
| } |
| |
| void Mle::HandleParentRequestTimer(Timer &aTimer) |
| { |
| GetOwner(aTimer).HandleParentRequestTimer(); |
| } |
| |
| void Mle::HandleParentRequestTimer(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| switch (mParentRequestState) |
| { |
| case kParentIdle: |
| if (mParent.GetState() == Neighbor::kStateValid) |
| { |
| SendChildUpdateRequest(); |
| } |
| else |
| { |
| BecomeDetached(); |
| } |
| |
| break; |
| |
| case kParentSynchronize: |
| mParentRequestState = kParentIdle; |
| BecomeChild(kAttachAny); |
| break; |
| |
| case kParentRequestStart: |
| mParentRequestState = kParentRequestRouter; |
| mParentCandidate.SetState(Neighbor::kStateInvalid); |
| SendParentRequest(); |
| break; |
| |
| case kParentRequestRouter: |
| mParentRequestState = kParentRequestChild; |
| |
| if (mParentCandidate.GetState() == Neighbor::kStateValid) |
| { |
| SendChildIdRequest(); |
| mParentRequestState = kChildIdRequest; |
| mParentRequestTimer.Start(kParentRequestChildTimeout); |
| } |
| else |
| { |
| SendParentRequest(); |
| } |
| |
| break; |
| |
| case kParentRequestChild: |
| mParentRequestState = kParentRequestChild; |
| |
| if (mParentCandidate.GetState() == Neighbor::kStateValid) |
| { |
| SendChildIdRequest(); |
| mParentRequestState = kChildIdRequest; |
| mParentRequestTimer.Start(kParentRequestChildTimeout); |
| } |
| else |
| { |
| ResetParentCandidate(); |
| |
| if (mReattachState == kReattachActive) |
| { |
| if (netif.GetPendingDataset().Restore() == OT_ERROR_NONE) |
| { |
| netif.GetPendingDataset().ApplyConfiguration(); |
| mReattachState = kReattachPending; |
| mParentRequestState = kParentRequestStart; |
| mParentRequestTimer.Start(kParentRequestRouterTimeout); |
| } |
| else |
| { |
| mReattachState = kReattachStop; |
| } |
| } |
| else if (mReattachState == kReattachPending) |
| { |
| mReattachState = kReattachStop; |
| netif.GetActiveDataset().Restore(); |
| } |
| |
| if (mReattachState == kReattachStop) |
| { |
| switch (mParentRequestMode) |
| { |
| case kAttachAny: |
| if (mPreviousPanId != Mac::kPanIdBroadcast) |
| { |
| netif.GetMac().SetChannel(mPreviousChannel); |
| netif.GetMac().SetPanId(mPreviousPanId); |
| mPreviousPanId = Mac::kPanIdBroadcast; |
| BecomeDetached(); |
| } |
| else if ((mDeviceMode & ModeTlv::kModeFFD) == 0) |
| { |
| SendOrphanAnnounce(); |
| BecomeDetached(); |
| } |
| else if (netif.GetMle().BecomeLeader() != OT_ERROR_NONE) |
| { |
| mParentRequestState = kParentIdle; |
| BecomeDetached(); |
| } |
| |
| break; |
| |
| case kAttachSame1: |
| mParentRequestState = kParentIdle; |
| BecomeChild(kAttachSame2); |
| break; |
| |
| case kAttachSame2: |
| mParentRequestState = kParentIdle; |
| BecomeChild(kAttachAny); |
| break; |
| |
| case kAttachBetter: |
| mParentRequestState = kParentIdle; |
| |
| if (mRole == OT_DEVICE_ROLE_CHILD) |
| { |
| // Restart keep-alive timer as it was disturbed by attachment procedure. |
| mParentRequestTimer.Start(0); |
| } |
| |
| break; |
| } |
| } |
| } |
| |
| break; |
| |
| case kChildIdRequest: |
| mParentRequestState = kParentIdle; |
| ResetParentCandidate(); |
| |
| if ((mParentRequestMode == kAttachBetter) || |
| (mRole == OT_DEVICE_ROLE_ROUTER) || |
| (mRole == OT_DEVICE_ROLE_LEADER)) |
| { |
| if (mRole == OT_DEVICE_ROLE_CHILD) |
| { |
| // Restart keep-alive timer as it was disturbed by attachment procedure. |
| mParentRequestTimer.Start(0); |
| } |
| } |
| else |
| { |
| BecomeDetached(); |
| } |
| |
| break; |
| } |
| } |
| |
| void Mle::HandleDelayedResponseTimer(Timer &aTimer) |
| { |
| GetOwner(aTimer).HandleDelayedResponseTimer(); |
| } |
| |
| void Mle::HandleDelayedResponseTimer(void) |
| { |
| DelayedResponseHeader delayedResponse; |
| uint32_t now = otPlatAlarmGetNow(); |
| uint32_t nextDelay = 0xffffffff; |
| Message *message = mDelayedResponses.GetHead(); |
| Message *nextMessage = NULL; |
| |
| while (message != NULL) |
| { |
| nextMessage = message->GetNext(); |
| delayedResponse.ReadFrom(*message); |
| |
| if (delayedResponse.IsLater(now)) |
| { |
| // Calculate the next delay and choose the lowest. |
| if (delayedResponse.GetSendTime() - now < nextDelay) |
| { |
| nextDelay = delayedResponse.GetSendTime() - now; |
| } |
| } |
| else |
| { |
| mDelayedResponses.Dequeue(*message); |
| |
| // Remove the DelayedResponseHeader from the message. |
| DelayedResponseHeader::RemoveFrom(*message); |
| |
| // Send the message. |
| if (SendMessage(*message, delayedResponse.GetDestination()) == OT_ERROR_NONE) |
| { |
| otLogInfoMle(GetInstance(), "Sent delayed response"); |
| } |
| else |
| { |
| message->Free(); |
| } |
| } |
| |
| message = nextMessage; |
| } |
| |
| if (nextDelay != 0xffffffff) |
| { |
| mDelayedResponseTimer.Start(nextDelay); |
| } |
| } |
| |
| otError Mle::SendParentRequest(void) |
| { |
| otError error = OT_ERROR_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<uint8_t>(otPlatRandomGet()); |
| } |
| |
| switch (mParentRequestState) |
| { |
| case kParentRequestRouter: |
| scanMask = ScanMaskTlv::kRouterFlag; |
| |
| if (mParentRequestMode == kAttachSame1 || |
| mParentRequestMode == kAttachSame2) |
| { |
| scanMask |= ScanMaskTlv::kEndDeviceFlag; |
| } |
| |
| break; |
| |
| case kParentRequestChild: |
| scanMask = ScanMaskTlv::kRouterFlag | ScanMaskTlv::kEndDeviceFlag; |
| break; |
| |
| default: |
| assert(false); |
| break; |
| } |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandParentRequest)); |
| SuccessOrExit(error = AppendMode(*message, mDeviceMode)); |
| SuccessOrExit(error = AppendChallenge(*message, mParentRequest.mChallenge, sizeof(mParentRequest.mChallenge))); |
| 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)); |
| |
| if ((scanMask & ScanMaskTlv::kEndDeviceFlag) == 0) |
| { |
| otLogInfoMle(GetInstance(), "Sent parent request to routers"); |
| } |
| else |
| { |
| otLogInfoMle(GetInstance(), "Sent parent request to all devices"); |
| } |
| |
| exit: |
| |
| if ((scanMask & ScanMaskTlv::kEndDeviceFlag) == 0) |
| { |
| mParentRequestTimer.Start(kParentRequestRouterTimeout); |
| } |
| else |
| { |
| mParentRequestTimer.Start(kParentRequestChildTimeout); |
| } |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otError Mle::SendChildIdRequest(void) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t tlvs[] = {Tlv::kAddress16, Tlv::kNetworkData, Tlv::kRoute}; |
| Message *message; |
| Ip6::Address destination; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| 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)); |
| SuccessOrExit(error = AppendPendingTimestamp(*message)); |
| |
| memset(&destination, 0, sizeof(destination)); |
| destination.mFields.m16[0] = HostSwap16(0xfe80); |
| destination.SetIid(mParentCandidate.GetExtAddress()); |
| SuccessOrExit(error = SendMessage(*message, destination)); |
| otLogInfoMle(GetInstance(), "Sent Child ID Request"); |
| |
| if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) |
| { |
| GetNetif().GetMeshForwarder().GetDataPollManager().SetAttachMode(true); |
| GetNetif().GetMeshForwarder().SetRxOnWhenIdle(false); |
| } |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError Mle::SendDataRequest(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength, |
| uint16_t aDelay) |
| { |
| otError error = OT_ERROR_NONE; |
| Message *message; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandDataRequest)); |
| SuccessOrExit(error = AppendTlvRequest(*message, aTlvs, aTlvsLength)); |
| SuccessOrExit(error = AppendActiveTimestamp(*message)); |
| SuccessOrExit(error = AppendPendingTimestamp(*message)); |
| |
| if (aDelay) |
| { |
| SuccessOrExit(error = AddDelayedResponse(*message, aDestination, aDelay)); |
| } |
| else |
| { |
| SuccessOrExit(error = SendMessage(*message, aDestination)); |
| |
| if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) |
| { |
| GetNetif().GetMeshForwarder().GetDataPollManager().SendFastPolls(DataPollManager::kDefaultFastPolls); |
| } |
| } |
| |
| otLogInfoMle(GetInstance(), "Sent Data Request"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| void Mle::HandleSendChildUpdateRequest(Tasklet &aTasklet) |
| { |
| GetOwner(aTasklet).HandleSendChildUpdateRequest(); |
| } |
| |
| void Mle::HandleSendChildUpdateRequest(void) |
| { |
| // a Network Data update 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 (GetNetif().IsStateChangedCallbackPending()) |
| { |
| mSendChildUpdateRequest.Post(); |
| } |
| else |
| { |
| SendChildUpdateRequest(); |
| } |
| } |
| |
| otError Mle::SendChildUpdateRequest(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Ip6::Address destination; |
| Message *message = NULL; |
| |
| if (mChildUpdateAttempts >= kMaxChildKeepAliveAttempts) |
| { |
| mChildUpdateAttempts = 0; |
| BecomeDetached(); |
| ExitNow(); |
| } |
| |
| mParentRequestTimer.Start(kUnicastRetransmissionDelay); |
| mChildUpdateAttempts++; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| message->SetSubType(Message::kSubTypeMleChildUpdateRequest); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandChildUpdateRequest)); |
| SuccessOrExit(error = AppendMode(*message, mDeviceMode)); |
| |
| if ((mDeviceMode & ModeTlv::kModeFFD) == 0) |
| { |
| SuccessOrExit(error = AppendAddressRegistration(*message)); |
| } |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DETACHED: |
| for (uint8_t i = 0; i < sizeof(mParentRequest.mChallenge); i++) |
| { |
| mParentRequest.mChallenge[i] = static_cast<uint8_t>(otPlatRandomGet()); |
| } |
| |
| SuccessOrExit(error = AppendChallenge(*message, mParentRequest.mChallenge, |
| sizeof(mParentRequest.mChallenge))); |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| SuccessOrExit(error = AppendSourceAddress(*message)); |
| SuccessOrExit(error = AppendLeaderData(*message)); |
| SuccessOrExit(error = AppendTimeout(*message, mTimeout)); |
| break; |
| |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| assert(false); |
| break; |
| } |
| |
| memset(&destination, 0, sizeof(destination)); |
| destination.mFields.m16[0] = HostSwap16(0xfe80); |
| destination.SetIid(mParent.GetExtAddress()); |
| SuccessOrExit(error = SendMessage(*message, destination)); |
| |
| otLogInfoMle(GetInstance(), "Sent Child Update Request to parent"); |
| |
| if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) |
| { |
| netif.GetMeshForwarder().GetDataPollManager().SetAttachMode(true); |
| netif.GetMeshForwarder().SetRxOnWhenIdle(false); |
| } |
| else |
| { |
| netif.GetMeshForwarder().SetRxOnWhenIdle(true); |
| } |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError Mle::SendChildUpdateResponse(const uint8_t *aTlvs, uint8_t aNumTlvs, const ChallengeTlv &aChallenge) |
| { |
| otError error = OT_ERROR_NONE; |
| Ip6::Address destination; |
| Message *message; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandChildUpdateResponse)); |
| SuccessOrExit(error = AppendSourceAddress(*message)); |
| SuccessOrExit(error = AppendLeaderData(*message)); |
| |
| for (int i = 0; i < aNumTlvs; i++) |
| { |
| switch (aTlvs[i]) |
| { |
| case Tlv::kTimeout: |
| SuccessOrExit(error = AppendTimeout(*message, mTimeout)); |
| break; |
| |
| case Tlv::kAddressRegistration: |
| if ((mDeviceMode & ModeTlv::kModeFFD) == 0) |
| { |
| SuccessOrExit(error = AppendAddressRegistration(*message)); |
| } |
| |
| break; |
| |
| case Tlv::kResponse: |
| SuccessOrExit(error = AppendResponse(*message, aChallenge.GetChallenge(), aChallenge.GetLength())); |
| break; |
| |
| case Tlv::kLinkFrameCounter: |
| SuccessOrExit(error = AppendLinkFrameCounter(*message)); |
| break; |
| |
| case Tlv::kMleFrameCounter: |
| SuccessOrExit(error = AppendMleFrameCounter(*message)); |
| break; |
| } |
| } |
| |
| memset(&destination, 0, sizeof(destination)); |
| destination.mFields.m16[0] = HostSwap16(0xfe80); |
| destination.SetIid(mParent.GetExtAddress()); |
| SuccessOrExit(error = SendMessage(*message, destination)); |
| |
| otLogInfoMle(GetInstance(), "Sent Child Update Response to parent"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError Mle::SendAnnounce(uint8_t aChannel, bool aOrphanAnnounce) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| ChannelTlv channel; |
| PanIdTlv panid; |
| ActiveTimestampTlv activeTimestamp; |
| Ip6::Address destination; |
| Message *message; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| message->SetLinkSecurityEnabled(true); |
| message->SetSubType(Message::kSubTypeMleAnnounce); |
| message->SetChannel(aChannel); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandAnnounce)); |
| |
| channel.Init(); |
| channel.SetChannelPage(0); |
| channel.SetChannel(netif.GetMac().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)); |
| } |
| |
| panid.Init(); |
| panid.SetPanId(netif.GetMac().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(GetInstance(), "sent announce on channel %d", aChannel); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| void Mle::SendOrphanAnnounce(void) |
| { |
| const MeshCoP::ChannelMask0Tlv *channelMask; |
| uint8_t channel; |
| |
| channelMask = static_cast<const MeshCoP::ChannelMask0Tlv *>(GetNetif().GetActiveDataset().GetTlv( |
| 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 > OT_RADIO_CHANNEL_MAX) |
| { |
| channel = OT_RADIO_CHANNEL_MIN; |
| } |
| |
| VerifyOrExit(channel != mAnnounceChannel); |
| } |
| |
| // Send Announce message |
| SendAnnounce(channel, true); |
| |
| // Move to next channel |
| mAnnounceChannel = channel + 1; |
| |
| if (mAnnounceChannel > OT_RADIO_CHANNEL_MAX) |
| { |
| mAnnounceChannel = OT_RADIO_CHANNEL_MIN; |
| } |
| |
| exit: |
| return; |
| } |
| |
| otError Mle::SendMessage(Message &aMessage, const Ip6::Address &aDestination) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_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(netif.GetKeyManager().GetMleFrameCounter()); |
| |
| keySequence = netif.GetKeyManager().GetCurrentKeySequence(); |
| header.SetKeyId(keySequence); |
| |
| aMessage.Write(0, header.GetLength(), &header); |
| |
| GenerateNonce(*netif.GetMac().GetExtAddress(), |
| netif.GetKeyManager().GetMleFrameCounter(), |
| Mac::Frame::kSecEncMic32, |
| nonce); |
| |
| aesCcm.SetKey(netif.GetKeyManager().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)); |
| |
| netif.GetKeyManager().IncrementMleFrameCounter(); |
| } |
| |
| messageInfo.SetPeerAddr(aDestination); |
| messageInfo.SetSockAddr(mLinkLocal64.GetAddress()); |
| messageInfo.SetPeerPort(kUdpPort); |
| messageInfo.SetInterfaceId(netif.GetInterfaceId()); |
| messageInfo.SetHopLimit(255); |
| |
| SuccessOrExit(error = mSocket.SendTo(aMessage, messageInfo)); |
| |
| exit: |
| return error; |
| } |
| |
| otError Mle::AddDelayedResponse(Message &aMessage, const Ip6::Address &aDestination, uint16_t aDelay) |
| { |
| otError error = OT_ERROR_NONE; |
| uint32_t alarmFireTime; |
| uint32_t sendTime = otPlatAlarmGetNow() + aDelay; |
| |
| // Append the message with DelayedRespnoseHeader and add to the list. |
| DelayedResponseHeader delayedResponse(sendTime, aDestination); |
| SuccessOrExit(error = delayedResponse.AppendTo(aMessage)); |
| mDelayedResponses.Enqueue(aMessage); |
| |
| if (mDelayedResponseTimer.IsRunning()) |
| { |
| // If timer is already running, check if it should be restarted with earlier fire time. |
| alarmFireTime = mDelayedResponseTimer.GetFireTime(); |
| |
| if (delayedResponse.IsEarlier(alarmFireTime)) |
| { |
| mDelayedResponseTimer.Start(aDelay); |
| } |
| } |
| else |
| { |
| // Otherwise just set the timer. |
| mDelayedResponseTimer.Start(aDelay); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| void Mle::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) |
| { |
| static_cast<Mle *>(aContext)->HandleUdpReceive(*static_cast<Message *>(aMessage), |
| *static_cast<const Ip6::MessageInfo *>(aMessageInfo)); |
| } |
| |
| void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadNetif &netif = GetNetif(); |
| MleRouter &mle = netif.GetMle(); |
| 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: |
| mle.HandleDiscoveryRequest(aMessage, aMessageInfo); |
| break; |
| |
| case Header::kCommandDiscoveryResponse: |
| HandleDiscoveryResponse(aMessage, aMessageInfo); |
| break; |
| |
| default: |
| break; |
| } |
| |
| ExitNow(); |
| } |
| |
| VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED && header.GetSecuritySuite() == Header::k154Security); |
| |
| keySequence = header.GetKeyId(); |
| |
| if (keySequence == netif.GetKeyManager().GetCurrentKeySequence()) |
| { |
| mleKey = netif.GetKeyManager().GetCurrentMleKey(); |
| } |
| else |
| { |
| mleKey = netif.GetKeyManager().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 > netif.GetKeyManager().GetCurrentKeySequence()) |
| { |
| netif.GetKeyManager().SetCurrentKeySequence(keySequence); |
| } |
| |
| aMessage.SetOffset(mleOffset); |
| |
| aMessage.Read(aMessage.GetOffset(), sizeof(command), &command); |
| aMessage.MoveOffset(sizeof(command)); |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DETACHED: |
| case OT_DEVICE_ROLE_CHILD: |
| neighbor = GetNeighbor(macAddr); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| if (command == Header::kCommandChildIdResponse) |
| { |
| neighbor = GetNeighbor(macAddr); |
| } |
| else |
| { |
| neighbor = mle.GetNeighbor(macAddr); |
| } |
| |
| break; |
| |
| default: |
| neighbor = NULL; |
| break; |
| } |
| |
| if (neighbor != NULL && neighbor->GetState() == Neighbor::kStateValid) |
| { |
| if (keySequence == neighbor->GetKeySequence()) |
| { |
| if (frameCounter < neighbor->GetMleFrameCounter()) |
| { |
| otLogDebgMle(GetInstance(), "mle frame reject 1"); |
| ExitNow(); |
| } |
| } |
| else |
| { |
| if (keySequence <= neighbor->GetKeySequence()) |
| { |
| otLogDebgMle(GetInstance(), "mle frame reject 2"); |
| ExitNow(); |
| } |
| |
| neighbor->SetKeySequence(keySequence); |
| neighbor->SetLinkFrameCounter(0); |
| } |
| |
| neighbor->SetMleFrameCounter(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::kCommandChildUpdateResponse || |
| command == Header::kCommandAnnounce)) |
| { |
| otLogDebgMle(GetInstance(), "mle sequence unknown! %d", command); |
| ExitNow(); |
| } |
| } |
| |
| switch (command) |
| { |
| case Header::kCommandLinkRequest: |
| mle.HandleLinkRequest(aMessage, aMessageInfo); |
| break; |
| |
| case Header::kCommandLinkAccept: |
| mle.HandleLinkAccept(aMessage, aMessageInfo, keySequence); |
| break; |
| |
| case Header::kCommandLinkAcceptAndRequest: |
| mle.HandleLinkAcceptAndRequest(aMessage, aMessageInfo, keySequence); |
| break; |
| |
| case Header::kCommandAdvertisement: |
| HandleAdvertisement(aMessage, aMessageInfo); |
| break; |
| |
| case Header::kCommandDataRequest: |
| mle.HandleDataRequest(aMessage, aMessageInfo); |
| break; |
| |
| case Header::kCommandDataResponse: |
| HandleDataResponse(aMessage, aMessageInfo); |
| break; |
| |
| case Header::kCommandParentRequest: |
| mle.HandleParentRequest(aMessage, aMessageInfo); |
| break; |
| |
| case Header::kCommandParentResponse: |
| HandleParentResponse(aMessage, aMessageInfo, keySequence); |
| break; |
| |
| case Header::kCommandChildIdRequest: |
| mle.HandleChildIdRequest(aMessage, aMessageInfo, keySequence); |
| break; |
| |
| case Header::kCommandChildIdResponse: |
| HandleChildIdResponse(aMessage, aMessageInfo); |
| break; |
| |
| case Header::kCommandChildUpdateRequest: |
| if (mRole == OT_DEVICE_ROLE_LEADER || mRole == OT_DEVICE_ROLE_ROUTER) |
| { |
| mle.HandleChildUpdateRequest(aMessage, aMessageInfo, keySequence); |
| } |
| else |
| { |
| HandleChildUpdateRequest(aMessage, aMessageInfo); |
| } |
| |
| break; |
| |
| case Header::kCommandChildUpdateResponse: |
| if (mRole == OT_DEVICE_ROLE_LEADER || mRole == OT_DEVICE_ROLE_ROUTER) |
| { |
| mle.HandleChildUpdateResponse(aMessage, aMessageInfo, keySequence); |
| } |
| else |
| { |
| HandleChildUpdateResponse(aMessage, aMessageInfo); |
| } |
| |
| break; |
| |
| case Header::kCommandAnnounce: |
| HandleAnnounce(aMessage, aMessageInfo); |
| break; |
| } |
| |
| exit: |
| return; |
| } |
| |
| otError Mle::HandleAdvertisement(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Mac::ExtAddress macAddr; |
| bool isNeighbor; |
| Neighbor *neighbor; |
| SourceAddressTlv sourceAddress; |
| LeaderDataTlv leaderData; |
| RouteTlv route; |
| uint8_t tlvs[] = {Tlv::kNetworkData}; |
| uint16_t delay; |
| |
| // Source Address |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); |
| VerifyOrExit(sourceAddress.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Leader Data |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); |
| VerifyOrExit(leaderData.IsValid(), error = OT_ERROR_PARSE); |
| |
| otLogInfoMle(GetInstance(), "Received advertisement from %04x", sourceAddress.GetRloc16()); |
| |
| if (mRole != OT_DEVICE_ROLE_DETACHED) |
| { |
| SuccessOrExit(error = netif.GetMle().HandleAdvertisement(aMessage, aMessageInfo)); |
| } |
| |
| macAddr.Set(aMessageInfo.GetPeerAddr()); |
| |
| isNeighbor = false; |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| if (memcmp(&mParent.GetExtAddress(), &macAddr, sizeof(macAddr))) |
| { |
| break; |
| } |
| |
| if ((mParent.GetRloc16() == sourceAddress.GetRloc16()) && |
| (leaderData.GetPartitionId() != mLeaderData.GetPartitionId() || |
| leaderData.GetLeaderRouterId() != GetLeaderId())) |
| { |
| SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId()); |
| |
| if ((mDeviceMode & ModeTlv::kModeFFD) && |
| (Tlv::GetTlv(aMessage, Tlv::kRoute, sizeof(route), route) == OT_ERROR_NONE) && |
| route.IsValid()) |
| { |
| // Overwrite Route Data |
| netif.GetMle().ProcessRouteTlv(route); |
| } |
| |
| mRetrieveNewNetworkData = true; |
| } |
| |
| isNeighbor = true; |
| mParent.SetLastHeard(Timer::GetNow()); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| if ((neighbor = netif.GetMle().GetNeighbor(macAddr)) != NULL && |
| neighbor->GetState() == Neighbor::kStateValid) |
| { |
| isNeighbor = true; |
| } |
| |
| break; |
| } |
| |
| if (isNeighbor) |
| { |
| if (mRetrieveNewNetworkData || |
| (static_cast<int8_t>(leaderData.GetDataVersion() - netif.GetNetworkDataLeader().GetVersion()) > 0)) |
| { |
| delay = otPlatRandomGet() % kMleMaxResponseDelay; |
| SendDataRequest(aMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs), delay); |
| } |
| } |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE) |
| { |
| otLogWarnMleErr(GetInstance(), error, "Failed to process Advertisement"); |
| } |
| |
| return error; |
| } |
| |
| otError Mle::HandleDataResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| otError error; |
| |
| otLogInfoMle(GetInstance(), "Received Data Response"); |
| |
| error = HandleLeaderData(aMessage, aMessageInfo); |
| |
| if (error != OT_ERROR_NONE) |
| { |
| otLogWarnMleErr(GetInstance(), error, "Failed to process Data Response"); |
| } |
| |
| return error; |
| } |
| |
| otError Mle::HandleLeaderData(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| LeaderDataTlv leaderData; |
| NetworkDataTlv networkData; |
| ActiveTimestampTlv activeTimestamp; |
| PendingTimestampTlv pendingTimestamp; |
| uint16_t activeDatasetOffset = 0; |
| uint16_t pendingDatasetOffset = 0; |
| bool dataRequest = false; |
| Tlv tlv; |
| uint16_t delay; |
| |
| // Leader Data |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); |
| VerifyOrExit(leaderData.IsValid(), error = OT_ERROR_PARSE); |
| |
| if ((leaderData.GetPartitionId() != mLeaderData.GetPartitionId()) || |
| (leaderData.GetWeighting() != mLeaderData.GetWeighting()) || |
| (leaderData.GetLeaderRouterId() != GetLeaderId())) |
| { |
| if (mRole == OT_DEVICE_ROLE_CHILD) |
| { |
| SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId()); |
| mRetrieveNewNetworkData = true; |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_DROP); |
| } |
| } |
| else if (!mRetrieveNewNetworkData) |
| { |
| int8_t diff; |
| |
| if (mDeviceMode & ModeTlv::kModeFullNetworkData) |
| { |
| diff = static_cast<int8_t>(leaderData.GetDataVersion() - netif.GetNetworkDataLeader().GetVersion()); |
| } |
| else |
| { |
| diff = static_cast<int8_t>(leaderData.GetStableDataVersion() - |
| netif.GetNetworkDataLeader().GetStableVersion()); |
| } |
| |
| VerifyOrExit(diff > 0); |
| } |
| |
| // Active Timestamp |
| if (Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(activeTimestamp), activeTimestamp) == OT_ERROR_NONE) |
| { |
| const MeshCoP::Timestamp *timestamp; |
| |
| VerifyOrExit(activeTimestamp.IsValid(), error = OT_ERROR_PARSE); |
| timestamp = netif.GetActiveDataset().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) != OT_ERROR_NONE)) |
| { |
| ExitNow(dataRequest = true); |
| } |
| else |
| { |
| dataRequest = true; |
| } |
| } |
| else |
| { |
| activeTimestamp.SetLength(0); |
| } |
| |
| // Pending Timestamp |
| if (Tlv::GetTlv(aMessage, Tlv::kPendingTimestamp, sizeof(pendingTimestamp), pendingTimestamp) == OT_ERROR_NONE) |
| { |
| const MeshCoP::Timestamp *timestamp; |
| |
| VerifyOrExit(pendingTimestamp.IsValid(), error = OT_ERROR_PARSE); |
| timestamp = netif.GetPendingDataset().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) != OT_ERROR_NONE)) |
| { |
| ExitNow(dataRequest = true); |
| } |
| else |
| { |
| dataRequest = true; |
| } |
| } |
| else |
| { |
| pendingTimestamp.SetLength(0); |
| } |
| |
| if (Tlv::GetTlv(aMessage, Tlv::kNetworkData, sizeof(networkData), networkData) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(networkData.IsValid(), error = OT_ERROR_PARSE); |
| |
| netif.GetNetworkDataLeader().SetNetworkData(leaderData.GetDataVersion(), leaderData.GetStableDataVersion(), |
| (mDeviceMode & ModeTlv::kModeFullNetworkData) == 0, |
| networkData.GetNetworkData(), networkData.GetLength()); |
| } |
| else |
| { |
| ExitNow(dataRequest = true); |
| } |
| |
| // Active Dataset |
| if (activeTimestamp.GetLength() > 0) |
| { |
| if (activeDatasetOffset > 0) |
| { |
| aMessage.Read(activeDatasetOffset, sizeof(tlv), &tlv); |
| netif.GetActiveDataset().Set(activeTimestamp, aMessage, activeDatasetOffset + sizeof(tlv), |
| tlv.GetLength()); |
| } |
| } |
| |
| // Pending Dataset |
| if (pendingTimestamp.GetLength() > 0) |
| { |
| if (pendingDatasetOffset > 0) |
| { |
| aMessage.Read(pendingDatasetOffset, sizeof(tlv), &tlv); |
| netif.GetPendingDataset().Set(pendingTimestamp, aMessage, pendingDatasetOffset + sizeof(tlv), |
| tlv.GetLength()); |
| } |
| } |
| |
| mRetrieveNewNetworkData = false; |
| |
| exit: |
| |
| OT_UNUSED_VARIABLE(aMessageInfo); |
| |
| if (dataRequest) |
| { |
| static const uint8_t tlvs[] = {Tlv::kNetworkData}; |
| |
| delay = aMessageInfo.GetSockAddr().IsMulticast() ? (otPlatRandomGet() % kMleMaxResponseDelay) : 0; |
| |
| SendDataRequest(aMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs), delay); |
| } |
| |
| return error; |
| } |
| |
| bool Mle::IsBetterParent(uint16_t aRloc16, uint8_t aLinkQuality, ConnectivityTlv &aConnectivityTlv) |
| { |
| bool rval = false; |
| |
| uint8_t candidateLinkQualityIn = mParentCandidate.GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor()); |
| uint8_t candidateTwoWayLinkQuality = (candidateLinkQualityIn < mParentCandidate.GetLinkQualityOut()) |
| ? candidateLinkQualityIn : mParentCandidate.GetLinkQualityOut(); |
| |
| if (aLinkQuality != candidateTwoWayLinkQuality) |
| { |
| ExitNow(rval = (aLinkQuality > candidateTwoWayLinkQuality)); |
| } |
| |
| if (IsActiveRouter(aRloc16) != IsActiveRouter(mParentCandidate.GetRloc16())) |
| { |
| 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; |
| } |
| |
| void Mle::ResetParentCandidate(void) |
| { |
| memset(&mParentCandidate, 0, sizeof(mParentCandidate)); |
| mParentCandidate.SetState(Neighbor::kStateInvalid); |
| } |
| |
| otError Mle::HandleParentResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, |
| uint32_t aKeySequence) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| const ThreadMessageInfo *threadMessageInfo = static_cast<const ThreadMessageInfo *>(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(GetInstance(), "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 = OT_ERROR_PARSE); |
| |
| // Source Address |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); |
| VerifyOrExit(sourceAddress.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Leader Data |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); |
| VerifyOrExit(leaderData.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Link Quality |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLinkMargin, sizeof(linkMarginTlv), linkMarginTlv)); |
| VerifyOrExit(linkMarginTlv.IsValid(), error = OT_ERROR_PARSE); |
| |
| linkMargin = LinkQualityInfo::ConvertRssToLinkMargin(netif.GetMac().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 = OT_ERROR_PARSE); |
| |
| if ((mDeviceMode & ModeTlv::kModeFFD) && (mRole != OT_DEVICE_ROLE_DETACHED)) |
| { |
| diff = static_cast<int8_t>(connectivity.GetIdSequence() - netif.GetMle().GetRouterIdSequence()); |
| |
| switch (mParentRequestMode) |
| { |
| case kAttachAny: |
| VerifyOrExit(leaderData.GetPartitionId() != mLeaderData.GetPartitionId() || diff > 0); |
| break; |
| |
| case kAttachSame1: |
| case kAttachSame2: |
| VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId()); |
| VerifyOrExit(diff > 0 || |
| (diff == 0 && netif.GetMle().GetLeaderAge() < netif.GetMle().GetNetworkIdTimeout())); |
| break; |
| |
| case kAttachBetter: |
| VerifyOrExit(leaderData.GetPartitionId() != mLeaderData.GetPartitionId()); |
| VerifyOrExit(netif.GetMle().ComparePartitions(connectivity.GetActiveRouters() <= 1, leaderData, |
| netif.GetMle().IsSingleton(), mLeaderData) > 0); |
| break; |
| } |
| } |
| |
| // if already have a candidate parent, only seek a better parent |
| if (mParentCandidate.GetState() == Neighbor::kStateValid) |
| { |
| int compare = 0; |
| |
| if (mDeviceMode & ModeTlv::kModeFFD) |
| { |
| compare = netif.GetMle().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 = OT_ERROR_PARSE); |
| |
| // Mle Frame Counter |
| if (Tlv::GetTlv(aMessage, Tlv::kMleFrameCounter, sizeof(mleFrameCounter), mleFrameCounter) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(mleFrameCounter.IsValid()); |
| } |
| else |
| { |
| mleFrameCounter.SetFrameCounter(linkFrameCounter.GetFrameCounter()); |
| } |
| |
| // Challenge |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kChallenge, sizeof(challenge), challenge)); |
| VerifyOrExit(challenge.IsValid(), error = OT_ERROR_PARSE); |
| memcpy(mChildIdRequest.mChallenge, challenge.GetChallenge(), challenge.GetLength()); |
| mChildIdRequest.mChallengeLength = challenge.GetLength(); |
| |
| mParentCandidate.GetExtAddress().Set(aMessageInfo.GetPeerAddr()); |
| mParentCandidate.SetRloc16(sourceAddress.GetRloc16()); |
| mParentCandidate.SetLinkFrameCounter(linkFrameCounter.GetFrameCounter()); |
| mParentCandidate.SetMleFrameCounter(mleFrameCounter.GetFrameCounter()); |
| mParentCandidate.SetDeviceMode(ModeTlv::kModeFFD | ModeTlv::kModeRxOnWhenIdle | ModeTlv::kModeFullNetworkData | |
| ModeTlv::kModeSecureDataRequest); |
| mParentCandidate.GetLinkInfo().Clear(); |
| mParentCandidate.GetLinkInfo().AddRss(netif.GetMac().GetNoiseFloor(), threadMessageInfo->mRss); |
| mParentCandidate.ResetLinkFailures(); |
| mParentCandidate.SetLinkQualityOut(LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMarginTlv.GetLinkMargin())); |
| mParentCandidate.SetState(Neighbor::kStateValid); |
| mParentCandidate.SetKeySequence(aKeySequence); |
| |
| mParentPriority = connectivity.GetParentPriority(); |
| mParentLinkQuality3 = connectivity.GetLinkQuality3(); |
| mParentLinkQuality2 = connectivity.GetLinkQuality2(); |
| mParentLinkQuality1 = connectivity.GetLinkQuality1(); |
| mParentLeaderCost = connectivity.GetLeaderCost(); |
| mParentLeaderData = leaderData; |
| mParentIsSingleton = connectivity.GetActiveRouters() <= 1; |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE) |
| { |
| otLogWarnMleErr(GetInstance(), error, "Failed to process Parent Response"); |
| } |
| |
| return error; |
| } |
| |
| otError Mle::HandleChildIdResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| LeaderDataTlv leaderData; |
| SourceAddressTlv sourceAddress; |
| Address16Tlv shortAddress; |
| NetworkDataTlv networkData; |
| RouteTlv route; |
| ActiveTimestampTlv activeTimestamp; |
| PendingTimestampTlv pendingTimestamp; |
| Tlv tlv; |
| uint16_t offset; |
| |
| otLogInfoMle(GetInstance(), "Received Child ID Response"); |
| |
| VerifyOrExit(mParentRequestState == kChildIdRequest); |
| |
| // Leader Data |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); |
| VerifyOrExit(leaderData.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Source Address |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); |
| VerifyOrExit(sourceAddress.IsValid(), error = OT_ERROR_PARSE); |
| |
| // ShortAddress |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kAddress16, sizeof(shortAddress), shortAddress)); |
| VerifyOrExit(shortAddress.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Network Data |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kNetworkData, sizeof(networkData), networkData)); |
| |
| // Active Timestamp |
| if (Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(activeTimestamp), activeTimestamp) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(activeTimestamp.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Active Dataset |
| if (Tlv::GetOffset(aMessage, Tlv::kActiveDataset, offset) == OT_ERROR_NONE) |
| { |
| aMessage.Read(offset, sizeof(tlv), &tlv); |
| netif.GetActiveDataset().Set(activeTimestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()); |
| } |
| } |
| |
| // clear Pending Dataset if device succeed to reattach using stored Pending Dataset |
| if (mReattachState == kReattachPending) |
| { |
| netif.GetPendingDataset().Clear(); |
| } |
| |
| // Pending Timestamp |
| if (Tlv::GetTlv(aMessage, Tlv::kPendingTimestamp, sizeof(pendingTimestamp), pendingTimestamp) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(pendingTimestamp.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Pending Dataset |
| if (Tlv::GetOffset(aMessage, Tlv::kPendingDataset, offset) == OT_ERROR_NONE) |
| { |
| aMessage.Read(offset, sizeof(tlv), &tlv); |
| netif.GetPendingDataset().Set(pendingTimestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()); |
| } |
| } |
| else |
| { |
| netif.GetPendingDataset().ClearNetwork(); |
| } |
| |
| // Parent Attach Success |
| mParentRequestTimer.Stop(); |
| mReattachState = kReattachStop; |
| SetStateDetached(); |
| |
| SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId()); |
| |
| if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) |
| { |
| netif.GetMeshForwarder().GetDataPollManager().SetAttachMode(false); |
| netif.GetMeshForwarder().SetRxOnWhenIdle(false); |
| } |
| else |
| { |
| netif.GetMeshForwarder().SetRxOnWhenIdle(true); |
| } |
| |
| // Route |
| if ((Tlv::GetTlv(aMessage, Tlv::kRoute, sizeof(route), route) == OT_ERROR_NONE) && |
| (mDeviceMode & ModeTlv::kModeFFD)) |
| { |
| SuccessOrExit(error = netif.GetMle().ProcessRouteTlv(route)); |
| } |
| |
| mParent = mParentCandidate; |
| ResetParentCandidate(); |
| |
| mParent.SetRloc16(sourceAddress.GetRloc16()); |
| |
| netif.GetNetworkDataLeader().SetNetworkData(leaderData.GetDataVersion(), leaderData.GetStableDataVersion(), |
| (mDeviceMode & ModeTlv::kModeFullNetworkData) == 0, |
| networkData.GetNetworkData(), networkData.GetLength()); |
| |
| netif.GetActiveDataset().ApplyConfiguration(); |
| |
| SuccessOrExit(error = SetStateChild(shortAddress.GetRloc16())); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE) |
| { |
| otLogWarnMleErr(GetInstance(), error, "Failed to process Child ID Response"); |
| } |
| |
| OT_UNUSED_VARIABLE(aMessageInfo); |
| return error; |
| } |
| |
| otError Mle::HandleChildUpdateRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| static const uint8_t kMaxResponseTlvs = 5; |
| |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| SourceAddressTlv sourceAddress; |
| LeaderDataTlv leaderData; |
| NetworkDataTlv networkData; |
| ChallengeTlv challenge; |
| TlvRequestTlv tlvRequest; |
| uint8_t dataRequestTlvs[] = {Tlv::kNetworkData}; |
| uint8_t tlvs[kMaxResponseTlvs] = {}; |
| uint8_t numTlvs = 0; |
| |
| otLogInfoMle(GetInstance(), "Received Child Update Request from parent"); |
| |
| // Source Address |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); |
| VerifyOrExit(sourceAddress.IsValid(), error = OT_ERROR_PARSE); |
| VerifyOrExit(mParent.GetRloc16() == sourceAddress.GetRloc16(), error = OT_ERROR_DROP); |
| |
| // Leader Data |
| if (Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(leaderData.IsValid(), error = OT_ERROR_PARSE); |
| SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId()); |
| |
| if ((mDeviceMode & ModeTlv::kModeFullNetworkData && |
| leaderData.GetDataVersion() != netif.GetNetworkDataLeader().GetVersion()) || |
| ((mDeviceMode & ModeTlv::kModeFullNetworkData) == 0 && |
| leaderData.GetStableDataVersion() != netif.GetNetworkDataLeader().GetStableVersion())) |
| { |
| mRetrieveNewNetworkData = true; |
| } |
| |
| // Network Data |
| if (Tlv::GetTlv(aMessage, Tlv::kNetworkData, sizeof(networkData), networkData) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(networkData.IsValid(), error = OT_ERROR_PARSE); |
| netif.GetNetworkDataLeader().SetNetworkData(leaderData.GetDataVersion(), |
| leaderData.GetStableDataVersion(), |
| (mDeviceMode & ModeTlv::kModeFullNetworkData) == 0, |
| networkData.GetNetworkData(), |
| networkData.GetLength()); |
| } |
| } |
| |
| // TLV Request |
| if (Tlv::GetTlv(aMessage, Tlv::kTlvRequest, sizeof(tlvRequest), tlvRequest) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(tlvRequest.IsValid() && tlvRequest.GetLength() <= sizeof(tlvs), error = OT_ERROR_PARSE); |
| memcpy(tlvs, tlvRequest.GetTlvs(), tlvRequest.GetLength()); |
| numTlvs += tlvRequest.GetLength(); |
| } |
| |
| // Challenge |
| if (Tlv::GetTlv(aMessage, Tlv::kChallenge, sizeof(challenge), challenge) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(challenge.IsValid(), error = OT_ERROR_PARSE); |
| VerifyOrExit(static_cast<size_t>(numTlvs + 3) <= sizeof(tlvs), error = OT_ERROR_NO_BUFS); |
| tlvs[numTlvs++] = Tlv::kResponse; |
| tlvs[numTlvs++] = Tlv::kMleFrameCounter; |
| tlvs[numTlvs++] = Tlv::kLinkFrameCounter; |
| } |
| |
| SuccessOrExit(error = SendChildUpdateResponse(tlvs, numTlvs, challenge)); |
| |
| if (mRetrieveNewNetworkData) |
| { |
| SendDataRequest(aMessageInfo.GetPeerAddr(), dataRequestTlvs, sizeof(dataRequestTlvs), 0); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError Mle::HandleChildUpdateResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| StatusTlv status; |
| ModeTlv mode; |
| ResponseTlv response; |
| LinkFrameCounterTlv linkFrameCounter; |
| MleFrameCounterTlv mleFrameCounter; |
| SourceAddressTlv sourceAddress; |
| TimeoutTlv timeout; |
| |
| otLogInfoMle(GetInstance(), "Received Child Update Response from parent"); |
| |
| // Status |
| if (Tlv::GetTlv(aMessage, Tlv::kStatus, sizeof(status), status) == OT_ERROR_NONE) |
| { |
| BecomeDetached(); |
| ExitNow(); |
| } |
| |
| // Mode |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kMode, sizeof(mode), mode)); |
| VerifyOrExit(mode.IsValid(), error = OT_ERROR_PARSE); |
| VerifyOrExit(mode.GetMode() == mDeviceMode, error = OT_ERROR_DROP); |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DETACHED: |
| // Response |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kResponse, sizeof(response), response)); |
| VerifyOrExit(response.IsValid(), error = OT_ERROR_PARSE); |
| VerifyOrExit(memcmp(response.GetResponse(), mParentRequest.mChallenge, |
| sizeof(mParentRequest.mChallenge)) == 0, |
| error = OT_ERROR_DROP); |
| |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLinkFrameCounter, sizeof(linkFrameCounter), |
| linkFrameCounter)); |
| VerifyOrExit(linkFrameCounter.IsValid(), error = OT_ERROR_PARSE); |
| |
| if (Tlv::GetTlv(aMessage, Tlv::kMleFrameCounter, sizeof(mleFrameCounter), mleFrameCounter) == |
| OT_ERROR_NONE) |
| { |
| VerifyOrExit(mleFrameCounter.IsValid(), error = OT_ERROR_PARSE); |
| } |
| else |
| { |
| mleFrameCounter.SetFrameCounter(linkFrameCounter.GetFrameCounter()); |
| } |
| |
| mParent.SetLinkFrameCounter(linkFrameCounter.GetFrameCounter()); |
| mParent.SetMleFrameCounter(mleFrameCounter.GetFrameCounter()); |
| |
| mParent.SetState(Neighbor::kStateValid); |
| SetStateChild(GetRloc16()); |
| |
| // fall through |
| |
| case OT_DEVICE_ROLE_CHILD: |
| // Source Address |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); |
| VerifyOrExit(sourceAddress.IsValid(), error = OT_ERROR_PARSE); |
| |
| if (GetRouterId(sourceAddress.GetRloc16()) != GetRouterId(GetRloc16())) |
| { |
| BecomeDetached(); |
| ExitNow(); |
| } |
| |
| // Leader Data, Network Data, Active Timestamp, Pending Timestamp |
| SuccessOrExit(error = HandleLeaderData(aMessage, aMessageInfo)); |
| |
| // Timeout optional |
| if (Tlv::GetTlv(aMessage, Tlv::kTimeout, sizeof(timeout), timeout) == OT_ERROR_NONE) |
| { |
| |