| /* |
| * 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 Router and Leader roles. |
| */ |
| |
| #if OPENTHREAD_FTD |
| |
| #define WPP_NAME "mle_router.tmh" |
| |
| #include <openthread/config.h> |
| |
| #include "mle_router.hpp" |
| |
| #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 "mac/mac_frame.hpp" |
| #include "net/icmp6.hpp" |
| #include "thread/thread_netif.hpp" |
| #include "thread/thread_tlvs.hpp" |
| #include "thread/thread_uri_paths.hpp" |
| |
| using ot::Encoding::BigEndian::HostSwap16; |
| |
| namespace ot { |
| namespace Mle { |
| |
| MleRouter::MleRouter(ThreadNetif &aThreadNetif): |
| Mle(aThreadNetif), |
| mAdvertiseTimer(aThreadNetif.GetIp6().mTimerScheduler, &MleRouter::HandleAdvertiseTimer, NULL, this), |
| mStateUpdateTimer(aThreadNetif.GetIp6().mTimerScheduler, &MleRouter::HandleStateUpdateTimer, this), |
| mAddressSolicit(OT_URI_PATH_ADDRESS_SOLICIT, &MleRouter::HandleAddressSolicit, this), |
| mAddressRelease(OT_URI_PATH_ADDRESS_RELEASE, &MleRouter::HandleAddressRelease, this), |
| mRouterIdSequence(0), |
| mRouterIdSequenceLastUpdated(0), |
| mMaxChildrenAllowed(kMaxChildren), |
| mChallengeTimeout(0), |
| mNextChildId(kMaxChildId), |
| mNetworkIdTimeout(kNetworkIdTimeout), |
| mRouterUpgradeThreshold(kRouterUpgradeThreshold), |
| mRouterDowngradeThreshold(kRouterDowngradeThreshold), |
| mLeaderWeight(kLeaderWeight), |
| mFixedLeaderPartitionId(0), |
| mRouterRoleEnabled(true), |
| mIsRouterRestoringChildren(false), |
| mPreviousPartitionId(0), |
| mRouterSelectionJitter(kRouterSelectionJitter), |
| mRouterSelectionJitterTimeout(0), |
| mParentPriority(kParentPriorityUnspecified) |
| { |
| mDeviceMode |= ModeTlv::kModeFFD | ModeTlv::kModeFullNetworkData; |
| |
| memset(mChildren, 0, sizeof(mChildren)); |
| memset(mRouters, 0, sizeof(mRouters)); |
| |
| SetRouterId(kInvalidRouterId); |
| } |
| |
| bool MleRouter::IsRouterRoleEnabled(void) const |
| { |
| return mRouterRoleEnabled && (mDeviceMode & ModeTlv::kModeFFD); |
| } |
| |
| void MleRouter::SetRouterRoleEnabled(bool aEnabled) |
| { |
| mRouterRoleEnabled = aEnabled; |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| GetNetif().GetMac().SetBeaconEnabled(mRouterRoleEnabled); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| if (!mRouterRoleEnabled) |
| { |
| BecomeDetached(); |
| } |
| |
| break; |
| } |
| } |
| |
| uint8_t MleRouter::AllocateRouterId(void) |
| { |
| uint8_t rval = kInvalidRouterId; |
| |
| // count available router ids |
| uint8_t numAvailable = 0; |
| uint8_t numAllocated = 0; |
| |
| for (int i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].IsAllocated()) |
| { |
| numAllocated++; |
| } |
| else if (mRouters[i].IsReclaimDelay() == false) |
| { |
| numAvailable++; |
| } |
| } |
| |
| VerifyOrExit(numAllocated < kMaxRouters && numAvailable > 0, rval = kInvalidRouterId); |
| |
| // choose available router id at random |
| uint8_t freeBit; |
| freeBit = otPlatRandomGet() % numAvailable; |
| |
| // allocate router id |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].IsAllocated() || mRouters[i].IsReclaimDelay()) |
| { |
| continue; |
| } |
| |
| if (freeBit == 0) |
| { |
| rval = AllocateRouterId(i); |
| ExitNow(); |
| } |
| |
| freeBit--; |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| uint8_t MleRouter::AllocateRouterId(uint8_t aRouterId) |
| { |
| uint8_t rval = kInvalidRouterId; |
| Router *router; |
| |
| router = GetRouter(aRouterId); |
| assert(router != NULL); |
| |
| VerifyOrExit(!router->IsAllocated(), rval = kInvalidRouterId); |
| |
| // init router state |
| router->SetAllocated(true); |
| router->SetLastHeard(Timer::GetNow()); |
| router->ClearExtAddress(); |
| |
| // bump sequence number |
| mRouterIdSequence++; |
| mRouterIdSequenceLastUpdated = Timer::GetNow(); |
| rval = aRouterId; |
| |
| otLogInfoMle(GetInstance(), "add router id %d", aRouterId); |
| |
| exit: |
| return rval; |
| } |
| |
| otError MleRouter::ReleaseRouterId(uint8_t aRouterId) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Router *router = GetRouter(aRouterId); |
| |
| VerifyOrExit(router != NULL, error = OT_ERROR_INVALID_ARGS); |
| VerifyOrExit(mRole == OT_DEVICE_ROLE_LEADER, error = OT_ERROR_INVALID_STATE); |
| |
| otLogInfoMle(GetInstance(), "delete router id %d", aRouterId); |
| router->SetAllocated(false); |
| router->SetReclaimDelay(true); |
| router->SetState(Neighbor::kStateInvalid); |
| router->SetNextHop(kInvalidRouterId); |
| |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].GetNextHop() == aRouterId) |
| { |
| mRouters[i].SetNextHop(kInvalidRouterId); |
| mRouters[i].SetCost(0); |
| } |
| } |
| |
| mRouterIdSequence++; |
| mRouterIdSequenceLastUpdated = Timer::GetNow(); |
| netif.GetAddressResolver().Remove(aRouterId); |
| netif.GetNetworkDataLeader().RemoveBorderRouter(GetRloc16(aRouterId)); |
| ResetAdvertiseInterval(); |
| |
| exit: |
| return error; |
| } |
| |
| uint32_t MleRouter::GetLeaderAge(void) const |
| { |
| return Timer::MsecToSec(Timer::GetNow() - mRouterIdSequenceLastUpdated); |
| } |
| |
| otError MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE); |
| VerifyOrExit(mRole != OT_DEVICE_ROLE_ROUTER, error = OT_ERROR_NONE); |
| VerifyOrExit(IsRouterRoleEnabled(), error = OT_ERROR_NOT_CAPABLE); |
| |
| for (int i = 0; i <= kMaxRouterId; i++) |
| { |
| mRouters[i].SetAllocated(false); |
| mRouters[i].SetReclaimDelay(false); |
| mRouters[i].SetState(Neighbor::kStateInvalid); |
| mRouters[i].SetNextHop(kInvalidRouterId); |
| } |
| |
| mAdvertiseTimer.Stop(); |
| netif.GetAddressResolver().Clear(); |
| netif.GetMeshForwarder().SetRxOnWhenIdle(true); |
| mRouterSelectionJitterTimeout = 0; |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DETACHED: |
| SuccessOrExit(error = SendLinkRequest(NULL)); |
| mStateUpdateTimer.Start(kStateUpdatePeriod); |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| SuccessOrExit(error = SendAddressSolicit(aStatus)); |
| break; |
| |
| default: |
| assert(false); |
| break; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::BecomeLeader(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| uint8_t routerId; |
| Router *router; |
| |
| VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE); |
| VerifyOrExit(mRole != OT_DEVICE_ROLE_LEADER, error = OT_ERROR_NONE); |
| VerifyOrExit(IsRouterRoleEnabled(), error = OT_ERROR_NOT_CAPABLE); |
| |
| for (int i = 0; i <= kMaxRouterId; i++) |
| { |
| mRouters[i].SetAllocated(false); |
| mRouters[i].SetReclaimDelay(false); |
| mRouters[i].SetState(Neighbor::kStateInvalid); |
| mRouters[i].SetNextHop(kInvalidRouterId); |
| } |
| |
| routerId = IsRouterIdValid(mPreviousRouterId) ? AllocateRouterId(mPreviousRouterId) : AllocateRouterId(); |
| router = GetRouter(routerId); |
| VerifyOrExit(router != NULL, error = OT_ERROR_NO_BUFS); |
| |
| SetRouterId(routerId); |
| |
| router->SetExtAddress(*netif.GetMac().GetExtAddress()); |
| mAdvertiseTimer.Stop(); |
| netif.GetAddressResolver().Clear(); |
| |
| if (mFixedLeaderPartitionId != 0) |
| { |
| SetLeaderData(mFixedLeaderPartitionId, mLeaderWeight, mRouterId); |
| } |
| else |
| { |
| SetLeaderData(otPlatRandomGet(), mLeaderWeight, mRouterId); |
| } |
| |
| mRouterIdSequence = static_cast<uint8_t>(otPlatRandomGet()); |
| |
| netif.GetNetworkDataLeader().Reset(); |
| netif.GetLeader().SetEmptyCommissionerData(); |
| |
| SuccessOrExit(error = SetStateLeader(GetRloc16(mRouterId))); |
| |
| exit: |
| return error; |
| } |
| |
| void MleRouter::StopLeader(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| netif.GetCoap().RemoveResource(mAddressSolicit); |
| netif.GetCoap().RemoveResource(mAddressRelease); |
| netif.GetActiveDataset().StopLeader(); |
| netif.GetPendingDataset().StopLeader(); |
| mAdvertiseTimer.Stop(); |
| netif.GetNetworkDataLeader().Stop(); |
| #if !OPENTHREAD_CONFIG_NEST_STAY_SUBSCRIBED_TO_ALL_ROUTERS_MULTICAST_ADDR |
| netif.UnsubscribeAllRoutersMulticast(); |
| #endif |
| } |
| |
| otError MleRouter::HandleDetachStart(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| GetNetif().GetAddressResolver().Clear(); |
| |
| for (int i = 0; i <= kMaxRouterId; i++) |
| { |
| mRouters[i].SetState(Neighbor::kStateInvalid); |
| } |
| |
| StopLeader(); |
| mStateUpdateTimer.Stop(); |
| |
| return error; |
| } |
| |
| otError MleRouter::HandleChildStart(AttachMode aMode) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| mRouterIdSequenceLastUpdated = Timer::GetNow(); |
| mRouterSelectionJitterTimeout = (otPlatRandomGet() % mRouterSelectionJitter) + 1; |
| |
| StopLeader(); |
| mStateUpdateTimer.Start(kStateUpdatePeriod); |
| |
| if (mRouterRoleEnabled) |
| { |
| netif.GetMac().SetBeaconEnabled(true); |
| } |
| |
| netif.SubscribeAllRoutersMulticast(); |
| |
| VerifyOrExit(IsRouterIdValid(mPreviousRouterId), error = OT_ERROR_INVALID_STATE); |
| |
| switch (aMode) |
| { |
| case kAttachSame1: |
| case kAttachSame2: |
| |
| // downgrade |
| if (GetActiveRouterCount() > mRouterDowngradeThreshold) |
| { |
| SendAddressRelease(); |
| |
| // reset children info if any |
| if (HasChildren()) |
| { |
| RemoveChildren(); |
| } |
| |
| // reset routerId info |
| SetRouterId(kInvalidRouterId); |
| } |
| else if (HasChildren()) |
| { |
| BecomeRouter(ThreadStatusTlv::kHaveChildIdRequest); |
| } |
| |
| break; |
| |
| case kAttachAny: |
| case kAttachBetter: |
| if (HasChildren() && |
| mPreviousPartitionId != mLeaderData.GetPartitionId()) |
| { |
| BecomeRouter(ThreadStatusTlv::kParentPartitionChange); |
| } |
| |
| break; |
| } |
| |
| exit: |
| |
| if (GetActiveRouterCount() >= mRouterUpgradeThreshold && |
| (!IsRouterIdValid(mPreviousRouterId) || !HasChildren())) |
| { |
| SetRouterId(kInvalidRouterId); |
| } |
| |
| return error; |
| } |
| |
| otError MleRouter::SetStateRouter(uint16_t aRloc16) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| if (mRole != OT_DEVICE_ROLE_ROUTER) |
| { |
| netif.SetStateChangedFlags(OT_CHANGED_THREAD_ROLE); |
| } |
| |
| SetRloc16(aRloc16); |
| mRole = OT_DEVICE_ROLE_ROUTER; |
| mParentRequestState = kParentIdle; |
| mParentRequestTimer.Stop(); |
| ResetAdvertiseInterval(); |
| |
| netif.SubscribeAllRoutersMulticast(); |
| mRouters[mRouterId].SetNextHop(mRouterId); |
| mPreviousPartitionId = mLeaderData.GetPartitionId(); |
| netif.GetNetworkDataLeader().Stop(); |
| mStateUpdateTimer.Start(kStateUpdatePeriod); |
| netif.GetIp6().SetForwardingEnabled(true); |
| netif.GetIp6().mMpl.SetTimerExpirations(kMplRouterDataMessageTimerExpirations); |
| netif.GetMac().SetBeaconEnabled(true); |
| |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].GetState() == Neighbor::kStateRestored) |
| { |
| mIsRouterRestoringChildren = true; |
| break; |
| } |
| } |
| |
| otLogInfoMle(GetInstance(), "Mode -> Router"); |
| return OT_ERROR_NONE; |
| } |
| |
| otError MleRouter::SetStateLeader(uint16_t aRloc16) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| if (mRole != OT_DEVICE_ROLE_LEADER) |
| { |
| netif.SetStateChangedFlags(OT_CHANGED_THREAD_ROLE); |
| } |
| |
| SetRloc16(aRloc16); |
| mRole = OT_DEVICE_ROLE_LEADER; |
| mParentRequestState = kParentIdle; |
| mParentRequestTimer.Stop(); |
| ResetAdvertiseInterval(); |
| AddLeaderAloc(); |
| |
| netif.SubscribeAllRoutersMulticast(); |
| mRouters[mRouterId].SetNextHop(mRouterId); |
| mPreviousPartitionId = mLeaderData.GetPartitionId(); |
| mStateUpdateTimer.Start(kStateUpdatePeriod); |
| mRouters[mRouterId].SetLastHeard(Timer::GetNow()); |
| |
| netif.GetNetworkDataLeader().Start(); |
| netif.GetActiveDataset().StartLeader(); |
| netif.GetPendingDataset().StartLeader(); |
| netif.GetCoap().AddResource(mAddressSolicit); |
| netif.GetCoap().AddResource(mAddressRelease); |
| netif.GetIp6().SetForwardingEnabled(true); |
| netif.GetIp6().mMpl.SetTimerExpirations(kMplRouterDataMessageTimerExpirations); |
| netif.GetMac().SetBeaconEnabled(true); |
| |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].GetState() == Neighbor::kStateRestored) |
| { |
| mIsRouterRestoringChildren = true; |
| break; |
| } |
| } |
| |
| otLogInfoMle(GetInstance(), "Mode -> Leader %d", mLeaderData.GetPartitionId()); |
| return OT_ERROR_NONE; |
| } |
| |
| bool MleRouter::HandleAdvertiseTimer(TrickleTimer &aTimer) |
| { |
| return GetOwner(aTimer).HandleAdvertiseTimer(); |
| } |
| |
| bool MleRouter::HandleAdvertiseTimer(void) |
| { |
| if ((mDeviceMode & ModeTlv::kModeFFD) == 0) |
| { |
| return false; |
| } |
| |
| SendAdvertisement(); |
| |
| return true; |
| } |
| |
| void MleRouter::StopAdvertiseTimer(void) |
| { |
| mAdvertiseTimer.Stop(); |
| } |
| |
| void MleRouter::ResetAdvertiseInterval(void) |
| { |
| VerifyOrExit(mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER); |
| |
| if (!mAdvertiseTimer.IsRunning()) |
| { |
| mAdvertiseTimer.Start( |
| Timer::SecToMsec(kAdvertiseIntervalMin), |
| Timer::SecToMsec(kAdvertiseIntervalMax), |
| TrickleTimer::kModeNormal); |
| } |
| |
| mAdvertiseTimer.IndicateInconsistent(); |
| |
| exit: |
| return; |
| } |
| |
| otError MleRouter::SendAdvertisement(void) |
| { |
| otError error = OT_ERROR_NONE; |
| Ip6::Address destination; |
| Message *message; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandAdvertisement)); |
| SuccessOrExit(error = AppendSourceAddress(*message)); |
| SuccessOrExit(error = AppendLeaderData(*message)); |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| assert(false); |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| SuccessOrExit(error = AppendRoute(*message)); |
| break; |
| } |
| |
| 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 advertisement"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError MleRouter::SendLinkRequest(Neighbor *aNeighbor) |
| { |
| static const uint8_t detachedTlvs[] = {Tlv::kAddress16, Tlv::kRoute}; |
| static const uint8_t routerTlvs[] = {Tlv::kLinkMargin}; |
| otError error = OT_ERROR_NONE; |
| Message *message; |
| Ip6::Address destination; |
| |
| memset(&destination, 0, sizeof(destination)); |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandLinkRequest)); |
| SuccessOrExit(error = AppendVersion(*message)); |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| assert(false); |
| break; |
| |
| case OT_DEVICE_ROLE_DETACHED: |
| SuccessOrExit(error = AppendTlvRequest(*message, detachedTlvs, sizeof(detachedTlvs))); |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| SuccessOrExit(error = AppendSourceAddress(*message)); |
| SuccessOrExit(error = AppendLeaderData(*message)); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| SuccessOrExit(error = AppendTlvRequest(*message, routerTlvs, sizeof(routerTlvs))); |
| SuccessOrExit(error = AppendSourceAddress(*message)); |
| SuccessOrExit(error = AppendLeaderData(*message)); |
| break; |
| } |
| |
| if (aNeighbor == NULL) |
| { |
| for (uint8_t i = 0; i < sizeof(mChallenge); i++) |
| { |
| mChallenge[i] = static_cast<uint8_t>(otPlatRandomGet()); |
| } |
| |
| mChallengeTimeout = (((2 * kMaxResponseDelay) + kStateUpdatePeriod - 1) / kStateUpdatePeriod); |
| |
| SuccessOrExit(error = AppendChallenge(*message, mChallenge, sizeof(mChallenge))); |
| destination.mFields.m8[0] = 0xff; |
| destination.mFields.m8[1] = 0x02; |
| destination.mFields.m8[15] = 2; |
| } |
| else |
| { |
| aNeighbor->GenerateChallenge(); |
| |
| SuccessOrExit(error = AppendChallenge(*message, aNeighbor->GetChallenge(), aNeighbor->GetChallengeSize())); |
| destination.mFields.m16[0] = HostSwap16(0xfe80); |
| destination.SetIid(aNeighbor->GetExtAddress()); |
| } |
| |
| SuccessOrExit(error = SendMessage(*message, destination)); |
| |
| otLogInfoMle(GetInstance(), "Sent link request"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError MleRouter::HandleLinkRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| Neighbor *neighbor = NULL; |
| Mac::ExtAddress macAddr; |
| ChallengeTlv challenge; |
| VersionTlv version; |
| LeaderDataTlv leaderData; |
| SourceAddressTlv sourceAddress; |
| TlvRequestTlv tlvRequest; |
| uint16_t rloc16; |
| |
| otLogInfoMle(GetInstance(), "Received link request"); |
| |
| VerifyOrExit(mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER, error = OT_ERROR_INVALID_STATE); |
| |
| VerifyOrExit(mParentRequestState == kParentIdle, error = OT_ERROR_INVALID_STATE); |
| |
| macAddr.Set(aMessageInfo.GetPeerAddr()); |
| |
| // Challenge |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kChallenge, sizeof(challenge), challenge)); |
| VerifyOrExit(challenge.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Version |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kVersion, sizeof(version), version)); |
| VerifyOrExit(version.IsValid() && version.GetVersion() >= kVersion, error = OT_ERROR_PARSE); |
| |
| // Leader Data |
| if (Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(leaderData.IsValid(), error = OT_ERROR_PARSE); |
| VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId(), error = OT_ERROR_INVALID_STATE); |
| } |
| |
| // Source Address |
| if (Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(sourceAddress.IsValid(), error = OT_ERROR_PARSE); |
| |
| rloc16 = sourceAddress.GetRloc16(); |
| |
| if ((neighbor = GetNeighbor(macAddr)) != NULL && neighbor->GetRloc16() != rloc16) |
| { |
| // remove stale neighbors |
| RemoveNeighbor(*neighbor); |
| neighbor = NULL; |
| } |
| |
| if (IsActiveRouter(rloc16)) |
| { |
| // source is a router |
| neighbor = GetRouter(GetRouterId(rloc16)); |
| VerifyOrExit(neighbor != NULL, error = OT_ERROR_PARSE); |
| |
| if (neighbor->GetState() != Neighbor::kStateValid) |
| { |
| const ThreadMessageInfo *threadMessageInfo = |
| static_cast<const ThreadMessageInfo *>(aMessageInfo.GetLinkInfo()); |
| |
| neighbor->SetExtAddress(macAddr); |
| neighbor->GetLinkInfo().Clear(); |
| neighbor->GetLinkInfo().AddRss(GetNetif().GetMac().GetNoiseFloor(), threadMessageInfo->mRss); |
| neighbor->ResetLinkFailures(); |
| neighbor->SetState(Neighbor::kStateLinkRequest); |
| } |
| else |
| { |
| VerifyOrExit(memcmp(&neighbor->GetExtAddress(), &macAddr, sizeof(macAddr)) == 0); |
| } |
| } |
| else |
| { |
| // source is not a router |
| neighbor = NULL; |
| } |
| } |
| else |
| { |
| // lack of source address indicates router coming out of reset |
| VerifyOrExit((neighbor = GetNeighbor(macAddr)) != NULL && |
| neighbor->GetState() == Neighbor::kStateValid && |
| IsActiveRouter(neighbor->GetRloc16()), |
| error = OT_ERROR_DROP); |
| } |
| |
| // TLV Request |
| if (Tlv::GetTlv(aMessage, Tlv::kTlvRequest, sizeof(tlvRequest), tlvRequest) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(tlvRequest.IsValid(), error = OT_ERROR_PARSE); |
| } |
| else |
| { |
| tlvRequest.SetLength(0); |
| } |
| |
| SuccessOrExit(error = SendLinkAccept(aMessageInfo, neighbor, tlvRequest, challenge)); |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor, |
| const TlvRequestTlv &aTlvRequest, const ChallengeTlv &aChallenge) |
| { |
| otError error = OT_ERROR_NONE; |
| const ThreadMessageInfo *threadMessageInfo = static_cast<const ThreadMessageInfo *>(aMessageInfo.GetLinkInfo()); |
| static const uint8_t routerTlvs[] = {Tlv::kLinkMargin}; |
| Message *message; |
| Header::Command command; |
| uint8_t linkMargin; |
| |
| command = (aNeighbor == NULL || aNeighbor->GetState() == Neighbor::kStateValid) ? |
| Header::kCommandLinkAccept : Header::kCommandLinkAcceptAndRequest; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = AppendHeader(*message, command)); |
| SuccessOrExit(error = AppendVersion(*message)); |
| SuccessOrExit(error = AppendSourceAddress(*message)); |
| SuccessOrExit(error = AppendResponse(*message, aChallenge.GetChallenge(), aChallenge.GetLength())); |
| SuccessOrExit(error = AppendLinkFrameCounter(*message)); |
| SuccessOrExit(error = AppendMleFrameCounter(*message)); |
| |
| // always append a link margin, regardless of whether or not it was requested |
| linkMargin = LinkQualityInfo::ConvertRssToLinkMargin(GetNetif().GetMac().GetNoiseFloor(), threadMessageInfo->mRss); |
| |
| // add for certification testing |
| if (isAssignLinkQuality && aNeighbor != NULL && |
| (memcmp(&aNeighbor->GetExtAddress(), mAddr64.m8, OT_EXT_ADDRESS_SIZE) == 0)) |
| { |
| linkMargin = mAssignLinkMargin; |
| } |
| |
| SuccessOrExit(error = AppendLinkMargin(*message, linkMargin)); |
| |
| if (aNeighbor != NULL && IsActiveRouter(aNeighbor->GetRloc16())) |
| { |
| SuccessOrExit(error = AppendLeaderData(*message)); |
| } |
| |
| for (uint8_t i = 0; i < aTlvRequest.GetLength(); i++) |
| { |
| switch (aTlvRequest.GetTlvs()[i]) |
| { |
| case Tlv::kRoute: |
| SuccessOrExit(error = AppendRoute(*message)); |
| break; |
| |
| case Tlv::kAddress16: |
| VerifyOrExit(aNeighbor != NULL, error = OT_ERROR_DROP); |
| SuccessOrExit(error = AppendAddress16(*message, aNeighbor->GetRloc16())); |
| break; |
| |
| case Tlv::kLinkMargin: |
| break; |
| |
| default: |
| ExitNow(error = OT_ERROR_DROP); |
| } |
| } |
| |
| if (aNeighbor != NULL && aNeighbor->GetState() != Neighbor::kStateValid) |
| { |
| aNeighbor->GenerateChallenge(); |
| |
| SuccessOrExit(error = AppendChallenge(*message, aNeighbor->GetChallenge(), aNeighbor->GetChallengeSize())); |
| SuccessOrExit(error = AppendTlvRequest(*message, routerTlvs, sizeof(routerTlvs))); |
| aNeighbor->SetState(Neighbor::kStateLinkRequest); |
| } |
| |
| if (aMessageInfo.GetSockAddr().IsMulticast()) |
| { |
| SuccessOrExit(error = AddDelayedResponse(*message, aMessageInfo.GetPeerAddr(), |
| (otPlatRandomGet() % kMaxResponseDelay) + 1)); |
| |
| otLogInfoMle(GetInstance(), "Delayed link accept"); |
| } |
| else |
| { |
| SuccessOrExit(error = SendMessage(*message, aMessageInfo.GetPeerAddr())); |
| |
| otLogInfoMle(GetInstance(), "Sent link accept"); |
| } |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError MleRouter::HandleLinkAccept(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, |
| uint32_t aKeySequence) |
| { |
| otLogInfoMle(GetInstance(), "Received link accept"); |
| return HandleLinkAccept(aMessage, aMessageInfo, aKeySequence, false); |
| } |
| |
| otError MleRouter::HandleLinkAcceptAndRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, |
| uint32_t aKeySequence) |
| { |
| otLogInfoMle(GetInstance(), "Received link accept and request"); |
| return HandleLinkAccept(aMessage, aMessageInfo, aKeySequence, true); |
| } |
| |
| otError MleRouter::HandleLinkAccept(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, |
| uint32_t aKeySequence, bool aRequest) |
| { |
| otError error = OT_ERROR_NONE; |
| const ThreadMessageInfo *threadMessageInfo = static_cast<const ThreadMessageInfo *>(aMessageInfo.GetLinkInfo()); |
| Router *router; |
| Neighbor *neighbor; |
| Mac::ExtAddress macAddr; |
| VersionTlv version; |
| ResponseTlv response; |
| SourceAddressTlv sourceAddress; |
| LinkFrameCounterTlv linkFrameCounter; |
| MleFrameCounterTlv mleFrameCounter; |
| uint8_t routerId; |
| Address16Tlv address16; |
| RouteTlv route; |
| LeaderDataTlv leaderData; |
| LinkMarginTlv linkMargin; |
| ChallengeTlv challenge; |
| TlvRequestTlv tlvRequest; |
| |
| macAddr.Set(aMessageInfo.GetPeerAddr()); |
| |
| // Version |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kVersion, sizeof(version), version)); |
| VerifyOrExit(version.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Response |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kResponse, sizeof(response), response)); |
| VerifyOrExit(response.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Source Address |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); |
| VerifyOrExit(sourceAddress.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Remove stale neighbors |
| if ((neighbor = GetNeighbor(macAddr)) != NULL && |
| neighbor->GetRloc16() != sourceAddress.GetRloc16()) |
| { |
| RemoveNeighbor(*neighbor); |
| } |
| |
| // Link-Layer 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(), error = OT_ERROR_PARSE); |
| } |
| else |
| { |
| mleFrameCounter.SetFrameCounter(linkFrameCounter.GetFrameCounter()); |
| } |
| |
| VerifyOrExit(IsActiveRouter(sourceAddress.GetRloc16()), error = OT_ERROR_PARSE); |
| |
| routerId = GetRouterId(sourceAddress.GetRloc16()); |
| router = GetRouter(routerId); |
| |
| VerifyOrExit(router != NULL, error = OT_ERROR_PARSE); |
| |
| // verify response |
| switch (router->GetState()) |
| { |
| case Neighbor::kStateLinkRequest: |
| VerifyOrExit(memcmp(router->GetChallenge(), response.GetResponse(), router->GetChallengeSize()) == 0, |
| error = OT_ERROR_SECURITY); |
| break; |
| |
| case Neighbor::kStateInvalid: |
| case Neighbor::kStateValid: |
| VerifyOrExit((mChallengeTimeout > 0) && (memcmp(mChallenge, response.GetResponse(), sizeof(mChallenge)) == 0), |
| error = OT_ERROR_SECURITY); |
| break; |
| |
| default: |
| ExitNow(error = OT_ERROR_INVALID_STATE); |
| } |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| assert(false); |
| break; |
| |
| case OT_DEVICE_ROLE_DETACHED: |
| // Address16 |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kAddress16, sizeof(address16), address16)); |
| VerifyOrExit(address16.IsValid(), error = OT_ERROR_PARSE); |
| VerifyOrExit(GetRloc16() == address16.GetRloc16(), error = OT_ERROR_DROP); |
| |
| // Route |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kRoute, sizeof(route), route)); |
| VerifyOrExit(route.IsValid(), error = OT_ERROR_PARSE); |
| SuccessOrExit(error = ProcessRouteTlv(route)); |
| |
| // Leader Data |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); |
| VerifyOrExit(leaderData.IsValid(), error = OT_ERROR_PARSE); |
| SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId()); |
| |
| if (mLeaderData.GetLeaderRouterId() == GetRouterId(GetRloc16())) |
| { |
| SetStateLeader(GetRloc16()); |
| } |
| else |
| { |
| static const uint8_t tlvs[] = {Tlv::kNetworkData}; |
| SetStateRouter(GetRloc16()); |
| mRetrieveNewNetworkData = true; |
| SendDataRequest(aMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs), 0); |
| } |
| |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLinkMargin, sizeof(linkMargin), linkMargin)); |
| VerifyOrExit(linkMargin.IsValid(), error = OT_ERROR_PARSE); |
| router->SetLinkQualityOut(LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMargin.GetLinkMargin())); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| // Leader Data |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); |
| VerifyOrExit(leaderData.IsValid(), error = OT_ERROR_PARSE); |
| VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId()); |
| |
| // Link Margin |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLinkMargin, sizeof(linkMargin), linkMargin)); |
| VerifyOrExit(linkMargin.IsValid(), error = OT_ERROR_PARSE); |
| router->SetLinkQualityOut(LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMargin.GetLinkMargin())); |
| |
| // update routing table |
| if (routerId != mRouterId && !IsRouterIdValid(router->GetNextHop())) |
| { |
| ResetAdvertiseInterval(); |
| } |
| |
| break; |
| } |
| |
| // finish link synchronization |
| router->SetExtAddress(macAddr); |
| router->SetRloc16(sourceAddress.GetRloc16()); |
| router->SetLinkFrameCounter(linkFrameCounter.GetFrameCounter()); |
| router->SetMleFrameCounter(mleFrameCounter.GetFrameCounter()); |
| router->SetLastHeard(Timer::GetNow()); |
| router->SetDeviceMode(ModeTlv::kModeFFD | ModeTlv::kModeRxOnWhenIdle | ModeTlv::kModeFullNetworkData); |
| router->GetLinkInfo().Clear(); |
| router->GetLinkInfo().AddRss(GetNetif().GetMac().GetNoiseFloor(), threadMessageInfo->mRss); |
| router->ResetLinkFailures(); |
| router->SetState(Neighbor::kStateValid); |
| router->SetKeySequence(aKeySequence); |
| |
| if (aRequest) |
| { |
| // Challenge |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kChallenge, sizeof(challenge), challenge)); |
| VerifyOrExit(challenge.IsValid(), error = OT_ERROR_PARSE); |
| |
| // TLV Request |
| if (Tlv::GetTlv(aMessage, Tlv::kTlvRequest, sizeof(tlvRequest), tlvRequest) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(tlvRequest.IsValid(), error = OT_ERROR_PARSE); |
| } |
| else |
| { |
| tlvRequest.SetLength(0); |
| } |
| |
| SuccessOrExit(error = SendLinkAccept(aMessageInfo, router, tlvRequest, challenge)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| Child *MleRouter::NewChild(void) |
| { |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].GetState() == Neighbor::kStateInvalid) |
| { |
| return &mChildren[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| Child *MleRouter::FindChild(uint16_t aChildId) |
| { |
| Child *rval = NULL; |
| |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].GetState() != Neighbor::kStateInvalid && |
| GetChildId(mChildren[i].GetRloc16()) == aChildId) |
| { |
| ExitNow(rval = &mChildren[i]); |
| } |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| Child *MleRouter::FindChild(const Mac::ExtAddress &aAddress) |
| { |
| Child *rval = NULL; |
| |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].GetState() != Neighbor::kStateInvalid && |
| memcmp(&mChildren[i].GetExtAddress(), &aAddress, sizeof(mChildren[i].GetExtAddress())) == 0) |
| { |
| ExitNow(rval = &mChildren[i]); |
| } |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| uint8_t MleRouter::LinkQualityToCost(uint8_t aLinkQuality) |
| { |
| switch (aLinkQuality) |
| { |
| case 1: |
| return kLinkQuality1LinkCost; |
| |
| case 2: |
| return kLinkQuality2LinkCost; |
| |
| case 3: |
| return kLinkQuality3LinkCost; |
| |
| default: |
| return kLinkQuality0LinkCost; |
| } |
| } |
| |
| uint8_t MleRouter::GetLinkCost(uint8_t aRouterId) |
| { |
| uint8_t rval = kMaxRouteCost; |
| Router *router; |
| |
| router = GetRouter(aRouterId); |
| |
| // NULL aRouterId indicates non-existing next hop, hence return kMaxRouteCost for it. |
| VerifyOrExit(aRouterId != mRouterId && router != NULL && router->GetState() == Neighbor::kStateValid); |
| |
| rval = router->GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor()); |
| |
| if (rval > router->GetLinkQualityOut()) |
| { |
| rval = router->GetLinkQualityOut(); |
| } |
| |
| // add for certification testing |
| if (isAssignLinkQuality && (memcmp(&router->GetExtAddress(), mAddr64.m8, OT_EXT_ADDRESS_SIZE) == 0)) |
| { |
| rval = mAssignLinkQuality; |
| } |
| |
| rval = LinkQualityToCost(rval); |
| |
| exit: |
| return rval; |
| } |
| |
| otError MleRouter::SetRouterSelectionJitter(uint8_t aRouterJitter) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aRouterJitter > 0, error = OT_ERROR_INVALID_ARGS); |
| |
| mRouterSelectionJitter = aRouterJitter; |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::ProcessRouteTlv(const RouteTlv &aRoute) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| mRouterIdSequence = aRoute.GetRouterIdSequence(); |
| mRouterIdSequenceLastUpdated = Timer::GetNow(); |
| |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| bool old = mRouters[i].IsAllocated(); |
| mRouters[i].SetAllocated(aRoute.IsRouterIdSet(i)); |
| |
| if (old && !mRouters[i].IsAllocated()) |
| { |
| mRouters[i].SetNextHop(kInvalidRouterId); |
| GetNetif().GetAddressResolver().Remove(i); |
| } |
| } |
| |
| if (mRole == OT_DEVICE_ROLE_ROUTER && !mRouters[mRouterId].IsAllocated()) |
| { |
| BecomeDetached(); |
| ExitNow(error = OT_ERROR_NO_ROUTE); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| bool MleRouter::IsSingleton(void) |
| { |
| bool rval = true; |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| ExitNow(rval = true); |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| ExitNow(rval = ((mDeviceMode & ModeTlv::kModeFFD) == 0)); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| ExitNow(rval = false); |
| break; |
| |
| case OT_DEVICE_ROLE_LEADER: |
| |
| // not a singleton if any other routers exist |
| for (int i = 0; i <= kMaxRouterId; i++) |
| { |
| if (i != mRouterId && mRouters[i].IsAllocated()) |
| { |
| ExitNow(rval = false); |
| } |
| } |
| |
| // not a singleton if any children are REEDs |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].GetState() == Neighbor::kStateValid && mChildren[i].IsFullThreadDevice()) |
| { |
| ExitNow(rval = false); |
| } |
| } |
| |
| break; |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| int MleRouter::ComparePartitions(bool aSingletonA, const LeaderDataTlv &aLeaderDataA, |
| bool aSingletonB, const LeaderDataTlv &aLeaderDataB) |
| { |
| int rval = 0; |
| |
| if (aSingletonA != aSingletonB) |
| { |
| ExitNow(rval = aSingletonB ? 1 : -1); |
| } |
| |
| if (aLeaderDataA.GetWeighting() != aLeaderDataB.GetWeighting()) |
| { |
| ExitNow(rval = aLeaderDataA.GetWeighting() > aLeaderDataB.GetWeighting() ? 1 : -1); |
| } |
| |
| if (aLeaderDataA.GetPartitionId() != aLeaderDataB.GetPartitionId()) |
| { |
| ExitNow(rval = aLeaderDataA.GetPartitionId() > aLeaderDataB.GetPartitionId() ? 1 : -1); |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| uint8_t MleRouter::GetActiveRouterCount(void) const |
| { |
| uint8_t rval = 0; |
| |
| for (int i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].IsAllocated()) |
| { |
| rval++; |
| } |
| } |
| |
| return rval; |
| } |
| |
| otError MleRouter::HandleAdvertisement(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| const ThreadMessageInfo *threadMessageInfo = static_cast<const ThreadMessageInfo *>(aMessageInfo.GetLinkInfo()); |
| Mac::ExtAddress macAddr; |
| SourceAddressTlv sourceAddress; |
| LeaderDataTlv leaderData; |
| RouteTlv route; |
| uint32_t partitionId; |
| Router *router; |
| Neighbor *neighbor; |
| uint8_t routerId; |
| uint8_t routerCount; |
| |
| macAddr.Set(aMessageInfo.GetPeerAddr()); |
| |
| // Source Address |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress)); |
| VerifyOrExit(sourceAddress.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Remove stale neighbors |
| if ((neighbor = GetNeighbor(macAddr)) != NULL && |
| neighbor->GetRloc16() != sourceAddress.GetRloc16()) |
| { |
| RemoveNeighbor(*neighbor); |
| } |
| |
| // Leader Data |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData)); |
| VerifyOrExit(leaderData.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Route Data |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kRoute, sizeof(route), route)); |
| VerifyOrExit(route.IsValid(), error = OT_ERROR_PARSE); |
| |
| partitionId = leaderData.GetPartitionId(); |
| |
| if (partitionId != mLeaderData.GetPartitionId()) |
| { |
| otLogDebgMle(GetInstance(), "different partition! %d %d %d %d", |
| leaderData.GetWeighting(), partitionId, |
| mLeaderData.GetWeighting(), mLeaderData.GetPartitionId()); |
| |
| if (partitionId == mLastPartitionId && (mDeviceMode & ModeTlv::kModeFFD)) |
| { |
| VerifyOrExit((static_cast<int8_t>(route.GetRouterIdSequence() - mLastPartitionRouterIdSequence) > 0), |
| error = OT_ERROR_DROP); |
| } |
| |
| if (mRole == OT_DEVICE_ROLE_CHILD && |
| (memcmp(&mParent.GetExtAddress(), &macAddr, sizeof(macAddr)) == 0 || !(mDeviceMode & ModeTlv::kModeFFD))) |
| { |
| ExitNow(); |
| } |
| |
| routerCount = 0; |
| |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| if (route.IsRouterIdSet(i)) |
| { |
| routerCount++; |
| } |
| } |
| |
| if (ComparePartitions(routerCount <= 1, leaderData, IsSingleton(), mLeaderData) > 0) |
| { |
| otLogDebgMle(GetInstance(), "trying to migrate"); |
| BecomeChild(kAttachBetter); |
| } |
| |
| ExitNow(error = OT_ERROR_DROP); |
| } |
| else if (leaderData.GetLeaderRouterId() != GetLeaderId()) |
| { |
| if (mRole != OT_DEVICE_ROLE_CHILD) |
| { |
| BecomeDetached(); |
| error = OT_ERROR_DROP; |
| } |
| |
| ExitNow(); |
| } |
| |
| VerifyOrExit(IsActiveRouter(sourceAddress.GetRloc16())); |
| routerId = GetRouterId(sourceAddress.GetRloc16()); |
| router = GetRouter(routerId); |
| VerifyOrExit(router != NULL, error = OT_ERROR_PARSE); |
| |
| if ((mDeviceMode & ModeTlv::kModeFFD) && |
| static_cast<int8_t>(route.GetRouterIdSequence() - mRouterIdSequence) > 0) |
| { |
| bool processRouteTlv = false; |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| if ((sourceAddress.GetRloc16() == mParent.GetRloc16()) || |
| (router->GetState() == Neighbor::kStateValid)) |
| { |
| processRouteTlv = true; |
| } |
| |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| processRouteTlv = true; |
| break; |
| } |
| |
| if (processRouteTlv) |
| { |
| SuccessOrExit(error = ProcessRouteTlv(route)); |
| } |
| } |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| ExitNow(); |
| |
| case OT_DEVICE_ROLE_CHILD: |
| if ((sourceAddress.GetRloc16() == mParent.GetRloc16() || router->GetState() == Neighbor::kStateValid) && |
| (mDeviceMode & ModeTlv::kModeFFD) && |
| (mRouterSelectionJitterTimeout == 0) && |
| (GetActiveRouterCount() < mRouterUpgradeThreshold)) |
| { |
| mRouterSelectionJitterTimeout = (otPlatRandomGet() % mRouterSelectionJitter) + 1; |
| ExitNow(); |
| } |
| |
| if (memcmp(&mParent.GetExtAddress(), &macAddr, sizeof(mParent.GetExtAddress())) == 0) |
| { |
| router = &mParent; |
| |
| if (mParent.GetRloc16() != sourceAddress.GetRloc16()) |
| { |
| BecomeDetached(); |
| ExitNow(error = OT_ERROR_NO_ROUTE); |
| } |
| |
| if (mDeviceMode & ModeTlv::kModeFFD) |
| { |
| for (uint8_t i = 0, routeCount = 0; i <= kMaxRouterId; i++) |
| { |
| if (route.IsRouterIdSet(i) == false) |
| { |
| continue; |
| } |
| |
| if (i != GetLeaderId()) |
| { |
| routeCount++; |
| continue; |
| } |
| |
| if (route.GetRouteCost(routeCount) > 0) |
| { |
| mRouters[GetLeaderId()].SetNextHop(routerId); |
| mRouters[GetLeaderId()].SetCost(route.GetRouteCost(routeCount)); |
| } |
| else |
| { |
| mRouters[GetLeaderId()].SetNextHop(kInvalidRouterId); |
| mRouters[GetLeaderId()].SetCost(0); |
| } |
| |
| break; |
| } |
| } |
| } |
| else if ((mDeviceMode & ModeTlv::kModeFFD) && (router->GetState() != Neighbor::kStateValid)) |
| { |
| router->SetExtAddress(macAddr); |
| router->GetLinkInfo().Clear(); |
| router->GetLinkInfo().AddRss(netif.GetMac().GetNoiseFloor(), threadMessageInfo->mRss); |
| router->ResetLinkFailures(); |
| router->SetState(Neighbor::kStateLinkRequest); |
| SendLinkRequest(router); |
| ExitNow(error = OT_ERROR_NO_ROUTE); |
| } |
| |
| router->SetLastHeard(Timer::GetNow()); |
| |
| ExitNow(); |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| // check current active router number |
| routerCount = 0; |
| |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| if (route.IsRouterIdSet(i)) |
| { |
| routerCount++; |
| } |
| } |
| |
| if (routerCount > mRouterDowngradeThreshold && |
| mRouterSelectionJitterTimeout == 0 && |
| HasMinDowngradeNeighborRouters() && |
| HasSmallNumberOfChildren() && |
| HasOneNeighborwithComparableConnectivity(route, routerId)) |
| { |
| mRouterSelectionJitterTimeout = (otPlatRandomGet() % mRouterSelectionJitter) + 1; |
| } |
| |
| // fall through |
| |
| case OT_DEVICE_ROLE_LEADER: |
| |
| // router is not in list, reject |
| if (!router->IsAllocated()) |
| { |
| ExitNow(error = OT_ERROR_NO_ROUTE); |
| } |
| |
| // Send link request if no link to router |
| if (router->GetState() != Neighbor::kStateValid) |
| { |
| router->SetExtAddress(macAddr); |
| router->GetLinkInfo().Clear(); |
| router->GetLinkInfo().AddRss(netif.GetMac().GetNoiseFloor(), threadMessageInfo->mRss); |
| router->ResetLinkFailures(); |
| router->SetState(Neighbor::kStateLinkRequest); |
| router->SetDataRequestPending(false); |
| SendLinkRequest(router); |
| ExitNow(error = OT_ERROR_NO_ROUTE); |
| } |
| |
| router->SetLastHeard(Timer::GetNow()); |
| break; |
| } |
| |
| UpdateRoutes(route, routerId); |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| netif.GetNetworkDataLocal().SendServerDataNotification(); |
| #endif |
| |
| exit: |
| return error; |
| } |
| |
| void MleRouter::UpdateRoutes(const RouteTlv &aRoute, uint8_t aRouterId) |
| { |
| uint8_t curCost; |
| uint8_t newCost; |
| uint8_t oldNextHop; |
| uint8_t cost; |
| uint8_t linkQuality; |
| bool update; |
| |
| // update routes |
| do |
| { |
| update = false; |
| |
| for (uint8_t i = 0, routeCount = 0; i <= kMaxRouterId; i++) |
| { |
| if (aRoute.IsRouterIdSet(i) == false) |
| { |
| continue; |
| } |
| |
| if (mRouters[i].IsAllocated() == false) |
| { |
| routeCount++; |
| continue; |
| } |
| |
| if (i == mRouterId) |
| { |
| linkQuality = aRoute.GetLinkQualityIn(routeCount); |
| |
| if (mRouters[aRouterId].GetLinkQualityOut() != linkQuality) |
| { |
| mRouters[aRouterId].SetLinkQualityOut(linkQuality); |
| update = true; |
| } |
| } |
| else |
| { |
| oldNextHop = mRouters[i].GetNextHop(); |
| |
| if (i == aRouterId) |
| { |
| cost = 0; |
| } |
| else |
| { |
| cost = aRoute.GetRouteCost(routeCount); |
| |
| if (cost == 0) |
| { |
| cost = kMaxRouteCost; |
| } |
| } |
| |
| if (!IsRouterIdValid(mRouters[i].GetNextHop()) || mRouters[i].GetNextHop() == aRouterId) |
| { |
| // route has no nexthop or nexthop is neighbor (sender) |
| |
| if (i != aRouterId) |
| { |
| if (cost + GetLinkCost(aRouterId) <= kMaxRouteCost) |
| { |
| if (!IsRouterIdValid(mRouters[i].GetNextHop()) && GetLinkCost(i) >= kMaxRouteCost) |
| { |
| ResetAdvertiseInterval(); |
| } |
| |
| mRouters[i].SetNextHop(aRouterId); |
| mRouters[i].SetCost(cost); |
| } |
| else if (mRouters[i].GetNextHop() == aRouterId) |
| { |
| if (GetLinkCost(i) >= kMaxRouteCost) |
| { |
| ResetAdvertiseInterval(); |
| } |
| |
| mRouters[i].SetNextHop(kInvalidRouterId); |
| mRouters[i].SetCost(0); |
| mRouters[i].SetLastHeard(Timer::GetNow()); |
| } |
| } |
| } |
| else |
| { |
| curCost = mRouters[i].GetCost() + GetLinkCost(mRouters[i].GetNextHop()); |
| newCost = cost + GetLinkCost(aRouterId); |
| |
| if (newCost < curCost && i != aRouterId) |
| { |
| mRouters[i].SetNextHop(aRouterId); |
| mRouters[i].SetCost(cost); |
| } |
| } |
| |
| update |= mRouters[i].GetNextHop() != oldNextHop; |
| } |
| |
| routeCount++; |
| } |
| } |
| while (update); |
| |
| #if 1 |
| |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].IsAllocated() == false || !IsRouterIdValid(mRouters[i].GetNextHop())) |
| { |
| continue; |
| } |
| |
| otLogDebgMle(GetInstance(), |
| "%x: %x %d %d %d %d", |
| GetRloc16(i), |
| GetRloc16(mRouters[i].GetNextHop()), |
| mRouters[i].GetCost(), |
| GetLinkCost(i), mRouters[i].GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor()), |
| mRouters[i].GetLinkQualityOut()); |
| } |
| |
| #endif |
| } |
| |
| otError MleRouter::HandleParentRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| const ThreadMessageInfo *threadMessageInfo = static_cast<const ThreadMessageInfo *>(aMessageInfo.GetLinkInfo()); |
| Mac::ExtAddress macAddr; |
| VersionTlv version; |
| ScanMaskTlv scanMask; |
| ChallengeTlv challenge; |
| Child *child; |
| |
| otLogInfoMle(GetInstance(), "Received parent request"); |
| |
| // A Router MUST NOT send an MLE Parent Response if: |
| |
| // 1. It has no available Child capacity (if Max Child Count minus |
| // Child Count would be equal to zero) |
| // ==> verified below when allocating a child entry |
| |
| // 2. It is disconnected from its Partition (that is, it has not |
| // received an updated ID sequence number within LEADER_TIMEOUT |
| // seconds |
| VerifyOrExit(GetLeaderAge() < mNetworkIdTimeout, error = OT_ERROR_DROP); |
| |
| // 3. Its current routing path cost to the Leader is infinite. |
| VerifyOrExit(mRole == OT_DEVICE_ROLE_LEADER || |
| GetLinkCost(GetLeaderId()) < kMaxRouteCost || |
| (mRole == OT_DEVICE_ROLE_CHILD && mRouters[GetLeaderId()].GetCost() + 1 < kMaxRouteCost) || |
| (mRouters[GetLeaderId()].GetCost() + GetLinkCost(mRouters[GetLeaderId()].GetNextHop()) < kMaxRouteCost), |
| error = OT_ERROR_DROP); |
| |
| macAddr.Set(aMessageInfo.GetPeerAddr()); |
| |
| // Version |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kVersion, sizeof(version), version)); |
| VerifyOrExit(version.IsValid() && version.GetVersion() >= kVersion, error = OT_ERROR_PARSE); |
| |
| // Scan Mask |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kScanMask, sizeof(scanMask), scanMask)); |
| VerifyOrExit(scanMask.IsValid(), error = OT_ERROR_PARSE); |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| ExitNow(); |
| |
| case OT_DEVICE_ROLE_CHILD: |
| VerifyOrExit(scanMask.IsEndDeviceFlagSet()); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| VerifyOrExit(scanMask.IsRouterFlagSet()); |
| break; |
| } |
| |
| // Challenge |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kChallenge, sizeof(challenge), challenge)); |
| VerifyOrExit(challenge.IsValid(), error = OT_ERROR_PARSE); |
| |
| child = FindChild(macAddr); |
| |
| if (child != NULL && !child->IsFullThreadDevice()) |
| { |
| // Parent Request from a MTD child means that the child had detached. It can be safely removed. |
| RemoveNeighbor(*child); |
| child = NULL; |
| } |
| |
| if (child == NULL) |
| { |
| VerifyOrExit((child = NewChild()) != NULL); |
| |
| memset(child, 0, sizeof(*child)); |
| |
| // MAC Address |
| child->SetExtAddress(macAddr); |
| child->GetLinkInfo().Clear(); |
| child->GetLinkInfo().AddRss(GetNetif().GetMac().GetNoiseFloor(), threadMessageInfo->mRss); |
| child->ResetLinkFailures(); |
| child->SetState(Neighbor::kStateParentRequest); |
| child->SetDataRequestPending(false); |
| |
| child->SetLastHeard(Timer::GetNow()); |
| child->SetTimeout(Timer::MsecToSec(kMaxChildIdRequestTimeout)); |
| } |
| |
| SuccessOrExit(error = SendParentResponse(child, challenge, !scanMask.IsEndDeviceFlagSet())); |
| |
| exit: |
| return error; |
| } |
| |
| void MleRouter::HandleStateUpdateTimer(Timer &aTimer) |
| { |
| GetOwner(aTimer).HandleStateUpdateTimer(); |
| } |
| |
| void MleRouter::HandleStateUpdateTimer(void) |
| { |
| bool routerStateUpdate = false; |
| |
| if (mChallengeTimeout > 0) |
| { |
| mChallengeTimeout--; |
| } |
| |
| if (mRouterSelectionJitterTimeout > 0) |
| { |
| mRouterSelectionJitterTimeout--; |
| |
| if (mRouterSelectionJitterTimeout == 0) |
| { |
| routerStateUpdate = true; |
| } |
| } |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| assert(false); |
| break; |
| |
| case OT_DEVICE_ROLE_DETACHED: |
| BecomeDetached(); |
| ExitNow(); |
| |
| case OT_DEVICE_ROLE_CHILD: |
| if (routerStateUpdate) |
| { |
| if (GetActiveRouterCount() < mRouterUpgradeThreshold) |
| { |
| // upgrade to Router |
| BecomeRouter(ThreadStatusTlv::kTooFewRouters); |
| } |
| else if (!mAdvertiseTimer.IsRunning()) |
| { |
| SendAdvertisement(); |
| |
| mAdvertiseTimer.Start( |
| Timer::SecToMsec(kReedAdvertiseInterval), |
| Timer::SecToMsec(kReedAdvertiseInterval + kReedAdvertiseJitter), |
| TrickleTimer::kModePlainTimer); |
| } |
| |
| ExitNow(); |
| } |
| |
| // fall through |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| // verify path to leader |
| otLogDebgMle(GetInstance(), "network id timeout = %d", GetLeaderAge()); |
| |
| if (GetLeaderAge() >= mNetworkIdTimeout) |
| { |
| BecomeChild(kAttachSame1); |
| } |
| |
| if (routerStateUpdate && GetActiveRouterCount() > mRouterDowngradeThreshold) |
| { |
| // downgrade to REED |
| BecomeChild(kAttachSame1); |
| } |
| |
| break; |
| |
| case OT_DEVICE_ROLE_LEADER: |
| |
| // update router id sequence |
| if (GetLeaderAge() >= kRouterIdSequencePeriod) |
| { |
| mRouterIdSequence++; |
| mRouterIdSequenceLastUpdated = Timer::GetNow(); |
| } |
| |
| break; |
| } |
| |
| if (mIsRouterRestoringChildren) |
| { |
| bool hasRestoringChildren = false; |
| |
| for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].GetState() == Neighbor::kStateRestored) |
| { |
| SendChildUpdateRequest(&mChildren[i]); |
| hasRestoringChildren = true; |
| } |
| } |
| |
| // no child to restore |
| if (!hasRestoringChildren) |
| { |
| mIsRouterRestoringChildren = false; |
| } |
| } |
| |
| // update children state |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| uint32_t timeout = 0; |
| |
| switch (mChildren[i].GetState()) |
| { |
| case Neighbor::kStateInvalid: |
| case Neighbor::kStateChildIdRequest: |
| continue; |
| |
| case Neighbor::kStateParentRequest: |
| case Neighbor::kStateValid: |
| case Neighbor::kStateRestored: |
| case Neighbor::kStateChildUpdateRequest: |
| timeout = Timer::SecToMsec(mChildren[i].GetTimeout()); |
| break; |
| |
| case Neighbor::kStateLinkRequest: |
| assert(false); |
| break; |
| } |
| |
| if ((Timer::GetNow() - mChildren[i].GetLastHeard()) >= timeout) |
| { |
| RemoveNeighbor(mChildren[i]); |
| } |
| } |
| |
| // update router state |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].GetState() == Neighbor::kStateValid) |
| { |
| if ((Timer::GetNow() - mRouters[i].GetLastHeard()) >= Timer::SecToMsec(kMaxNeighborAge)) |
| { |
| RemoveNeighbor(mRouters[i]); |
| } |
| } |
| |
| if (mRole == OT_DEVICE_ROLE_LEADER) |
| { |
| if (mRouters[i].IsAllocated()) |
| { |
| if (!IsRouterIdValid(mRouters[i].GetNextHop()) && |
| GetLinkCost(i) >= kMaxRouteCost && |
| (Timer::GetNow() - mRouters[i].GetLastHeard()) >= Timer::SecToMsec(kMaxLeaderToRouterTimeout)) |
| { |
| ReleaseRouterId(i); |
| } |
| } |
| else if (mRouters[i].IsReclaimDelay()) |
| { |
| if ((Timer::GetNow() - mRouters[i].GetLastHeard()) >= |
| Timer::SecToMsec((kMaxLeaderToRouterTimeout + kRouterIdReuseDelay))) |
| { |
| mRouters[i].SetReclaimDelay(false); |
| } |
| } |
| } |
| } |
| |
| SynchronizeChildNetworkData(); |
| |
| mStateUpdateTimer.Start(kStateUpdatePeriod); |
| |
| exit: |
| return; |
| } |
| |
| otError MleRouter::SendParentResponse(Child *aChild, const ChallengeTlv &challenge, bool aRoutersOnlyRequest) |
| { |
| otError error = OT_ERROR_NONE; |
| Ip6::Address destination; |
| Message *message; |
| uint16_t delay; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandParentResponse)); |
| SuccessOrExit(error = AppendSourceAddress(*message)); |
| SuccessOrExit(error = AppendLeaderData(*message)); |
| SuccessOrExit(error = AppendLinkFrameCounter(*message)); |
| SuccessOrExit(error = AppendMleFrameCounter(*message)); |
| SuccessOrExit(error = AppendResponse(*message, challenge.GetChallenge(), challenge.GetLength())); |
| |
| aChild->GenerateChallenge(); |
| |
| SuccessOrExit(error = AppendChallenge(*message, aChild->GetChallenge(), aChild->GetChallengeSize())); |
| |
| if (isAssignLinkQuality && |
| (memcmp(mAddr64.m8, &aChild->GetExtAddress(), OT_EXT_ADDRESS_SIZE) == 0)) |
| { |
| // use assigned one to ensure the link quality |
| SuccessOrExit(error = AppendLinkMargin(*message, mAssignLinkMargin)); |
| } |
| else |
| { |
| error = AppendLinkMargin(*message, aChild->GetLinkInfo().GetLinkMargin(GetNetif().GetMac().GetNoiseFloor())); |
| SuccessOrExit(error); |
| } |
| |
| SuccessOrExit(error = AppendConnectivity(*message)); |
| SuccessOrExit(error = AppendVersion(*message)); |
| |
| memset(&destination, 0, sizeof(destination)); |
| destination.mFields.m16[0] = HostSwap16(0xfe80); |
| destination.SetIid(aChild->GetExtAddress()); |
| |
| if (aRoutersOnlyRequest) |
| { |
| delay = (otPlatRandomGet() % kParentResponseMaxDelayRouters) + 1; |
| } |
| else |
| { |
| delay = (otPlatRandomGet() % kParentResponseMaxDelayAll) + 1; |
| } |
| |
| SuccessOrExit(error = AddDelayedResponse(*message, destination, delay)); |
| |
| otLogInfoMle(GetInstance(), "Delayed Parent Response"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otError MleRouter::UpdateChildAddresses(const AddressRegistrationTlv &aTlv, Child &aChild) |
| { |
| const AddressRegistrationEntry *entry; |
| Ip6::Address address; |
| Lowpan::Context context; |
| Child *child; |
| uint8_t count; |
| uint8_t index; |
| char stringBuffer[Ip6::Address::kIp6AddressStringSize]; |
| |
| aChild.ClearIp6Addresses(); |
| |
| for (count = 0; count < Child::kMaxIp6AddressPerChild; count++) |
| { |
| if ((entry = aTlv.GetAddressEntry(count)) == NULL) |
| { |
| break; |
| } |
| |
| if (entry->IsCompressed()) |
| { |
| // xxx check if context id exists |
| GetNetif().GetNetworkDataLeader().GetContext(entry->GetContextId(), context); |
| memcpy(&address, context.mPrefix, BitVectorBytes(context.mPrefixLength)); |
| address.SetIid(entry->GetIid()); |
| } |
| else |
| { |
| address = *entry->GetIp6Address(); |
| } |
| |
| aChild.GetIp6Address(count) = address; |
| |
| otLogInfoMle(GetInstance(), "Child 0x%04x IPv6 address[%d]=%s", aChild.GetRloc16(), count, |
| address.ToString(stringBuffer, sizeof(stringBuffer))); |
| |
| // We check if the same address is in-use by another child, if so |
| // remove it. This implements "last-in wins" duplicate address |
| // resolution policy. |
| // |
| // Duplicate addresses can occur if a previously attached child |
| // attaches to same parent again (after a reset, memory wipe) using |
| // a new random extended address before the old entry in the child |
| // table is timed out and then trying to register its globally unique |
| // IPv6 address as the new child. |
| |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| child = &mChildren[i]; |
| |
| if (!child->IsStateValidOrRestoring() || (child == &aChild)) |
| { |
| continue; |
| } |
| |
| if (child->FindIp6Address(address, &index) == OT_ERROR_NONE) |
| { |
| child->RemoveIp6Address(index); |
| } |
| } |
| } |
| |
| if (count == 0) |
| { |
| otLogInfoMle(GetInstance(), "Child 0x%04x has no registered IPv6 address", aChild.GetRloc16()); |
| } |
| else |
| { |
| otLogInfoMle(GetInstance(), "Child 0x%04x has %d registered IPv6 address%s", aChild.GetRloc16(), count, |
| (count == 1) ? "" : "es"); |
| } |
| |
| OT_UNUSED_VARIABLE(stringBuffer); |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otError MleRouter::HandleChildIdRequest(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()); |
| Mac::ExtAddress macAddr; |
| ResponseTlv response; |
| LinkFrameCounterTlv linkFrameCounter; |
| MleFrameCounterTlv mleFrameCounter; |
| ModeTlv mode; |
| TimeoutTlv timeout; |
| AddressRegistrationTlv address; |
| TlvRequestTlv tlvRequest; |
| ActiveTimestampTlv activeTimestamp; |
| PendingTimestampTlv pendingTimestamp; |
| Child *child; |
| uint8_t numTlvs; |
| |
| otLogInfoMle(GetInstance(), "Received Child ID Request"); |
| |
| // only process message when operating as a child, router, or leader |
| VerifyOrExit(mRole >= OT_DEVICE_ROLE_CHILD, error = OT_ERROR_INVALID_STATE); |
| |
| // Find Child |
| macAddr.Set(aMessageInfo.GetPeerAddr()); |
| |
| VerifyOrExit((child = FindChild(macAddr)) != NULL, error = OT_ERROR_ALREADY); |
| |
| // Response |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kResponse, sizeof(response), response)); |
| VerifyOrExit(response.IsValid() && |
| memcmp(response.GetResponse(), child->GetChallenge(), child->GetChallengeSize()) == 0, |
| error = OT_ERROR_SECURITY); |
| |
| // Link-Layer 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(), error = OT_ERROR_PARSE); |
| } |
| else |
| { |
| mleFrameCounter.SetFrameCounter(linkFrameCounter.GetFrameCounter()); |
| } |
| |
| // Mode |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kMode, sizeof(mode), mode)); |
| VerifyOrExit(mode.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Timeout |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kTimeout, sizeof(timeout), timeout)); |
| VerifyOrExit(timeout.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Ip6 Address |
| address.SetLength(0); |
| |
| if ((mode.GetMode() & ModeTlv::kModeFFD) == 0) |
| { |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kAddressRegistration, sizeof(address), address)); |
| VerifyOrExit(address.IsValid(), error = OT_ERROR_PARSE); |
| } |
| |
| // TLV Request |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kTlvRequest, sizeof(tlvRequest), tlvRequest)); |
| VerifyOrExit(tlvRequest.IsValid() && tlvRequest.GetLength() <= Child::kMaxRequestTlvs, |
| error = OT_ERROR_PARSE); |
| |
| // Active Timestamp |
| activeTimestamp.SetLength(0); |
| |
| if (Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(activeTimestamp), activeTimestamp) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(activeTimestamp.IsValid(), error = OT_ERROR_PARSE); |
| } |
| |
| // Pending Timestamp |
| pendingTimestamp.SetLength(0); |
| |
| if (Tlv::GetTlv(aMessage, Tlv::kPendingTimestamp, sizeof(pendingTimestamp), pendingTimestamp) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(pendingTimestamp.IsValid(), error = OT_ERROR_PARSE); |
| } |
| |
| // Remove from router table |
| for (int i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].GetState() != Neighbor::kStateInvalid && |
| memcmp(&mRouters[i].GetExtAddress(), &macAddr, sizeof(macAddr)) == 0) |
| { |
| RemoveNeighbor(mRouters[i]); |
| break; |
| } |
| } |
| |
| if (child->GetState() != Neighbor::kStateValid) |
| { |
| child->SetState(Neighbor::kStateChildIdRequest); |
| } |
| else |
| { |
| RemoveStoredChild(child->GetRloc16()); |
| |
| if (!child->IsRxOnWhenIdle()) |
| { |
| netif.GetMeshForwarder().ClearChildIndirectMessages(*child); |
| } |
| } |
| |
| |
| child->SetLastHeard(Timer::GetNow()); |
| child->SetLinkFrameCounter(linkFrameCounter.GetFrameCounter()); |
| child->SetMleFrameCounter(mleFrameCounter.GetFrameCounter()); |
| child->SetKeySequence(aKeySequence); |
| child->SetDeviceMode(mode.GetMode()); |
| child->GetLinkInfo().AddRss(netif.GetMac().GetNoiseFloor(), threadMessageInfo->mRss); |
| child->SetTimeout(timeout.GetTimeout()); |
| |
| if (mode.GetMode() & ModeTlv::kModeFullNetworkData) |
| { |
| child->SetNetworkDataVersion(mLeaderData.GetDataVersion()); |
| } |
| else |
| { |
| child->SetNetworkDataVersion(mLeaderData.GetStableDataVersion()); |
| } |
| |
| UpdateChildAddresses(address, *child); |
| |
| child->ClearRequestTlvs(); |
| |
| for (numTlvs = 0; numTlvs < tlvRequest.GetLength(); numTlvs++) |
| { |
| child->SetRequestTlv(numTlvs, tlvRequest.GetTlvs()[numTlvs]); |
| } |
| |
| if (activeTimestamp.GetLength() == 0 || |
| netif.GetActiveDataset().Compare(activeTimestamp) != 0) |
| { |
| child->SetRequestTlv(numTlvs++, Tlv::kActiveDataset); |
| } |
| |
| if (pendingTimestamp.GetLength() == 0 || |
| netif.GetPendingDataset().Compare(pendingTimestamp) != 0) |
| { |
| child->SetRequestTlv(numTlvs++, Tlv::kPendingDataset); |
| } |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| assert(false); |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| child->SetState(Neighbor::kStateChildIdRequest); |
| BecomeRouter(ThreadStatusTlv::kHaveChildIdRequest); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| SuccessOrExit(error = SendChildIdResponse(child)); |
| break; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::HandleChildUpdateRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, |
| uint32_t aKeySequence) |
| { |
| static const uint8_t kMaxResponseTlvs = 10; |
| |
| otError error = OT_ERROR_NONE; |
| Mac::ExtAddress macAddr; |
| ModeTlv mode; |
| ChallengeTlv challenge; |
| AddressRegistrationTlv address; |
| LeaderDataTlv leaderData; |
| TimeoutTlv timeout; |
| Child *child; |
| TlvRequestTlv tlvRequest; |
| uint8_t tlvs[kMaxResponseTlvs]; |
| uint8_t tlvslength = 0; |
| |
| otLogInfoMle(GetInstance(), "Received Child Update Request from child"); |
| |
| // Mode |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kMode, sizeof(mode), mode)); |
| VerifyOrExit(mode.IsValid(), error = OT_ERROR_PARSE); |
| |
| // Find Child |
| macAddr.Set(aMessageInfo.GetPeerAddr()); |
| child = FindChild(macAddr); |
| |
| tlvs[tlvslength++] = Tlv::kSourceAddress; |
| |
| // Not proceed if the Child Update Request is from the peer which is not the device's child or |
| // which was the device's child but becomes invalid. |
| if (child == NULL || child->GetState() == Neighbor::kStateInvalid) |
| { |
| // For invalid non-sleepy child, Send Child Update Response with status TLV (error) |
| if (mode.GetMode() & ModeTlv::kModeRxOnWhenIdle) |
| { |
| tlvs[tlvslength++] = Tlv::kStatus; |
| SendChildUpdateResponse(NULL, aMessageInfo, tlvs, tlvslength, NULL); |
| } |
| |
| ExitNow(); |
| } |
| |
| child->SetDeviceMode(mode.GetMode()); |
| tlvs[tlvslength++] = Tlv::kMode; |
| |
| // Parent MUST include Leader Data TLV in Child Update Response |
| tlvs[tlvslength++] = Tlv::kLeaderData; |
| |
| // Challenge |
| if (Tlv::GetTlv(aMessage, Tlv::kChallenge, sizeof(challenge), challenge) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(challenge.IsValid(), error = OT_ERROR_PARSE); |
| tlvs[tlvslength++] = Tlv::kResponse; |
| tlvs[tlvslength++] = Tlv::kMleFrameCounter; |
| tlvs[tlvslength++] = Tlv::kLinkFrameCounter; |
| } |
| |
| // Ip6 Address TLV |
| if (Tlv::GetTlv(aMessage, Tlv::kAddressRegistration, sizeof(address), address) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(address.IsValid(), error = OT_ERROR_PARSE); |
| UpdateChildAddresses(address, *child); |
| tlvs[tlvslength++] = Tlv::kAddressRegistration; |
| } |
| |
| // Leader Data |
| if (Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(leaderData.IsValid(), error = OT_ERROR_PARSE); |
| } |
| |
| // Timeout |
| if (Tlv::GetTlv(aMessage, Tlv::kTimeout, sizeof(timeout), timeout) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(timeout.IsValid(), error = OT_ERROR_PARSE); |
| child->SetTimeout(timeout.GetTimeout()); |
| tlvs[tlvslength++] = Tlv::kTimeout; |
| } |
| |
| // TLV Request |
| if (Tlv::GetTlv(aMessage, Tlv::kTlvRequest, sizeof(tlvRequest), tlvRequest) == OT_ERROR_NONE) |
| { |
| uint8_t tlv; |
| TlvRequestIterator iterator = TLVREQUESTTLV_ITERATOR_INIT; |
| |
| VerifyOrExit(tlvRequest.IsValid() && tlvRequest.GetLength() <= (Child::kMaxRequestTlvs - tlvslength), |
| error = OT_ERROR_PARSE); |
| |
| while (tlvRequest.GetNextTlv(iterator, tlv) == OT_ERROR_NONE) |
| { |
| // Here skips Tlv::kLeaderData because it has already been included by default |
| if (tlv != Tlv::kLeaderData) |
| { |
| tlvs[tlvslength++] = tlv; |
| } |
| } |
| } |
| |
| child->SetLastHeard(Timer::GetNow()); |
| |
| if (child->IsStateRestoring()) |
| { |
| SetChildStateToValid(child); |
| child->SetKeySequence(aKeySequence); |
| } |
| |
| SendChildUpdateResponse(child, aMessageInfo, tlvs, tlvslength, &challenge); |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::HandleChildUpdateResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, |
| uint32_t aKeySequence) |
| { |
| otError error = OT_ERROR_NONE; |
| const ThreadMessageInfo *threadMessageInfo = static_cast<const ThreadMessageInfo *>(aMessageInfo.GetLinkInfo()); |
| Mac::ExtAddress macAddr; |
| SourceAddressTlv sourceAddress; |
| TimeoutTlv timeout; |
| AddressRegistrationTlv address; |
| ResponseTlv response; |
| LinkFrameCounterTlv linkFrameCounter; |
| MleFrameCounterTlv mleFrameCounter; |
| LeaderDataTlv leaderData; |
| Child *child; |
| |
| otLogInfoMle(GetInstance(), "Received Child Update Response from child"); |
| |
| // Find Child |
| macAddr.Set(aMessageInfo.GetPeerAddr()); |
| |
| VerifyOrExit((child = FindChild(macAddr)) != NULL, error = OT_ERROR_NOT_FOUND); |
| |
| // Source Address |
| if (Tlv::GetTlv(aMessage, Tlv::kSourceAddress, sizeof(sourceAddress), sourceAddress) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(sourceAddress.IsValid(), error = OT_ERROR_PARSE); |
| VerifyOrExit(child->GetRloc16() == sourceAddress.GetRloc16(), error = OT_ERROR_PARSE); |
| } |
| |
| // Response |
| if (Tlv::GetTlv(aMessage, Tlv::kResponse, sizeof(response), response) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(response.IsValid() && |
| memcmp(response.GetResponse(), child->GetChallenge(), child->GetChallengeSize()) == 0, |
| error = OT_ERROR_SECURITY); |
| } |
| |
| // Link-Layer Frame Counter |
| if (Tlv::GetTlv(aMessage, Tlv::kLinkFrameCounter, sizeof(linkFrameCounter), linkFrameCounter) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(linkFrameCounter.IsValid(), error = OT_ERROR_PARSE); |
| child->SetLinkFrameCounter(linkFrameCounter.GetFrameCounter()); |
| } |
| |
| // MLE Frame Counter |
| if (Tlv::GetTlv(aMessage, Tlv::kMleFrameCounter, sizeof(mleFrameCounter), mleFrameCounter) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(mleFrameCounter.IsValid(), error = OT_ERROR_PARSE); |
| child->SetMleFrameCounter(mleFrameCounter.GetFrameCounter()); |
| } |
| |
| // Timeout |
| if (Tlv::GetTlv(aMessage, Tlv::kTimeout, sizeof(timeout), timeout) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(timeout.IsValid(), error = OT_ERROR_PARSE); |
| child->SetTimeout(timeout.GetTimeout()); |
| } |
| |
| // Ip6 Address |
| if (Tlv::GetTlv(aMessage, Tlv::kAddressRegistration, sizeof(address), address) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(address.IsValid(), error = OT_ERROR_PARSE); |
| UpdateChildAddresses(address, *child); |
| } |
| |
| // Leader Data |
| if (Tlv::GetTlv(aMessage, Tlv::kLeaderData, sizeof(leaderData), leaderData) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(leaderData.IsValid(), error = OT_ERROR_PARSE); |
| |
| if (child->IsFullNetworkData()) |
| { |
| child->SetNetworkDataVersion(leaderData.GetDataVersion()); |
| } |
| else |
| { |
| child->SetNetworkDataVersion(leaderData.GetStableDataVersion()); |
| } |
| } |
| |
| SetChildStateToValid(child); |
| child->SetLastHeard(Timer::GetNow()); |
| child->SetKeySequence(aKeySequence); |
| child->GetLinkInfo().AddRss(GetNetif().GetMac().GetNoiseFloor(), threadMessageInfo->mRss); |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::HandleDataRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| TlvRequestTlv tlvRequest; |
| ActiveTimestampTlv activeTimestamp; |
| PendingTimestampTlv pendingTimestamp; |
| uint8_t tlvs[4]; |
| uint8_t numTlvs; |
| |
| otLogInfoMle(GetInstance(), "Received Data Request"); |
| |
| // TLV Request |
| SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kTlvRequest, sizeof(tlvRequest), tlvRequest)); |
| VerifyOrExit(tlvRequest.IsValid() && tlvRequest.GetLength() <= sizeof(tlvs), error = OT_ERROR_PARSE); |
| |
| // Active Timestamp |
| activeTimestamp.SetLength(0); |
| |
| if (Tlv::GetTlv(aMessage, Tlv::kActiveTimestamp, sizeof(activeTimestamp), activeTimestamp) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(activeTimestamp.IsValid(), error = OT_ERROR_PARSE); |
| } |
| |
| // Pending Timestamp |
| pendingTimestamp.SetLength(0); |
| |
| if (Tlv::GetTlv(aMessage, Tlv::kPendingTimestamp, sizeof(pendingTimestamp), pendingTimestamp) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(pendingTimestamp.IsValid(), error = OT_ERROR_PARSE); |
| } |
| |
| memset(tlvs, Tlv::kInvalid, sizeof(tlvs)); |
| memcpy(tlvs, tlvRequest.GetTlvs(), tlvRequest.GetLength()); |
| numTlvs = tlvRequest.GetLength(); |
| |
| if (activeTimestamp.GetLength() == 0 || |
| netif.GetActiveDataset().Compare(activeTimestamp)) |
| { |
| tlvs[numTlvs++] = Tlv::kActiveDataset; |
| } |
| |
| if (pendingTimestamp.GetLength() == 0 || |
| netif.GetPendingDataset().Compare(pendingTimestamp)) |
| { |
| tlvs[numTlvs++] = Tlv::kPendingDataset; |
| } |
| |
| SendDataResponse(aMessageInfo.GetPeerAddr(), tlvs, numTlvs, 0); |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::HandleNetworkDataUpdateRouter(void) |
| { |
| static const uint8_t tlvs[] = {Tlv::kNetworkData}; |
| Ip6::Address destination; |
| uint16_t delay; |
| |
| VerifyOrExit(mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER); |
| |
| memset(&destination, 0, sizeof(destination)); |
| destination.mFields.m16[0] = HostSwap16(0xff02); |
| destination.mFields.m16[7] = HostSwap16(0x0001); |
| |
| delay = (mRole == OT_DEVICE_ROLE_LEADER) ? 0 : (otPlatRandomGet() % kUnsolicitedDataResponseJitter); |
| SendDataResponse(destination, tlvs, sizeof(tlvs), delay); |
| |
| SynchronizeChildNetworkData(); |
| |
| exit: |
| return OT_ERROR_NONE; |
| } |
| |
| void MleRouter::SynchronizeChildNetworkData(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| VerifyOrExit(mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER); |
| |
| for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| Child *child = &mChildren[i]; |
| uint8_t version; |
| |
| if (child->GetState() != Neighbor::kStateValid || child->IsRxOnWhenIdle()) |
| { |
| continue; |
| } |
| |
| if (child->IsFullNetworkData()) |
| { |
| version = netif.GetNetworkDataLeader().GetVersion(); |
| } |
| else |
| { |
| version = netif.GetNetworkDataLeader().GetStableVersion(); |
| } |
| |
| if (child->GetNetworkDataVersion() == version) |
| { |
| continue; |
| } |
| |
| SuccessOrExit(SendChildUpdateRequest(child)); |
| } |
| |
| exit: |
| return; |
| } |
| |
| #if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB |
| otError MleRouter::SetSteeringData(otExtAddress *aExtAddress) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t nullExtAddr[OT_EXT_ADDRESS_SIZE]; |
| uint8_t allowAnyExtAddr[OT_EXT_ADDRESS_SIZE]; |
| |
| memset(nullExtAddr, 0, sizeof(nullExtAddr)); |
| memset(allowAnyExtAddr, 0xFF, sizeof(allowAnyExtAddr)); |
| |
| mSteeringData.Init(); |
| |
| if ((aExtAddress == NULL) || (memcmp(aExtAddress, &nullExtAddr, sizeof(nullExtAddr)) == 0)) |
| { |
| // Clear steering data |
| mSteeringData.Clear(); |
| } |
| else if (memcmp(aExtAddress, &allowAnyExtAddr, sizeof(allowAnyExtAddr)) == 0) |
| { |
| // Set steering data to 0xFF |
| mSteeringData.SetLength(1); |
| mSteeringData.Set(); |
| } |
| else |
| { |
| // Set bloom filter with the extended address passed in |
| mSteeringData.ComputeBloomFilter(aExtAddress); |
| } |
| |
| return error; |
| } |
| #endif // OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB |
| |
| otError MleRouter::HandleDiscoveryRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Tlv tlv; |
| MeshCoP::Tlv meshcopTlv; |
| MeshCoP::DiscoveryRequestTlv discoveryRequest; |
| MeshCoP::ExtendedPanIdTlv extPanId; |
| uint16_t offset; |
| uint16_t end; |
| |
| otLogInfoMle(GetInstance(), "Received discovery request"); |
| |
| // only Routers and REEDs respond |
| VerifyOrExit((mDeviceMode & ModeTlv::kModeFFD) != 0, error = OT_ERROR_INVALID_STATE); |
| |
| // 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 + sizeof(tlv) + tlv.GetLength(); |
| |
| while (offset < end) |
| { |
| aMessage.Read(offset, sizeof(meshcopTlv), &meshcopTlv); |
| |
| switch (meshcopTlv.GetType()) |
| { |
| case MeshCoP::Tlv::kDiscoveryRequest: |
| aMessage.Read(offset, sizeof(discoveryRequest), &discoveryRequest); |
| VerifyOrExit(discoveryRequest.IsValid(), error = OT_ERROR_PARSE); |
| |
| if (discoveryRequest.IsJoiner()) |
| { |
| #if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB |
| |
| if (!mSteeringData.IsCleared()) |
| { |
| break; |
| } |
| else // if steering data is not set out of band, fall back to network data |
| #endif // OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB |
| { |
| VerifyOrExit(netif.GetNetworkDataLeader().IsJoiningEnabled(), error = OT_ERROR_NOT_CAPABLE); |
| } |
| } |
| |
| break; |
| |
| case MeshCoP::Tlv::kExtendedPanId: |
| aMessage.Read(offset, sizeof(extPanId), &extPanId); |
| VerifyOrExit(extPanId.IsValid(), error = OT_ERROR_PARSE); |
| VerifyOrExit(memcmp(netif.GetMac().GetExtendedPanId(), extPanId.GetExtendedPanId(), OT_EXT_PAN_ID_SIZE), |
| error = OT_ERROR_DROP); |
| break; |
| |
| default: |
| break; |
| } |
| |
| offset += sizeof(meshcopTlv) + meshcopTlv.GetLength(); |
| } |
| |
| error = SendDiscoveryResponse(aMessageInfo.GetPeerAddr(), aMessage.GetPanId()); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE) |
| { |
| otLogWarnMleErr(GetInstance(), error, "Failed to process Discovery Request"); |
| } |
| |
| return error; |
| } |
| |
| otError MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, uint16_t aPanId) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Message *message; |
| uint16_t startOffset; |
| Tlv tlv; |
| MeshCoP::DiscoveryResponseTlv discoveryResponse; |
| MeshCoP::ExtendedPanIdTlv extPanId; |
| MeshCoP::NetworkNameTlv networkName; |
| MeshCoP::JoinerUdpPortTlv joinerUdpPort; |
| MeshCoP::Tlv *steeringData; |
| uint16_t delay; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| message->SetSubType(Message::kSubTypeMleDiscoverResponse); |
| message->SetPanId(aPanId); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandDiscoveryResponse)); |
| |
| // Discovery TLV |
| tlv.SetType(Tlv::kDiscovery); |
| SuccessOrExit(error = message->Append(&tlv, sizeof(tlv))); |
| |
| startOffset = message->GetLength(); |
| |
| // Discovery Response TLV |
| discoveryResponse.Init(); |
| discoveryResponse.SetVersion(kVersion); |
| |
| if (netif.GetKeyManager().GetSecurityPolicyFlags() & OT_SECURITY_POLICY_NATIVE_COMMISSIONING) |
| { |
| discoveryResponse.SetNativeCommissioner(true); |
| } |
| else |
| { |
| discoveryResponse.SetNativeCommissioner(false); |
| } |
| |
| SuccessOrExit(error = message->Append(&discoveryResponse, sizeof(discoveryResponse))); |
| |
| // Extended PAN ID TLV |
| extPanId.Init(); |
| extPanId.SetExtendedPanId(netif.GetMac().GetExtendedPanId()); |
| SuccessOrExit(error = message->Append(&extPanId, sizeof(extPanId))); |
| |
| // Network Name TLV |
| networkName.Init(); |
| networkName.SetNetworkName(netif.GetMac().GetNetworkName()); |
| SuccessOrExit(error = message->Append(&networkName, sizeof(tlv) + networkName.GetLength())); |
| |
| #if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB |
| |
| // If steering data is set out of band, use that value. |
| // Otherwise use the one from commissioning data. |
| if (!mSteeringData.IsCleared()) |
| { |
| SuccessOrExit(error = message->Append(&mSteeringData, sizeof(mSteeringData) + mSteeringData.GetLength())); |
| } |
| else |
| #endif // OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB |
| { |
| // Steering Data TLV |
| steeringData = netif.GetNetworkDataLeader().GetCommissioningDataSubTlv(MeshCoP::Tlv::kSteeringData); |
| |
| if (steeringData != NULL) |
| { |
| SuccessOrExit(error = message->Append(steeringData, sizeof(*steeringData) + steeringData->GetLength())); |
| } |
| } |
| |
| // Joiner UDP Port TLV |
| joinerUdpPort.Init(); |
| joinerUdpPort.SetUdpPort(netif.GetJoinerRouter().GetJoinerUdpPort()); |
| SuccessOrExit(error = message->Append(&joinerUdpPort, sizeof(tlv) + joinerUdpPort.GetLength())); |
| |
| tlv.SetLength(static_cast<uint8_t>(message->GetLength() - startOffset)); |
| message->Write(startOffset - sizeof(tlv), sizeof(tlv), &tlv); |
| |
| delay = otPlatRandomGet() % (kDiscoveryMaxJitter + 1); |
| |
| SuccessOrExit(error = AddDelayedResponse(*message, aDestination, delay)); |
| |
| otLogInfoMle(GetInstance(), "Sent discovery response"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError MleRouter::SendChildIdResponse(Child *aChild) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Ip6::Address destination; |
| Message *message; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandChildIdResponse)); |
| SuccessOrExit(error = AppendSourceAddress(*message)); |
| SuccessOrExit(error = AppendLeaderData(*message)); |
| SuccessOrExit(error = AppendActiveTimestamp(*message)); |
| SuccessOrExit(error = AppendPendingTimestamp(*message)); |
| |
| if (aChild->GetRloc16() == 0) |
| { |
| // pick next Child ID that is not being used |
| do |
| { |
| mNextChildId++; |
| |
| if (mNextChildId > kMaxChildId) |
| { |
| mNextChildId = kMinChildId; |
| } |
| } |
| while (FindChild(mNextChildId) != NULL); |
| |
| // allocate Child ID |
| aChild->SetRloc16(netif.GetMac().GetShortAddress() | mNextChildId); |
| } |
| |
| SuccessOrExit(error = AppendAddress16(*message, aChild->GetRloc16())); |
| |
| for (uint8_t i = 0; i < Child::kMaxRequestTlvs; i++) |
| { |
| switch (aChild->GetRequestTlv(i)) |
| { |
| case Tlv::kNetworkData: |
| SuccessOrExit(error = AppendNetworkData(*message, !aChild->IsFullNetworkData())); |
| break; |
| |
| case Tlv::kRoute: |
| SuccessOrExit(error = AppendRoute(*message)); |
| break; |
| |
| case Tlv::kActiveDataset: |
| SuccessOrExit(error = AppendActiveDataset(*message)); |
| break; |
| |
| case Tlv::kPendingDataset: |
| SuccessOrExit(error = AppendPendingDataset(*message)); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| if (!aChild->IsFullThreadDevice()) |
| { |
| SuccessOrExit(error = AppendChildAddresses(*message, *aChild)); |
| } |
| |
| SetChildStateToValid(aChild); |
| |
| if (!aChild->IsRxOnWhenIdle()) |
| { |
| netif.GetMeshForwarder().GetSourceMatchController().SetSrcMatchAsShort(*aChild, false); |
| } |
| |
| memset(&destination, 0, sizeof(destination)); |
| destination.mFields.m16[0] = HostSwap16(0xfe80); |
| destination.SetIid(aChild->GetExtAddress()); |
| SuccessOrExit(error = SendMessage(*message, destination)); |
| |
| otLogInfoMle(GetInstance(), "Sent Child ID Response"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError MleRouter::SendChildUpdateRequest(Child *aChild) |
| { |
| static const uint8_t tlvs[] = {Tlv::kTimeout, Tlv::kAddressRegistration}; |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Ip6::Address destination; |
| Message *message; |
| |
| if (!aChild->IsRxOnWhenIdle()) |
| { |
| uint8_t childIndex = netif.GetMle().GetChildIndex(*aChild); |
| |
| // No need to send "Child Update Request" to the sleepy child if there is one already queued for it. |
| for (message = netif.GetMeshForwarder().GetSendQueue().GetHead(); message; message = message->GetNext()) |
| { |
| if (message->GetChildMask(childIndex) && message->GetSubType() == Message::kSubTypeMleChildUpdateRequest) |
| { |
| ExitNow(); |
| } |
| } |
| } |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| message->SetSubType(Message::kSubTypeMleChildUpdateRequest); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandChildUpdateRequest)); |
| SuccessOrExit(error = AppendSourceAddress(*message)); |
| SuccessOrExit(error = AppendLeaderData(*message)); |
| SuccessOrExit(error = AppendNetworkData(*message, !aChild->IsFullNetworkData())); |
| SuccessOrExit(error = AppendActiveTimestamp(*message)); |
| SuccessOrExit(error = AppendPendingTimestamp(*message)); |
| SuccessOrExit(error = AppendTlvRequest(*message, tlvs, sizeof(tlvs))); |
| |
| aChild->GenerateChallenge(); |
| |
| SuccessOrExit(error = AppendChallenge(*message, aChild->GetChallenge(), aChild->GetChallengeSize())); |
| |
| memset(&destination, 0, sizeof(destination)); |
| destination.mFields.m16[0] = HostSwap16(0xfe80); |
| destination.SetIid(aChild->GetExtAddress()); |
| SuccessOrExit(error = SendMessage(*message, destination)); |
| |
| if (aChild->IsRxOnWhenIdle()) |
| { |
| // only try to send a single Child Update Request message to an rx-on-when-idle child |
| aChild->SetState(Child::kStateChildUpdateRequest); |
| } |
| |
| otLogInfoMle(GetInstance(), "Sent Child Update Request to child"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError MleRouter::SendChildUpdateResponse(Child *aChild, const Ip6::MessageInfo &aMessageInfo, |
| const uint8_t *aTlvs, uint8_t aTlvslength, |
| const ChallengeTlv *aChallenge) |
| { |
| otError error = OT_ERROR_NONE; |
| Message *message; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandChildUpdateResponse)); |
| |
| for (int i = 0; i < aTlvslength; i++) |
| { |
| switch (aTlvs[i]) |
| { |
| case Tlv::kStatus: |
| SuccessOrExit(error = AppendStatus(*message, StatusTlv::kError)); |
| break; |
| |
| case Tlv::kAddressRegistration: |
| SuccessOrExit(error = AppendChildAddresses(*message, *aChild)); |
| break; |
| |
| case Tlv::kLeaderData: |
| SuccessOrExit(error = AppendLeaderData(*message)); |
| break; |
| |
| case Tlv::kMode: |
| SuccessOrExit(error = AppendMode(*message, aChild->GetDeviceMode())); |
| break; |
| |
| case Tlv::kNetworkData: |
| SuccessOrExit(error = AppendNetworkData(*message, !aChild->IsFullNetworkData())); |
| SuccessOrExit(error = AppendActiveTimestamp(*message)); |
| SuccessOrExit(error = AppendPendingTimestamp(*message)); |
| break; |
| |
| case Tlv::kResponse: |
| SuccessOrExit(error = AppendResponse(*message, aChallenge->GetChallenge(), aChallenge->GetLength())); |
| break; |
| |
| case Tlv::kSourceAddress: |
| SuccessOrExit(error = AppendSourceAddress(*message)); |
| break; |
| |
| case Tlv::kTimeout: |
| SuccessOrExit(error = AppendTimeout(*message, aChild->GetTimeout())); |
| break; |
| |
| case Tlv::kMleFrameCounter: |
| SuccessOrExit(error = AppendMleFrameCounter(*message)); |
| break; |
| |
| case Tlv::kLinkFrameCounter: |
| SuccessOrExit(error = AppendLinkFrameCounter(*message)); |
| break; |
| } |
| } |
| |
| SuccessOrExit(error = SendMessage(*message, aMessageInfo.GetPeerAddr())); |
| |
| otLogInfoMle(GetInstance(), "Sent Child Update Response to child"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otError MleRouter::SendDataResponse(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength, |
| uint16_t aDelay) |
| { |
| otError error = OT_ERROR_NONE; |
| Message *message; |
| Neighbor *neighbor; |
| bool stableOnly; |
| |
| VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = AppendHeader(*message, Header::kCommandDataResponse)); |
| SuccessOrExit(error = AppendSourceAddress(*message)); |
| |