Enable data poll triggered indirect retransmissions to a sleepy child (#1432)

If transmission of an indirect frame (frame to a sleepy child)
fails, the sender retransmits the frame following the reception of
a new data request command (a new data poll) from the sleepy child.

It is ensured that the re-transmissions use the same security frame
counter, key id, and data sequence number as the earlier attempts.
To realize this, info about the indirect transmissions (such as attempt
counter, frame counter, key id) is saved in the child table.

In `openthread-core-default-config.h` a set of new OpenThread options
are added to allow the maximum number of attempts to be configured
for both direct and indirect transmissions.
This commit is contained in:
Abtin Keshavarzian
2017-03-08 12:35:27 -08:00
committed by Jonathan Hui
parent 746a40a340
commit 98f9eaf8da
10 changed files with 248 additions and 37 deletions
+2
View File
@@ -105,8 +105,10 @@ typedef struct RadioPacket
uint8_t mChannel; ///< Channel used to transmit/receive the frame.
int8_t mPower; ///< Transmit/receive power in dBm.
uint8_t mLqi; ///< Link Quality Indicator for received frames.
uint8_t mMaxTxAttempts; ///< Max number of transmit attempts for an outbound frame.
bool mSecurityValid: 1; ///< Security Enabled flag is set and frame passes security checks.
bool mDidTX: 1; ///< Set to true if this packet sent from the radio. Ignored by radio driver.
bool mIsARetx: 1; ///< Set to true if this packet is a retransmission. Should be ignored by radio driver.
} RadioPacket;
/**
+1 -1
View File
@@ -407,7 +407,7 @@ void LinkRaw::InvokeTransmitDone(RadioPacket *aPacket, bool aFramePending, Threa
if (aError == kThreadError_NoAck)
{
if (mTransmitAttempts < Mac::kMaxFrameAttempts)
if (mTransmitAttempts < aPacket->mMaxTxAttempts)
{
mTransmitAttempts++;
StartCsmaBackoff();
+38 -11
View File
@@ -694,24 +694,41 @@ void Mac::ProcessTransmitSecurity(Frame &aFrame)
{
case Frame::kKeyIdMode0:
key = mNetif.GetKeyManager().GetKek();
frameCounter = mNetif.GetKeyManager().GetKekFrameCounter();
mNetif.GetKeyManager().IncrementKekFrameCounter();
extAddress = &mExtAddress;
if (!aFrame.IsARetransmission())
{
aFrame.SetFrameCounter(mNetif.GetKeyManager().GetKekFrameCounter());
mNetif.GetKeyManager().IncrementKekFrameCounter();
}
break;
case Frame::kKeyIdMode1:
key = mNetif.GetKeyManager().GetCurrentMacKey();
frameCounter = mNetif.GetKeyManager().GetMacFrameCounter();
mNetif.GetKeyManager().IncrementMacFrameCounter();
aFrame.SetKeyId((mNetif.GetKeyManager().GetCurrentKeySequence() & 0x7f) + 1);
extAddress = &mExtAddress;
// If the frame is marked as a retransmission, the `Mac::Sender` which
// prepared the frame should set the frame counter and key id to the
// same values used in the earlier transmit attempt. For a new frame (not
// a retransmission), we get a new frame counter and key id from the key
// manager.
if (!aFrame.IsARetransmission())
{
aFrame.SetFrameCounter(mNetif.GetKeyManager().GetMacFrameCounter());
mNetif.GetKeyManager().IncrementMacFrameCounter();
aFrame.SetKeyId((mNetif.GetKeyManager().GetCurrentKeySequence() & 0x7f) + 1);
}
break;
case Frame::kKeyIdMode2:
{
const uint8_t keySource[] = {0xff, 0xff, 0xff, 0xff};
key = sMode2Key;
frameCounter = mKeyIdMode2FrameCounter++;
mKeyIdMode2FrameCounter++;
aFrame.SetFrameCounter(mKeyIdMode2FrameCounter);
aFrame.SetKeySource(keySource);
aFrame.SetKeyId(0xff);
extAddress = static_cast<const ExtAddress *>(&sMode2ExtAddress);
@@ -724,7 +741,7 @@ void Mac::ProcessTransmitSecurity(Frame &aFrame)
}
aFrame.GetSecurityLevel(securityLevel);
aFrame.SetFrameCounter(frameCounter);
aFrame.GetFrameCounter(frameCounter);
GenerateNonce(*extAddress, frameCounter, securityLevel, nonce);
@@ -768,7 +785,13 @@ void Mac::HandleBeginTransmit(void)
case kStateTransmitData:
sendFrame.SetChannel(mChannel);
SuccessOrExit(error = mSendHead->HandleFrameRequest(sendFrame));
sendFrame.SetSequence(mDataSequence);
// If the frame is marked as a retransmission, then data sequence number is already set by the `Sender`.
if (!sendFrame.IsARetransmission())
{
sendFrame.SetSequence(mDataSequence);
}
break;
default:
@@ -990,7 +1013,7 @@ void Mac::SentFrame(ThreadError aError)
otDumpDebgMac("NO ACK", sendFrame.GetHeader(), 16);
if (!RadioSupportsRetries() &&
mTransmitAttempts < kMaxFrameAttempts)
mTransmitAttempts < sendFrame.GetMaxTxAttempts())
{
mTransmitAttempts++;
StartCsmaBackoff();
@@ -1056,7 +1079,11 @@ void Mac::SentFrame(ThreadError aError)
sender->mNext = NULL;
mDataSequence++;
if (!sendFrame.IsARetransmission())
{
mDataSequence++;
}
otDumpDebgMac("TX", sendFrame.GetHeader(), sendFrame.GetLength());
sender->HandleSentFrame(sendFrame, aError);
@@ -1245,7 +1272,7 @@ void Mac::ReceiveDoneTask(Frame *aFrame, ThreadError aError)
VerifyOrExit(error == kThreadError_None, ;);
VerifyOrExit(aFrame != NULL, error = kThreadError_NoFrameReceived);
aFrame->mSecurityValid = false;
aFrame->SetSecurityValid(false);
if (mPcapCallback)
{
-2
View File
@@ -72,11 +72,9 @@ enum
kMinBE = 3, ///< macMinBE (IEEE 802.15.4-2006)
kMaxBE = 5, ///< macMaxBE (IEEE 802.15.4-2006)
kMaxCSMABackoffs = 4, ///< macMaxCSMABackoffs (IEEE 802.15.4-2006)
kMaxFrameRetries = 3, ///< macMaxFrameRetries (IEEE 802.15.4-2006)
kUnitBackoffPeriod = 20, ///< Number of symbols (IEEE 802.15.4-2006)
kMinBackoff = 1, ///< Minimum backoff (milliseconds).
kMaxFrameAttempts = kMaxFrameRetries + 1, ///< Number of transmission attempts.
kAckTimeout = 16, ///< Timeout for waiting on an ACK (milliseconds).
kDataPollTimeout = 100, ///< Timeout for receiving Data Frame (milliseconds).
+34
View File
@@ -632,6 +632,23 @@ public:
*/
void SetLqi(uint8_t aLqi) { mLqi = aLqi; }
/**
* This method returns the maximum number of transmit attempts for the frame.
*
* @returns The maximum number of transmit attempts.
*
*/
uint8_t GetMaxTxAttempts(void) const { return mMaxTxAttempts; }
/**
* This method set the maximum number of transmit attempts for frame.
*
* @returns The maximum number of transmit attempts.
*
*/
void SetMaxTxAttempts(uint8_t aMaxTxAttempts) { mMaxTxAttempts = aMaxTxAttempts; }
/**
* This method indicates whether or not frame security was enabled and passed security validation.
*
@@ -649,6 +666,23 @@ public:
*/
void SetSecurityValid(bool aSecurityValid) { mSecurityValid = aSecurityValid; }
/**
* This method indicates whether or not the frame is a retransmission.
*
* @retval TRUE Frame is a retransmission
* @retval FALSE This is a new frame and not a retransmission of an earlier frame.
*
*/
bool IsARetransmission(void) const { return mIsARetx; }
/**
* This method sets the retransmission flag attribute.
*
* @param[in] aIsARetx TRUE if frame is a retransmission of an earlier frame, FALSE otherwise.
*
*/
void SetIsARetransmission(bool aIsARetx) { mIsARetx = aIsARetx; }
/**
* This method returns the IEEE 802.15.4 PSDU length.
*
+36 -2
View File
@@ -115,6 +115,40 @@
#define OPENTHREAD_CONFIG_DEFAULT_MAX_TRANSMIT_POWER 0
#endif // OPENTHREAD_CONFIG_DEFAULT_MAX_TRANSMIT_POWER
/**
* @def OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT
*
* Maximum number of MAC layer transmit attempts for an outbound direct frame.
* Per IEEE 802.15.4-2006, default value is set to (macMaxFrameRetries + 1) with macMaxFrameRetries = 3.
*
*/
#ifndef OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT
#define OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT 4
#endif // OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT
/**
* @def OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL
*
* Maximum number of MAC layer transmit attempts for an outbound indirect frame (to a sleepy child) after receiving
* a data request command (data poll) from the child.
*
*/
#ifndef OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL
#define OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL 1
#endif // OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL
/**
* @def OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS
*
* Maximum number of transmit attempts for an outbound indirect frame (for a sleepy child) each triggered by the
* reception of a new data request command (a new data poll) from the sleepy child. Each data poll triggered attempt is
* retried by the MAC layer up to `OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL` times.
*
*/
#ifndef OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS
#define OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS 4
#endif // OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS
/**
* @def OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD
*
@@ -536,7 +570,7 @@
*
*/
#ifndef OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE
#define OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE 1500
#define OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE 1300
#endif // OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE
/**
@@ -546,7 +580,7 @@
*
*/
#ifndef OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE
#define OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE 1500
#define OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE 1300
#endif // OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE
/**
+92 -17
View File
@@ -174,11 +174,6 @@ void MeshForwarder::HandleResolved(const Ip6::Address &aEid, ThreadError aError)
}
}
void MeshForwarder::ScheduleTransmissionTask(void *aContext)
{
static_cast<MeshForwarder *>(aContext)->ScheduleTransmissionTask();
}
void MeshForwarder::ClearChildIndirectMessages(Child &aChild)
{
Message *nextMessage;
@@ -230,7 +225,12 @@ void MeshForwarder::UpdateIndirectMessages(void)
}
}
void MeshForwarder::ScheduleTransmissionTask()
void MeshForwarder::ScheduleTransmissionTask(void *aContext)
{
static_cast<MeshForwarder *>(aContext)->ScheduleTransmissionTask();
}
void MeshForwarder::ScheduleTransmissionTask(void)
{
ThreadError error = kThreadError_None;
uint8_t numChildren;
@@ -240,6 +240,8 @@ void MeshForwarder::ScheduleTransmissionTask()
UpdateIndirectMessages();
mSendMessageIsARetransmission = false;
children = mNetif.GetMle().GetChildren(&numChildren);
for (int i = 0; i < numChildren; i++)
@@ -251,18 +253,16 @@ void MeshForwarder::ScheduleTransmissionTask()
continue;
}
mSendMessage = child.mIndirectSendMessage;
mSendMessage = child.mIndirectSendInfo.mMessage;
mSendMessageMaxMacTxAttempts = kIndirectFrameMacTxAttempts;
if (mSendMessage == NULL)
{
mSendMessage = GetIndirectTransmission(child);
child.mIndirectSendMessage = mSendMessage;
child.mFragmentOffset = 0;
}
if (mSendMessage != NULL)
{
mSendMessage->SetOffset(child.mFragmentOffset);
PrepareIndirectTransmission(*mSendMessage, child);
}
else
@@ -294,6 +294,7 @@ void MeshForwarder::ScheduleTransmissionTask()
if ((mSendMessage = GetDirectTransmission()) != NULL)
{
mNetif.GetMac().SendFrameRequest(mMacSender);
mSendMessageMaxMacTxAttempts = kDirectFrameMacTxAttempts;
ExitNow();
}
@@ -570,7 +571,7 @@ exit:
return curMessage;
}
Message *MeshForwarder::GetIndirectTransmission(const Child &aChild)
Message *MeshForwarder::GetIndirectTransmission(Child &aChild)
{
Message *message = NULL;
uint8_t childIndex = mNetif.GetMle().GetChildIndex(aChild);
@@ -583,11 +584,25 @@ Message *MeshForwarder::GetIndirectTransmission(const Child &aChild)
}
}
aChild.mIndirectSendInfo.mMessage = message;
aChild.mIndirectSendInfo.mFragmentOffset = 0;
aChild.mIndirectSendInfo.mTxAttemptCounter = 0;
return message;
}
void MeshForwarder::PrepareIndirectTransmission(const Message &aMessage, const Child &aChild)
void MeshForwarder::PrepareIndirectTransmission(Message &aMessage, const Child &aChild)
{
if (aChild.mIndirectSendInfo.mTxAttemptCounter > 0)
{
mSendMessageIsARetransmission = true;
mSendMessageFrameCounter = aChild.mIndirectSendInfo.mFrameCounter;
mSendMessageKeyId = aChild.mIndirectSendInfo.mKeyId;
mSendMessageDataSequenceNumber = aChild.mIndirectSendInfo.mDataSequenceNumber;
}
aMessage.SetOffset(aChild.mIndirectSendInfo.mFragmentOffset);
switch (aMessage.GetType())
{
case Message::kTypeIp6:
@@ -1070,6 +1085,8 @@ ThreadError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
if (mSendMessage == NULL)
{
SendEmptyFrame(aFrame);
aFrame.SetIsARetransmission(false);
aFrame.SetMaxTxAttempts(kDirectFrameMacTxAttempts);
ExitNow();
}
@@ -1149,6 +1166,24 @@ ThreadError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
aFrame.SetFramePending(true);
}
aFrame.SetIsARetransmission(mSendMessageIsARetransmission);
aFrame.SetMaxTxAttempts(mSendMessageMaxMacTxAttempts);
if (mSendMessageIsARetransmission)
{
// If this is the re-transmission of an indirect frame to a sleepy child, we
// ensure to use the same frame counter, key id, and data sequence number as
// the last attempt.
aFrame.SetSequence(mSendMessageDataSequenceNumber);
if (aFrame.GetSecurityEnabled())
{
aFrame.SetFrameCounter(mSendMessageFrameCounter);
aFrame.SetKeyId(mSendMessageKeyId);
}
}
exit:
return error;
}
@@ -1600,19 +1635,59 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, ThreadError aError)
VerifyOrExit(mSendMessage != NULL, ;);
if (mSendMessage == child->mIndirectSendInfo.mMessage)
{
switch (aError)
{
case kThreadError_None:
child->mIndirectSendInfo.mTxAttemptCounter = 0;
break;
default:
child->mIndirectSendInfo.mTxAttemptCounter++;
if (child->mIndirectSendInfo.mTxAttemptCounter < 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.
if (aFrame.GetSecurityEnabled())
{
aFrame.GetFrameCounter(child->mIndirectSendInfo.mFrameCounter);
aFrame.GetKeyId(child->mIndirectSendInfo.mKeyId);
child->mIndirectSendInfo.mDataSequenceNumber = aFrame.GetSequence();
}
ExitNow();
}
child->mIndirectSendInfo.mTxAttemptCounter = 0;
// 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();
break;
}
}
if (mMessageNextOffset < mSendMessage->GetLength())
{
if (mSendMessage == child->mIndirectSendMessage)
if (mSendMessage == child->mIndirectSendInfo.mMessage)
{
child->mFragmentOffset = mMessageNextOffset;
child->mIndirectSendInfo.mFragmentOffset = mMessageNextOffset;
}
}
else
{
if (mSendMessage == child->mIndirectSendMessage)
if (mSendMessage == child->mIndirectSendInfo.mMessage)
{
child->mFragmentOffset = 0;
child->mIndirectSendMessage = NULL;
child->mIndirectSendInfo.mFragmentOffset = 0;
child->mIndirectSendInfo.mMessage = NULL;
}
mSendMessage->ClearChildMask(mNetif.GetMle().GetChildIndex(*child));
+32 -2
View File
@@ -248,6 +248,30 @@ private:
kDataRequestRetryDelay = 200, ///< Retry delay in milliseconds (for sending data request if no buffer).
};
enum
{
/**
* Maximum number of MAC layer tx attempts for an outbound direct frame.
*
*/
kDirectFrameMacTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT,
/**
* Maximum number of MAC layer tx attempts for an outbound indirect frame (for a sleepy child) after receiving
* a data request command (data poll) from the child.
*
*/
kIndirectFrameMacTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL,
/**
* 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,
};
ThreadError CheckReachability(uint8_t *aFrame, uint8_t aFrameLength,
const Mac::Address &aMeshSource, const Mac::Address &aMeshDest);
@@ -255,8 +279,8 @@ private:
ThreadError GetMacDestinationAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr);
ThreadError GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr);
Message *GetDirectTransmission(void);
Message *GetIndirectTransmission(const Child &aChild);
void PrepareIndirectTransmission(const Message &aMessage, const Child &aChild);
Message *GetIndirectTransmission(Child &aChild);
void PrepareIndirectTransmission(Message &aMessage, const Child &aChild);
void HandleMesh(uint8_t *aFrame, uint8_t aPayloadLength, const Mac::Address &aMacSource,
const ThreadMessageInfo &aMessageInfo);
void HandleFragment(uint8_t *aFrame, uint8_t aPayloadLength,
@@ -311,7 +335,13 @@ private:
uint16_t mMessageNextOffset;
uint32_t mPollPeriod;
uint32_t mAssignPollPeriod; ///< only for certification test
uint32_t mSendMessageFrameCounter;
Message *mSendMessage;
bool mSendMessageIsARetransmission;
uint8_t mSendMessageMaxMacTxAttempts;
uint8_t mSendMessageKeyId;
uint8_t mSendMessageDataSequenceNumber;
Mac::Address mMacSource;
Mac::Address mMacDest;
+10 -2
View File
@@ -127,10 +127,18 @@ public:
kMaxIp6AddressPerChild = OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD,
kMaxRequestTlvs = 5,
};
Ip6::Address mIp6Address[kMaxIp6AddressPerChild]; ///< Registered IPv6 addresses
uint32_t mTimeout; ///< Child timeout
uint16_t mFragmentOffset; ///< 6LoWPAN fragment offset for the indirect message
Message *mIndirectSendMessage; ///< Current indirect message being sent.
struct
{
uint32_t mFrameCounter; ///< Frame counter for current indirect message (used fore retx).
Message *mMessage; ///< Current indirect message.
uint16_t mFragmentOffset; ///< 6LoWPAN fragment offset for the indirect message.
uint8_t mKeyId; ///< Key Id for current indirect message (used for retx).
uint8_t mTxAttemptCounter; ///< Number of data poll triggered tx attempts.
uint8_t mDataSequenceNumber; ///< MAC level Data Sequence Number (DSN) for retx attempts.
} mIndirectSendInfo; ///< Info about current outbound indirect message.
union
{
uint8_t mRequestTlvs[kMaxRequestTlvs]; ///< Requested MLE TLVs
+3
View File
@@ -3939,6 +3939,9 @@ ThreadError NcpBase::SetPropertyHandler_STREAM_RAW(uint8_t header, spinel_prop_k
packet->mLength = static_cast<uint8_t>(frame_len);
memcpy(packet->mPsdu, frame_buffer, packet->mLength);
// TODO: This should be later added in the STREAM_RAW argument to allow user to directly specify it.
packet->mMaxTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT;
// Pass packet to the radio layer. Note, this fails if we
// haven't enabled raw stream or are already transmitting.
errorCode = otLinkRawTransmit(mInstance, packet, &NcpBase::LinkRawTransmitDone);