diff --git a/src/core/thread/mle_constants.hpp b/src/core/thread/mle_constants.hpp index 12ee698a8..851b2c097 100644 --- a/src/core/thread/mle_constants.hpp +++ b/src/core/thread/mle_constants.hpp @@ -55,11 +55,13 @@ enum */ enum { - kVersion = 2, ///< MLE Version - kUdpPort = 19788, ///< MLE UDP Port - kParentRequestRouterTimeout = 1000, ///< Router Request timeout - kParentRequestChildTimeout = 2000, ///< End Device Request timeout - kMaxResponseDelay = 1000, ///< Maximum delay before responding to a multicast request + kVersion = 2, ///< MLE Version + kUdpPort = 19788, ///< MLE UDP Port + kParentRequestRouterTimeout = 1000, ///< Router Request timeout + kParentRequestChildTimeout = 2000, ///< End Device Request timeout + kParentResponseMaxDelayRouters = 500, ///< Maximum delay for response for Parent Request sent to routers only + kParentResponseMaxDelayAll = 1000, ///< Maximum delay for response for Parent Request sent to all devices + kMaxResponseDelay = 1000, ///< Maximum delay before responding to a multicast request }; enum diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 6c0f1676a..87f75dac6 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -52,6 +52,7 @@ MleRouter::MleRouter(ThreadNetif &aThreadNetif): Mle(aThreadNetif), mAdvertiseTimer(aThreadNetif.GetIp6().mTimerScheduler, &MleRouter::HandleAdvertiseTimer, NULL, this), mStateUpdateTimer(aThreadNetif.GetIp6().mTimerScheduler, &MleRouter::HandleStateUpdateTimer, this), + mDelayedResponseTimer(aThreadNetif.GetIp6().mTimerScheduler, &MleRouter::HandleDelayedResponseTimer, this), mSocket(aThreadNetif.GetIp6().mUdp), mAddressSolicit(OPENTHREAD_URI_ADDRESS_SOLICIT, &MleRouter::HandleAddressSolicit, this), mAddressRelease(OPENTHREAD_URI_ADDRESS_RELEASE, &MleRouter::HandleAddressRelease, this), @@ -750,9 +751,19 @@ ThreadError MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, Neig aNeighbor->mState = Neighbor::kStateLinkRequest; } - SuccessOrExit(error = SendMessage(*message, aMessageInfo.GetPeerAddr())); + if (aMessageInfo.GetSockAddr().IsMulticast()) + { + SuccessOrExit(error = AddDelayedResponse(*message, aMessageInfo.GetPeerAddr(), + (otPlatRandomGet() % kMaxResponseDelay) + 1)); - otLogInfoMle("Sent link accept\n"); + otLogInfoMle("Delayed link accept\n"); + } + else + { + SuccessOrExit(error = SendMessage(*message, aMessageInfo.GetPeerAddr())); + + otLogInfoMle("Sent link accept\n"); + } exit: @@ -1638,8 +1649,9 @@ ThreadError MleRouter::HandleParentRequest(const Message &aMessage, const Ip6::M child->mState = Neighbor::kStateParentRequest; child->mDataRequest = false; + child->mLastHeard = Timer::GetNow(); child->mTimeout = Timer::SecToMsec(2 * kParentRequestChildTimeout); - SuccessOrExit(error = SendParentResponse(child, challenge)); + SuccessOrExit(error = SendParentResponse(child, challenge, !scanMask.IsEndDeviceFlagSet())); exit: return error; @@ -1767,11 +1779,96 @@ exit: {} } -ThreadError MleRouter::SendParentResponse(Child *aChild, const ChallengeTlv &challenge) +void MleRouter::HandleDelayedResponseTimer(void *aContext) +{ + static_cast(aContext)->HandleDelayedResponseTimer(); +} + +void MleRouter::HandleDelayedResponseTimer(void) +{ + DelayedResponseHeader delayedResponse; + uint32_t now = otPlatAlarmGetNow(); + uint32_t nextDelay = 0xffffffff; + Message *message = mDelayedResponses.GetHead(); + Message *nextMessage = NULL; + + while (message != NULL) + { + nextMessage = message->GetNext(); + delayedResponse.ReadFrom(*message); + + if (delayedResponse.IsLater(now)) + { + // Calculate the next delay and choose the lowest. + if (delayedResponse.GetSendTime() - now < nextDelay) + { + nextDelay = delayedResponse.GetSendTime() - now; + } + } + else + { + mDelayedResponses.Dequeue(*message); + + // Remove the DelayedResponseHeader from the message. + DelayedResponseHeader::RemoveFrom(*message); + + // Send the message. + if (SendMessage(*message, delayedResponse.GetDestination()) == kThreadError_None) + { + otLogInfoMle("Sent delayed response\n"); + } + else + { + message->Free(); + } + } + + message = nextMessage; + } + + if (nextDelay != 0xffffffff) + { + mDelayedResponseTimer.Start(nextDelay); + } +} + +ThreadError MleRouter::AddDelayedResponse(Message &aMessage, const Ip6::Address &aDestination, uint16_t aDelay) +{ + ThreadError error = kThreadError_None; + uint32_t alarmFireTime; + uint32_t sendTime = otPlatAlarmGetNow() + aDelay; + + // Append the message with DelayedRespnoseHeader and add to the list. + DelayedResponseHeader delayedResponse(sendTime, aDestination); + SuccessOrExit(error = delayedResponse.AppendTo(aMessage)); + mDelayedResponses.Enqueue(aMessage); + + if (mDelayedResponseTimer.IsRunning()) + { + // If timer is already running, check if it should be restarted with earlier fire time. + alarmFireTime = mDelayedResponseTimer.Gett0() + mDelayedResponseTimer.Getdt(); + + if (delayedResponse.IsEarlier(alarmFireTime)) + { + mDelayedResponseTimer.Start(aDelay); + } + } + else + { + // Otherwise just set the timer. + mDelayedResponseTimer.Start(aDelay); + } + +exit: + return error; +} + +ThreadError MleRouter::SendParentResponse(Child *aChild, const ChallengeTlv &challenge, bool aRoutersOnlyRequest) { ThreadError error = kThreadError_None; Ip6::Address destination; Message *message; + uint16_t delay; VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, ;); message->SetLinkSecurityEnabled(false); @@ -1806,9 +1903,19 @@ ThreadError MleRouter::SendParentResponse(Child *aChild, const ChallengeTlv &cha memset(&destination, 0, sizeof(destination)); destination.mFields.m16[0] = HostSwap16(0xfe80); destination.SetIid(aChild->mMacAddr); - SuccessOrExit(error = SendMessage(*message, destination)); - otLogInfoMle("Sent Parent Response\n"); + if (aRoutersOnlyRequest) + { + delay = (otPlatRandomGet() % kParentResponseMaxDelayRouters) + 1; + } + else + { + delay = (otPlatRandomGet() % kParentResponseMaxDelayAll) + 1; + } + + SuccessOrExit(error = AddDelayedResponse(*message, destination, delay)); + + otLogInfoMle("Delayed Parent Response\n"); exit: diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 8f6cd768d..42b1dcc94 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -34,6 +34,8 @@ #ifndef MLE_ROUTER_HPP_ #define MLE_ROUTER_HPP_ +#include + #include #include #include @@ -61,6 +63,110 @@ class NetworkDataLeader; * @{ */ +/** + * This class implements functionality required for delaying MLE responses. + * + */ +OT_TOOL_PACKED_BEGIN +class DelayedResponseHeader +{ +public: + /** + * Default constructor for the object. + * + */ + DelayedResponseHeader(void) { memset(this, 0, sizeof(*this)); }; + + /** + * This constructor initializes the object with specific values. + * + * @param[in] aSendTime Time when the message shall be sent. + * @param[in] aDestination IPv6 address of the message destination. + * + */ + DelayedResponseHeader(uint32_t aSendTime, const Ip6::Address &aDestination) { + mSendTime = aSendTime; + mDestination = aDestination; + }; + + /** + * This method appends delayed response header to the message. + * + * @param[in] aMessage A reference to the message. + * + * @retval kThreadError_None Successfully appended the bytes. + * @retval kThreadError_NoBufs Insufficient available buffers to grow the message. + * + */ + ThreadError AppendTo(Message &aMessage) { + return aMessage.Append(this, sizeof(*this)); + }; + + /** + * This method reads delayed response header from the message. + * + * @param[in] aMessage A reference to the message. + * + * @returns The number of bytes read. + * + */ + uint16_t ReadFrom(Message &aMessage) { + return aMessage.Read(aMessage.GetLength() - sizeof(*this), sizeof(*this), this); + }; + + /** + * This method removes delayed response header from the message. + * + * @param[in] aMessage A reference to the message. + * + * @retval kThreadError_None Successfully removed the header. + * + */ + static ThreadError RemoveFrom(Message &aMessage) { + return aMessage.SetLength(aMessage.GetLength() - sizeof(DelayedResponseHeader)); + }; + + /** + * This method returns a time when the message shall be sent. + * + * @returns A time when the message shall be sent. + * + */ + uint32_t GetSendTime(void) const { return mSendTime; }; + + /** + * This method returns a destination of the delayed message. + * + * @returns A destination of the delayed message. + * + */ + const Ip6::Address &GetDestination(void) const { return mDestination; }; + + /** + * This method checks if the message shall be sent before the given time. + * + * @param[in] aTime A time to compare. + * + * @retval TRUE If the message shall be sent before the given time. + * @retval FALSE Otherwise. + */ + bool IsEarlier(uint32_t aTime) { return (static_cast(aTime - mSendTime) > 0); }; + + /** + * This method checks if the message shall be sent after the given time. + * + * @param[in] aTime A time to compare. + * + * @retval TRUE If the message shall be sent after the given time. + * @retval FALSE Otherwise. + */ + bool IsLater(uint32_t aTime) { return (static_cast(aTime - mSendTime) < 0); }; + +private: + Ip6::Address mDestination; ///< IPv6 address of the message destination. + uint32_t mSendTime; ///< Time when the message shall be sent. +} OT_TOOL_PACKED_END; + /** * This class implements MLE functionality required by the Thread Router and Leader roles. * @@ -552,7 +658,7 @@ private: ThreadError SendLinkRequest(Neighbor *aNeighbor); ThreadError SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor, const TlvRequestTlv &aTlvRequest, const ChallengeTlv &aChallenge); - ThreadError SendParentResponse(Child *aChild, const ChallengeTlv &aChallenge); + ThreadError SendParentResponse(Child *aChild, const ChallengeTlv &aChallenge, bool aRoutersOnlyRequest); ThreadError SendChildIdResponse(Child *aChild); ThreadError SendChildUpdateResponse(Child *aChild, const Ip6::MessageInfo &aMessageInfo, const uint8_t *aTlvs, uint8_t aTlvsLength, const ChallengeTlv *challenge); @@ -593,9 +699,16 @@ private: bool HandleAdvertiseTimer(void); static void HandleStateUpdateTimer(void *aContext); void HandleStateUpdateTimer(void); + static void HandleDelayedResponseTimer(void *aContext); + void HandleDelayedResponseTimer(void); + + ThreadError AddDelayedResponse(Message &aMessage, const Ip6::Address &aDestination, uint16_t aDelay); + + MessageQueue mDelayedResponses; TrickleTimer mAdvertiseTimer; Timer mStateUpdateTimer; + Timer mDelayedResponseTimer; Ip6::UdpSocket mSocket; Coap::Resource mAddressSolicit;