From f56e12d205b67e6e304428a9ab56e49139866590 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 19 Jun 2019 11:29:52 -0700 Subject: [PATCH] [indirect-sender] adding new class IndirectSender (#3952) This commit adds a new module/class `IndirectSender` for handling of the indirect transmission to sleepy children. The commit just moves the existing code form `MeshForwarder` into the new class. --- Android.mk | 1 + src/core/Makefile.am | 2 + src/core/common/instance.hpp | 7 +- src/core/thread/indirect_sender.cpp | 500 +++++++++++++++++++++++ src/core/thread/indirect_sender.hpp | 154 +++++++ src/core/thread/mesh_forwarder.cpp | 21 +- src/core/thread/mesh_forwarder.hpp | 27 +- src/core/thread/mesh_forwarder_ftd.cpp | 427 +------------------ src/core/thread/mle_router.cpp | 2 +- src/core/thread/src_match_controller.cpp | 1 + src/core/thread/src_match_controller.hpp | 4 +- 11 files changed, 689 insertions(+), 457 deletions(-) create mode 100644 src/core/thread/indirect_sender.cpp create mode 100644 src/core/thread/indirect_sender.hpp diff --git a/Android.mk b/Android.mk index 52e025c41..d9f44f602 100644 --- a/Android.mk +++ b/Android.mk @@ -191,6 +191,7 @@ LOCAL_SRC_FILES := \ src/core/thread/announce_sender.cpp \ src/core/thread/child_table.cpp \ src/core/thread/energy_scan_server.cpp \ + src/core/thread/indirect_sender.cpp \ src/core/thread/key_manager.cpp \ src/core/thread/link_quality.cpp \ src/core/thread/lowpan.cpp \ diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 598c1a3aa..e627597a4 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -197,6 +197,7 @@ SOURCES_COMMON = \ thread/announce_sender.cpp \ thread/child_table.cpp \ thread/energy_scan_server.cpp \ + thread/indirect_sender.cpp \ thread/key_manager.cpp \ thread/link_quality.cpp \ thread/lowpan.cpp \ @@ -364,6 +365,7 @@ HEADERS_COMMON = \ thread/announce_sender.hpp \ thread/child_table.hpp \ thread/energy_scan_server.hpp \ + thread/indirect_sender.hpp \ thread/key_manager.hpp \ thread/link_quality.hpp \ thread/lowpan.hpp \ diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 68d829f68..54da18179 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -458,9 +458,14 @@ template <> inline Ip6::Filter &Instance::Get(void) } #if OPENTHREAD_FTD +template <> inline IndirectSender &Instance::Get(void) +{ + return mThreadNetif.mMeshForwarder.mIndirectSender; +} + template <> inline SourceMatchController &Instance::Get(void) { - return mThreadNetif.mMeshForwarder.mSourceMatchController; + return mThreadNetif.mMeshForwarder.mIndirectSender.mSourceMatchController; } template <> inline AddressResolver &Instance::Get(void) diff --git a/src/core/thread/indirect_sender.cpp b/src/core/thread/indirect_sender.cpp new file mode 100644 index 000000000..c373c8620 --- /dev/null +++ b/src/core/thread/indirect_sender.cpp @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2019, 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 handling indirect transmission. + */ + +#if OPENTHREAD_FTD + +#include "indirect_sender.hpp" + +#include "common/code_utils.hpp" +#include "common/instance.hpp" +#include "common/locator-getters.hpp" +#include "common/logging.hpp" +#include "common/message.hpp" +#include "thread/mesh_forwarder.hpp" +#include "thread/topology.hpp" + +namespace ot { + +IndirectSender::IndirectSender(Instance &aInstance) + : InstanceLocator(aInstance) + , mEnabled(false) + , mSourceMatchController(aInstance) + , mIndirectStartingChild(NULL) +{ +} + +void IndirectSender::Stop(void) +{ + VerifyOrExit(mEnabled); + + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter++) + { + iter.GetChild()->SetIndirectMessage(NULL); + mSourceMatchController.ResetMessageCount(*iter.GetChild()); + } + + mIndirectStartingChild = NULL; + +exit: + mEnabled = false; +} + +otError IndirectSender::AddMessageForSleepyChild(Message &aMessage, Child &aChild) +{ + otError error = OT_ERROR_NONE; + uint8_t childIndex; + + VerifyOrExit(!aChild.IsRxOnWhenIdle(), error = OT_ERROR_INVALID_STATE); + + childIndex = Get().GetChildIndex(aChild); + VerifyOrExit(!aMessage.GetChildMask(childIndex), error = OT_ERROR_ALREADY); + + aMessage.SetChildMask(childIndex); + mSourceMatchController.IncrementMessageCount(aChild); + +exit: + return error; +} + +otError IndirectSender::RemoveMessageFromSleepyChild(Message &aMessage, Child &aChild) +{ + otError error = OT_ERROR_NONE; + uint8_t childIndex = Get().GetChildIndex(aChild); + + VerifyOrExit(aMessage.GetChildMask(childIndex), error = OT_ERROR_NOT_FOUND); + + aMessage.ClearChildMask(childIndex); + mSourceMatchController.DecrementMessageCount(aChild); + + if (aChild.GetIndirectMessage() == &aMessage) + { + aChild.SetIndirectMessage(NULL); + } + +exit: + return error; +} + +void IndirectSender::ClearAllMessagesForSleepyChild(Child &aChild) +{ + Message *nextMessage; + + VerifyOrExit(aChild.GetIndirectMessageCount() > 0); + + for (Message *message = Get().mSendQueue.GetHead(); message; message = nextMessage) + { + nextMessage = message->GetNext(); + + message->ClearChildMask(Get().GetChildIndex(aChild)); + + if (!message->IsChildPending() && !message->GetDirectTransmission()) + { + if (Get().mSendMessage == message) + { + Get().mSendMessage = NULL; + } + + Get().mSendQueue.Dequeue(*message); + message->Free(); + } + } + + aChild.SetIndirectMessage(NULL); + mSourceMatchController.ResetMessageCount(aChild); + +exit: + return; +} + +void IndirectSender::HandleDataPoll(const Mac::Frame & aFrame, + const Mac::Address & aMacSource, + const otThreadLinkInfo &aLinkInfo) +{ + Child * child; + uint16_t indirectMsgCount; + + // Security Check: only process secure Data Poll frames. + VerifyOrExit(aLinkInfo.mLinkSecurity); + + VerifyOrExit(Get().GetRole() != OT_DEVICE_ROLE_DETACHED); + + child = Get().FindChild(aMacSource, ChildTable::kInStateValidOrRestoring); + VerifyOrExit(child != NULL); + + child->SetLastHeard(TimerMilli::GetNow()); + child->ResetLinkFailures(); + indirectMsgCount = child->GetIndirectMessageCount(); + + otLogInfoMac("Rx data poll, src:0x%04x, qed_msgs:%d, rss:%d, ack-fp:%d", child->GetRloc16(), indirectMsgCount, + aLinkInfo.mRss, aFrame.IsAckedWithFramePending()); + VerifyOrExit(aFrame.IsAckedWithFramePending()); + + if (!mSourceMatchController.IsEnabled() || (indirectMsgCount > 0)) + { + child->SetDataRequestPending(true); + } + + Get().mScheduleTransmissionTask.Post(); + +exit: + return; +} + +otError IndirectSender::GetIndirectTransmission(void) +{ + otError error = OT_ERROR_NOT_FOUND; + + UpdateIndirectMessages(); + + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild); + !iter.IsDone(); iter++) + { + Child &child = *iter.GetChild(); + + if (!child.IsDataRequestPending()) + { + continue; + } + + Get().mSendMessage = child.GetIndirectMessage(); + Get().mSendMessageMaxCsmaBackoffs = Mac::kMaxCsmaBackoffsIndirect; + Get().mSendMessageMaxFrameRetries = Mac::kMaxFrameRetriesIndirect; + + if (Get().mSendMessage == NULL) + { + Get().mSendMessage = GetIndirectTransmission(child); + } + + if (Get().mSendMessage != NULL) + { + PrepareIndirectTransmission(*Get().mSendMessage, child); + } + else + { + // A NULL `mSendMessage` triggers an empty frame to be sent to the child. + + if (child.IsIndirectSourceMatchShort()) + { + Get().mMacSource.SetShort(Get().GetShortAddress()); + } + else + { + Get().mMacSource.SetExtended(Get().GetExtAddress()); + } + + child.GetMacAddress(Get().mMacDest); + } + + // Remember the current child and move it to next one in the + // list after the indirect transmission has completed. + + mIndirectStartingChild = &child; + + Get().RequestFrameTransmission(); + ExitNow(error = OT_ERROR_NONE); + } + +exit: + return error; +} + +Message *IndirectSender::GetIndirectTransmission(Child &aChild) +{ + Message *message = NULL; + Message *next; + uint8_t childIndex = Get().GetChildIndex(aChild); + + for (message = Get().mSendQueue.GetHead(); message; message = next) + { + next = message->GetNext(); + + if (message->GetChildMask(childIndex)) + { + // Skip and remove the supervision message if there are other messages queued for the child. + + if ((message->GetType() == Message::kTypeSupervision) && (aChild.GetIndirectMessageCount() > 1)) + { + message->ClearChildMask(childIndex); + mSourceMatchController.DecrementMessageCount(aChild); + Get().mSendQueue.Dequeue(*message); + message->Free(); + continue; + } + + break; + } + } + + aChild.SetIndirectMessage(message); + aChild.SetIndirectFragmentOffset(0); + aChild.ResetIndirectTxAttempts(); + aChild.SetIndirectTxSuccess(true); + + if (message != NULL) + { + Mac::Address macAddr; + + Get().LogMessage(MeshForwarder::kMessagePrepareIndirect, *message, + &aChild.GetMacAddress(macAddr), OT_ERROR_NONE); + } + + return message; +} + +void IndirectSender::PrepareIndirectTransmission(Message &aMessage, const Child &aChild) +{ + if (aChild.GetIndirectTxAttempts() > 0) + { + Get().mSendMessageIsARetransmission = true; + Get().mSendMessageFrameCounter = aChild.GetIndirectFrameCounter(); + Get().mSendMessageKeyId = aChild.GetIndirectKeyId(); + Get().mSendMessageDataSequenceNumber = aChild.GetIndirectDataSequenceNumber(); + } + + aMessage.SetOffset(aChild.GetIndirectFragmentOffset()); + + switch (aMessage.GetType()) + { + case Message::kTypeIp6: + { + Ip6::Header ip6Header; + + aMessage.Read(0, sizeof(ip6Header), &ip6Header); + + Get().mAddMeshHeader = false; + Get().GetMacSourceAddress(ip6Header.GetSource(), Get().mMacSource); + + if (ip6Header.GetDestination().IsLinkLocal()) + { + Get().GetMacDestinationAddress(ip6Header.GetDestination(), Get().mMacDest); + } + else + { + aChild.GetMacAddress(Get().mMacDest); + } + + break; + } + + case Message::kTypeSupervision: + aChild.GetMacAddress(Get().mMacDest); + break; + + default: + assert(false); + break; + } +} + +void IndirectSender::UpdateIndirectMessages(void) +{ + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptValidOrRestoring); !iter.IsDone(); + iter++) + { + if (iter.GetChild()->GetIndirectMessageCount() == 0) + { + continue; + } + + ClearAllMessagesForSleepyChild(*iter.GetChild()); + } +} + +void IndirectSender::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aError, const Mac::Address &aMacDest) +{ + Child *child; + + child = Get().FindChild(aMacDest, ChildTable::kInStateValidOrRestoring); + VerifyOrExit(child != NULL); + + child->SetDataRequestPending(false); + + VerifyOrExit(Get().mSendMessage != NULL); + + if (Get().mSendMessage == child->GetIndirectMessage()) + { + // To ensure fairness in handling of data requests from sleepy + // children, once a message is completed for indirect transmission to a + // child (on both success or failure), the `mIndirectStartingChild` is + // updated to the next `Child` entry after the current one. Subsequent + // call to `ScheduleTransmissionTask()` will begin the iteration + // through the children list from this child. + + ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild); + iter++; + mIndirectStartingChild = iter.GetChild(); + + switch (aError) + { + case OT_ERROR_NONE: + child->ResetIndirectTxAttempts(); + break; + + case OT_ERROR_NO_ACK: + child->IncrementIndirectTxAttempts(); + // fall through + + case OT_ERROR_CHANNEL_ACCESS_FAILURE: + case OT_ERROR_ABORT: + + otLogInfoMac("Indirect tx to child %04x failed, attempt %d/%d, error:%s", child->GetRloc16(), + child->GetIndirectTxAttempts(), kMaxPollTriggeredTxAttempts, otThreadErrorToString(aError)); + + if (child->GetIndirectTxAttempts() < kMaxPollTriggeredTxAttempts) + { + // We save the frame counter, key id, and data sequence number of + // current frame so we use the same values for the retransmission + // of the frame following the receipt of a data request command (data + // poll) from the sleepy child. + + child->SetIndirectDataSequenceNumber(aFrame.GetSequence()); + + if (aFrame.GetSecurityEnabled()) + { + uint32_t frameCounter; + uint8_t keyId; + + aFrame.GetFrameCounter(frameCounter); + child->SetIndirectFrameCounter(frameCounter); + + aFrame.GetKeyId(keyId); + child->SetIndirectKeyId(keyId); + } + + ExitNow(); + } + + child->ResetIndirectTxAttempts(); + child->SetIndirectTxSuccess(false); + +#if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE + // We set the NextOffset to end of message, since there is no need to + // send any remaining fragments in the message to the child, if all tx + // attempts of current frame already failed. + + Get().mMessageNextOffset = Get().mSendMessage->GetLength(); +#endif + + break; + + default: + assert(false); + break; + } + } + + if (Get().mMessageNextOffset < Get().mSendMessage->GetLength()) + { + if (Get().mSendMessage == child->GetIndirectMessage()) + { + child->SetIndirectFragmentOffset(Get().mMessageNextOffset); + } + } + else + { + otError txError = aError; + uint8_t childIndex; + + if (Get().mSendMessage == child->GetIndirectMessage()) + { + child->SetIndirectFragmentOffset(0); + child->SetIndirectMessage(NULL); + child->GetLinkInfo().AddMessageTxStatus(child->GetIndirectTxSuccess()); + + // Enable short source address matching after the first indirect + // message transmission attempt to the child. We intentionally do + // not check for successful tx here to address the scenario where + // the child does receive "Child ID Response" but parent misses the + // 15.4 ack from child. If the "Child ID Response" does not make it + // to the child, then the child will need to send a new "Child ID + // Request" which will cause the parent to switch to using long + // address mode for source address matching. + + mSourceMatchController.SetSrcMatchAsShort(*child, true); + +#if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE + + // When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is + // disabled, all fragment frames of a larger message are + // sent even if the transmission of an earlier fragment fail. + // Note that `GetIndirectTxSuccess() tracks the tx success of + // the entire message to the child, while `txError = aError` + // represents the error status of the last fragment frame + // transmission. + + if (!child->GetIndirectTxSuccess() && (txError == OT_ERROR_NONE)) + { + txError = OT_ERROR_FAILED; + } +#endif + } + + childIndex = Get().GetChildIndex(*child); + + if (Get().mSendMessage->GetChildMask(childIndex)) + { + Get().mSendMessage->ClearChildMask(childIndex); + mSourceMatchController.DecrementMessageCount(*child); + } + + if (!Get().mSendMessage->GetDirectTransmission()) + { + Get().LogMessage(MeshForwarder::kMessageTransmit, *Get().mSendMessage, + &aMacDest, txError); + + if (Get().mSendMessage->GetType() == Message::kTypeIp6) + { + if (Get().mSendMessage->GetTxSuccess()) + { + Get().mIpCounters.mTxSuccess++; + } + else + { + Get().mIpCounters.mTxFailure++; + } + } + } + } + + if (aError == OT_ERROR_NONE) + { + Get().UpdateOnSend(*child); + } + +exit: + return; +} + +} // namespace ot + +#endif // #if OPENTHREAD_FTD diff --git a/src/core/thread/indirect_sender.hpp b/src/core/thread/indirect_sender.hpp new file mode 100644 index 000000000..c209df238 --- /dev/null +++ b/src/core/thread/indirect_sender.hpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2019, 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 handling indirect transmission. + */ + +#ifndef INDIRECT_SENDER_HPP_ +#define INDIRECT_SENDER_HPP_ + +#include "openthread-core-config.h" + +#include "common/locator.hpp" +#include "common/message.hpp" +#include "mac/mac_frame.hpp" +#include "thread/src_match_controller.hpp" + +namespace ot { + +/** + * @addtogroup core-mesh-forwarding + * + * @brief + * This module includes definitions for handling indirect transmissions. + * + * @{ + */ + +class Child; + +/** + * This class implements indirect transmission. + * + */ +class IndirectSender : public InstanceLocator +{ + friend class Instance; + +public: + /** + * This constructor initializes the object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit IndirectSender(Instance &aInstance); + + /** + * This method enables indirect transmissions. + * + */ + void Start(void) { mEnabled = true; } + + /** + * This method disables indirect transmission. + * + * Any previously scheduled indirect transmission is canceled. + * + */ + void Stop(void); + + /** + * This method adds a message for indirect transmission to a sleepy child. + * + * @param[in] aMessage The message to add. + * @param[in] aChild The (sleepy) child for indirect transmission. + * + * @retval OT_ERROR_NONE Successfully added the message for indirect transmission. + * @retval OT_ERROR_ALREADY The message was already added for indirect transmission to same child. + * @retval OT_ERROR_INVALID_STATE The child is not sleepy. + * + */ + otError AddMessageForSleepyChild(Message &aMessage, Child &aChild); + + /** + * This method removes a message for indirect transmission to a sleepy child. + * + * @param[in] aMessage The message to update. + * @param[in] aChild The (sleepy) child for indirect transmission. + * + * @retval OT_ERROR_NONE Successfully removed the message for indirect transmission. + * @retval OT_ERROR_NOT_FOUND The message was not scheduled for indirect transmission to the child. + * + */ + otError RemoveMessageFromSleepyChild(Message &aMessage, Child &aChild); + + /** + * This method removes all added messages for a specific child and frees message (with no indirect/direct tx). + * + * @param[in] aChild A reference to a child whose messages shall be removed. + * + */ + void ClearAllMessagesForSleepyChild(Child &aChild); + + void HandleDataPoll(const Mac::Frame &aFrame, const Mac::Address &aMacSource, const otThreadLinkInfo &aLinkInfo); + otError GetIndirectTransmission(void); + void HandleSentFrameToChild(const Mac::Frame &aFrame, otError aError, const Mac::Address &aMacDest); + +private: + enum + { + /** + * Maximum number of tx attempts by `MeshForwarder` for an outbound indirect frame (for a sleepy child). The + * `MeshForwader` attempts occur following the reception of a new data request command (a new data poll) from + * the sleepy child. + * + */ + kMaxPollTriggeredTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS, + }; + + Message *GetIndirectTransmission(Child &aChild); + void PrepareIndirectTransmission(Message &aMessage, const Child &aChild); + void UpdateIndirectMessages(void); + + bool mEnabled; + + SourceMatchController mSourceMatchController; + Child * mIndirectStartingChild; +}; + +/** + * @} + * + */ + +} // namespace ot + +#endif // INDIRECT_SENDER_HPP_ diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 730825a66..f6cbea649 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -76,7 +76,7 @@ MeshForwarder::MeshForwarder(Instance &aInstance) , mRestorePanId(Mac::kPanIdBroadcast) , mScanning(false) #if OPENTHREAD_FTD - , mSourceMatchController(aInstance) + , mIndirectSender(aInstance) , mSendMessageFrameCounter(0) , mSendMessageKeyId(0) , mSendMessageDataSequenceNumber(0) @@ -101,6 +101,10 @@ void MeshForwarder::Start(void) if (mEnabled == false) { Get().SetRxOnWhenIdle(true); +#if OPENTHREAD_FTD + mIndirectSender.Start(); +#endif + mEnabled = true; } } @@ -132,12 +136,7 @@ void MeshForwarder::Stop(void) } #if OPENTHREAD_FTD - for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter++) - { - iter.GetChild()->SetIndirectMessage(NULL); - Get().ResetMessageCount(*iter.GetChild()); - } - + mIndirectSender.Stop(); memset(mFragmentEntries, 0, sizeof(mFragmentEntries)); #endif @@ -154,7 +153,7 @@ void MeshForwarder::RemoveMessage(Message &aMessage) #if OPENTHREAD_FTD for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter++) { - IgnoreReturnValue(RemoveMessageFromSleepyChild(aMessage, *iter.GetChild())); + IgnoreReturnValue(mIndirectSender.RemoveMessageFromSleepyChild(aMessage, *iter.GetChild())); } #endif @@ -180,7 +179,7 @@ void MeshForwarder::ScheduleTransmissionTask(void) mSendMessageIsARetransmission = false; #if OPENTHREAD_FTD - if (GetIndirectTransmission() == OT_ERROR_NONE) + if (mIndirectSender.GetIndirectTransmission() == OT_ERROR_NONE) { ExitNow(); } @@ -992,7 +991,7 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, otError aError) neighbor = UpdateNeighborOnSentFrame(aFrame, aError, macDest); #if OPENTHREAD_FTD - HandleSentFrameToChild(aFrame, aError, macDest); + mIndirectSender.HandleSentFrameToChild(aFrame, aError, macDest); #endif VerifyOrExit(mSendMessage != NULL); @@ -1217,7 +1216,7 @@ void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame) if (commandId == Mac::Frame::kMacCmdDataRequest) { - HandleDataRequest(aFrame, macSource, linkInfo); + mIndirectSender.HandleDataPoll(aFrame, macSource, linkInfo); } else { diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index c39f33ea3..3e82f297a 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -43,9 +43,9 @@ #include "mac/mac.hpp" #include "net/ip6.hpp" #include "thread/address_resolver.hpp" +#include "thread/indirect_sender.hpp" #include "thread/lowpan.hpp" #include "thread/network_data_leader.hpp" -#include "thread/src_match_controller.hpp" #include "thread/topology.hpp" namespace ot { @@ -170,6 +170,7 @@ class MeshForwarder : public InstanceLocator friend class Mac::Mac; friend class Instance; friend class DataPollSender; + friend class IndirectSender; public: /** @@ -239,14 +240,6 @@ public: */ void SetDiscoverParameters(const Mac::ChannelMask &aScanChannels); - /** - * This method frees any indirect messages queued for a specific child. - * - * @param[in] aChild A reference to a child whom messages shall be removed. - * - */ - void ClearChildIndirectMessages(Child &aChild); - /** * This method frees any indirect messages queued for children that are no longer attached. * @@ -329,14 +322,6 @@ private: */ kNumFragmentPriorityEntries = OPENTHREAD_CONFIG_NUM_FRAGMENT_PRIORITY_ENTRIES, - /** - * Maximum number of tx attempts by `MeshForwarder` for an outbound indirect frame (for a sleepy child). The - * `MeshForwader` attempts occur following the reception of a new data request command (a new data poll) from - * the sleepy child. - * - */ - kMaxPollTriggeredTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS, - /** * Indicates whether to set/enable 15.4 ack request in the MAC header of a supervision message. * @@ -380,10 +365,7 @@ private: void GetMacDestinationAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); void GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); Message *GetDirectTransmission(void); - otError GetIndirectTransmission(void); - Message *GetIndirectTransmission(Child &aChild); otError PrepareDiscoverRequest(void); - void PrepareIndirectTransmission(Message &aMessage, const Child &aChild); void HandleMesh(uint8_t * aFrame, uint16_t aFrameLength, const Mac::Address & aMacSource, @@ -398,7 +380,6 @@ private: const Mac::Address & aMacSource, const Mac::Address & aMacDest, const otThreadLinkInfo &aLinkInfo); - void HandleDataRequest(const Mac::Frame &aFrame, const Mac::Address &aMacSource, const otThreadLinkInfo &aLinkInfo); static otError GetFragmentHeader(const uint8_t * aFrame, uint16_t aFrameLength, @@ -418,7 +399,6 @@ private: uint8_t aPriority); otError HandleDatagram(Message &aMessage, const otThreadLinkInfo &aLinkInfo, const Mac::Address &aMacSource); void ClearReassemblyList(void); - otError RemoveMessageFromSleepyChild(Message &aMessage, Child &aChild); void RemoveMessage(Message &aMessage); void HandleDiscoverComplete(void); @@ -426,7 +406,6 @@ private: otError HandleFrameRequest(Mac::Frame &aFrame); Neighbor *UpdateNeighborOnSentFrame(Mac::Frame &aFrame, otError aError, const Mac::Address &aMacDest); void HandleSentFrame(Mac::Frame &aFrame, otError aError); - void HandleSentFrameToChild(const Mac::Frame &aFrame, otError aError, const Mac::Address &aMacDest); static void HandleDiscoverTimer(Timer &aTimer); void HandleDiscoverTimer(void); @@ -549,7 +528,7 @@ private: #if OPENTHREAD_FTD FragmentPriorityEntry mFragmentEntries[kNumFragmentPriorityEntries]; MessageQueue mResolvingQueue; - SourceMatchController mSourceMatchController; + IndirectSender mIndirectSender; uint32_t mSendMessageFrameCounter; uint8_t mSendMessageKeyId; uint8_t mSendMessageDataSequenceNumber; diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index a0b8f0a62..83b883258 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -46,9 +46,8 @@ namespace ot { otError MeshForwarder::SendMessage(Message &aMessage) { - Mle::MleRouter &mle = Get(); - ChildTable & childTable = Get(); - otError error = OT_ERROR_NONE; + Mle::MleRouter &mle = Get(); + otError error = OT_ERROR_NONE; Neighbor * neighbor; switch (aMessage.GetType()) @@ -84,8 +83,7 @@ otError MeshForwarder::SendMessage(Message &aMessage) if (!child.IsRxOnWhenIdle()) { - aMessage.SetChildMask(childTable.GetChildIndex(child)); - mSourceMatchController.IncrementMessageCount(child); + mIndirectSender.AddMessageForSleepyChild(aMessage, child); } } } @@ -99,8 +97,7 @@ otError MeshForwarder::SendMessage(Message &aMessage) if (mle.IsSleepyChildSubscribed(ip6Header.GetDestination(), child)) { - aMessage.SetChildMask(childTable.GetChildIndex(child)); - mSourceMatchController.IncrementMessageCount(child); + mIndirectSender.AddMessageForSleepyChild(aMessage, child); } } } @@ -111,8 +108,7 @@ otError MeshForwarder::SendMessage(Message &aMessage) { // destined for a sleepy child Child &child = *static_cast(neighbor); - aMessage.SetChildMask(childTable.GetChildIndex(child)); - mSourceMatchController.IncrementMessageCount(child); + mIndirectSender.AddMessageForSleepyChild(aMessage, child); } else { @@ -129,8 +125,7 @@ otError MeshForwarder::SendMessage(Message &aMessage) VerifyOrExit(child != NULL, error = OT_ERROR_DROP); VerifyOrExit(!child->IsRxOnWhenIdle(), error = OT_ERROR_DROP); - aMessage.SetChildMask(childTable.GetChildIndex(*child)); - mSourceMatchController.IncrementMessageCount(*child); + mIndirectSender.AddMessageForSleepyChild(aMessage, *child); break; } @@ -188,51 +183,6 @@ void MeshForwarder::HandleResolved(const Ip6::Address &aEid, otError aError) } } -void MeshForwarder::ClearChildIndirectMessages(Child &aChild) -{ - Message *nextMessage; - - VerifyOrExit(aChild.GetIndirectMessageCount() > 0); - - for (Message *message = mSendQueue.GetHead(); message; message = nextMessage) - { - nextMessage = message->GetNext(); - - message->ClearChildMask(Get().GetChildIndex(aChild)); - - if (!message->IsChildPending() && !message->GetDirectTransmission()) - { - if (mSendMessage == message) - { - mSendMessage = NULL; - } - - mSendQueue.Dequeue(*message); - message->Free(); - } - } - - aChild.SetIndirectMessage(NULL); - mSourceMatchController.ResetMessageCount(aChild); - -exit: - return; -} - -void MeshForwarder::UpdateIndirectMessages(void) -{ - for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptValidOrRestoring); !iter.IsDone(); - iter++) - { - if (iter.GetChild()->GetIndirectMessageCount() == 0) - { - continue; - } - - ClearChildIndirectMessages(*iter.GetChild()); - } -} - otError MeshForwarder::EvictMessage(uint8_t aPriority) { otError error = OT_ERROR_NOT_FOUND; @@ -268,25 +218,6 @@ exit: return error; } -otError MeshForwarder::RemoveMessageFromSleepyChild(Message &aMessage, Child &aChild) -{ - otError error = OT_ERROR_NONE; - uint8_t childIndex = Get().GetChildIndex(aChild); - - VerifyOrExit(aMessage.GetChildMask(childIndex) == true, error = OT_ERROR_NOT_FOUND); - - aMessage.ClearChildMask(childIndex); - mSourceMatchController.DecrementMessageCount(aChild); - - if (aChild.GetIndirectMessage() == &aMessage) - { - aChild.SetIndirectMessage(NULL); - } - -exit: - return error; -} - void MeshForwarder::RemoveMessages(Child &aChild, uint8_t aSubType) { Mle::MleRouter &mle = Get(); @@ -301,7 +232,7 @@ void MeshForwarder::RemoveMessages(Child &aChild, uint8_t aSubType) continue; } - if (RemoveMessageFromSleepyChild(*message, aChild) != OT_ERROR_NONE) + if (mIndirectSender.RemoveMessageFromSleepyChild(*message, aChild) != OT_ERROR_NONE) { switch (message->GetType()) { @@ -368,7 +299,7 @@ void MeshForwarder::RemoveDataResponseMessages(void) { for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter++) { - IgnoreReturnValue(RemoveMessageFromSleepyChild(*message, *iter.GetChild())); + IgnoreReturnValue(mIndirectSender.RemoveMessageFromSleepyChild(*message, *iter.GetChild())); } } @@ -383,150 +314,6 @@ void MeshForwarder::RemoveDataResponseMessages(void) } } -otError MeshForwarder::GetIndirectTransmission(void) -{ - otError error = OT_ERROR_NOT_FOUND; - - UpdateIndirectMessages(); - - for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild); - !iter.IsDone(); iter++) - { - Child &child = *iter.GetChild(); - - if (!child.IsDataRequestPending()) - { - continue; - } - - mSendMessage = child.GetIndirectMessage(); - mSendMessageMaxCsmaBackoffs = Mac::kMaxCsmaBackoffsIndirect; - mSendMessageMaxFrameRetries = Mac::kMaxFrameRetriesIndirect; - - if (mSendMessage == NULL) - { - mSendMessage = GetIndirectTransmission(child); - } - - if (mSendMessage != NULL) - { - PrepareIndirectTransmission(*mSendMessage, child); - } - else - { - // A NULL `mSendMessage` triggers an empty frame to be sent to the child. - - if (child.IsIndirectSourceMatchShort()) - { - mMacSource.SetShort(Get().GetShortAddress()); - } - else - { - mMacSource.SetExtended(Get().GetExtAddress()); - } - - child.GetMacAddress(mMacDest); - } - - // Remember the current child and move it to next one in the list after the indirect transmission has completed. - - mIndirectStartingChild = &child; - - Get().RequestFrameTransmission(); - ExitNow(error = OT_ERROR_NONE); - } - -exit: - return error; -} - -Message *MeshForwarder::GetIndirectTransmission(Child &aChild) -{ - Message *message = NULL; - Message *next; - uint8_t childIndex = Get().GetChildIndex(aChild); - - for (message = mSendQueue.GetHead(); message; message = next) - { - next = message->GetNext(); - - if (message->GetChildMask(childIndex)) - { - // Skip and remove the supervision message if there are other messages queued for the child. - - if ((message->GetType() == Message::kTypeSupervision) && (aChild.GetIndirectMessageCount() > 1)) - { - message->ClearChildMask(childIndex); - mSourceMatchController.DecrementMessageCount(aChild); - mSendQueue.Dequeue(*message); - message->Free(); - continue; - } - - break; - } - } - - aChild.SetIndirectMessage(message); - aChild.SetIndirectFragmentOffset(0); - aChild.ResetIndirectTxAttempts(); - aChild.SetIndirectTxSuccess(true); - - if (message != NULL) - { - Mac::Address macAddr; - - LogMessage(kMessagePrepareIndirect, *message, &aChild.GetMacAddress(macAddr), OT_ERROR_NONE); - } - - return message; -} - -void MeshForwarder::PrepareIndirectTransmission(Message &aMessage, const Child &aChild) -{ - if (aChild.GetIndirectTxAttempts() > 0) - { - mSendMessageIsARetransmission = true; - mSendMessageFrameCounter = aChild.GetIndirectFrameCounter(); - mSendMessageKeyId = aChild.GetIndirectKeyId(); - mSendMessageDataSequenceNumber = aChild.GetIndirectDataSequenceNumber(); - } - - aMessage.SetOffset(aChild.GetIndirectFragmentOffset()); - - switch (aMessage.GetType()) - { - case Message::kTypeIp6: - { - Ip6::Header ip6Header; - - aMessage.Read(0, sizeof(ip6Header), &ip6Header); - - mAddMeshHeader = false; - GetMacSourceAddress(ip6Header.GetSource(), mMacSource); - - if (ip6Header.GetDestination().IsLinkLocal()) - { - GetMacDestinationAddress(ip6Header.GetDestination(), mMacDest); - } - else - { - aChild.GetMacAddress(mMacDest); - } - - break; - } - - case Message::kTypeSupervision: - aChild.GetMacAddress(mMacDest); - break; - - default: - assert(false); - break; - } -} - void MeshForwarder::SendMesh(Message &aMessage, Mac::Frame &aFrame) { uint16_t fcf; @@ -549,204 +336,6 @@ void MeshForwarder::SendMesh(Message &aMessage, Mac::Frame &aFrame) mMessageNextOffset = aMessage.GetLength(); } -void MeshForwarder::HandleDataRequest(const Mac::Frame & aFrame, - const Mac::Address & aMacSource, - const otThreadLinkInfo &aLinkInfo) -{ - Child * child; - uint16_t indirectMsgCount; - - // Security Check: only process secure Data Poll frames. - VerifyOrExit(aLinkInfo.mLinkSecurity); - - VerifyOrExit(Get().GetRole() != OT_DEVICE_ROLE_DETACHED); - - child = Get().FindChild(aMacSource, ChildTable::kInStateValidOrRestoring); - VerifyOrExit(child != NULL); - - child->SetLastHeard(TimerMilli::GetNow()); - child->ResetLinkFailures(); - indirectMsgCount = child->GetIndirectMessageCount(); - - otLogInfoMac("Rx data poll, src:0x%04x, qed_msgs:%d, rss:%d, ack-fp:%d", child->GetRloc16(), indirectMsgCount, - aLinkInfo.mRss, aFrame.IsAckedWithFramePending()); - VerifyOrExit(aFrame.IsAckedWithFramePending()); - - if (!mSourceMatchController.IsEnabled() || (indirectMsgCount > 0)) - { - child->SetDataRequestPending(true); - } - - mScheduleTransmissionTask.Post(); - -exit: - return; -} - -void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aError, const Mac::Address &aMacDest) -{ - Child *child; - - child = Get().FindChild(aMacDest, ChildTable::kInStateValidOrRestoring); - VerifyOrExit(child != NULL); - - child->SetDataRequestPending(false); - - VerifyOrExit(mSendMessage != NULL); - - if (mSendMessage == child->GetIndirectMessage()) - { - // To ensure fairness in handling of data requests from sleepy - // children, once a message is completed for indirect transmission to a - // child (on both success or failure), the `mIndirectStartingChild` is - // updated to the next `Child` entry after the current one. Subsequent - // call to `ScheduleTransmissionTask()` will begin the iteration - // through the children list from this child. - - ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild); - iter++; - mIndirectStartingChild = iter.GetChild(); - - switch (aError) - { - case OT_ERROR_NONE: - child->ResetIndirectTxAttempts(); - break; - - case OT_ERROR_NO_ACK: - child->IncrementIndirectTxAttempts(); - // fall through - - case OT_ERROR_CHANNEL_ACCESS_FAILURE: - case OT_ERROR_ABORT: - - otLogInfoMac("Indirect tx to child %04x failed, attempt %d/%d, error:%s", child->GetRloc16(), - child->GetIndirectTxAttempts(), kMaxPollTriggeredTxAttempts, otThreadErrorToString(aError)); - - if (child->GetIndirectTxAttempts() < kMaxPollTriggeredTxAttempts) - { - // We save the frame counter, key id, and data sequence number of - // current frame so we use the same values for the retransmission - // of the frame following the receipt of a data request command (data - // poll) from the sleepy child. - - child->SetIndirectDataSequenceNumber(aFrame.GetSequence()); - - if (aFrame.GetSecurityEnabled()) - { - uint32_t frameCounter; - uint8_t keyId; - - aFrame.GetFrameCounter(frameCounter); - child->SetIndirectFrameCounter(frameCounter); - - aFrame.GetKeyId(keyId); - child->SetIndirectKeyId(keyId); - } - - ExitNow(); - } - - child->ResetIndirectTxAttempts(); - child->SetIndirectTxSuccess(false); - -#if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE - // We set the NextOffset to end of message, since there is no need to - // send any remaining fragments in the message to the child, if all tx - // attempts of current frame already failed. - - mMessageNextOffset = mSendMessage->GetLength(); -#endif - - break; - - default: - assert(false); - break; - } - } - - if (mMessageNextOffset < mSendMessage->GetLength()) - { - if (mSendMessage == child->GetIndirectMessage()) - { - child->SetIndirectFragmentOffset(mMessageNextOffset); - } - } - else - { - otError txError = aError; - uint8_t childIndex; - - if (mSendMessage == child->GetIndirectMessage()) - { - child->SetIndirectFragmentOffset(0); - child->SetIndirectMessage(NULL); - child->GetLinkInfo().AddMessageTxStatus(child->GetIndirectTxSuccess()); - - // Enable short source address matching after the first indirect - // message transmission attempt to the child. We intentionally do - // not check for successful tx here to address the scenario where - // the child does receive "Child ID Response" but parent misses the - // 15.4 ack from child. If the "Child ID Response" does not make it - // to the child, then the child will need to send a new "Child ID - // Request" which will cause the parent to switch to using long - // address mode for source address matching. - - mSourceMatchController.SetSrcMatchAsShort(*child, true); - -#if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE - - // When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is - // disabled, all fragment frames of a larger message are - // sent even if the transmission of an earlier fragment fail. - // Note that `GetIndirectTxSuccess() tracks the tx success of - // the entire message to the child, while `txError = aError` - // represents the error status of the last fragment frame - // transmission. - - if (!child->GetIndirectTxSuccess() && (txError == OT_ERROR_NONE)) - { - txError = OT_ERROR_FAILED; - } -#endif - } - - childIndex = Get().GetChildIndex(*child); - - if (mSendMessage->GetChildMask(childIndex)) - { - mSendMessage->ClearChildMask(childIndex); - mSourceMatchController.DecrementMessageCount(*child); - } - - if (!mSendMessage->GetDirectTransmission()) - { - LogMessage(kMessageTransmit, *mSendMessage, &aMacDest, txError); - - if (mSendMessage->GetType() == Message::kTypeIp6) - { - if (mSendMessage->GetTxSuccess()) - { - mIpCounters.mTxSuccess++; - } - else - { - mIpCounters.mTxFailure++; - } - } - } - } - - if (aError == OT_ERROR_NONE) - { - Get().UpdateOnSend(*child); - } - -exit: - return; -} - otError MeshForwarder::UpdateMeshRoute(Message &aMessage) { otError error = OT_ERROR_NONE; diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index eb6195ad5..e9375143e 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -3150,7 +3150,7 @@ void MleRouter::RemoveNeighbor(Neighbor &aNeighbor) aNeighbor.SetState(Neighbor::kStateInvalid); - Get().ClearChildIndirectMessages(static_cast(aNeighbor)); + Get().ClearAllMessagesForSleepyChild(static_cast(aNeighbor)); Get().SendServerDataNotification(aNeighbor.GetRloc16()); if (aNeighbor.GetDeviceMode() & ModeTlv::kModeFullThreadDevice) diff --git a/src/core/thread/src_match_controller.cpp b/src/core/thread/src_match_controller.cpp index 093481fde..a597be218 100644 --- a/src/core/thread/src_match_controller.cpp +++ b/src/core/thread/src_match_controller.cpp @@ -40,6 +40,7 @@ #include "mac/mac_frame.hpp" #include "thread/mesh_forwarder.hpp" #include "thread/thread_netif.hpp" +#include "thread/topology.hpp" namespace ot { diff --git a/src/core/thread/src_match_controller.hpp b/src/core/thread/src_match_controller.hpp index 110d6e605..ee8d9a2d3 100644 --- a/src/core/thread/src_match_controller.hpp +++ b/src/core/thread/src_match_controller.hpp @@ -36,11 +36,13 @@ #include "openthread-core-config.h" +#include #include "common/locator.hpp" -#include "thread/topology.hpp" namespace ot { +class Child; + /** * @addtogroup core-source-match-controller *