blob: 3d8428f411a4c8a34b0e2cc4925e5a342ce4d781 [file] [log] [blame]
/*
* 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 *>(&timestampTlv) = *timestamp;
error = aMessage.Append(&timestampTlv, 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 *>(&timestampTlv) = *timestamp;
error = aMessage.Append(&timestampTlv, 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)
{
VerifyOrExit(timeout.IsValid(), error = OT_ERROR_PARSE);
mTimeout = timeout.GetTimeout();
}
if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0)
{
netif.GetMeshForwarder().GetDataPollManager().SetAttachMode(false);
netif.GetMeshForwarder().SetRxOnWhenIdle(false);
mParentRequestTimer.Stop();
}
else
{
mParentRequestTimer.Start(Timer::SecToMsec(mTimeout) -
static_cast<uint32_t>(kUnicastRetransmissionDelay) * kMaxChildKeepAliveAttempts);
netif.GetMeshForwarder().SetRxOnWhenIdle(true);
}
mChildUpdateAttempts = 0;
break;
default:
assert(false);
break;
}
exit:
if (error != OT_ERROR_NONE)
{
otLogWarnMleErr(GetInstance(), error, "Failed to process Child Update Response");
}
return error;
}
otError Mle::HandleAnnounce(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NONE;
ChannelTlv channel;
ActiveTimestampTlv timestamp;
const MeshCoP::Timestamp *localTimestamp;
PanIdTlv panid;
otLogInfoMle(GetInstance(), "Received announce");
SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kChannel, sizeof(channel), channel));
VerifyOrExit(channel.IsValid(), error = OT_ERROR_PARSE);
SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(timestamp), timestamp));
VerifyOrExit(timestamp.IsValid(), error = OT_ERROR_PARSE);
SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kPanId, sizeof(panid), panid));
VerifyOrExit(panid.IsValid(), error = OT_ERROR_PARSE);
localTimestamp = netif.GetActiveDataset().GetTimestamp();
if (localTimestamp == NULL || localTimestamp->Compare(timestamp) > 0)
{
Stop(false);
mPreviousChannel = netif.GetMac().GetChannel();
mPreviousPanId = netif.GetMac().GetPanId();
netif.GetMac().SetChannel(static_cast<uint8_t>(channel.GetChannel()));
netif.GetMac().SetPanId(panid.GetPanId());
Start(false, true);
}
else if (localTimestamp->Compare(timestamp) < 0)
{
SendAnnounce(static_cast<uint8_t>(channel.GetChannel()), false);
}
else
{
// do nothing
// timestamps are equal: no behaviour specified by the Thread spec.
// If SendAnnounce is executed at this point, there exists a scenario where
// multiple devices keep sending MLE Announce messages to one another indefinitely.
}
exit:
OT_UNUSED_VARIABLE(aMessageInfo);
return error;
}
otError Mle::HandleDiscoveryResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
otError error = OT_ERROR_NONE;
const ThreadMessageInfo *threadMessageInfo = static_cast<const ThreadMessageInfo *>(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(GetInstance(), "Handle discovery response");
VerifyOrExit(mIsDiscoverInProgress, error = OT_ERROR_DROP);
// find MLE Discovery TLV
VerifyOrExit(Tlv::GetOffset(aMessage, Tlv::kDiscovery, offset) == OT_ERROR_NONE, error = OT_ERROR_PARSE);
aMessage.Read(offset, sizeof(tlv), &tlv);
offset += sizeof(tlv);
end = offset + 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<Mac::ExtAddress *>(&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 = OT_ERROR_PARSE);
result.mVersion = discoveryResponse.GetVersion();
result.mIsNative = discoveryResponse.IsNativeCommissioner();
break;
case MeshCoP::Tlv::kExtendedPanId:
aMessage.Read(offset, sizeof(extPanId), &extPanId);
VerifyOrExit(extPanId.IsValid(), error = OT_ERROR_PARSE);
memcpy(&result.mExtendedPanId, extPanId.GetExtendedPanId(), sizeof(result.mExtendedPanId));
break;
case MeshCoP::Tlv::kNetworkName:
aMessage.Read(offset, sizeof(networkName), &networkName);
VerifyOrExit(networkName.IsValid(), error = OT_ERROR_PARSE);
memcpy(&result.mNetworkName, networkName.GetNetworkName(), networkName.GetLength());
break;
case MeshCoP::Tlv::kSteeringData:
aMessage.Read(offset, sizeof(steeringData), &steeringData);
VerifyOrExit(steeringData.IsValid(), error = OT_ERROR_PARSE);
// Pass up MLE discovery responses only if the steering data is set to all 0xFFs,
// or if it matches the factory set EUI64.
if (mEnableEui64Filtering)
{
otExtAddress mfgEUI64;
Crc16 ccitt(Crc16::kCcitt);
Crc16 ansi(Crc16::kAnsi);
// Get Factory set EUI64
otPlatRadioGetIeeeEui64(GetInstance(), mfgEUI64.m8);
// Compute bloom filter
for (size_t i = 0; i < sizeof(mfgEUI64.m8); i++)
{
ccitt.Update(mfgEUI64.m8[i]);
ansi.Update(mfgEUI64.m8[i]);
}
// Drop responses that don't match the bloom filter
if (!steeringData.GetBit(ccitt.Get() % steeringData.GetNumBits()) ||
!steeringData.GetBit(ansi.Get() % steeringData.GetNumBits()))
{
ExitNow(error = OT_ERROR_NONE);
}
}
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 = OT_ERROR_PARSE);
result.mJoinerUdpPort = JoinerUdpPort.GetUdpPort();
break;
default:
break;
}
offset += sizeof(meshcopTlv) + meshcopTlv.GetLength();
}
// signal callback
mDiscoverHandler(&result, mDiscoverContext);
exit:
if (error != OT_ERROR_NONE)
{
otLogWarnMleErr(GetInstance(), error, "Failed to process Discovery Response");
}
return error;
}
Neighbor *Mle::GetNeighbor(uint16_t aAddress)
{
if ((mParent.IsStateValidOrRestoring()) && (mParent.GetRloc16() == aAddress))
{
return &mParent;
}
if ((mParentCandidate.GetState() == Neighbor::kStateValid) && (mParentCandidate.GetRloc16() == aAddress))
{
return &mParentCandidate;
}
return NULL;
}
Neighbor *Mle::GetNeighbor(const Mac::ExtAddress &aAddress)
{
if ((mParent.IsStateValidOrRestoring()) &&
(memcmp(&mParent.GetExtAddress(), &aAddress, sizeof(aAddress)) == 0))
{
return &mParent;
}
if ((mParentCandidate.GetState() == Neighbor::kStateValid) &&
(memcmp(&mParentCandidate.GetExtAddress(), &aAddress, sizeof(aAddress)) == 0))
{
return &mParentCandidate;
}
return 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;
}
uint16_t Mle::GetNextHop(uint16_t aDestination) const
{
OT_UNUSED_VARIABLE(aDestination);
return (mParent.GetState() == Neighbor::kStateValid) ? mParent.GetRloc16() : static_cast<uint16_t>
(Mac::kShortAddrInvalid);
}
bool Mle::IsRoutingLocator(const Ip6::Address &aAddress) const
{
return memcmp(&mMeshLocal16, &aAddress, kRlocPrefixLength) == 0 &&
aAddress.mFields.m8[14] < Ip6::Address::kAloc16Mask &&
(aAddress.mFields.m8[14] & Ip6::Address::kRloc16ReservedBitMask) == 0;
}
bool Mle::IsAnycastLocator(const Ip6::Address &aAddress) const
{
return memcmp(&mMeshLocal16, &aAddress, kRlocPrefixLength) == 0 &&
aAddress.mFields.m8[14] == Ip6::Address::kAloc16Mask;
}
bool Mle::IsMeshLocalAddress(const Ip6::Address &aAddress) const
{
return aAddress.PrefixMatch(GetMeshLocal16()) >= Ip6::Address::kMeshLocalPrefixLength;
}
Router *Mle::GetParent(void)
{
if ((!mParent.IsStateValidOrRestoring()) && (mParentCandidate.GetState() == Neighbor::kStateValid))
{
return &mParentCandidate;
}
return &mParent;
}
otError Mle::CheckReachability(uint16_t aMeshSource, uint16_t aMeshDest, Ip6::Header &aIp6Header)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_DROP;
Ip6::MessageInfo messageInfo;
if (aMeshDest != GetRloc16())
{
ExitNow(error = OT_ERROR_NONE);
}
if (netif.IsUnicastAddress(aIp6Header.GetDestination()))
{
ExitNow(error = OT_ERROR_NONE);
}
messageInfo.GetPeerAddr() = GetMeshLocal16();
messageInfo.GetPeerAddr().mFields.m16[7] = HostSwap16(aMeshSource);
messageInfo.SetInterfaceId(netif.GetInterfaceId());
netif.GetIp6().mIcmp.SendError(Ip6::IcmpHeader::kTypeDstUnreach,
Ip6::IcmpHeader::kCodeDstUnreachNoRoute,
messageInfo, aIp6Header);
exit:
return error;
}
Mle &Mle::GetOwner(const Context &aContext)
{
#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
Mle &mle = *static_cast<Mle *>(aContext.GetContext());
#else
Mle &mle = otGetInstance()->mThreadNetif.GetMle();
OT_UNUSED_VARIABLE(aContext);
#endif
return mle;
}
} // namespace Mle
} // namespace ot