| /* |
| * 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(); |
| netif.UnsubscribeAllRoutersMulticast(); |
| } |
| |
| 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); |
| } |
| } |
| } |
| } |
| |
| 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}; |
| ThreadNetif &netif = GetNetif(); |
| 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); |
| |
| for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| Child *child = &mChildren[i]; |
| |
| if (child->GetState() != Neighbor::kStateValid || child->IsRxOnWhenIdle()) |
| { |
| continue; |
| } |
| |
| memset(&destination, 0, sizeof(destination)); |
| destination.mFields.m16[0] = HostSwap16(0xfe80); |
| destination.SetIid(child->GetExtAddress()); |
| |
| if (child->IsFullNetworkData()) |
| { |
| if (child->GetNetworkDataVersion() != netif.GetNetworkDataLeader().GetVersion()) |
| { |
| SendDataResponse(destination, tlvs, sizeof(tlvs), 0); |
| } |
| } |
| else |
| { |
| if (child->GetNetworkDataVersion() != netif.GetNetworkDataLeader().GetStableVersion()) |
| { |
| SendDataResponse(destination, tlvs, sizeof(tlvs), 0); |
| } |
| } |
| } |
| |
| exit: |
| return OT_ERROR_NONE; |
| } |
| |
| #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->GetState() != Neighbor::kStateValid) |
| { |
| // 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)); |
| SuccessOrExit(error = AppendLeaderData(*message)); |
| SuccessOrExit(error = AppendActiveTimestamp(*message)); |
| SuccessOrExit(error = AppendPendingTimestamp(*message)); |
| |
| for (int i = 0; i < aTlvsLength; i++) |
| { |
| switch (aTlvs[i]) |
| { |
| case Tlv::kNetworkData: |
| neighbor = GetNeighbor(aDestination); |
| stableOnly = neighbor != NULL ? !neighbor->IsFullNetworkData() : false; |
| SuccessOrExit(error = AppendNetworkData(*message, stableOnly)); |
| break; |
| |
| case Tlv::kActiveDataset: |
| SuccessOrExit(error = AppendActiveDataset(*message)); |
| break; |
| |
| case Tlv::kPendingDataset: |
| SuccessOrExit(error = AppendPendingDataset(*message)); |
| break; |
| } |
| } |
| |
| if (aDelay) |
| { |
| SuccessOrExit(error = AddDelayedResponse(*message, aDestination, aDelay)); |
| } |
| else |
| { |
| SuccessOrExit(error = SendMessage(*message, aDestination)); |
| } |
| |
| otLogInfoMle(GetInstance(), "Sent Data Response"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| Child *MleRouter::GetChild(uint16_t aAddress) |
| { |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].IsStateValidOrRestoring() && mChildren[i].GetRloc16() == aAddress) |
| { |
| return &mChildren[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| Child *MleRouter::GetChild(const Mac::ExtAddress &aAddress) |
| { |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].IsStateValidOrRestoring() && |
| memcmp(&mChildren[i].GetExtAddress(), &aAddress, sizeof(aAddress)) == 0) |
| { |
| return &mChildren[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| Child *MleRouter::GetChild(const Mac::Address &aAddress) |
| { |
| switch (aAddress.mLength) |
| { |
| case sizeof(aAddress.mShortAddress): |
| return GetChild(aAddress.mShortAddress); |
| |
| case sizeof(aAddress.mExtAddress): |
| return GetChild(aAddress.mExtAddress); |
| } |
| |
| return NULL; |
| } |
| |
| uint8_t MleRouter::GetChildIndex(const Child &child) |
| { |
| return static_cast<uint8_t>(&child - mChildren); |
| } |
| |
| Child *MleRouter::GetChildren(uint8_t *numChildren) |
| { |
| if (numChildren != NULL) |
| { |
| *numChildren = mMaxChildrenAllowed; |
| } |
| |
| return mChildren; |
| } |
| |
| otError MleRouter::SetMaxAllowedChildren(uint8_t aMaxChildren) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| // Ensure the value is between 1 and kMaxChildren |
| VerifyOrExit(aMaxChildren > 0 && aMaxChildren <= kMaxChildren, error = OT_ERROR_INVALID_ARGS); |
| |
| // Do not allow setting max children if MLE is running |
| VerifyOrExit(mRole == OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE); |
| |
| // Save the value |
| mMaxChildrenAllowed = aMaxChildren; |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::RemoveNeighbor(const Mac::Address &aAddress) |
| { |
| otError error = OT_ERROR_NONE; |
| Neighbor *neighbor; |
| |
| VerifyOrExit((neighbor = GetNeighbor(aAddress)) != NULL, error = OT_ERROR_NOT_FOUND); |
| SuccessOrExit(error = RemoveNeighbor(*neighbor)); |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::RemoveNeighbor(Neighbor &aNeighbor) |
| { |
| ThreadNetif &netif = GetNetif(); |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| if (&aNeighbor == &mParent) |
| { |
| BecomeDetached(); |
| } |
| |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| if (aNeighbor.IsStateValidOrRestoring() && !IsActiveRouter(aNeighbor.GetRloc16())) |
| { |
| aNeighbor.SetState(Neighbor::kStateInvalid); |
| netif.GetMeshForwarder().UpdateIndirectMessages(); |
| netif.SetStateChangedFlags(OT_CHANGED_THREAD_CHILD_REMOVED); |
| netif.GetNetworkDataLeader().SendServerDataNotification(aNeighbor.GetRloc16()); |
| RemoveStoredChild(aNeighbor.GetRloc16()); |
| } |
| else if ((aNeighbor.GetState() == Neighbor::kStateValid) && IsActiveRouter(aNeighbor.GetRloc16())) |
| { |
| Router &routerToRemove = static_cast<Router &>(aNeighbor); |
| |
| routerToRemove.SetLinkQualityOut(0); |
| routerToRemove.SetLastHeard(Timer::GetNow()); |
| |
| for (uint8_t j = 0; j <= kMaxRouterId; j++) |
| { |
| if (mRouters[j].GetNextHop() == GetRouterId(routerToRemove.GetRloc16())) |
| { |
| mRouters[j].SetNextHop(kInvalidRouterId); |
| mRouters[j].SetCost(0); |
| |
| if (GetLinkCost(j) >= kMaxRouteCost) |
| { |
| ResetAdvertiseInterval(); |
| } |
| } |
| } |
| |
| if (routerToRemove.GetNextHop() == kInvalidRouterId) |
| { |
| ResetAdvertiseInterval(); |
| } |
| } |
| |
| break; |
| } |
| |
| aNeighbor.GetLinkInfo().Clear(); |
| aNeighbor.SetState(Neighbor::kStateInvalid); |
| |
| return OT_ERROR_NONE; |
| } |
| |
| Neighbor *MleRouter::GetNeighbor(uint16_t aAddress) |
| { |
| Neighbor *rval = NULL; |
| |
| if (aAddress == Mac::kShortAddrBroadcast || aAddress == Mac::kShortAddrInvalid) |
| { |
| ExitNow(); |
| } |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| break; |
| |
| case OT_DEVICE_ROLE_DETACHED: |
| case OT_DEVICE_ROLE_CHILD: |
| rval = Mle::GetNeighbor(aAddress); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].IsStateValidOrRestoring() && mChildren[i].GetRloc16() == aAddress) |
| { |
| ExitNow(rval = &mChildren[i]); |
| } |
| } |
| |
| for (int i = 0; i <= kMaxRouterId; i++) |
| { |
| if (i == mRouterId) |
| { |
| continue; |
| } |
| |
| if (mRouters[i].GetState() == Neighbor::kStateValid && mRouters[i].GetRloc16() == aAddress) |
| { |
| ExitNow(rval = &mRouters[i]); |
| } |
| } |
| |
| break; |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| Neighbor *MleRouter::GetNeighbor(const Mac::ExtAddress &aAddress) |
| { |
| Neighbor *rval = NULL; |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| break; |
| |
| case OT_DEVICE_ROLE_DETACHED: |
| case OT_DEVICE_ROLE_CHILD: |
| rval = Mle::GetNeighbor(aAddress); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| case OT_DEVICE_ROLE_LEADER: |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].IsStateValidOrRestoring() && |
| memcmp(&mChildren[i].GetExtAddress(), &aAddress, sizeof(aAddress)) == 0) |
| { |
| ExitNow(rval = &mChildren[i]); |
| } |
| } |
| |
| for (int i = 0; i <= kMaxRouterId; i++) |
| { |
| if (i == mRouterId) |
| { |
| continue; |
| } |
| |
| if (mRouters[i].GetState() == Neighbor::kStateValid && |
| memcmp(&mRouters[i].GetExtAddress(), &aAddress, sizeof(aAddress)) == 0) |
| { |
| ExitNow(rval = &mRouters[i]); |
| } |
| } |
| |
| if (mParentRequestState != kParentIdle) |
| { |
| rval = Mle::GetNeighbor(aAddress); |
| } |
| |
| break; |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| Neighbor *MleRouter::GetNeighbor(const Mac::Address &aAddress) |
| { |
| Neighbor *rval = NULL; |
| |
| switch (aAddress.mLength) |
| { |
| case sizeof(aAddress.mShortAddress): |
| rval = GetNeighbor(aAddress.mShortAddress); |
| break; |
| |
| case sizeof(aAddress.mExtAddress): |
| rval = GetNeighbor(aAddress.mExtAddress); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return rval; |
| } |
| |
| Neighbor *MleRouter::GetNeighbor(const Ip6::Address &aAddress) |
| { |
| Mac::Address macaddr; |
| Lowpan::Context context; |
| Child *child; |
| Router *router; |
| Neighbor *rval = NULL; |
| |
| if (aAddress.IsLinkLocal()) |
| { |
| if (aAddress.mFields.m16[4] == HostSwap16(0x0000) && |
| aAddress.mFields.m16[5] == HostSwap16(0x00ff) && |
| aAddress.mFields.m16[6] == HostSwap16(0xfe00)) |
| { |
| macaddr.mLength = sizeof(macaddr.mShortAddress); |
| macaddr.mShortAddress = HostSwap16(aAddress.mFields.m16[7]); |
| } |
| else |
| { |
| macaddr.mLength = sizeof(macaddr.mExtAddress); |
| macaddr.mExtAddress.Set(aAddress); |
| } |
| |
| ExitNow(rval = GetNeighbor(macaddr)); |
| } |
| |
| if (GetNetif().GetNetworkDataLeader().GetContext(aAddress, context) != OT_ERROR_NONE) |
| { |
| context.mContextId = 0xff; |
| } |
| |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| child = &mChildren[i]; |
| |
| if (!child->IsStateValidOrRestoring()) |
| { |
| continue; |
| } |
| |
| if (context.mContextId == 0 && |
| aAddress.mFields.m16[4] == HostSwap16(0x0000) && |
| aAddress.mFields.m16[5] == HostSwap16(0x00ff) && |
| aAddress.mFields.m16[6] == HostSwap16(0xfe00) && |
| aAddress.mFields.m16[7] == HostSwap16(child->GetRloc16())) |
| { |
| ExitNow(rval = child); |
| } |
| |
| if (child->FindIp6Address(aAddress, NULL) == OT_ERROR_NONE) |
| { |
| ExitNow(rval = child); |
| } |
| } |
| |
| VerifyOrExit(context.mContextId == 0, rval = NULL); |
| |
| for (int i = 0; i <= kMaxRouterId; i++) |
| { |
| router = &mRouters[i]; |
| |
| if (router->GetState() != Neighbor::kStateValid || i == mRouterId) |
| { |
| continue; |
| } |
| |
| if (aAddress.mFields.m16[4] == HostSwap16(0x0000) && |
| aAddress.mFields.m16[5] == HostSwap16(0x00ff) && |
| aAddress.mFields.m16[6] == HostSwap16(0xfe00) && |
| aAddress.mFields.m16[7] == HostSwap16(router->GetRloc16())) |
| { |
| ExitNow(rval = router); |
| } |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| uint16_t MleRouter::GetNextHop(uint16_t aDestination) |
| { |
| uint8_t destinationId = GetRouterId(aDestination); |
| uint8_t routeCost; |
| uint8_t linkCost; |
| uint16_t rval = Mac::kShortAddrInvalid; |
| const Router *router; |
| const Router *nextHop; |
| |
| if (mRole == OT_DEVICE_ROLE_CHILD) |
| { |
| ExitNow(rval = Mle::GetNextHop(aDestination)); |
| } |
| |
| // The frame is destined to a child |
| if (destinationId == mRouterId) |
| { |
| ExitNow(rval = aDestination); |
| } |
| |
| router = GetRouter(destinationId); |
| VerifyOrExit(router != NULL); |
| |
| linkCost = GetLinkCost(destinationId); |
| routeCost = GetRouteCost(aDestination); |
| |
| if ((routeCost + GetLinkCost(router->GetNextHop())) < linkCost) |
| { |
| nextHop = GetRouter(router->GetNextHop()); |
| VerifyOrExit(nextHop != NULL && nextHop->GetState() != Neighbor::kStateInvalid); |
| |
| rval = GetRloc16(router->GetNextHop()); |
| } |
| else if (linkCost < kMaxRouteCost) |
| { |
| rval = GetRloc16(destinationId); |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| uint8_t MleRouter::GetCost(uint16_t aRloc16) |
| { |
| uint8_t routerId = GetRouterId(aRloc16); |
| uint8_t cost = GetLinkCost(routerId); |
| Router *router = GetRouter(routerId); |
| uint8_t routeCost; |
| |
| VerifyOrExit(router != NULL && GetRouter(router->GetNextHop()) != NULL); |
| |
| routeCost = GetRouteCost(aRloc16) + GetLinkCost(GetRouter(routerId)->GetNextHop()); |
| |
| if (cost > routeCost) |
| { |
| cost = routeCost; |
| } |
| |
| exit: |
| return cost; |
| } |
| |
| uint8_t MleRouter::GetRouteCost(uint16_t aRloc16) const |
| { |
| uint8_t rval = kMaxRouteCost; |
| const Router *router; |
| |
| router = GetRouter(GetRouterId(aRloc16)); |
| VerifyOrExit(router != NULL && GetRouter(router->GetNextHop()) != NULL); |
| |
| rval = router->GetCost(); |
| |
| exit: |
| return rval; |
| } |
| |
| otError MleRouter::SetPreferredRouterId(uint8_t aRouterId) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit((mRole == OT_DEVICE_ROLE_DETACHED) || (mRole == OT_DEVICE_ROLE_DISABLED), |
| error = OT_ERROR_INVALID_STATE); |
| |
| mPreviousRouterId = aRouterId; |
| |
| exit: |
| return error; |
| } |
| |
| void MleRouter::SetRouterId(uint8_t aRouterId) |
| { |
| mRouterId = aRouterId; |
| mPreviousRouterId = mRouterId; |
| } |
| |
| Router *MleRouter::GetRouters(uint8_t *aNumRouters) |
| { |
| if (aNumRouters != NULL) |
| { |
| *aNumRouters = kMaxRouterId + 1; |
| } |
| |
| return mRouters; |
| } |
| |
| Router *MleRouter::GetRouter(uint8_t aRouterId) |
| { |
| Router *rval = NULL; |
| |
| VerifyOrExit(aRouterId <= kMaxRouterId); |
| |
| rval = &mRouters[aRouterId]; |
| |
| exit: |
| return rval; |
| } |
| |
| const Router *MleRouter::GetRouter(uint8_t aRouterId) const |
| { |
| const Router *rval = NULL; |
| |
| VerifyOrExit(aRouterId <= kMaxRouterId); |
| |
| rval = &mRouters[aRouterId]; |
| |
| exit: |
| return rval; |
| } |
| |
| otError MleRouter::GetChildInfoById(uint16_t aChildId, otChildInfo &aChildInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| Child *child; |
| |
| if ((aChildId & ~kMaxChildId) != 0) |
| { |
| aChildId = GetChildId(aChildId); |
| } |
| |
| VerifyOrExit((child = FindChild(aChildId)) != NULL, error = OT_ERROR_NOT_FOUND); |
| error = GetChildInfo(*child, aChildInfo); |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::GetChildInfoByIndex(uint8_t aChildIndex, otChildInfo &aChildInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aChildIndex < mMaxChildrenAllowed, error = OT_ERROR_INVALID_ARGS); |
| error = GetChildInfo(mChildren[aChildIndex], aChildInfo); |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::RestoreChildren(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| for (uint8_t i = 0; ; i++) |
| { |
| Child *child; |
| Settings::ChildInfo childInfo; |
| uint16_t length; |
| |
| length = sizeof(childInfo); |
| SuccessOrExit(error = otPlatSettingsGet(GetInstance(), Settings::kKeyChildInfo, i, |
| reinterpret_cast<uint8_t *>(&childInfo), &length)); |
| VerifyOrExit(length >= sizeof(childInfo), error = OT_ERROR_PARSE); |
| |
| VerifyOrExit((child = NewChild()) != NULL, error = OT_ERROR_NO_BUFS); |
| memset(child, 0, sizeof(*child)); |
| |
| child->SetExtAddress(*static_cast<Mac::ExtAddress *>(&childInfo.mExtAddress)); |
| child->SetRloc16(childInfo.mRloc16); |
| child->SetTimeout(childInfo.mTimeout); |
| child->SetDeviceMode(childInfo.mMode); |
| child->SetState(Neighbor::kStateRestored); |
| child->SetLastHeard(Timer::GetNow()); |
| GetNetif().GetMeshForwarder().GetSourceMatchController().SetSrcMatchAsShort(*child, true); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::RemoveStoredChild(uint16_t aChildRloc16) |
| { |
| otError error = OT_ERROR_NOT_FOUND; |
| |
| for (uint8_t i = 0; i < kMaxChildren; i++) |
| { |
| Settings::ChildInfo childInfo; |
| uint16_t length = sizeof(childInfo); |
| |
| SuccessOrExit(error = otPlatSettingsGet(GetInstance(), Settings::kKeyChildInfo, i, |
| reinterpret_cast<uint8_t *>(&childInfo), &length)); |
| VerifyOrExit(length == sizeof(childInfo), error = OT_ERROR_PARSE); |
| |
| if (childInfo.mRloc16 == aChildRloc16) |
| { |
| error = otPlatSettingsDelete(GetNetif().GetInstance(), Settings::kKeyChildInfo, i); |
| ExitNow(); |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::StoreChild(uint16_t aChildRloc16) |
| { |
| otError error = OT_ERROR_NONE; |
| Child *child; |
| Settings::ChildInfo childInfo; |
| |
| VerifyOrExit((child = FindChild(GetChildId(aChildRloc16))) != NULL, error = OT_ERROR_NOT_FOUND); |
| |
| IgnoreReturnValue(RemoveStoredChild(aChildRloc16)); |
| |
| memset(&childInfo, 0, sizeof(childInfo)); |
| memcpy(&childInfo.mExtAddress, &child->GetExtAddress(), sizeof(childInfo.mExtAddress)); |
| |
| childInfo.mTimeout = child->GetTimeout(); |
| childInfo.mRloc16 = child->GetRloc16(); |
| childInfo.mMode = child->GetDeviceMode(); |
| |
| error = otPlatSettingsAdd(GetInstance(), Settings::kKeyChildInfo, reinterpret_cast<uint8_t *>(&childInfo), |
| sizeof(childInfo)); |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::RefreshStoredChildren(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = otPlatSettingsDelete(GetInstance(), Settings::kKeyChildInfo, -1)); |
| |
| for (uint8_t i = 0; i < kMaxChildren; i++) |
| { |
| if (mChildren[i].GetState() != Neighbor::kStateInvalid) |
| { |
| SuccessOrExit(error = StoreChild(mChildren[i].GetRloc16())); |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::GetChildInfo(Child &aChild, otChildInfo &aChildInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aChild.GetState() == Neighbor::kStateValid, error = OT_ERROR_NOT_FOUND); |
| |
| memset(&aChildInfo, 0, sizeof(aChildInfo)); |
| memcpy(&aChildInfo.mExtAddress, &aChild.GetExtAddress(), sizeof(aChildInfo.mExtAddress)); |
| |
| aChildInfo.mTimeout = aChild.GetTimeout(); |
| aChildInfo.mRloc16 = aChild.GetRloc16(); |
| aChildInfo.mChildId = GetChildId(aChild.GetRloc16()); |
| aChildInfo.mNetworkDataVersion = aChild.GetNetworkDataVersion(); |
| aChildInfo.mAge = Timer::MsecToSec(Timer::GetNow() - aChild.GetLastHeard()); |
| aChildInfo.mLinkQualityIn = aChild.GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor()); |
| aChildInfo.mAverageRssi = aChild.GetLinkInfo().GetAverageRss(); |
| aChildInfo.mLastRssi = aChild.GetLinkInfo().GetLastRss(); |
| |
| aChildInfo.mRxOnWhenIdle = aChild.IsRxOnWhenIdle(); |
| aChildInfo.mSecureDataRequest = aChild.IsSecureDataRequest(); |
| aChildInfo.mFullFunction = aChild.IsFullThreadDevice(); |
| aChildInfo.mFullNetworkData = aChild.IsFullNetworkData(); |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::GetRouterInfo(uint16_t aRouterId, otRouterInfo &aRouterInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| Router *router; |
| uint8_t routerId; |
| |
| if (aRouterId > kMaxRouterId && IsActiveRouter(aRouterId)) |
| { |
| routerId = GetRouterId(aRouterId); |
| } |
| else |
| { |
| routerId = static_cast<uint8_t>(aRouterId); |
| } |
| |
| router = GetRouter(routerId); |
| VerifyOrExit(router != NULL, error = OT_ERROR_INVALID_ARGS); |
| |
| memcpy(&aRouterInfo.mExtAddress, &router->GetExtAddress(), sizeof(aRouterInfo.mExtAddress)); |
| |
| aRouterInfo.mAllocated = router->IsAllocated(); |
| aRouterInfo.mRouterId = routerId; |
| aRouterInfo.mRloc16 = GetRloc16(routerId); |
| aRouterInfo.mNextHop = router->GetNextHop(); |
| aRouterInfo.mLinkEstablished = router->GetState() == Neighbor::kStateValid; |
| aRouterInfo.mPathCost = router->GetCost(); |
| aRouterInfo.mLinkQualityIn = router->GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor()); |
| aRouterInfo.mLinkQualityOut = router->GetLinkQualityOut(); |
| aRouterInfo.mAge = static_cast<uint8_t>(Timer::MsecToSec(Timer::GetNow() - router->GetLastHeard())); |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::GetNextNeighborInfo(otNeighborInfoIterator &aIterator, otNeighborInfo &aNeighInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| Neighbor *neighbor = NULL; |
| int16_t index; |
| |
| memset(&aNeighInfo, 0, sizeof(aNeighInfo)); |
| |
| // Non-negative iterator value gives the current index into mChildren array |
| |
| if (aIterator >= 0) |
| { |
| for (index = aIterator; index < mMaxChildrenAllowed; index++) |
| { |
| if (mChildren[index].GetState() == Neighbor::kStateValid) |
| { |
| neighbor = &mChildren[index]; |
| aNeighInfo.mIsChild = true; |
| index++; |
| aIterator = index; |
| ExitNow(); |
| } |
| } |
| |
| aIterator = 0; |
| } |
| |
| // Negative iterator value gives the current index into mRouters array |
| |
| for (index = -aIterator; index <= kMaxRouterId; index++) |
| { |
| if (mRouters[index].GetState() == Neighbor::kStateValid) |
| { |
| neighbor = &mRouters[index]; |
| aNeighInfo.mIsChild = false; |
| index++; |
| aIterator = -index; |
| ExitNow(); |
| } |
| } |
| |
| aIterator = -index; |
| error = OT_ERROR_NOT_FOUND; |
| |
| exit: |
| |
| if (neighbor != NULL) |
| { |
| memcpy(&aNeighInfo.mExtAddress, &neighbor->GetExtAddress(), sizeof(aNeighInfo.mExtAddress)); |
| aNeighInfo.mAge = Timer::MsecToSec(Timer::GetNow() - neighbor->GetLastHeard()); |
| aNeighInfo.mRloc16 = neighbor->GetRloc16(); |
| aNeighInfo.mLinkFrameCounter = neighbor->GetLinkFrameCounter(); |
| aNeighInfo.mMleFrameCounter = neighbor->GetMleFrameCounter(); |
| aNeighInfo.mLinkQualityIn = neighbor->GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor()); |
| aNeighInfo.mAverageRssi = neighbor->GetLinkInfo().GetAverageRss(); |
| aNeighInfo.mLastRssi = neighbor->GetLinkInfo().GetLastRss(); |
| aNeighInfo.mRxOnWhenIdle = neighbor->IsRxOnWhenIdle(); |
| aNeighInfo.mSecureDataRequest = neighbor->IsSecureDataRequest(); |
| aNeighInfo.mFullFunction = neighbor->IsFullThreadDevice(); |
| aNeighInfo.mFullNetworkData = neighbor->IsFullNetworkData(); |
| } |
| |
| return error; |
| } |
| |
| void MleRouter::ResolveRoutingLoops(uint16_t aSourceMac, uint16_t aDestRloc16) |
| { |
| if (aSourceMac == GetNextHop(aDestRloc16)) |
| { |
| // loop detected |
| Router *router = GetRouter(GetRouterId(aDestRloc16)); |
| assert(router != NULL); |
| |
| // invalidate next hop |
| router->SetNextHop(kInvalidRouterId); |
| ResetAdvertiseInterval(); |
| } |
| } |
| |
| otError MleRouter::CheckReachability(uint16_t aMeshSource, uint16_t aMeshDest, Ip6::Header &aIp6Header) |
| { |
| ThreadNetif &netif = GetNetif(); |
| Ip6::MessageInfo messageInfo; |
| |
| if (mRole == OT_DEVICE_ROLE_CHILD) |
| { |
| return Mle::CheckReachability(aMeshSource, aMeshDest, aIp6Header); |
| } |
| |
| if (aMeshDest == netif.GetMac().GetShortAddress()) |
| { |
| // mesh destination is this device |
| if (netif.IsUnicastAddress(aIp6Header.GetDestination())) |
| { |
| // IPv6 destination is this device |
| return OT_ERROR_NONE; |
| } |
| else if (GetNeighbor(aIp6Header.GetDestination()) != NULL) |
| { |
| // IPv6 destination is an RFD child |
| return OT_ERROR_NONE; |
| } |
| } |
| else if (GetRouterId(aMeshDest) == mRouterId) |
| { |
| // mesh destination is a child of this device |
| if (GetChild(aMeshDest)) |
| { |
| return OT_ERROR_NONE; |
| } |
| } |
| else if (GetNextHop(aMeshDest) != Mac::kShortAddrInvalid) |
| { |
| // forwarding to another router and route is known |
| return 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); |
| |
| return OT_ERROR_DROP; |
| } |
| |
| otError MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Coap::Header header; |
| ThreadExtMacAddressTlv macAddr64Tlv; |
| ThreadRloc16Tlv rlocTlv; |
| ThreadStatusTlv statusTlv; |
| Ip6::MessageInfo messageInfo; |
| Message *message; |
| |
| header.Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST); |
| header.SetToken(Coap::Header::kDefaultTokenLength); |
| header.AppendUriPathOptions(OT_URI_PATH_ADDRESS_SOLICIT); |
| header.SetPayloadMarker(); |
| |
| VerifyOrExit((message = netif.GetCoap().NewMessage(header)) != NULL, error = OT_ERROR_NO_BUFS); |
| |
| macAddr64Tlv.Init(); |
| macAddr64Tlv.SetMacAddr(*netif.GetMac().GetExtAddress()); |
| SuccessOrExit(error = message->Append(&macAddr64Tlv, sizeof(macAddr64Tlv))); |
| |
| if (IsRouterIdValid(mPreviousRouterId)) |
| { |
| rlocTlv.Init(); |
| rlocTlv.SetRloc16(GetRloc16(mPreviousRouterId)); |
| SuccessOrExit(error = message->Append(&rlocTlv, sizeof(rlocTlv))); |
| } |
| |
| statusTlv.Init(); |
| statusTlv.SetStatus(aStatus); |
| SuccessOrExit(error = message->Append(&statusTlv, sizeof(statusTlv))); |
| |
| SuccessOrExit(error = GetLeaderAddress(messageInfo.GetPeerAddr())); |
| messageInfo.SetSockAddr(GetMeshLocal16()); |
| messageInfo.SetPeerPort(kCoapUdpPort); |
| |
| SuccessOrExit(error = netif.GetCoap().SendMessage(*message, messageInfo, |
| &MleRouter::HandleAddressSolicitResponse, this)); |
| |
| otLogInfoMle(GetInstance(), "Sent address solicit to %04x", HostSwap16(messageInfo.GetPeerAddr().mFields.m16[7])); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError MleRouter::SendAddressRelease(void) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Coap::Header header; |
| ThreadRloc16Tlv rlocTlv; |
| ThreadExtMacAddressTlv macAddr64Tlv; |
| Ip6::MessageInfo messageInfo; |
| Message *message; |
| |
| header.Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST); |
| header.SetToken(Coap::Header::kDefaultTokenLength); |
| header.AppendUriPathOptions(OT_URI_PATH_ADDRESS_RELEASE); |
| header.SetPayloadMarker(); |
| |
| VerifyOrExit((message = netif.GetCoap().NewMessage(header)) != NULL, error = OT_ERROR_NO_BUFS); |
| |
| rlocTlv.Init(); |
| rlocTlv.SetRloc16(GetRloc16(mRouterId)); |
| SuccessOrExit(error = message->Append(&rlocTlv, sizeof(rlocTlv))); |
| |
| macAddr64Tlv.Init(); |
| macAddr64Tlv.SetMacAddr(*netif.GetMac().GetExtAddress()); |
| SuccessOrExit(error = message->Append(&macAddr64Tlv, sizeof(macAddr64Tlv))); |
| |
| messageInfo.SetSockAddr(GetMeshLocal16()); |
| SuccessOrExit(error = GetLeaderAddress(messageInfo.GetPeerAddr())); |
| messageInfo.SetPeerPort(kCoapUdpPort); |
| SuccessOrExit(error = netif.GetCoap().SendMessage(*message, messageInfo)); |
| |
| otLogInfoMle(GetInstance(), "Sent address release"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| void MleRouter::HandleAddressSolicitResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage, |
| const otMessageInfo *aMessageInfo, otError aResult) |
| { |
| static_cast<MleRouter *>(aContext)->HandleAddressSolicitResponse(static_cast<Coap::Header *>(aHeader), |
| static_cast<Message *>(aMessage), |
| static_cast<const Ip6::MessageInfo *>(aMessageInfo), |
| aResult); |
| } |
| |
| void MleRouter::HandleAddressSolicitResponse(Coap::Header *aHeader, Message *aMessage, |
| const Ip6::MessageInfo *aMessageInfo, otError aResult) |
| { |
| OT_UNUSED_VARIABLE(aResult); |
| OT_UNUSED_VARIABLE(aMessageInfo); |
| |
| ThreadStatusTlv statusTlv; |
| ThreadRloc16Tlv rlocTlv; |
| ThreadRouterMaskTlv routerMaskTlv; |
| uint8_t routerId; |
| Router *router; |
| bool old; |
| |
| VerifyOrExit(aResult == OT_ERROR_NONE && aHeader != NULL && aMessage != NULL); |
| |
| VerifyOrExit(aHeader->GetCode() == OT_COAP_CODE_CHANGED); |
| |
| otLogInfoMle(GetInstance(), "Received address reply"); |
| |
| SuccessOrExit(ThreadTlv::GetTlv(*aMessage, ThreadTlv::kStatus, sizeof(statusTlv), statusTlv)); |
| VerifyOrExit(statusTlv.IsValid()); |
| |
| if (statusTlv.GetStatus() != statusTlv.kSuccess) |
| { |
| if (IsRouterIdValid(mPreviousRouterId)) |
| { |
| if (HasChildren()) |
| { |
| RemoveChildren(); |
| } |
| |
| SetRouterId(kInvalidRouterId); |
| } |
| |
| ExitNow(); |
| } |
| |
| SuccessOrExit(ThreadTlv::GetTlv(*aMessage, ThreadTlv::kRloc16, sizeof(rlocTlv), rlocTlv)); |
| VerifyOrExit(rlocTlv.IsValid()); |
| routerId = GetRouterId(rlocTlv.GetRloc16()); |
| router = GetRouter(routerId); |
| VerifyOrExit(router != NULL); |
| |
| SuccessOrExit(ThreadTlv::GetTlv(*aMessage, ThreadTlv::kRouterMask, sizeof(routerMaskTlv), routerMaskTlv)); |
| VerifyOrExit(routerMaskTlv.IsValid()); |
| |
| // if allocated routerId is different from previous routerId |
| if (IsRouterIdValid(mPreviousRouterId) && routerId != mPreviousRouterId) |
| { |
| // reset children info if any |
| if (HasChildren()) |
| { |
| RemoveChildren(); |
| } |
| } |
| |
| // assign short address |
| SetRouterId(routerId); |
| |
| SuccessOrExit(SetStateRouter(GetRloc16(mRouterId))); |
| |
| router->SetCost(0); |
| |
| // copy router id information |
| mRouterIdSequence = routerMaskTlv.GetIdSequence(); |
| mRouterIdSequenceLastUpdated = Timer::GetNow(); |
| |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| old = mRouters[i].IsAllocated(); |
| mRouters[i].SetAllocated(routerMaskTlv.IsAssignedRouterIdSet(i)); |
| |
| if (old && !mRouters[i].IsAllocated()) |
| { |
| GetNetif().GetAddressResolver().Remove(i); |
| } |
| } |
| |
| // Keep route path to the Leader reported by the parent before it is updated. |
| if (mRouters[GetLeaderId()].GetCost() == 0) |
| { |
| mRouters[GetLeaderId()].SetCost(mParentLeaderCost); |
| } |
| |
| mRouters[GetLeaderId()].SetNextHop(GetRouterId(mParent.GetRloc16())); |
| |
| // Keep link to the parent in order to response to Parent Requests before new link is established. |
| mRouters[GetRouterId(mParent.GetRloc16())] = mParent; |
| mRouters[GetRouterId(mParent.GetRloc16())].SetAllocated(true); |
| |
| // send link request |
| SendLinkRequest(NULL); |
| |
| // send child id responses |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| switch (mChildren[i].GetState()) |
| { |
| case Neighbor::kStateChildIdRequest: |
| SendChildIdResponse(&mChildren[i]); |
| break; |
| |
| case Neighbor::kStateLinkRequest: |
| assert(false); |
| break; |
| |
| case Neighbor::kStateInvalid: |
| case Neighbor::kStateParentRequest: |
| case Neighbor::kStateValid: |
| case Neighbor::kStateRestored: |
| case Neighbor::kStateChildUpdateRequest: |
| break; |
| } |
| } |
| |
| exit: |
| return; |
| } |
| |
| void MleRouter::HandleAddressSolicit(void *aContext, otCoapHeader *aHeader, otMessage *aMessage, |
| const otMessageInfo *aMessageInfo) |
| { |
| static_cast<MleRouter *>(aContext)->HandleAddressSolicit( |
| *static_cast<Coap::Header *>(aHeader), *static_cast<Message *>(aMessage), |
| *static_cast<const Ip6::MessageInfo *>(aMessageInfo)); |
| } |
| |
| void MleRouter::HandleAddressSolicit(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| ThreadExtMacAddressTlv macAddr64Tlv; |
| ThreadRloc16Tlv rlocTlv; |
| ThreadStatusTlv statusTlv; |
| uint8_t routerId = kInvalidRouterId; |
| Router *router = NULL; |
| |
| VerifyOrExit(aHeader.GetType() == OT_COAP_TYPE_CONFIRMABLE && aHeader.GetCode() == OT_COAP_CODE_POST, |
| error = OT_ERROR_PARSE); |
| |
| otLogInfoMle(GetInstance(), "Received address solicit"); |
| |
| SuccessOrExit(error = ThreadTlv::GetTlv(aMessage, ThreadTlv::kExtMacAddress, sizeof(macAddr64Tlv), macAddr64Tlv)); |
| VerifyOrExit(macAddr64Tlv.IsValid(), error = OT_ERROR_PARSE); |
| |
| SuccessOrExit(error = ThreadTlv::GetTlv(aMessage, ThreadTlv::kStatus, sizeof(statusTlv), statusTlv)); |
| VerifyOrExit(statusTlv.IsValid(), error = OT_ERROR_PARSE); |
| |
| // see if allocation already exists |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].IsAllocated() && |
| memcmp(&mRouters[i].GetExtAddress(), macAddr64Tlv.GetMacAddr(), sizeof(mRouters[i].GetExtAddress())) == 0) |
| { |
| ExitNow(routerId = i); |
| } |
| } |
| |
| // check the request reason |
| switch (statusTlv.GetStatus()) |
| { |
| case ThreadStatusTlv::kTooFewRouters: |
| VerifyOrExit(GetActiveRouterCount() < mRouterUpgradeThreshold); |
| break; |
| |
| case ThreadStatusTlv::kHaveChildIdRequest: |
| case ThreadStatusTlv::kParentPartitionChange: |
| break; |
| |
| default: |
| ExitNow(error = OT_ERROR_PARSE); |
| break; |
| } |
| |
| if (ThreadTlv::GetTlv(aMessage, ThreadTlv::kRloc16, sizeof(rlocTlv), rlocTlv) == OT_ERROR_NONE) |
| { |
| // specific Router ID requested |
| VerifyOrExit(rlocTlv.IsValid(), error = OT_ERROR_PARSE); |
| routerId = GetRouterId(rlocTlv.GetRloc16()); |
| router = GetRouter(routerId); |
| |
| if (router != NULL) |
| { |
| if (router->IsAllocated() && |
| memcmp(&router->GetExtAddress(), macAddr64Tlv.GetMacAddr(), sizeof(router->GetExtAddress()))) |
| { |
| // requested Router ID is allocated to another device |
| routerId = kInvalidRouterId; |
| } |
| else if (!router->IsAllocated() && router->IsReclaimDelay()) |
| { |
| // requested Router ID is deallocated but within ID_REUSE_DELAY period |
| routerId = kInvalidRouterId; |
| } |
| else |
| { |
| routerId = AllocateRouterId(routerId); |
| } |
| } |
| } |
| |
| // allocate new router id |
| if (!IsRouterIdValid(routerId)) |
| { |
| routerId = AllocateRouterId(); |
| } |
| else |
| { |
| otLogInfoMle(GetInstance(), "router id requested and provided!"); |
| } |
| |
| router = GetRouter(routerId); |
| |
| if (router != NULL) |
| { |
| router->SetExtAddress(*macAddr64Tlv.GetMacAddr()); |
| } |
| else |
| { |
| otLogInfoMle(GetInstance(), "router address unavailable!"); |
| } |
| |
| exit: |
| |
| if (error == OT_ERROR_NONE) |
| { |
| SendAddressSolicitResponse(aHeader, routerId, aMessageInfo); |
| } |
| } |
| |
| void MleRouter::SendAddressSolicitResponse(const Coap::Header &aRequestHeader, uint8_t aRouterId, |
| const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Coap::Header responseHeader; |
| ThreadStatusTlv statusTlv; |
| ThreadRouterMaskTlv routerMaskTlv; |
| ThreadRloc16Tlv rlocTlv; |
| Message *message; |
| |
| responseHeader.SetDefaultResponseHeader(aRequestHeader); |
| responseHeader.SetPayloadMarker(); |
| |
| VerifyOrExit((message = netif.GetCoap().NewMessage(responseHeader)) != NULL, error = OT_ERROR_NO_BUFS); |
| |
| statusTlv.Init(); |
| statusTlv.SetStatus(!IsRouterIdValid(aRouterId) ? statusTlv.kNoAddressAvailable : statusTlv.kSuccess); |
| SuccessOrExit(error = message->Append(&statusTlv, sizeof(statusTlv))); |
| |
| if (IsRouterIdValid(aRouterId)) |
| { |
| rlocTlv.Init(); |
| rlocTlv.SetRloc16(GetRloc16(aRouterId)); |
| SuccessOrExit(error = message->Append(&rlocTlv, sizeof(rlocTlv))); |
| |
| routerMaskTlv.Init(); |
| routerMaskTlv.SetIdSequence(mRouterIdSequence); |
| routerMaskTlv.ClearAssignedRouterIdMask(); |
| |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].IsAllocated()) |
| { |
| routerMaskTlv.SetAssignedRouterId(i); |
| } |
| } |
| |
| SuccessOrExit(error = message->Append(&routerMaskTlv, sizeof(routerMaskTlv))); |
| } |
| |
| SuccessOrExit(error = netif.GetCoap().SendMessage(*message, aMessageInfo)); |
| |
| otLogInfoMle(GetInstance(), "Sent address reply"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| } |
| |
| void MleRouter::HandleAddressRelease(void *aContext, otCoapHeader *aHeader, otMessage *aMessage, |
| const otMessageInfo *aMessageInfo) |
| { |
| static_cast<MleRouter *>(aContext)->HandleAddressRelease( |
| *static_cast<Coap::Header *>(aHeader), *static_cast<Message *>(aMessage), |
| *static_cast<const Ip6::MessageInfo *>(aMessageInfo)); |
| } |
| |
| void MleRouter::HandleAddressRelease(Coap::Header &aHeader, Message &aMessage, |
| const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadRloc16Tlv rlocTlv; |
| ThreadExtMacAddressTlv macAddr64Tlv; |
| uint8_t routerId; |
| Router *router; |
| |
| VerifyOrExit(aHeader.GetType() == OT_COAP_TYPE_CONFIRMABLE && |
| aHeader.GetCode() == OT_COAP_CODE_POST); |
| |
| otLogInfoMle(GetInstance(), "Received address release"); |
| |
| SuccessOrExit(ThreadTlv::GetTlv(aMessage, ThreadTlv::kRloc16, sizeof(rlocTlv), rlocTlv)); |
| VerifyOrExit(rlocTlv.IsValid()); |
| |
| SuccessOrExit(ThreadTlv::GetTlv(aMessage, ThreadTlv::kExtMacAddress, sizeof(macAddr64Tlv), macAddr64Tlv)); |
| VerifyOrExit(macAddr64Tlv.IsValid()); |
| |
| routerId = GetRouterId(rlocTlv.GetRloc16()); |
| router = GetRouter(routerId); |
| |
| VerifyOrExit(router != NULL && |
| memcmp(&router->GetExtAddress(), macAddr64Tlv.GetMacAddr(), sizeof(router->GetExtAddress())) == 0); |
| |
| ReleaseRouterId(routerId); |
| |
| SuccessOrExit(GetNetif().GetCoap().SendEmptyAck(aHeader, aMessageInfo)); |
| |
| otLogInfoMle(GetInstance(), "Sent address release response"); |
| |
| exit: |
| return; |
| } |
| |
| void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv) |
| { |
| ConnectivityTlv &tlv = aTlv; |
| uint8_t cost; |
| uint8_t linkQuality; |
| uint8_t numChildren = 0; |
| int8_t parentPriority = kParentPriorityMedium; |
| int8_t noiseFloor = GetNetif().GetMac().GetNoiseFloor(); |
| |
| if (mParentPriority != kParentPriorityUnspecified) |
| { |
| parentPriority = mParentPriority; |
| } |
| else |
| { |
| for (int i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].GetState() == Neighbor::kStateValid) |
| { |
| numChildren++; |
| } |
| } |
| |
| if ((mMaxChildrenAllowed - numChildren) < (mMaxChildrenAllowed / 3)) |
| { |
| parentPriority = kParentPriorityLow; |
| } |
| else |
| { |
| parentPriority = kParentPriorityMedium; |
| } |
| } |
| |
| tlv.SetParentPriority(parentPriority); |
| |
| // compute leader cost and link qualities |
| tlv.SetLinkQuality1(0); |
| tlv.SetLinkQuality2(0); |
| tlv.SetLinkQuality3(0); |
| |
| cost = mRouters[GetLeaderId()].GetCost(); |
| |
| switch (mRole) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| assert(false); |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| switch (mParent.GetLinkInfo().GetLinkQuality(noiseFloor)) |
| { |
| case 1: |
| tlv.SetLinkQuality1(tlv.GetLinkQuality1() + 1); |
| break; |
| |
| case 2: |
| tlv.SetLinkQuality2(tlv.GetLinkQuality2() + 1); |
| break; |
| |
| case 3: |
| tlv.SetLinkQuality3(tlv.GetLinkQuality3() + 1); |
| break; |
| } |
| |
| cost += LinkQualityToCost(mParent.GetLinkInfo().GetLinkQuality(noiseFloor)); |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| cost += GetLinkCost(mRouters[GetLeaderId()].GetNextHop()); |
| |
| if (!IsRouterIdValid(mRouters[GetLeaderId()].GetNextHop()) || GetLinkCost(GetLeaderId()) < cost) |
| { |
| cost = GetLinkCost(GetLeaderId()); |
| } |
| |
| break; |
| |
| case OT_DEVICE_ROLE_LEADER: |
| cost = 0; |
| break; |
| } |
| |
| tlv.SetActiveRouters(0); |
| |
| for (int i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].IsAllocated()) |
| { |
| tlv.SetActiveRouters(tlv.GetActiveRouters() + 1); |
| } |
| |
| if (mRouters[i].GetState() != Neighbor::kStateValid || i == mRouterId) |
| { |
| continue; |
| } |
| |
| linkQuality = mRouters[i].GetLinkInfo().GetLinkQuality(noiseFloor); |
| |
| if (linkQuality > mRouters[i].GetLinkQualityOut()) |
| { |
| linkQuality = mRouters[i].GetLinkQualityOut(); |
| } |
| |
| switch (linkQuality) |
| { |
| case 1: |
| tlv.SetLinkQuality1(tlv.GetLinkQuality1() + 1); |
| break; |
| |
| case 2: |
| tlv.SetLinkQuality2(tlv.GetLinkQuality2() + 1); |
| break; |
| |
| case 3: |
| tlv.SetLinkQuality3(tlv.GetLinkQuality3() + 1); |
| break; |
| } |
| } |
| |
| tlv.SetLeaderCost((cost < kMaxRouteCost) ? cost : static_cast<uint8_t>(kMaxRouteCost)); |
| tlv.SetIdSequence(mRouterIdSequence); |
| tlv.SetSedBufferSize(1280); |
| tlv.SetSedDatagramCount(1); |
| } |
| |
| otError MleRouter::AppendConnectivity(Message &aMessage) |
| { |
| otError error; |
| ConnectivityTlv tlv; |
| |
| tlv.Init(); |
| FillConnectivityTlv(tlv); |
| |
| SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); |
| |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::AppendChildAddresses(Message &aMessage, Child &aChild) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error; |
| Tlv tlv; |
| AddressRegistrationEntry entry; |
| Lowpan::Context context; |
| uint8_t length = 0; |
| uint8_t startOffset = static_cast<uint8_t>(aMessage.GetLength()); |
| |
| tlv.SetType(Tlv::kAddressRegistration); |
| SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); |
| |
| for (uint8_t i = 0; i < Child::kMaxIp6AddressPerChild; i++) |
| { |
| if (aChild.GetIp6Address(i).IsUnspecified()) |
| { |
| break; |
| } |
| |
| if (netif.GetNetworkDataLeader().GetContext(aChild.GetIp6Address(i), context) == OT_ERROR_NONE) |
| { |
| // compressed entry |
| entry.SetContextId(context.mContextId); |
| entry.SetIid(aChild.GetIp6Address(i).GetIid()); |
| } |
| else |
| { |
| // uncompressed entry |
| entry.SetUncompressed(); |
| entry.SetIp6Address(aChild.GetIp6Address(i)); |
| } |
| |
| SuccessOrExit(error = aMessage.Append(&entry, entry.GetLength())); |
| length += entry.GetLength(); |
| } |
| |
| tlv.SetLength(length); |
| aMessage.Write(startOffset, sizeof(tlv), &tlv); |
| |
| exit: |
| return error; |
| } |
| |
| void MleRouter::FillRouteTlv(RouteTlv &tlv) |
| { |
| |
| uint8_t routeCount = 0; |
| uint8_t linkCost; |
| uint8_t cost; |
| |
| tlv.SetRouterIdSequence(mRouterIdSequence); |
| tlv.ClearRouterIdMask(); |
| |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].IsAllocated() == false) |
| { |
| continue; |
| } |
| |
| tlv.SetRouterId(i); |
| |
| if (i == mRouterId) |
| { |
| tlv.SetLinkQualityIn(routeCount, 0); |
| tlv.SetLinkQualityOut(routeCount, 0); |
| tlv.SetRouteCost(routeCount, 1); |
| } |
| else |
| { |
| linkCost = GetLinkCost(i); |
| |
| if (!IsRouterIdValid(mRouters[i].GetNextHop())) |
| { |
| cost = linkCost; |
| } |
| else |
| { |
| cost = mRouters[i].GetCost() + GetLinkCost(mRouters[i].GetNextHop()); |
| |
| if (linkCost < cost) |
| { |
| cost = linkCost; |
| } |
| } |
| |
| if (cost >= kMaxRouteCost) |
| { |
| cost = 0; |
| } |
| |
| tlv.SetRouteCost(routeCount, cost); |
| tlv.SetLinkQualityOut(routeCount, mRouters[i].GetLinkQualityOut()); |
| |
| if (isAssignLinkQuality && |
| (memcmp(&mRouters[i].GetExtAddress(), mAddr64.m8, OT_EXT_ADDRESS_SIZE) == 0)) |
| { |
| tlv.SetLinkQualityIn(routeCount, mAssignLinkQuality); |
| } |
| else |
| { |
| tlv.SetLinkQualityIn(routeCount, |
| mRouters[i].GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor())); |
| } |
| } |
| |
| routeCount++; |
| } |
| |
| tlv.SetRouteDataLength(routeCount); |
| } |
| |
| otError MleRouter::AppendRoute(Message &aMessage) |
| { |
| otError error; |
| RouteTlv tlv; |
| tlv.Init(); |
| FillRouteTlv(tlv); |
| SuccessOrExit(error = aMessage.Append(&tlv, sizeof(Tlv) + tlv.GetLength())); |
| exit: |
| return error; |
| } |
| |
| otError MleRouter::AppendActiveDataset(Message &aMessage) |
| { |
| return GetNetif().GetActiveDataset().AppendMleDatasetTlv(aMessage); |
| } |
| |
| otError MleRouter::AppendPendingDataset(Message &aMessage) |
| { |
| return GetNetif().GetPendingDataset().AppendMleDatasetTlv(aMessage); |
| } |
| |
| bool MleRouter::HasMinDowngradeNeighborRouters(void) |
| { |
| return GetMinDowngradeNeighborRouters() >= kMinDowngradeNeighbors; |
| } |
| |
| bool MleRouter::HasOneNeighborwithComparableConnectivity(const RouteTlv &aRoute, uint8_t aRouterId) |
| { |
| bool rval = true; |
| uint8_t localLinkQuality = 0; |
| uint8_t peerLinkQuality = 0; |
| uint8_t routerCount = 0; |
| |
| // process local neighbor routers |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| if (i == mRouterId) |
| { |
| routerCount++; |
| continue; |
| } |
| |
| // check if neighbor is valid |
| if (mRouters[i].GetState() == Neighbor::kStateValid) |
| { |
| // if neighbor is just peer |
| if (i == aRouterId) |
| { |
| routerCount++; |
| continue; |
| } |
| |
| localLinkQuality = mRouters[i].GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor()); |
| |
| if (localLinkQuality > mRouters[i].GetLinkQualityOut()) |
| { |
| localLinkQuality = mRouters[i].GetLinkQualityOut(); |
| } |
| |
| if (localLinkQuality >= 2) |
| { |
| // check if this neighbor router is in peer Route64 TLV |
| if (aRoute.IsRouterIdSet(i) == false) |
| { |
| ExitNow(rval = false); |
| } |
| |
| // get the peer's two-way link quality to this router |
| peerLinkQuality = aRoute.GetLinkQualityIn(routerCount); |
| |
| if (peerLinkQuality > aRoute.GetLinkQualityOut(routerCount)) |
| { |
| peerLinkQuality = aRoute.GetLinkQualityOut(routerCount); |
| } |
| |
| // compare local link quality to this router with peer's |
| if (peerLinkQuality >= localLinkQuality) |
| { |
| routerCount++; |
| continue; |
| } |
| else |
| { |
| ExitNow(rval = false); |
| } |
| } |
| |
| routerCount++; |
| } |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| void MleRouter::SetChildStateToValid(Child *aChild) |
| { |
| VerifyOrExit(aChild->GetState() != Neighbor::kStateValid); |
| |
| aChild->SetState(Neighbor::kStateValid); |
| GetNetif().SetStateChangedFlags(OT_CHANGED_THREAD_CHILD_ADDED); |
| StoreChild(aChild->GetRloc16()); |
| |
| exit: |
| return; |
| } |
| |
| bool MleRouter::HasChildren(void) |
| { |
| bool hasChildren = false; |
| |
| for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].GetState() == Neighbor::kStateRestored || |
| mChildren[i].GetState() >= Neighbor::kStateChildIdRequest) |
| { |
| ExitNow(hasChildren = true); |
| } |
| } |
| |
| exit: |
| return hasChildren; |
| } |
| |
| void MleRouter::RemoveChildren(void) |
| { |
| for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| switch (mChildren[i].GetState()) |
| { |
| case Neighbor::kStateValid: |
| GetNetif().SetStateChangedFlags(OT_CHANGED_THREAD_CHILD_REMOVED); |
| |
| // Fall-through to next case |
| |
| case Neighbor::kStateChildUpdateRequest: |
| case Neighbor::kStateRestored: |
| RemoveStoredChild(mChildren[i].GetRloc16()); |
| break; |
| |
| default: |
| break; |
| } |
| |
| mChildren[i].SetState(Neighbor::kStateInvalid); |
| } |
| } |
| |
| bool MleRouter::HasSmallNumberOfChildren(void) |
| { |
| uint8_t numChildren = 0; |
| uint8_t routerCount = GetActiveRouterCount(); |
| |
| VerifyOrExit(routerCount > mRouterDowngradeThreshold); |
| |
| for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].GetState() == Neighbor::kStateValid) |
| { |
| numChildren++; |
| } |
| } |
| |
| return numChildren < (routerCount - mRouterDowngradeThreshold) * 3; |
| |
| exit: |
| return false; |
| } |
| |
| uint8_t MleRouter::GetMinDowngradeNeighborRouters(void) |
| { |
| uint8_t linkQuality; |
| uint8_t routerCount = 0; |
| |
| for (uint8_t i = 0; i <= kMaxRouterId; i++) |
| { |
| if (mRouters[i].GetState() != Neighbor::kStateValid) |
| { |
| continue; |
| } |
| |
| linkQuality = mRouters[i].GetLinkInfo().GetLinkQuality(GetNetif().GetMac().GetNoiseFloor()); |
| |
| if (linkQuality > mRouters[i].GetLinkQualityOut()) |
| { |
| linkQuality = mRouters[i].GetLinkQualityOut(); |
| } |
| |
| if (linkQuality >= 2) |
| { |
| routerCount++; |
| } |
| } |
| |
| return routerCount; |
| } |
| |
| int8_t MleRouter::GetAssignParentPriority(void) const |
| { |
| return mParentPriority; |
| } |
| |
| otError MleRouter::SetAssignParentPriority(int8_t aParentPriority) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aParentPriority <= kParentPriorityHigh && |
| aParentPriority >= kParentPriorityUnspecified, error = OT_ERROR_INVALID_ARGS); |
| |
| mParentPriority = aParentPriority; |
| |
| exit: |
| return error; |
| } |
| |
| MleRouter &MleRouter::GetOwner(const Context &aContext) |
| { |
| #if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES |
| MleRouter &mle = *static_cast<MleRouter *>(aContext.GetContext()); |
| #else |
| MleRouter &mle = otGetThreadNetif().GetMle(); |
| OT_UNUSED_VARIABLE(aContext); |
| #endif |
| return mle; |
| } |
| |
| otError MleRouter::GetMaxChildTimeout(uint32_t &aTimeout) const |
| { |
| otError error = OT_ERROR_NOT_FOUND; |
| |
| VerifyOrExit(mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER, error = OT_ERROR_INVALID_STATE); |
| |
| for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) |
| { |
| if (mChildren[i].GetState() != Neighbor::kStateValid) |
| { |
| continue; |
| } |
| |
| if (mChildren[i].IsFullThreadDevice()) |
| { |
| continue; |
| } |
| |
| if (mChildren[i].GetTimeout() > aTimeout) |
| { |
| aTimeout = mChildren[i].GetTimeout(); |
| } |
| |
| error = OT_ERROR_NONE; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| } // namespace Mle |
| } // namespace ot |
| |
| #endif // OPENTHREAD_FTD |
| |