[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.
This commit is contained in:
Abtin Keshavarzian
2019-07-16 08:38:29 -07:00
committed by Jonathan Hui
parent 6db6ad8045
commit f56e12d205
11 changed files with 689 additions and 457 deletions
+1
View File
@@ -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 \
+2
View File
@@ -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 \
+6 -1
View File
@@ -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)
+500
View File
@@ -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<ChildTable>().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<ChildTable>().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<MeshForwarder>().mSendQueue.GetHead(); message; message = nextMessage)
{
nextMessage = message->GetNext();
message->ClearChildMask(Get<ChildTable>().GetChildIndex(aChild));
if (!message->IsChildPending() && !message->GetDirectTransmission())
{
if (Get<MeshForwarder>().mSendMessage == message)
{
Get<MeshForwarder>().mSendMessage = NULL;
}
Get<MeshForwarder>().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<Mle::MleRouter>().GetRole() != OT_DEVICE_ROLE_DETACHED);
child = Get<ChildTable>().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<MeshForwarder>().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<MeshForwarder>().mSendMessage = child.GetIndirectMessage();
Get<MeshForwarder>().mSendMessageMaxCsmaBackoffs = Mac::kMaxCsmaBackoffsIndirect;
Get<MeshForwarder>().mSendMessageMaxFrameRetries = Mac::kMaxFrameRetriesIndirect;
if (Get<MeshForwarder>().mSendMessage == NULL)
{
Get<MeshForwarder>().mSendMessage = GetIndirectTransmission(child);
}
if (Get<MeshForwarder>().mSendMessage != NULL)
{
PrepareIndirectTransmission(*Get<MeshForwarder>().mSendMessage, child);
}
else
{
// A NULL `mSendMessage` triggers an empty frame to be sent to the child.
if (child.IsIndirectSourceMatchShort())
{
Get<MeshForwarder>().mMacSource.SetShort(Get<Mac::Mac>().GetShortAddress());
}
else
{
Get<MeshForwarder>().mMacSource.SetExtended(Get<Mac::Mac>().GetExtAddress());
}
child.GetMacAddress(Get<MeshForwarder>().mMacDest);
}
// Remember the current child and move it to next one in the
// list after the indirect transmission has completed.
mIndirectStartingChild = &child;
Get<Mac::Mac>().RequestFrameTransmission();
ExitNow(error = OT_ERROR_NONE);
}
exit:
return error;
}
Message *IndirectSender::GetIndirectTransmission(Child &aChild)
{
Message *message = NULL;
Message *next;
uint8_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
for (message = Get<MeshForwarder>().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<MeshForwarder>().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<MeshForwarder>().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<MeshForwarder>().mSendMessageIsARetransmission = true;
Get<MeshForwarder>().mSendMessageFrameCounter = aChild.GetIndirectFrameCounter();
Get<MeshForwarder>().mSendMessageKeyId = aChild.GetIndirectKeyId();
Get<MeshForwarder>().mSendMessageDataSequenceNumber = aChild.GetIndirectDataSequenceNumber();
}
aMessage.SetOffset(aChild.GetIndirectFragmentOffset());
switch (aMessage.GetType())
{
case Message::kTypeIp6:
{
Ip6::Header ip6Header;
aMessage.Read(0, sizeof(ip6Header), &ip6Header);
Get<MeshForwarder>().mAddMeshHeader = false;
Get<MeshForwarder>().GetMacSourceAddress(ip6Header.GetSource(), Get<MeshForwarder>().mMacSource);
if (ip6Header.GetDestination().IsLinkLocal())
{
Get<MeshForwarder>().GetMacDestinationAddress(ip6Header.GetDestination(), Get<MeshForwarder>().mMacDest);
}
else
{
aChild.GetMacAddress(Get<MeshForwarder>().mMacDest);
}
break;
}
case Message::kTypeSupervision:
aChild.GetMacAddress(Get<MeshForwarder>().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<ChildTable>().FindChild(aMacDest, ChildTable::kInStateValidOrRestoring);
VerifyOrExit(child != NULL);
child->SetDataRequestPending(false);
VerifyOrExit(Get<MeshForwarder>().mSendMessage != NULL);
if (Get<MeshForwarder>().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<MeshForwarder>().mMessageNextOffset = Get<MeshForwarder>().mSendMessage->GetLength();
#endif
break;
default:
assert(false);
break;
}
}
if (Get<MeshForwarder>().mMessageNextOffset < Get<MeshForwarder>().mSendMessage->GetLength())
{
if (Get<MeshForwarder>().mSendMessage == child->GetIndirectMessage())
{
child->SetIndirectFragmentOffset(Get<MeshForwarder>().mMessageNextOffset);
}
}
else
{
otError txError = aError;
uint8_t childIndex;
if (Get<MeshForwarder>().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<ChildTable>().GetChildIndex(*child);
if (Get<MeshForwarder>().mSendMessage->GetChildMask(childIndex))
{
Get<MeshForwarder>().mSendMessage->ClearChildMask(childIndex);
mSourceMatchController.DecrementMessageCount(*child);
}
if (!Get<MeshForwarder>().mSendMessage->GetDirectTransmission())
{
Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageTransmit, *Get<MeshForwarder>().mSendMessage,
&aMacDest, txError);
if (Get<MeshForwarder>().mSendMessage->GetType() == Message::kTypeIp6)
{
if (Get<MeshForwarder>().mSendMessage->GetTxSuccess())
{
Get<MeshForwarder>().mIpCounters.mTxSuccess++;
}
else
{
Get<MeshForwarder>().mIpCounters.mTxFailure++;
}
}
}
}
if (aError == OT_ERROR_NONE)
{
Get<Utils::ChildSupervisor>().UpdateOnSend(*child);
}
exit:
return;
}
} // namespace ot
#endif // #if OPENTHREAD_FTD
+154
View File
@@ -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_
+10 -11
View File
@@ -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<Mac::Mac>().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<SourceMatchController>().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
{
+3 -24
View File
@@ -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;
+8 -419
View File
@@ -46,9 +46,8 @@ namespace ot {
otError MeshForwarder::SendMessage(Message &aMessage)
{
Mle::MleRouter &mle = Get<Mle::MleRouter>();
ChildTable & childTable = Get<ChildTable>();
otError error = OT_ERROR_NONE;
Mle::MleRouter &mle = Get<Mle::MleRouter>();
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<Child *>(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<ChildTable>().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<ChildTable>().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<Mle::MleRouter>();
@@ -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<Mac::Mac>().GetShortAddress());
}
else
{
mMacSource.SetExtended(Get<Mac::Mac>().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<Mac::Mac>().RequestFrameTransmission();
ExitNow(error = OT_ERROR_NONE);
}
exit:
return error;
}
Message *MeshForwarder::GetIndirectTransmission(Child &aChild)
{
Message *message = NULL;
Message *next;
uint8_t childIndex = Get<ChildTable>().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<Mle::MleRouter>().GetRole() != OT_DEVICE_ROLE_DETACHED);
child = Get<ChildTable>().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<ChildTable>().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<ChildTable>().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<Utils::ChildSupervisor>().UpdateOnSend(*child);
}
exit:
return;
}
otError MeshForwarder::UpdateMeshRoute(Message &aMessage)
{
otError error = OT_ERROR_NONE;
+1 -1
View File
@@ -3150,7 +3150,7 @@ void MleRouter::RemoveNeighbor(Neighbor &aNeighbor)
aNeighbor.SetState(Neighbor::kStateInvalid);
Get<MeshForwarder>().ClearChildIndirectMessages(static_cast<Child &>(aNeighbor));
Get<IndirectSender>().ClearAllMessagesForSleepyChild(static_cast<Child &>(aNeighbor));
Get<NetworkData::Leader>().SendServerDataNotification(aNeighbor.GetRloc16());
if (aNeighbor.GetDeviceMode() & ModeTlv::kModeFullThreadDevice)
+1
View File
@@ -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 {
+3 -1
View File
@@ -36,11 +36,13 @@
#include "openthread-core-config.h"
#include <openthread/error.h>
#include "common/locator.hpp"
#include "thread/topology.hpp"
namespace ot {
class Child;
/**
* @addtogroup core-source-match-controller
*