diff --git a/include/openthread/types.h b/include/openthread/types.h
index abd1be67f..78a2ddefd 100644
--- a/include/openthread/types.h
+++ b/include/openthread/types.h
@@ -895,6 +895,8 @@ typedef enum
/**
* This structure holds diagnostic information for a neighboring Thread node
*
+ * `mFrameErrorRate` and `mMessageErrorRate` require `OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING` feature to be
+ * enabled.
*/
typedef struct
{
@@ -906,6 +908,8 @@ typedef struct
uint8_t mLinkQualityIn; ///< Link Quality In
int8_t mAverageRssi; ///< Average RSSI
int8_t mLastRssi; ///< Last observed RSSI
+ uint16_t mFrameErrorRate; ///< Frame error rate (0xffff->100%). Requires error tracking feature.
+ uint16_t mMessageErrorRate; ///< (IPv6) msg error rate (0xffff->100%). Requires error tracking feature.
bool mRxOnWhenIdle : 1; ///< rx-on-when-idle
bool mSecureDataRequest : 1; ///< Secure Data Requests
bool mFullFunction : 1; ///< Full Function Device
@@ -920,6 +924,9 @@ typedef int16_t otNeighborInfoIterator; ///< Used to iterate through neighbor
/**
* This structure holds diagnostic information for a Thread Child
*
+ * `mFrameErrorRate` and `mMessageErrorRate` require `OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING` feature to be
+ * enabled.
+ *
*/
typedef struct
{
@@ -932,6 +939,8 @@ typedef struct
uint8_t mLinkQualityIn; ///< Link Quality In
int8_t mAverageRssi; ///< Average RSSI
int8_t mLastRssi; ///< Last observed RSSI
+ uint16_t mFrameErrorRate; ///< Frame error rate (0xffff->100%). Requires error tracking feature.
+ uint16_t mMessageErrorRate; ///< (IPv6) msg error rate (0xffff->100%). Requires error tracking feature.
bool mRxOnWhenIdle : 1; ///< rx-on-when-idle
bool mSecureDataRequest : 1; ///< Secure Data Requests
bool mFullFunction : 1; ///< Full Function Device
diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp
index 636d27543..be88ac872 100644
--- a/src/core/common/message.hpp
+++ b/src/core/common/message.hpp
@@ -113,6 +113,7 @@ struct MessageInfo
bool mLinkSecurity : 1; ///< Indicates whether or not link security is enabled.
uint8_t mPriority : 2; ///< Identifies the message priority level (lower value is higher priority).
bool mInPriorityQ : 1; ///< Indicates whether the message is queued in normal or priority queue.
+ bool mTxSuccess : 1; ///< Indicates whether the direct tx of the message was successful.
};
/**
@@ -602,6 +603,24 @@ public:
*/
void SetDirectTransmission(void) { mBuffer.mHead.mInfo.mDirectTx = true; }
+ /**
+ * This methods indicates whether the direct transmission of message was successful.
+ *
+ * @retval TRUE If direct transmission of message was successful (all fragments were delivered and acked).
+ * @retval FALSE If direct transmission of message failed (at least one fragment failed).
+ *
+ */
+ bool GetTxSuccess(void) const { return mBuffer.mHead.mInfo.mTxSuccess; }
+
+ /**
+ * This methods sets whether the direct transmission of message was successful.
+ *
+ * @param[in] aTxSuccess TRUE if the direct transmission is successful, FALSE otherwise (i.e., at least one
+ * fragment transmission failed).
+ *
+ */
+ void SetTxSuccess(bool aTxSuccess) { mBuffer.mHead.mInfo.mTxSuccess = aTxSuccess; }
+
/**
* This method indicates whether or not link security is enabled for the message.
*
diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp
index d5d7f6250..22be25cab 100644
--- a/src/core/mac/mac.cpp
+++ b/src/core/mac/mac.cpp
@@ -1077,6 +1077,8 @@ void Mac::HandleTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otEr
bool framePending = false;
bool ccaSuccess = true;
Address dstAddr;
+ bool ackRequested;
+ Neighbor *neighbor;
// Stop the ack timer.
@@ -1127,6 +1129,40 @@ void Mac::HandleTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otEr
mCsmaAttempts = 0;
+ sendFrame.GetDstAddr(dstAddr);
+ neighbor = GetNetif().GetMle().GetNeighbor(dstAddr);
+ ackRequested = sendFrame.GetAckRequest();
+
+#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
+
+ // Record frame transmission success/failure state (for a neighbor).
+
+ if ((neighbor != NULL) && ackRequested)
+ {
+ bool frameTxSuccess = true;
+
+ // CCA or abort errors are excluded from frame tx error
+ // rate tracking, since when they occur, the frame is
+ // not actually sent over the air.
+
+ switch (aError)
+ {
+ case OT_ERROR_NO_ACK:
+ frameTxSuccess = false;
+
+ // Fall through
+
+ case OT_ERROR_NONE:
+ neighbor->GetLinkInfo().AddFrameTxStatus(frameTxSuccess);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+#endif // OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
+
// Determine whether to re-transmit the frame.
mTransmitAttempts++;
@@ -1154,12 +1190,9 @@ void Mac::HandleTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otEr
// Process the ack frame for "frame pending".
- sendFrame.GetDstAddr(dstAddr);
-
- if (aError == OT_ERROR_NONE && sendFrame.GetAckRequest() && aAckFrame != NULL)
+ if (aError == OT_ERROR_NONE && ackRequested && aAckFrame != NULL)
{
Frame &ackFrame = *static_cast(aAckFrame);
- Neighbor *neighbor = GetNetif().GetMle().GetNeighbor(dstAddr);
framePending = ackFrame.GetFramePending();
@@ -1187,7 +1220,7 @@ void Mac::HandleTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otEr
mCounters.mTxUnicast++;
}
- if (sendFrame.GetAckRequest())
+ if (ackRequested)
{
mCounters.mTxAckRequested++;
diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h
index fc9e54488..0f4a78b9e 100644
--- a/src/core/openthread-core-default-config.h
+++ b/src/core/openthread-core-default-config.h
@@ -946,6 +946,49 @@
#define OPENTHREAD_CONFIG_CCA_FAILURE_RATE_AVERAGING_WINDOW 512
#endif
+/**
+ * @def OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
+ *
+ * Define as 1 to enable transmission error rate tracking (for both MAC frames and IPv6 messages)
+ *
+ * When enabled, OpenThread will track average error rate of MAC frame transmissions and IPv6 message error rate for
+ * every neighbor.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
+#define OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING 1
+#endif
+
+/**
+ * @def OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW
+ *
+ * Applicable only if error rate tracking is enabled (i.e., `OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING` is set).
+ *
+ * OpenThread's MAC implementation maintains the average error rate of MAC frame transmissions per neighbor. This
+ * parameter specifies the window (in terms of number of frames/sample) over which the average error rate is maintained.
+ * Practically, the average value can be considered as the percentage of failed (no ack) MAC frame transmissions over
+ * (approximately) last AVERAGING_WINDOW frame transmission attempts to a specific neighbor.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW
+#define OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW 128
+#endif
+
+/**
+ * @def OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW
+ *
+ * Applicable only if error rate tracking is enabled (i.e., `OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING` is set).
+ *
+ * OpenThread maintains the average error rate of IPv6 messages per neighbor. This parameter specifies the
+ * window (in terms of number of messages) over which the average error rate is maintained. Practically, the average
+ * value can be considered as the percentage of failed (no ack) messages over (approximately) last AVERAGING_WINDOW
+ * IPv6 messages sent to a specific neighbor.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW
+#define OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW 128
+#endif
+
/**
* @def OPENTHREAD_CONFIG_CHANNEL_MONITOR_SAMPLE_INTERVAL
*
diff --git a/src/core/thread/link_quality.cpp b/src/core/thread/link_quality.cpp
index 3a86db202..f89e5c54d 100644
--- a/src/core/thread/link_quality.cpp
+++ b/src/core/thread/link_quality.cpp
@@ -50,14 +50,14 @@ static const char *const kDigitsString[8] =
void SuccessRateTracker::AddSample(bool aSuccess, uint16_t aWeight)
{
- uint32_t oldAverage = mSuccessRate;
- uint32_t newValue = (aSuccess) ? kMaxRateValue : 0;
+ uint32_t oldAverage = mFailureRate;
+ uint32_t newValue = (aSuccess) ? 0 : kMaxRateValue;
uint32_t n = aWeight;
// `n/2` is added to the sum to ensure rounding the value to the nearest integer when dividing by `n`
// (e.g., 1.2 -> 1, 3.5 -> 4).
- mSuccessRate = static_cast(((oldAverage * (n - 1)) + newValue + (n / 2)) / n);
+ mFailureRate = static_cast(((oldAverage * (n - 1)) + newValue + (n / 2)) / n);
}
void RssAverager::Reset(void)
diff --git a/src/core/thread/link_quality.hpp b/src/core/thread/link_quality.hpp
index e758e3988..1a5998034 100644
--- a/src/core/thread/link_quality.hpp
+++ b/src/core/thread/link_quality.hpp
@@ -71,13 +71,13 @@ public:
* After initialization the tracker starts with success rate 100% (failure rate 0%).
*
*/
- SuccessRateTracker(void): mSuccessRate(kMaxRateValue) { }
+ SuccessRateTracker(void): mFailureRate(0) { }
/**
* This method resets the tracker to its initialized state, setting success rate to 100%.
*
*/
- void Reset(void) { mSuccessRate = kMaxRateValue; }
+ void Reset(void) { mFailureRate = 0; }
/**
* This method adds a sample (success or failure) to `SuccessRateTracker`.
@@ -94,7 +94,7 @@ public:
* @retval the average failure rate `[0-kMaxRateValue]` with `kMaxRateValue` corresponding to 100%.
*
*/
- uint16_t GetFailureRate(void) const { return kMaxRateValue - mSuccessRate; }
+ uint16_t GetFailureRate(void) const { return mFailureRate; }
/**
* This method returns the average success rate.
@@ -102,7 +102,7 @@ public:
* @retval the average success rate as [0-kMaxRateValue] with `kMaxRateValue` corresponding to 100%.
*
*/
- uint16_t GetSuccessRate(void) const { return mSuccessRate; }
+ uint16_t GetSuccessRate(void) const { return kMaxRateValue - mFailureRate; }
private:
enum
@@ -110,7 +110,7 @@ private:
kDefaultWeight = 64,
};
- uint16_t mSuccessRate;
+ uint16_t mFailureRate;
};
/**
@@ -121,8 +121,6 @@ private:
*/
class RssAverager
{
- friend class LinkQualityInfo;
-
public:
enum
{
@@ -212,9 +210,8 @@ private:
// Member variables fit into two bytes.
- uint16_t mAverage : 11; // The raw average signal strength value (stored as RSS times precision multiple).
- uint8_t mCount : 3; // Number of RSS values added to averager so far (limited to 2^kCoeffBitShift-1).
- uint8_t mLinkQuality : 2; // Used by friend class LinkQualityInfo to store LinkQuality (0-3) value.
+ uint16_t mAverage : 11; // The raw average signal strength value (stored as RSS times precision multiple).
+ uint16_t mCount : 5; // Number of RSS values added to averager so far (limited to 2^kCoeffBitShift-1).
};
/**
@@ -307,7 +304,7 @@ public:
* @returns The current link quality value (value 0-3 as per Thread specification).
*
*/
- uint8_t GetLinkQuality(void) const { return mRssAverager.mLinkQuality; }
+ uint8_t GetLinkQuality(void) const { return mLinkQuality; }
/**
* Returns the most recent RSS value.
@@ -317,6 +314,58 @@ public:
*/
int8_t GetLastRss(void) const { return mLastRss; }
+#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
+
+ /**
+ * This method adds a MAC frame transmission status (success/failure) and updates the frame tx error rate.
+ *
+ * @param[in] aTxStatus Success/Failure of MAC frame transmission (`true` -> success, `false` -> failure).
+ *
+ */
+ void AddFrameTxStatus(bool aTxStatus) {
+ mFrameErrorRate.AddSample(aTxStatus, OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW);
+ }
+
+ /**
+ * This method adds a message transmission status (success/failure) and updates the message error rate.
+ *
+ * @param[in] aTxStatus Success/Failure of message (`true` -> success, `false` -> message tx failed).
+ * A larger (IPv6) message may be fragmented and sent as multiple MAC frames. The message
+ * transmission is considered a failure, if any of its fragments fail after all MAC retry
+ * attempts.
+ *
+ */
+ void AddMessageTxStatus(bool aTxStatus) {
+ mMessageErrorRate.AddSample(aTxStatus, OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW);
+ }
+
+ /**
+ * This method returns the MAC frame transmission error rate for the link.
+ *
+ * The rate is maintained over a window of (roughly) last `OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW`
+ * frame transmissions.
+ *
+ * @returns The error rate with maximum value `0xffff` corresponding to 100% failure rate.
+ *
+ */
+ uint16_t GetFrameErrorRate(void) const { return mFrameErrorRate.GetFailureRate(); }
+
+ /**
+ * This method returns the message error rate for the link.
+ *
+ * The rate is maintained over a window of (roughly) last `OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW`
+ * (IPv6) messages.
+ *
+ * Note that a larger (IPv6) message can be fragmented and sent as multiple MAC frames. The message transmission is
+ * considered a failure, if any of its fragments fail after all MAC retry attempts.
+ *
+ * @returns The error rate with maximum value `0xffff` corresponding to 100% failure rate.
+ *
+ */
+ uint16_t GetMessageErrorRate(void) const { return mMessageErrorRate.GetFailureRate(); }
+
+#endif // OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
+
/**
* This method converts a received signal strength value to a link margin value.
*
@@ -366,22 +415,22 @@ private:
{
// Constants for obtaining link quality from link margin:
- kThreshold3 = 20, // Link margin threshold for quality 3 link.
- kThreshold2 = 10, // Link margin threshold for quality 2 link.
- kThreshold1 = 2, // Link margin threshold for quality 1 link.
- kHysteresisThreshold = 2, // Link margin hysteresis threshold.
+ kThreshold3 = 20, ///< Link margin threshold for quality 3 link.
+ kThreshold2 = 10, ///< Link margin threshold for quality 2 link.
+ kThreshold1 = 2, ///< Link margin threshold for quality 1 link.
+ kHysteresisThreshold = 2, ///< Link margin hysteresis threshold.
// constants for test:
- kLinkQuality3LinkMargin = 50, ///< link margin for Link Quality 3 (21 - 255)
- kLinkQuality2LinkMargin = 15, ///< link margin for Link Quality 3 (21 - 255)
- kLinkQuality1LinkMargin = 5, ///< link margin for Link Quality 3 (21 - 255)
- kLinkQuality0LinkMargin = 0, ///< link margin for Link Quality 3 (21 - 255)
+ kLinkQuality3LinkMargin = 50, ///< link margin for Link Quality 3 (21 - 255)
+ kLinkQuality2LinkMargin = 15, ///< link margin for Link Quality 3 (21 - 255)
+ kLinkQuality1LinkMargin = 5, ///< link margin for Link Quality 3 (21 - 255)
+ kLinkQuality0LinkMargin = 0, ///< link margin for Link Quality 3 (21 - 255)
kNoLinkQuality = 0xff, // Used to indicate that there is no previous/last link quality.
};
- void SetLinkQuality(uint8_t aLinkQuality) { mRssAverager.mLinkQuality = aLinkQuality; }
+ void SetLinkQuality(uint8_t aLinkQuality) { mLinkQuality = aLinkQuality; }
/* Static private method to calculate the link quality from a given link margin while taking into account the last
* link quality value and adding the hysteresis value to the thresholds. If there is no previous value for link
@@ -391,7 +440,12 @@ private:
static uint8_t CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality);
RssAverager mRssAverager;
+ uint8_t mLinkQuality;
int8_t mLastRss;
+#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
+ SuccessRateTracker mFrameErrorRate;
+ SuccessRateTracker mMessageErrorRate;
+#endif
};
/**
diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp
index 5c03148d7..cf7926719 100644
--- a/src/core/thread/mesh_forwarder.cpp
+++ b/src/core/thread/mesh_forwarder.cpp
@@ -187,6 +187,11 @@ void MeshForwarder::ScheduleTransmissionTask(void)
if ((mSendMessage = GetDirectTransmission()) != NULL)
{
+ if (mSendMessage->GetOffset() == 0)
+ {
+ mSendMessage->SetTxSuccess(true);
+ }
+
mSendMessageMaxMacTxAttempts = Mac::kDirectFrameMacTxAttempts;
GetNetif().GetMac().SendFrameRequest(mMacSender);
ExitNow();
@@ -962,18 +967,22 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, otError aError)
if (mSendMessage->GetDirectTransmission())
{
+ if (aError != OT_ERROR_NONE)
+ {
+ // If the transmission of any fragment frame fails,
+ // the overall message transmission is considered
+ // as failed
+
+ mSendMessage->SetTxSuccess(false);
#if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
- if (aError != OT_ERROR_NONE)
- {
// We set the NextOffset to end of message to avoid sending
// any remaining fragments in the message.
mMessageNextOffset = mSendMessage->GetLength();
- }
-
#endif
+ }
if (mMessageNextOffset < mSendMessage->GetLength())
{
@@ -983,6 +992,11 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, otError aError)
{
mSendMessage->ClearDirectTransmission();
mSendMessage->SetOffset(0);
+
+ if (neighbor != NULL)
+ {
+ neighbor->GetLinkInfo().AddMessageTxStatus(mSendMessage->GetTxSuccess());
+ }
}
if (mSendMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest)
diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp
index f52018e0f..a03757d4e 100644
--- a/src/core/thread/mesh_forwarder_ftd.cpp
+++ b/src/core/thread/mesh_forwarder_ftd.cpp
@@ -477,6 +477,7 @@ Message *MeshForwarder::GetIndirectTransmission(Child &aChild)
aChild.SetIndirectMessage(message);
aChild.SetIndirectFragmentOffset(0);
aChild.ResetIndirectTxAttempts();
+ aChild.SetIndirectTxSuccess(true);
if (message != NULL)
{
@@ -644,6 +645,7 @@ void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aEr
}
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
@@ -670,6 +672,7 @@ void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aEr
{
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
diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp
index 0ea60b878..8f57f1fe2 100644
--- a/src/core/thread/mle_router.cpp
+++ b/src/core/thread/mle_router.cpp
@@ -3949,11 +3949,16 @@ otError MleRouter::GetChildInfo(Child &aChild, otChildInfo &aChildInfo)
aChildInfo.mAverageRssi = aChild.GetLinkInfo().GetAverageRss();
aChildInfo.mLastRssi = aChild.GetLinkInfo().GetLastRss();
- aChildInfo.mRxOnWhenIdle = aChild.IsRxOnWhenIdle();
- aChildInfo.mSecureDataRequest = aChild.IsSecureDataRequest();
- aChildInfo.mFullFunction = aChild.IsFullThreadDevice();
- aChildInfo.mFullNetworkData = aChild.IsFullNetworkData();
- aChildInfo.mIsStateRestoring = aChild.IsStateRestoring();
+#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
+ aChildInfo.mFrameErrorRate = aChild.GetLinkInfo().GetFrameErrorRate();
+ aChildInfo.mMessageErrorRate = aChild.GetLinkInfo().GetMessageErrorRate();
+#endif
+
+ aChildInfo.mRxOnWhenIdle = aChild.IsRxOnWhenIdle();
+ aChildInfo.mSecureDataRequest = aChild.IsSecureDataRequest();
+ aChildInfo.mFullFunction = aChild.IsFullThreadDevice();
+ aChildInfo.mFullNetworkData = aChild.IsFullNetworkData();
+ aChildInfo.mIsStateRestoring = aChild.IsStateRestoring();
exit:
return error;
@@ -4049,6 +4054,10 @@ exit:
aNeighInfo.mLinkQualityIn = neighbor->GetLinkInfo().GetLinkQuality();
aNeighInfo.mAverageRssi = neighbor->GetLinkInfo().GetAverageRss();
aNeighInfo.mLastRssi = neighbor->GetLinkInfo().GetLastRss();
+#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
+ aNeighInfo.mFrameErrorRate = neighbor->GetLinkInfo().GetFrameErrorRate();
+ aNeighInfo.mMessageErrorRate = neighbor->GetLinkInfo().GetMessageErrorRate();
+#endif
aNeighInfo.mRxOnWhenIdle = neighbor->IsRxOnWhenIdle();
aNeighInfo.mSecureDataRequest = neighbor->IsSecureDataRequest();
aNeighInfo.mFullFunction = neighbor->IsFullThreadDevice();
diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp
index 34116d84c..c426f4bbd 100644
--- a/src/core/thread/topology.hpp
+++ b/src/core/thread/topology.hpp
@@ -585,6 +585,22 @@ public:
*/
void SetIndirectFragmentOffset(uint16_t aFragmentOffset) { mIndirectFragmentOffset = aFragmentOffset; }
+ /**
+ * This method gets the transmission status (success/failure) of the indirect transmission.
+ *
+ * @returns The transmission status of indirect transmission, `true` indicating success, `false` indicating failure.
+ *
+ */
+ bool GetIndirectTxSuccess(void) const { return mIndirectTxSuccess; }
+
+ /**
+ * This method sets the transmission status (success/failure) of the indirect transmission.
+ *
+ * @param[in] aTxStatus The transmission status, `true` indicating success, `false` indicating failure.
+ *
+ */
+ void SetIndirectTxSuccess(bool aTxStatus) { mIndirectTxSuccess = aTxStatus; }
+
/**
* This method gets the IEEE 802.15.4 Key ID to use for indirect retransmissions.
*
@@ -778,7 +794,8 @@ private:
uint32_t mIndirectFrameCounter; ///< Frame counter for current indirect message (used fore retx).
Message *mIndirectMessage; ///< Current indirect message.
- uint16_t mIndirectFragmentOffset; ///< 6LoWPAN fragment offset for the indirect message.
+ uint16_t mIndirectFragmentOffset : 15; ///< 6LoWPAN fragment offset for the indirect message.
+ bool mIndirectTxSuccess : 1; ///< Indicates tx success/failure of current indirect message.
uint8_t mIndirectKeyId; ///< Key Id for current indirect message (used for retx).
uint8_t mIndirectTxAttempts; ///< Number of data poll triggered tx attempts.
uint8_t mIndirectDsn; ///< MAC level Data Sequence Number (DSN) for retx attempts.