From e9e900a5dc1c4fadd008db6f22c3bce94d9f1632 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 11 Jul 2019 16:22:10 -0700 Subject: [PATCH] [indirect-sender] convert queued child messages when mode changes (#4001) This commit ensures all queued messages for a child are accordingly converted (indirect to direct transmission) when a child switches its mode from sleepy to non-sleepy. --- src/core/thread/indirect_sender.cpp | 40 +++++++++++++++++++++++++++++ src/core/thread/indirect_sender.hpp | 9 +++++++ src/core/thread/mle_router.cpp | 39 ++++++++++++++++------------ 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/src/core/thread/indirect_sender.cpp b/src/core/thread/indirect_sender.cpp index 7a23fbaca..25acd0924 100644 --- a/src/core/thread/indirect_sender.cpp +++ b/src/core/thread/indirect_sender.cpp @@ -41,6 +41,7 @@ #include "common/logging.hpp" #include "common/message.hpp" #include "thread/mesh_forwarder.hpp" +#include "thread/mle_tlvs.hpp" #include "thread/topology.hpp" namespace ot { @@ -163,6 +164,45 @@ exit: return; } +void IndirectSender::HandleChildModeChange(Child &aChild, uint8_t aOldMode) +{ + bool wasRxOnWhenIdle = ((aOldMode & Mle::ModeTlv::kModeRxOnWhenIdle) != 0); + + if (!aChild.IsRxOnWhenIdle() && (aChild.GetState() == Neighbor::kStateValid)) + { + SetChildUseShortAddress(aChild, true); + } + + // On sleepy to non-sleepy mode change, convert indirect messages in + // the send queue destined to the child to direct. + + if (!wasRxOnWhenIdle && aChild.IsRxOnWhenIdle() && (aChild.GetIndirectMessageCount() > 0)) + { + uint8_t childIndex = Get().GetChildIndex(aChild); + + for (Message *message = Get().mSendQueue.GetHead(); message; message = message->GetNext()) + { + if (message->GetChildMask(childIndex)) + { + message->ClearChildMask(childIndex); + message->SetDirectTransmission(); + } + } + + aChild.SetIndirectMessage(NULL); + mSourceMatchController.ResetMessageCount(aChild); + + mDataPollHandler.RequestFrameChange(DataPollHandler::kPurgeFrame, aChild); + } + + // Since the queuing delays for direct transmissions are expected to + // be relatively small especially when compared to indirect, for a + // non-sleepy to sleepy mode change, we allow any direct message + // (for the child) already in the send queue to remain as is. This + // is equivalent to dropping the already queued messages in this + // case. +} + Message *IndirectSender::FindIndirectMessage(Child &aChild) { Message *message; diff --git a/src/core/thread/indirect_sender.hpp b/src/core/thread/indirect_sender.hpp index 46f6689d9..d46bb00d9 100644 --- a/src/core/thread/indirect_sender.hpp +++ b/src/core/thread/indirect_sender.hpp @@ -186,6 +186,15 @@ public: */ void SetChildUseShortAddress(Child &aChild, bool aUseShortAddress); + /** + * This method handles a child mode change and updates any queued messages for the child accordingly. + * + * @param[in] aChild The child whose device mode was changed. + * @param[in] aOldMode The old device mode of the child. + * + */ + void HandleChildModeChange(Child &aChild, uint8_t aOldMode); + private: enum { diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 50a83570f..3cb9a90aa 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -2225,6 +2225,7 @@ otError MleRouter::HandleChildUpdateRequest(const Message & aMessage, LeaderDataTlv leaderData; TimeoutTlv timeout; Child * child; + uint8_t oldMode; TlvRequestTlv tlvRequest; uint8_t tlvs[kMaxResponseTlvs]; uint8_t tlvslength = 0; @@ -2257,23 +2258,8 @@ otError MleRouter::HandleChildUpdateRequest(const Message & aMessage, ExitNow(); } - if (child->GetDeviceMode() != mode.GetMode()) - { - otLogNoteMle("Child 0x%04x mode change 0x%02x -> 0x%02x [rx-on:%s, sec-data-req:%s, ftd:%s, full-netdata:%s]", - child->GetRloc16(), child->GetDeviceMode(), mode.GetMode(), - (mode.GetMode() & ModeTlv::kModeRxOnWhenIdle) ? "yes" : " no", - (mode.GetMode() & ModeTlv::kModeSecureDataRequest) ? "yes" : " no", - (mode.GetMode() & ModeTlv::kModeFullThreadDevice) ? "yes" : "no", - (mode.GetMode() & ModeTlv::kModeFullNetworkData) ? "yes" : "no"); - - child->SetDeviceMode(mode.GetMode()); - childDidChange = true; - - if (!(mode.GetMode() & ModeTlv::kModeRxOnWhenIdle) && (child->GetState() == Neighbor::kStateValid)) - { - Get().SetChildUseShortAddress(*child, true); - } - } + oldMode = child->GetDeviceMode(); + child->SetDeviceMode(mode.GetMode()); tlvs[tlvslength++] = Tlv::kMode; @@ -2337,6 +2323,25 @@ otError MleRouter::HandleChildUpdateRequest(const Message & aMessage, child->SetLastHeard(TimerMilli::GetNow()); + if (oldMode != child->GetDeviceMode()) + { + otLogNoteMle("Child 0x%04x mode change 0x%02x -> 0x%02x [rx-on:%s, sec-data-req:%s, ftd:%s, full-netdata:%s]", + child->GetRloc16(), oldMode, mode.GetMode(), + (mode.GetMode() & ModeTlv::kModeRxOnWhenIdle) ? "yes" : "no", + (mode.GetMode() & ModeTlv::kModeSecureDataRequest) ? "yes" : "no", + (mode.GetMode() & ModeTlv::kModeFullThreadDevice) ? "yes" : "no", + (mode.GetMode() & ModeTlv::kModeFullNetworkData) ? "yes" : "no"); + + childDidChange = true; + + // The `IndirectSender::HandleChildModeChange()` needs to happen + // after "Child Update" message is fully parsed to ensure that + // any registered IPv6 addresses included in the "Child Update" + // are added to the child. + + Get().HandleChildModeChange(*child, oldMode); + } + if (child->IsStateRestoring()) { SetChildStateToValid(*child);