openthread/src/core/mac/data_poll_sender.cpp

632 lines
16 KiB
C++

/*
* Copyright (c) 2017, 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 implements data poll (mac data request command) sender class.
*/
#include "data_poll_sender.hpp"
#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "common/locator_getters.hpp"
#include "common/log.hpp"
#include "common/message.hpp"
#include "net/ip6.hpp"
#include "net/netif.hpp"
#include "thread/mesh_forwarder.hpp"
#include "thread/mle.hpp"
#include "thread/thread_netif.hpp"
namespace ot {
RegisterLogModule("DataPollSender");
DataPollSender::DataPollSender(Instance &aInstance)
: InstanceLocator(aInstance)
, mTimerStartTime(0)
, mPollPeriod(0)
, mExternalPollPeriod(0)
, mFastPollsUsers(0)
, mTimer(aInstance, DataPollSender::HandlePollTimer)
, mEnabled(false)
, mAttachMode(false)
, mRetxMode(false)
, mPollTimeoutCounter(0)
, mPollTxFailureCounter(0)
, mRemainingFastPolls(0)
{
}
const Neighbor &DataPollSender::GetParent(void) const
{
const Neighbor &parentCandidate = Get<Mle::MleRouter>().GetParentCandidate();
return parentCandidate.IsStateValid() ? parentCandidate : Get<Mle::MleRouter>().GetParent();
}
void DataPollSender::StartPolling(void)
{
VerifyOrExit(!mEnabled);
OT_ASSERT(!Get<Mle::MleRouter>().IsRxOnWhenIdle());
mEnabled = true;
ScheduleNextPoll(kRecalculatePollPeriod);
exit:
return;
}
void DataPollSender::StopPolling(void)
{
mTimer.Stop();
mAttachMode = false;
mRetxMode = false;
mPollTimeoutCounter = 0;
mPollTxFailureCounter = 0;
mRemainingFastPolls = 0;
mFastPollsUsers = 0;
mEnabled = false;
}
Error DataPollSender::SendDataPoll(void)
{
Error error;
VerifyOrExit(mEnabled, error = kErrorInvalidState);
VerifyOrExit(!Get<Mac::Mac>().GetRxOnWhenIdle(), error = kErrorInvalidState);
VerifyOrExit(GetParent().IsStateValidOrRestoring(), error = kErrorInvalidState);
mTimer.Stop();
SuccessOrExit(error = Get<Mac::Mac>().RequestDataPollTransmission());
exit:
switch (error)
{
case kErrorNone:
LogDebg("Sending data poll");
ScheduleNextPoll(kUsePreviousPollPeriod);
break;
case kErrorInvalidState:
LogWarn("Data poll tx requested while data polling was not enabled!");
StopPolling();
break;
default:
LogWarn("Unexpected error %s requesting data poll", ErrorToString(error));
ScheduleNextPoll(kRecalculatePollPeriod);
break;
}
return error;
}
#if OPENTHREAD_CONFIG_MULTI_RADIO
Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest, Mac::RadioType &aRadioType) const
#else
Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest) const
#endif
{
Error error = kErrorNone;
const Neighbor &parent = GetParent();
VerifyOrExit(parent.IsStateValidOrRestoring(), error = kErrorAbort);
// Use extended address attaching to a new parent (i.e. parent is the parent candidate).
if ((Get<Mac::Mac>().GetShortAddress() == Mac::kShortAddrInvalid) ||
(&parent == &Get<Mle::MleRouter>().GetParentCandidate()))
{
aDest.SetExtended(parent.GetExtAddress());
}
else
{
aDest.SetShort(parent.GetRloc16());
}
#if OPENTHREAD_CONFIG_MULTI_RADIO
aRadioType = Get<RadioSelector>().SelectPollFrameRadio(parent);
#endif
exit:
return error;
}
Error DataPollSender::SetExternalPollPeriod(uint32_t aPeriod)
{
Error error = kErrorNone;
if (aPeriod != 0)
{
VerifyOrExit(aPeriod >= OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD, error = kErrorInvalidArgs);
// Clipped by the maximal value.
if (aPeriod > kMaxExternalPeriod)
{
aPeriod = kMaxExternalPeriod;
}
}
if (mExternalPollPeriod != aPeriod)
{
mExternalPollPeriod = aPeriod;
if (mEnabled)
{
ScheduleNextPoll(kRecalculatePollPeriod);
}
}
exit:
return error;
}
uint32_t DataPollSender::GetKeepAlivePollPeriod(void) const
{
uint32_t period = GetDefaultPollPeriod();
if (mExternalPollPeriod != 0)
{
period = OT_MIN(period, mExternalPollPeriod);
}
return period;
}
void DataPollSender::HandlePollSent(Mac::TxFrame &aFrame, Error aError)
{
Mac::Address macDest;
bool shouldRecalculatePollPeriod = false;
VerifyOrExit(mEnabled);
if (!aFrame.IsEmpty())
{
IgnoreError(aFrame.GetDstAddr(macDest));
Get<MeshForwarder>().UpdateNeighborOnSentFrame(aFrame, aError, macDest, /* aIsDataPoll */ true);
}
if (GetParent().IsStateInvalid())
{
StopPolling();
IgnoreError(Get<Mle::MleRouter>().BecomeDetached());
ExitNow();
}
switch (aError)
{
case kErrorNone:
if (mRemainingFastPolls != 0)
{
mRemainingFastPolls--;
if (mRemainingFastPolls == 0)
{
shouldRecalculatePollPeriod = true;
mFastPollsUsers = 0;
}
}
if (mRetxMode)
{
mRetxMode = false;
mPollTxFailureCounter = 0;
shouldRecalculatePollPeriod = true;
}
break;
case kErrorChannelAccessFailure:
case kErrorAbort:
mRetxMode = true;
shouldRecalculatePollPeriod = true;
break;
default:
mPollTxFailureCounter++;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
(aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts
: kMaxPollRetxAttempts);
#else
LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
kMaxPollRetxAttempts);
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (mPollTxFailureCounter <
((aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts))
#else
if (mPollTxFailureCounter < kMaxPollRetxAttempts)
#endif
{
if (!mRetxMode)
{
mRetxMode = true;
shouldRecalculatePollPeriod = true;
}
}
else
{
mRetxMode = false;
mPollTxFailureCounter = 0;
shouldRecalculatePollPeriod = true;
}
break;
}
if (shouldRecalculatePollPeriod)
{
ScheduleNextPoll(kRecalculatePollPeriod);
}
exit:
return;
}
void DataPollSender::HandlePollTimeout(void)
{
// A data poll timeout happened, i.e., the ack in response to
// a data poll indicated that a frame was pending, but no frame
// was received after timeout interval.
VerifyOrExit(mEnabled);
mPollTimeoutCounter++;
LogInfo("Data poll timeout, retry:%d/%d", mPollTimeoutCounter, kQuickPollsAfterTimeout);
if (mPollTimeoutCounter < kQuickPollsAfterTimeout)
{
IgnoreError(SendDataPoll());
}
else
{
mPollTimeoutCounter = 0;
}
exit:
return;
}
void DataPollSender::ProcessRxFrame(const Mac::RxFrame &aFrame)
{
VerifyOrExit(mEnabled);
mPollTimeoutCounter = 0;
if (aFrame.GetFramePending())
{
IgnoreError(SendDataPoll());
}
exit:
return;
}
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
void DataPollSender::ProcessTxDone(const Mac::TxFrame &aFrame, const Mac::RxFrame *aAckFrame, Error aError)
{
bool sendDataPoll = false;
VerifyOrExit(mEnabled);
VerifyOrExit(Get<Mle::MleRouter>().GetParent().IsEnhancedKeepAliveSupported());
VerifyOrExit(aFrame.GetSecurityEnabled());
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (aFrame.mInfo.mTxInfo.mIsARetx && (aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr))
{
// For retransmission frame, use a data poll to resync its parent with correct CSL phase
sendDataPoll = true;
}
#endif
if (aError == kErrorNone && aAckFrame != nullptr)
{
mPollTimeoutCounter = 0;
if (aAckFrame->GetFramePending())
{
sendDataPoll = true;
}
else
{
ResetKeepAliveTimer();
}
}
if (sendDataPoll)
{
IgnoreError(SendDataPoll());
}
exit:
return;
}
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
void DataPollSender::RecalculatePollPeriod(void)
{
if (mEnabled)
{
ScheduleNextPoll(kRecalculatePollPeriod);
}
}
void DataPollSender::SetAttachMode(bool aMode)
{
if (mAttachMode != aMode)
{
mAttachMode = aMode;
if (mEnabled)
{
ScheduleNextPoll(kRecalculatePollPeriod);
}
}
}
void DataPollSender::SendFastPolls(uint8_t aNumFastPolls)
{
bool shouldRecalculatePollPeriod = (mRemainingFastPolls == 0);
if (mFastPollsUsers < kMaxFastPollsUsers)
{
mFastPollsUsers++;
}
if (aNumFastPolls == 0)
{
aNumFastPolls = kDefaultFastPolls;
}
if (aNumFastPolls > kMaxFastPolls)
{
aNumFastPolls = kMaxFastPolls;
}
if (mRemainingFastPolls < aNumFastPolls)
{
mRemainingFastPolls = aNumFastPolls;
}
if (mEnabled && shouldRecalculatePollPeriod)
{
ScheduleNextPoll(kRecalculatePollPeriod);
}
}
void DataPollSender::StopFastPolls(void)
{
VerifyOrExit(mFastPollsUsers != 0);
// If `mFastPollsUsers` hits the max, let it be cleared
// from `HandlePollSent()` (after all fast polls are sent).
VerifyOrExit(mFastPollsUsers < kMaxFastPollsUsers);
mFastPollsUsers--;
VerifyOrExit(mFastPollsUsers == 0);
mRemainingFastPolls = 0;
ScheduleNextPoll(kRecalculatePollPeriod);
exit:
return;
}
void DataPollSender::ResetKeepAliveTimer(void)
{
if (mTimer.IsRunning() && mPollPeriod == GetDefaultPollPeriod())
{
mTimerStartTime = TimerMilli::GetNow();
mTimer.StartAt(mTimerStartTime, mPollPeriod);
}
}
void DataPollSender::ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)
{
TimeMilli now;
uint32_t oldPeriod = mPollPeriod;
if (aPollPeriodSelector == kRecalculatePollPeriod)
{
mPollPeriod = CalculatePollPeriod();
}
now = TimerMilli::GetNow();
if (mTimer.IsRunning())
{
if (oldPeriod != mPollPeriod)
{
// If poll interval did change and re-starting the timer from
// last start time with new poll interval would fire quickly
// (i.e., fires within window `[now, now + kMinPollPeriod]`)
// add an extra minimum delay of `kMinPollPeriod`. This
// ensures that when an internal or external request triggers
// a switch to a shorter poll interval, the first data poll
// will not be sent too quickly (and possibly before the
// response is available/prepared on the parent node).
if (mTimerStartTime + mPollPeriod < now + kMinPollPeriod)
{
mTimer.StartAt(now, kMinPollPeriod);
}
else
{
mTimer.StartAt(mTimerStartTime, mPollPeriod);
}
}
// Do nothing on the running poll timer if the poll interval doesn't change
}
else
{
mTimerStartTime = now;
mTimer.StartAt(mTimerStartTime, mPollPeriod);
}
}
uint32_t DataPollSender::CalculatePollPeriod(void) const
{
uint32_t period = GetDefaultPollPeriod();
if (mAttachMode)
{
period = OT_MIN(period, kAttachDataPollPeriod);
}
if (mRetxMode)
{
period = OT_MIN(period, kRetxPollPeriod);
}
if (mRemainingFastPolls != 0)
{
period = OT_MIN(period, kFastPollPeriod);
}
if (mExternalPollPeriod != 0)
{
period = OT_MIN(period, mExternalPollPeriod);
}
if (period == 0)
{
period = kMinPollPeriod;
}
return period;
}
void DataPollSender::HandlePollTimer(Timer &aTimer)
{
IgnoreError(aTimer.Get<DataPollSender>().SendDataPoll());
}
uint32_t DataPollSender::GetDefaultPollPeriod(void) const
{
uint32_t period = Time::SecToMsec(Get<Mle::MleRouter>().GetTimeout());
uint32_t pollAhead = static_cast<uint32_t>(kRetxPollPeriod) * kMaxPollRetxAttempts;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE
if (Get<Mac::Mac>().IsCslEnabled())
{
period = OT_MIN(period, Time::SecToMsec(Get<Mle::MleRouter>().GetCslTimeout()));
pollAhead = static_cast<uint32_t>(kRetxPollPeriod);
}
#endif
if (period > pollAhead)
{
period -= pollAhead;
}
return period;
}
Mac::TxFrame *DataPollSender::PrepareDataRequest(Mac::TxFrames &aTxFrames)
{
Mac::TxFrame *frame = nullptr;
Mac::Address src, dst;
uint16_t fcf;
bool iePresent;
#if OPENTHREAD_CONFIG_MULTI_RADIO
Mac::RadioType radio;
SuccessOrExit(GetPollDestinationAddress(dst, radio));
frame = &aTxFrames.GetTxFrame(radio);
#else
SuccessOrExit(GetPollDestinationAddress(dst));
frame = &aTxFrames.GetTxFrame();
#endif
fcf = Mac::Frame::kFcfFrameMacCmd | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfAckRequest |
Mac::Frame::kFcfSecurityEnabled;
iePresent = Get<MeshForwarder>().CalcIePresent(nullptr);
if (iePresent)
{
fcf |= Mac::Frame::kFcfIePresent;
}
fcf |= Get<MeshForwarder>().CalcFrameVersion(Get<NeighborTable>().FindNeighbor(dst), iePresent);
if (dst.IsExtended())
{
fcf |= Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt;
src.SetExtended(Get<Mac::Mac>().GetExtAddress());
}
else
{
fcf |= Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort;
src.SetShort(Get<Mac::Mac>().GetShortAddress());
}
frame->InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32);
if (frame->IsDstPanIdPresent())
{
frame->SetDstPanId(Get<Mac::Mac>().GetPanId());
}
frame->SetSrcAddr(src);
frame->SetDstAddr(dst);
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
if (iePresent)
{
Get<MeshForwarder>().AppendHeaderIe(nullptr, *frame);
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (frame->GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr)
{
// Disable frame retransmission when the data poll has CSL IE included
aTxFrames.SetMaxFrameRetries(0);
}
#endif
#endif
IgnoreError(frame->SetCommandId(Mac::Frame::kMacCmdDataRequest));
exit:
return frame;
}
} // namespace ot