diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 18c12823d..4306443a3 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -43,6 +43,7 @@ libopenthread_a_SOURCES = \ common/message.cpp \ common/tasklet.cpp \ common/timer.cpp \ + common/trickle_timer.cpp \ crypto/aes_ccm.cpp \ crypto/aes_ecb.cpp \ crypto/hmac_sha256.cpp \ @@ -115,6 +116,7 @@ noinst_HEADERS = \ common/new.hpp \ common/tasklet.hpp \ common/timer.hpp \ + common/trickle_timer.hpp \ crypto/aes_ccm.hpp \ crypto/aes_ecb.hpp \ crypto/hmac_sha256.hpp \ diff --git a/src/core/common/trickle_timer.cpp b/src/core/common/trickle_timer.cpp new file mode 100644 index 000000000..7cb060cd9 --- /dev/null +++ b/src/core/common/trickle_timer.cpp @@ -0,0 +1,225 @@ +/* + * 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 the trickle timer logic. + */ + +#include +#include +#include +#include + +namespace Thread { + +TrickleTimer::TrickleTimer( + TimerScheduler &aScheduler, +#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT + uint32_t aRedundancyConstant, +#endif + Handler aTransmitHandler, Handler aIntervalExpiredHandler, void *aContext) + : + mTimer(aScheduler, HandleTimerFired, this), +#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT + k(aRedundancyConstant), +#endif + mPhase(kPhaseDormant), + mTransmitHandler(aTransmitHandler), + mIntervalExpiredHandler(aIntervalExpiredHandler), + mContext(aContext) +{ +} + +bool TrickleTimer::IsRunning(void) const +{ + return mPhase != kPhaseDormant; +} + +void TrickleTimer::Start(uint32_t aIntervalMin, uint32_t aIntervalMax, Mode aMode) +{ + assert(!IsRunning()); + + // Set the interval limits and mode + Imin = aIntervalMin; + Imax = aIntervalMax; + mMode = aMode; + + // Initialize I to [Imin, Imax] + I = Imin + otPlatRandomGet() % (Imax - Imin); + + // Start a new interval + StartNewInterval(); +} + +void TrickleTimer::Stop(void) +{ + mPhase = kPhaseDormant; + mTimer.Stop(); +} + +#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT +void TrickleTimer::IndicateConsistent(void) +{ + // Increment counter + c++; +} +#endif + +void TrickleTimer::IndicateInconsistent(void) +{ + // Only relavent if we aren't already at 'I' == 'Imin' + if (IsRunning() && I != Imin) + { + // Reset I to Imin + I = Imin; + + // Stop the existing timer + mTimer.Stop(); + + // Start a new interval + StartNewInterval(); + } +} + +void TrickleTimer::StartNewInterval(void) +{ + // Reset the counter and timer phase + +#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT + c = 0; +#endif + mPhase = kPhaseTransmit; + + // Initialize t + if (I == 0) + { + // Immediate interval, just set t to 0 + t = 0; + } + else if (mMode == kModeMPL) + { + // Initialize t to random value between (0, I] + t = otPlatRandomGet() % I; + } + else if (mMode == kModePlainTimer) + { + // Initialize t to I, which has already been randomized in Start + t = I; + } + else + { + // Initialize t to random value between (I/2, I] + t = (I / 2) + otPlatRandomGet() % (I / 2); + } + + // Start the timer for 't' milliseconds from now + mTimer.Start(t); +} + +void TrickleTimer::HandleTimerFired(void *aContext) +{ + TrickleTimer *obj = static_cast(aContext); + obj->HandleTimerFired(); +} + +void TrickleTimer::HandleTimerFired(void) +{ + Phase curPhase = mPhase; + bool shouldContinue = true; + + // Default the current state to Dormant + mPhase = kPhaseDormant; + + switch (curPhase) + { + // We have just reached time 't' + case kPhaseTransmit: + { + // Are we not using reduncancy or is the counter still less than it? +#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT + if (k == 0 || c < k) +#endif + { + // Invoke the transmission callback + shouldContinue = TransmitFired(); + } + + // Wait for the rest of the interval to elapse + if (shouldContinue) + { + // If we are in plain timer mode, just randomize I and restart the interval + if (mMode == kModePlainTimer) + { + // Initialize I to [Imin, Imax] + I = Imin + otPlatRandomGet() % (Imax - Imin); + + // Start a new interval + StartNewInterval(); + } + else + { + // Start next phase of the timer + mPhase = kPhaseInterval; + + // Start the time for 'I - t' milliseconds + mTimer.Start(I - t); + } + } + + break; + } + + // We have just reached time 'I' + case kPhaseInterval: + { + // Double 'I' to get the new interval length + uint32_t newI = I == 0 ? 1 : I << 1; + + if (newI > Imax) { newI = Imax; } + + I = newI; + + // Invoke the interval expiration callback + shouldContinue = IntervalExpiredFired(); + + if (shouldContinue) + { + // Start a new interval + StartNewInterval(); + } + + break; + } + + default: + assert(false); + } +} + +} // namespace Thread diff --git a/src/core/common/trickle_timer.hpp b/src/core/common/trickle_timer.hpp new file mode 100644 index 000000000..bdb5ad7ee --- /dev/null +++ b/src/core/common/trickle_timer.hpp @@ -0,0 +1,190 @@ +/* + * 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 includes definitions for the trickle timer logic. + */ + +#ifndef TRICKLE_TIMER_HPP_ +#define TRICKLE_TIMER_HPP_ + +#include + +namespace Thread { + +/** + * @addtogroup core-timer-trickle + * + * @brief + * This module includes definitions for the trickle timer logic. + * + * @{ + * + */ + +/** + * This class implements a trickle timer. + * + */ +class TrickleTimer +{ +public: + + /** + * Represents the modes of operation for the TrickleTimer + */ + typedef enum Mode + { + kModeNormal = 0, ///< Runs the normal trickle logic. + kModePlainTimer = 1, ///< Runs a normal timer between Imin and Imax. + kModeMPL = 2, ///< Runs the trickle logic modified for MPL. + } Mode; + + /** + * This function pointer is called when the timer expires. + * + * @param[in] aContext A pointer to arbitrary context information. + * + * @retval TRUE If the trickle timer should continue running. + * @retval FALSE If the trickle timer should stop running. + */ + typedef bool (*Handler)(void *aContext); + + /** + * This constructor creates a trickle timer instance. + * + * @param[in] aScheduler A refrence to the timer scheduler. + * @param[in] aRedundancyConstant The redundancy constant for the timer, k. + * @param[in] aTransmitHandler A pointer to a function that is called when transmission should occur. + * @param[in] aIntervalExpiredHandler An optional pointer to a function that is called when the interval expires. + * @param[in] aContext A pointer to arbitrary context information. + * + */ + TrickleTimer(TimerScheduler &aScheduler, +#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT + uint32_t aRedundancyConstant, +#endif + Handler aTransmitHandler, Handler aIntervalExpiredHandler, void *aContext); + + /** + * This method indicates whether or not the trickle timer instance is running. + * + * @retval TRUE If the trickle timer is running. + * @retval FALSE If the trickle timer is not running. + */ + bool IsRunning(void) const; + + /** + * This method start the trickle timer. + * + * @param[in] aIntervalMin The minimum interval for the timer, Imin. + * @param[in] aIntervalMax The maximum interval for the timer, Imax. + * @param[in] aMode The operating mode for the timer. + * + */ + void Start(uint32_t aIntervalMin, uint32_t aIntervalMax, Mode aMode); + + /** + * This method stops the trickle timer. + * + */ + void Stop(void); + +#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT + /** + * This method indicates to the trickle timer a 'consistent' state. + * + */ + void IndicateConsistent(void); +#endif + + /** + * This method indicates to the trickle timer an 'inconsistent' state. + * + */ + void IndicateInconsistent(void); + +private: + bool TransmitFired(void) { return mTransmitHandler(mContext); } + bool IntervalExpiredFired(void) { return mIntervalExpiredHandler ? mIntervalExpiredHandler(mContext) : true; } + + void StartNewInterval(void); + + static void HandleTimerFired(void *aContext); + void HandleTimerFired(void); + + typedef enum Phase + { + ///< Indicates we are currently not running + kPhaseDormant = 1, + ///< Indicates that when the timer expires, it should evaluate for transmit callbacks + kPhaseTransmit = 2, + ///< Indicates that when the timer expires, it should process interval expiration callbacks + kPhaseInterval = 3, + } Phase; + + Timer mTimer; + +#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT + // Redundancy constant + const uint32_t k; +#endif + + // Minimum interval size + uint32_t Imin; + // Maximum interval size + uint32_t Imax; + // The mode of operation + Mode mMode; + + // The current interval size (in milliseconds) + uint32_t I; + // The time (in milliseconds) into the interval at which we should transmit + uint32_t t; +#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT + // A counter, keeping track of the number of "consistent" transmissions received + uint32_t c; +#endif + // The current trickle phase for the timer + Phase mPhase; + + // Callback variables + Handler mTransmitHandler; + Handler mIntervalExpiredHandler; + void *mContext; +}; + +/** + * @} + * + */ + +} // namespace Thread + +#endif // TRICKLE_TIMER_HPP_ diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index f885d3634..da84004a8 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -50,7 +50,7 @@ namespace Mle { MleRouter::MleRouter(ThreadNetif &aThreadNetif): Mle(aThreadNetif), - mAdvertiseTimer(aThreadNetif.GetIp6().mTimerScheduler, &HandleAdvertiseTimer, this), + mAdvertiseTimer(aThreadNetif.GetIp6().mTimerScheduler, HandleAdvertiseTimer, NULL, this), mStateUpdateTimer(aThreadNetif.GetIp6().mTimerScheduler, &HandleStateUpdateTimer, this), mSocket(aThreadNetif.GetIp6().mUdp), mAddressSolicit(OPENTHREAD_URI_ADDRESS_SOLICIT, &HandleAddressSolicit, this), @@ -70,7 +70,6 @@ MleRouter::MleRouter(ThreadNetif &aThreadNetif): mMaxChildrenAllowed = kMaxChildren; mRouterId = kMaxRouterId; mPreviousRouterId = kMaxRouterId; - mAdvertiseInterval = kAdvertiseIntervalMin; mRouterIdSequenceLastUpdated = 0; mRouterRoleEnabled = true; mRouterSelectionJitterTimeout = 0; @@ -241,7 +240,6 @@ ThreadError MleRouter::BecomeLeader(void) mSocket.Open(&HandleUdpReceive, this); mAdvertiseTimer.Stop(); - ResetAdvertiseInterval(); mStateUpdateTimer.Start(kStateUpdatePeriod); mAddressResolver.Clear(); @@ -267,6 +265,8 @@ ThreadError MleRouter::BecomeLeader(void) SuccessOrExit(error = SetStateLeader(GetRloc16(mRouterId))); + ResetAdvertiseInterval(); + exit: return error; } @@ -299,8 +299,6 @@ ThreadError MleRouter::HandleDetachStart(void) ThreadError MleRouter::HandleChildStart(otMleAttachFilter aFilter) { - uint32_t advertiseDelay; - mRouterIdSequenceLastUpdated = Timer::GetNow(); StopLeader(); @@ -322,15 +320,12 @@ ThreadError MleRouter::HandleChildStart(otMleAttachFilter aFilter) if (mDeviceMode & ModeTlv::kModeFFD) { - advertiseDelay = Timer::SecToMsec(kReedAdvertiseInterval + - (otPlatRandomGet() % kReedAdvertiseJitter)); - mAdvertiseTimer.Start(advertiseDelay); + mAdvertiseTimer.Start( + Timer::SecToMsec(kReedAdvertiseInterval), + Timer::SecToMsec(kReedAdvertiseInterval + kReedAdvertiseJitter), + TrickleTimer::kModePlainTimer); mNetif.SubscribeAllRoutersMulticast(); } - else - { - - } return kThreadError_None; } @@ -414,68 +409,38 @@ void MleRouter::SetRouterDowngradeThreshold(uint8_t aThreshold) mRouterDowngradeThreshold = aThreshold; } -void MleRouter::HandleAdvertiseTimer(void *aContext) +bool MleRouter::HandleAdvertiseTimer(void *aContext) { - MleRouter *obj = reinterpret_cast(aContext); - obj->HandleAdvertiseTimer(); + MleRouter *obj = static_cast(aContext); + return obj->HandleAdvertiseTimer(); } -void MleRouter::HandleAdvertiseTimer(void) +bool MleRouter::HandleAdvertiseTimer(void) { - uint32_t advertiseDelay; - if ((mDeviceMode & ModeTlv::kModeFFD) == 0) { - return; + return false; } SendAdvertisement(); - switch (GetDeviceState()) - { - case kDeviceStateDisabled: - case kDeviceStateDetached: - assert(false); - break; - - case kDeviceStateChild: - advertiseDelay = Timer::SecToMsec(kReedAdvertiseInterval + - (otPlatRandomGet() % kReedAdvertiseJitter)); - mAdvertiseTimer.Start(advertiseDelay); - break; - - case kDeviceStateRouter: - case kDeviceStateLeader: - mAdvertiseInterval *= 2; - - if (mAdvertiseInterval > kAdvertiseIntervalMax) - { - mAdvertiseInterval = kAdvertiseIntervalMax; - } - - advertiseDelay = Timer::SecToMsec(mAdvertiseInterval) / 2; - advertiseDelay += otPlatRandomGet() % (advertiseDelay); - mAdvertiseTimer.Start(advertiseDelay); - break; - } + return true; } -ThreadError MleRouter::ResetAdvertiseInterval(void) +void MleRouter::ResetAdvertiseInterval(void) { - uint32_t advertiseDelay; + assert(GetDeviceState() == kDeviceStateRouter || + GetDeviceState() == kDeviceStateLeader); - VerifyOrExit(mAdvertiseInterval != kAdvertiseIntervalMin || !mAdvertiseTimer.IsRunning(), ;); + if (!mAdvertiseTimer.IsRunning()) + { + mAdvertiseTimer.Start( + Timer::SecToMsec(kAdvertiseIntervalMin), + Timer::SecToMsec(kAdvertiseIntervalMax), + TrickleTimer::kModeNormal); + } - mAdvertiseInterval = kAdvertiseIntervalMin; - - advertiseDelay = Timer::SecToMsec(mAdvertiseInterval) / 2; - advertiseDelay += otPlatRandomGet() % advertiseDelay; - mAdvertiseTimer.Start(advertiseDelay); - - otLogInfoMle("reset advertise interval\n"); - -exit: - return kThreadError_None; + mAdvertiseTimer.IndicateInconsistent(); } ThreadError MleRouter::SendAdvertisement(void) diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 048e81a17..ae6944c6f 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -500,7 +501,7 @@ private: ThreadError HandleNetworkDataUpdateRouter(void); ThreadError ProcessRouteTlv(const RouteTlv &aRoute); - ThreadError ResetAdvertiseInterval(void); + void ResetAdvertiseInterval(void); ThreadError SendAddressSolicit(ThreadStatusTlv::Status aStatus); ThreadError SendAddressRelease(void); void SendAddressSolicitResponse(const Coap::Header &aRequest, uint8_t aRouterId, @@ -547,12 +548,12 @@ private: uint8_t AllocateRouterId(uint8_t aRouterId); bool InRouterIdMask(uint8_t aRouterId); - static void HandleAdvertiseTimer(void *aContext); - void HandleAdvertiseTimer(void); + static bool HandleAdvertiseTimer(void *aContext); + bool HandleAdvertiseTimer(void); static void HandleStateUpdateTimer(void *aContext); void HandleStateUpdateTimer(void); - Timer mAdvertiseTimer; + TrickleTimer mAdvertiseTimer; Timer mStateUpdateTimer; Ip6::UdpSocket mSocket; @@ -577,7 +578,6 @@ private: uint8_t mRouterId; uint8_t mPreviousRouterId; - uint32_t mAdvertiseInterval; Coap::Server &mCoapServer; uint8_t mCoapToken[2];