mirror of
https://github.com/espressif/openthread.git
synced 2026-08-14 14:47:46 +00:00
[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:
committed by
Jonathan Hui
parent
6db6ad8045
commit
f56e12d205
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user