diff --git a/include/openthread.h b/include/openthread.h index 48966d794..886a19870 100644 --- a/include/openthread.h +++ b/include/openthread.h @@ -1492,6 +1492,24 @@ void otSetAssignLinkQuality(otInstance *aInstance, const uint8_t *aExtAddr, uint */ void otPlatformReset(otInstance *aInstance); +/** + * Get the ROUTER_DOWNGRADE_THRESHOLD parameter used in the Router role. + * + * @returns The ROUTER_DOWNGRADE_THRESHOLD value. + * + * @sa otSetRouterDowngradeThreshold + */ +uint8_t otGetRouterDowngradeThreshold(void); + +/** + * Set the ROUTER_DOWNGRADE_THRESHOLD parameter used in the Leader role. + * + * @param[in] aThreshold The ROUTER_DOWNGRADE_THRESHOLD value. + * + * @sa otGetRouterDowngradeThreshold + */ +void otSetRouterDowngradeThreshold(uint8_t aThreshold); + /** * @} * diff --git a/src/cli/README.md b/src/cli/README.md index 40244afab..957cc8352 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -41,6 +41,7 @@ OpenThread test scripts use the CLI to execute test cases. * [rloc16](#rloc16) * [route](#route) * [router](#router) +* [routerdowngradethreshold](#routerdowngradethreshold) * [routerrole](#routerrole) * [routerupgradethreshold](#routerupgradethreshold) * [scan](#scan) @@ -1023,6 +1024,25 @@ Age: 7 Done ``` +### routerdowngradethreshold + +Get the ROUTER_DOWNGRADE_THRESHOLD value. + +```bash +> routerdowngradethreshold +23 +Done +``` + +### routerdowngradethreshold \ + +Set the ROUTER_DOWNGRADE_THRESHOLD value. + +```bash +> routerdowngradethreshold 23 +Done +``` + ### routerrole Indicates whether the router role is enabled or disabled. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index a93dfddba..a82ad491a 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -116,6 +116,7 @@ const struct Command Interpreter::sCommands[] = { "rloc16", &Interpreter::ProcessRloc16 }, { "route", &Interpreter::ProcessRoute }, { "router", &Interpreter::ProcessRouter }, + { "routerdowngradethreshold", &Interpreter::ProcessRouterDowngradeThreshold }, { "routerrole", &Interpreter::ProcessRouterRole }, { "routerupgradethreshold", &Interpreter::ProcessRouterUpgradeThreshold }, { "scan", &Interpreter::ProcessScan }, @@ -1657,6 +1658,25 @@ exit: AppendResult(error); } +void Interpreter::ProcessRouterDowngradeThreshold(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + long value; + + if (argc == 0) + { + sServer->OutputFormat("%d\r\n", otGetRouterDowngradeThreshold()); + } + else + { + SuccessOrExit(error = ParseLong(argv[0], value)); + otSetRouterDowngradeThreshold(static_cast(value)); + } + +exit: + AppendResult(error); +} + void Interpreter::ProcessRouterRole(int argc, char *argv[]) { ThreadError error = kThreadError_None; diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index a107c7bf5..9f33683e7 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -189,6 +189,7 @@ private: void ProcessReset(int argc, char *argv[]); void ProcessRoute(int argc, char *argv[]); void ProcessRouter(int argc, char *argv[]); + void ProcessRouterDowngradeThreshold(int argc, char *argv[]); void ProcessRouterRole(int argc, char *argv[]); ThreadError ProcessRouteAdd(int argc, char *argv[]); ThreadError ProcessRouteRemove(int argc, char *argv[]); diff --git a/src/core/openthread.cpp b/src/core/openthread.cpp index 111a32b78..1f762a769 100644 --- a/src/core/openthread.cpp +++ b/src/core/openthread.cpp @@ -663,6 +663,16 @@ void otPlatformReset(otInstance *aInstance) otPlatReset(aInstance); } +uint8_t otGetRouterDowngradeThreshold(void) +{ + return sThreadNetif->GetMle().GetRouterDowngradeThreshold(); +} + +void otSetRouterDowngradeThreshold(uint8_t aThreshold) +{ + sThreadNetif->GetMle().SetRouterDowngradeThreshold(aThreshold); +} + ThreadError otGetChildInfoById(otInstance *, uint16_t aChildId, otChildInfo *aChildInfo) { ThreadError error = kThreadError_None; diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index c9ead7b40..7ff254fa9 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -64,6 +64,7 @@ MleRouter::MleRouter(ThreadNetif &aThreadNetif): mNetworkIdTimeout = kNetworkIdTimeout; mRouterUpgradeThreshold = kRouterUpgradeThreshold; + mRouterDowngradeThreshold = kRouterDowngradeThreshold; mLeaderWeight = kLeaderWeight; mFixedLeaderPartitionId = 0; mMaxChildrenAllowed = kMaxChildren; @@ -72,6 +73,7 @@ MleRouter::MleRouter(ThreadNetif &aThreadNetif): mAdvertiseInterval = kAdvertiseIntervalMin; mRouterIdSequenceLastUpdated = 0; mRouterRoleEnabled = true; + mRouterSelectionJitterTimeout = 0; mCoapMessageId = static_cast(otPlatRandomGet()); } @@ -394,6 +396,16 @@ void MleRouter::SetRouterUpgradeThreshold(uint8_t aThreshold) mRouterUpgradeThreshold = aThreshold; } +uint8_t MleRouter::GetRouterDowngradeThreshold(void) const +{ + return mRouterDowngradeThreshold; +} + +void MleRouter::SetRouterDowngradeThreshold(uint8_t aThreshold) +{ + mRouterDowngradeThreshold = aThreshold; +} + void MleRouter::HandleAdvertiseTimer(void *aContext) { MleRouter *obj = reinterpret_cast(aContext); @@ -1371,6 +1383,28 @@ ThreadError MleRouter::HandleAdvertisement(const Message &aMessage, const Ip6::M ExitNow(); case kDeviceStateRouter: + // 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() % kRouterSelectionJitter; + } + + // fall through + case kDeviceStateLeader: router = &mRouters[routerId]; @@ -1631,6 +1665,18 @@ void MleRouter::HandleStateUpdateTimer(void) BecomeChild(kMleAttachSamePartition); } + if (mRouterSelectionJitterTimeout > 0) + { + mRouterSelectionJitterTimeout--; + + if (mRouterSelectionJitterTimeout == 0 && + GetActiveRouterCount() > mRouterDowngradeThreshold) + { + // downgrade to REED + BecomeChild(kMleAttachSamePartition); + } + } + break; case kDeviceStateLeader: @@ -3494,5 +3540,132 @@ exit: return error; } +bool MleRouter::HasMinDowngradeNeighborRouters(void) +{ + return GetMinDowngradeNeighborRouters() >= kMinDowngradeNeighbors; +} + +bool MleRouter::HasOneNeighborwithComparableConnectivity(const RouteTlv &aRoute, uint8_t aRouterId) +{ + bool rval = true; + uint8_t localLqi = 0; + uint8_t peerLqi = 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].mState == Neighbor::kStateValid) + { + // if neighbor is just peer + if (i == aRouterId) + { + routerCount++; + continue; + } + + localLqi = mRouters[i].mLinkInfo.GetLinkQuality(mMac.GetNoiseFloor()); + + if (localLqi > mRouters[i].mLinkQualityOut) + { + localLqi = mRouters[i].mLinkQualityOut; + } + + if (localLqi >= 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 lqi to this router + peerLqi = aRoute.GetLinkQualityIn(routerCount); + + if (peerLqi > aRoute.GetLinkQualityOut(routerCount)) + { + peerLqi = aRoute.GetLinkQualityOut(routerCount); + } + + // compare local lqi to this router with peer's + if (peerLqi >= localLqi) + { + routerCount++; + continue; + } + else + { + ExitNow(rval = false); + } + } + + routerCount++; + } + } + +exit: + return rval; +} + +bool MleRouter::HasSmallNumberOfChildren(void) +{ + Child *children; + uint8_t maxChildCount = 0; + uint8_t numChildren = 0; + uint8_t routerCount = GetActiveRouterCount(); + + VerifyOrExit(routerCount > mRouterDowngradeThreshold, ;); + + children = GetChildren(&maxChildCount); + + for (uint8_t i = 0; i < maxChildCount; i++) + { + if (children[i].mState == Neighbor::kStateValid) + { + numChildren++; + } + } + + return numChildren < (routerCount - mRouterDowngradeThreshold) * 3; + +exit: + return false; +} + +uint8_t MleRouter::GetMinDowngradeNeighborRouters(void) +{ + uint8_t lqi; + uint8_t routerCount = 0; + + for (uint8_t i = 0; i < kMaxRouterId; i++) + { + if (mRouters[i].mState != Neighbor::kStateValid) + { + continue; + } + + lqi = mRouters[i].mLinkInfo.GetLinkQuality(mMac.GetNoiseFloor()); + + if (lqi > mRouters[i].mLinkQualityOut) + { + lqi = mRouters[i].mLinkQualityOut; + } + + if (lqi >= 2) + { + routerCount++; + } + } + + return routerCount; +} + } // namespace Mle } // namespace Thread diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index a6259987d..aa896a11c 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -234,6 +234,22 @@ public: */ void SetRouterUpgradeThreshold(uint8_t aThreshold); + /** + * This method returns the ROUTER_DOWNGRADE_THRESHOLD value. + * + * @returns The ROUTER_DOWNGRADE_THRESHOLD value. + * + */ + uint8_t GetRouterDowngradeThreshold(void) const; + + /** + * This method sets the ROUTER_DOWNGRADE_THRESHOLD value. + * + * @returns The ROUTER_DOWNGRADE_THRESHOLD value. + * + */ + void SetRouterDowngradeThreshold(uint8_t aThreshold); + /** * This method release a given Router ID. * @@ -521,6 +537,11 @@ private: Child *FindChild(uint16_t aChildId); Child *FindChild(const Mac::ExtAddress &aMacAddr); + bool HasMinDowngradeNeighborRouters(void); + bool HasOneNeighborwithComparableConnectivity(const RouteTlv &aRoute, uint8_t aRouterId); + bool HasSmallNumberOfChildren(void); + uint8_t GetMinDowngradeNeighborRouters(void); + uint8_t AllocateRouterId(void); uint8_t AllocateRouterId(uint8_t aRouterId); bool InRouterIdMask(uint8_t aRouterId); @@ -547,9 +568,11 @@ private: uint16_t mNextChildId; uint8_t mNetworkIdTimeout; uint8_t mRouterUpgradeThreshold; + uint8_t mRouterDowngradeThreshold; uint8_t mLeaderWeight; uint32_t mFixedLeaderPartitionId; ///< only for certification testing bool mRouterRoleEnabled; + uint8_t mRouterSelectionJitterTimeout; uint8_t mRouterId; uint8_t mPreviousRouterId;