From 201b7b1a3d337bd1afaa97880f32c3cca77cf283 Mon Sep 17 00:00:00 2001 From: Jintao Lin Date: Thu, 4 Jun 2020 13:09:16 +0800 Subject: [PATCH] [mac] move auxiliary frame counter from Mac to SubMac (#4976) In Thread 1.2, it is possible to receive an IEEE 802.15.4-2015 packet. Per specification, receiver should acknowledge this packet with an IEEE 802.15.4-2015 ACK(Enh-ACK). This Enh-ACK can include header IE with it and requires security enabled bit in FCF be set to true. It is impractical for the host to generate the Enh-ACK and send to RCP for transmission within AIFS time(192us). So RCP should prepare the Enh-ACK by itself, which requires it to fill in the frame counter and do the encryption/authentication. This commit tries to address the need of filling auxiliary frame counter in RCP by including the following changes: - Move frame counter related functions from MAC layer to SubMac layer, which is mirrored in RCP. - Set mMacFrameCounter to RCP using newly added spinel properties. - RCP reports last used frame counter to host in TxDone and RxDone. - Add a simulation test for reset verification. --- examples/platforms/simulation/radio.c | 1 + include/openthread/instance.h | 2 +- include/openthread/link_raw.h | 12 +++ include/openthread/platform/radio.h | 23 ++++- src/core/api/link_raw_api.cpp | 5 + src/core/mac/link_raw.cpp | 11 +++ src/core/mac/link_raw.hpp | 11 +++ src/core/mac/mac.cpp | 15 +-- src/core/mac/sub_mac.cpp | 42 +++++++++ src/core/mac/sub_mac.hpp | 26 ++++++ src/core/mac/sub_mac_callbacks.cpp | 10 ++ src/core/radio/radio.hpp | 11 +++ src/core/radio/radio_platform.cpp | 6 ++ src/core/thread/key_manager.cpp | 17 +++- src/core/thread/key_manager.hpp | 19 ++-- src/lib/spinel/radio_spinel.hpp | 10 +- src/lib/spinel/radio_spinel_impl.hpp | 116 ++++++++++++++---------- src/lib/spinel/spinel.h | 21 ++++- src/ncp/ncp_base.hpp | 1 + src/ncp/ncp_base_dispatcher.cpp | 1 + src/ncp/ncp_base_radio.cpp | 107 ++++++++++++++-------- src/posix/platform/radio.cpp | 6 ++ tests/scripts/thread-cert/Makefile.am | 6 +- tests/scripts/thread-cert/test_reset.py | 109 ++++++++++++++++++++++ 24 files changed, 466 insertions(+), 122 deletions(-) create mode 100755 tests/scripts/thread-cert/test_reset.py diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index c0e26dcd9..1145c4d9c 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -778,6 +778,7 @@ void radioProcessFrame(otInstance *aInstance) sReceiveFrame.mInfo.mRxInfo.mLqi = OT_RADIO_LQI_NONE; sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending = false; + sReceiveFrame.mInfo.mRxInfo.mAckedWithSecEnhAck = false; otEXPECT(sPromiscuous == false); diff --git a/include/openthread/instance.h b/include/openthread/instance.h index d32eb65d8..910eefd47 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (1) +#define OPENTHREAD_API_VERSION (2) /** * @addtogroup api-instance diff --git a/include/openthread/link_raw.h b/include/openthread/link_raw.h index 486a5282a..5d5c90825 100644 --- a/include/openthread/link_raw.h +++ b/include/openthread/link_raw.h @@ -351,6 +351,18 @@ otError otLinkRawSetMacKey(otInstance * aInstance, const otMacKey *aCurrKey, const otMacKey *aNextKey); +/** + * Sets the current MAC frame counter value. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aMacFrameCounter The MAC frame counter value. + * + * @retval OT_ERROR_NONE If successful. + * @retval OT_ERROR_INVALID_STATE If the raw link-layer isn't enabled. + * + */ +otError otLinkRawSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter); + /** * @} * diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index b5cd19f82..8701115cd 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -234,11 +234,14 @@ typedef struct otRadioFrame */ uint64_t mTimestamp; - int8_t mRssi; ///< Received signal strength indicator in dBm for received frames. - uint8_t mLqi; ///< Link Quality Indicator for received frames. + uint32_t mAckFrameCounter; ///< ACK security frame counter (applicable when `mAckedWithSecEnhAck` is set). + uint8_t mAckKeyId; ///< ACK security key index (applicable when `mAckedWithSecEnhAck` is set). + int8_t mRssi; ///< Received signal strength indicator in dBm for received frames. + uint8_t mLqi; ///< Link Quality Indicator for received frames. // Flags - bool mAckedWithFramePending : 1; /// This indicates if this frame was acknowledged with frame pending set. + bool mAckedWithFramePending : 1; ///< This indicates if this frame was acknowledged with frame pending set. + bool mAckedWithSecEnhAck : 1; ///< This indicates if this frame was acknowledged with secured enhance ACK. } mRxInfo; } mInfo; } otRadioFrame; @@ -475,6 +478,17 @@ void otPlatRadioSetMacKey(otInstance * aInstance, const otMacKey *aCurrKey, const otMacKey *aNextKey); +/** + * This method sets the current MAC frame counter value. + * + * This function is used when radio provides `OT_RADIO_CAPS_TRANSMIT_SEC` capability. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aMacFrameCounter The MAC frame counter value. + * + */ +void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter); + /** * @} * @@ -635,6 +649,9 @@ extern void otPlatRadioTxStarted(otInstance *aInstance, otRadioFrame *aFrame); * The radio driver calls this function to notify OpenThread that the transmit operation has completed, * providing both the transmitted frame and, if applicable, the received ack frame. * + * When radio provides `OT_RADIO_CAPS_TRANSMIT_SEC` capability, radio platform layer updates @p aFrame + * with the security frame counter and key index values maintained by the radio. + * * @param[in] aInstance The OpenThread instance structure. * @param[in] aFrame A pointer to the frame that was transmitted. * @param[in] aAckFrame A pointer to the ACK frame, NULL if no ACK was received. diff --git a/src/core/api/link_raw_api.cpp b/src/core/api/link_raw_api.cpp index e3f7f1389..f887eabf4 100644 --- a/src/core/api/link_raw_api.cpp +++ b/src/core/api/link_raw_api.cpp @@ -233,6 +233,11 @@ otError otLinkRawSetMacKey(otInstance * aInstance, *static_cast(aNextKey)); } +otError otLinkRawSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter) +{ + return static_cast(aInstance)->Get().SetMacFrameCounter(aMacFrameCounter); +} + #if OPENTHREAD_RADIO otDeviceRole otThreadGetDeviceRole(otInstance *aInstance) diff --git a/src/core/mac/link_raw.cpp b/src/core/mac/link_raw.cpp index 91f78866b..2e0e420dd 100644 --- a/src/core/mac/link_raw.cpp +++ b/src/core/mac/link_raw.cpp @@ -220,6 +220,17 @@ exit: return error; } +otError LinkRaw::SetMacFrameCounter(uint32_t aMacFrameCounter) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(IsEnabled(), error = OT_ERROR_INVALID_STATE); + mSubMac.SetFrameCounter(aMacFrameCounter); + +exit: + return error; +} + // LCOV_EXCL_START #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1) diff --git a/src/core/mac/link_raw.hpp b/src/core/mac/link_raw.hpp index 4cfd99c6a..fcd9a1229 100644 --- a/src/core/mac/link_raw.hpp +++ b/src/core/mac/link_raw.hpp @@ -263,6 +263,17 @@ public: const Key &aCurrKey, const Key &aNextKey); + /** + * This method sets the current MAC frame counter value. + * + * @param[in] aMacFrameCounter The MAC frame counter value. + * + * @retval OT_ERROR_NONE If successful. + * @retval OT_ERROR_INVALID_STATE If the raw link-layer isn't enabled. + * + */ + otError SetMacFrameCounter(uint32_t aMacFrameCounter); + /** * This method records the status of a frame transmission attempt and is mainly used for logging failures. * diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 34b026433..5760ebac2 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -970,19 +970,8 @@ void Mac::ProcessTransmitSecurity(TxFrame &aFrame) break; case Frame::kKeyIdMode1: - // If the frame is marked as a retransmission, `MeshForwarder` 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(keyManager.GetMacFrameCounter()); - keyManager.IncrementMacFrameCounter(); - } - - // For MAC key ID mode 1, the AES CCM* is done at SubMac or Radio if supported + // For MAC Key ID Mode 1, the security frame counter update and CCM* is done at SubMac or Radio depending on + // `OT_RADIO_CAPS_TRANSMIT_SEC`. ExitNow(); break; diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 589be6216..ee87c698f 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -59,6 +59,7 @@ SubMac::SubMac(Instance &aInstance) , mCallbacks(aInstance) , mPcapCallback(NULL) , mPcapCallbackContext(NULL) + , mFrameCounter(0) , mKeyId(0) , mTimer(aInstance, &SubMac::HandleTimer, this) { @@ -201,6 +202,11 @@ void SubMac::HandleReceiveDone(RxFrame *aFrame, otError aError) mPcapCallback(aFrame, false, mPcapCallbackContext); } + if (!ShouldHandleTransmitSecurity() && aFrame != NULL && aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck) + { + UpdateFrameCounter(aFrame->mInfo.mRxInfo.mAckFrameCounter); + } + mCallbacks.ReceiveDone(aFrame, aError); } @@ -247,7 +253,11 @@ void SubMac::ProcessTransmitSecurity(void) if (!mTransmitFrame.IsARetransmission()) { + uint32_t frameCounter = GetFrameCounter(); + mTransmitFrame.SetKeyId(mKeyId); + mTransmitFrame.SetFrameCounter(frameCounter); + UpdateFrameCounter(frameCounter + 1); } extAddress = &GetExtAddress(); @@ -378,6 +388,19 @@ void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, otError aEr OT_UNREACHABLE_CODE(ExitNow()); } + if (!ShouldHandleTransmitSecurity() && aFrame.GetSecurityEnabled()) + { + uint8_t keyIdMode; + uint32_t frameCounter = 0; + + IgnoreError(aFrame.GetKeyIdMode(keyIdMode)); + if (keyIdMode == Frame::kKeyIdMode1) + { + OT_ASSERT(aFrame.GetFrameCounter(frameCounter) == OT_ERROR_NONE); + UpdateFrameCounter(frameCounter); + } + } + // Determine whether a CSMA retry is required. if (!ccaSuccess && ShouldHandleCsmaBackOff() && mCsmaBackoffs < aFrame.GetMaxCsmaBackoffs()) @@ -655,6 +678,25 @@ exit: return; } +void SubMac::UpdateFrameCounter(uint32_t aFrameCounter) +{ + mFrameCounter = aFrameCounter; + + mCallbacks.FrameCounterUpdated(aFrameCounter); +} + +void SubMac::SetFrameCounter(uint32_t aFrameCounter) +{ + mFrameCounter = aFrameCounter; + + VerifyOrExit(!ShouldHandleTransmitSecurity(), OT_NOOP); + + Get().SetMacFrameCounter(aFrameCounter); + +exit: + return; +} + // LCOV_EXCL_START const char *SubMac::StateToString(State aState) diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 4279daeff..57e6d5556 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -167,6 +167,14 @@ public: * */ void EnergyScanDone(int8_t aMaxRssi); + + /** + * This method notifies user of `SubMac` that MAC frame counter is updated. + * + * @param[in] aFrameCounter The MAC frame counter value. + * + */ + void FrameCounterUpdated(uint32_t aFrameCounter); }; /** @@ -383,6 +391,22 @@ public: */ const Key &GetNextMacKey(void) const { return mNextKey; } + /** + * This method returns the current MAC frame counter value. + * + * @returns The current MAC frame counter value. + * + */ + uint32_t GetFrameCounter(void) const { return mFrameCounter; }; + + /** + * This method sets the current MAC Frame Counter value. + * + * @param[in] aFrameCounter The MAC Frame Counter value. + * + */ + void SetFrameCounter(uint32_t aFrameCounter); + private: enum { @@ -426,6 +450,7 @@ private: bool ShouldHandleEnergyScan(void) const; void ProcessTransmitSecurity(void); + void UpdateFrameCounter(uint32_t aFrameCounter); void StartCsmaBackoff(void); void BeginTransmit(void); void SampleRssi(void); @@ -457,6 +482,7 @@ private: Key mPrevKey; Key mCurrKey; Key mNextKey; + uint32_t mFrameCounter; uint8_t mKeyId; #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE TimerMicro mTimer; diff --git a/src/core/mac/sub_mac_callbacks.cpp b/src/core/mac/sub_mac_callbacks.cpp index add39ac22..6fa84b747 100644 --- a/src/core/mac/sub_mac_callbacks.cpp +++ b/src/core/mac/sub_mac_callbacks.cpp @@ -107,6 +107,11 @@ void SubMac::Callbacks::EnergyScanDone(int8_t aMaxRssi) } } +void SubMac::Callbacks::FrameCounterUpdated(uint32_t aFrameCounter) +{ + Get().MacFrameCounterUpdated(aFrameCounter); +} + #elif OPENTHREAD_RADIO void SubMac::Callbacks::ReceiveDone(RxFrame *aFrame, otError aError) @@ -137,6 +142,11 @@ void SubMac::Callbacks::EnergyScanDone(int8_t aMaxRssi) Get().InvokeEnergyScanDone(aMaxRssi); } +void SubMac::Callbacks::FrameCounterUpdated(uint32_t aFrameCounter) +{ + OT_UNUSED_VARIABLE(aFrameCounter); +} + #endif // OPENTHREAD_RADIO } // namespace Mac diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index 3d18e7a13..428fb8320 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -267,6 +267,17 @@ public: otPlatRadioSetMacKey(GetInstance(), aKeyIdMode, aKeyId, &aPrevKey, &aCurrKey, &aNextKey); } + /** + * This method sets the current MAC Frame Counter value. + * + * @param[in] aMacFrameCounter The MAC Frame Counter value. + * + */ + void SetMacFrameCounter(uint32_t aMacFrameCounter) + { + otPlatRadioSetMacFrameCounter(GetInstance(), aMacFrameCounter); + } + /** * This method gets the radio's transmit power in dBm. * diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp index f73c21c4c..6839f148f 100644 --- a/src/core/radio/radio_platform.cpp +++ b/src/core/radio/radio_platform.cpp @@ -139,3 +139,9 @@ OT_TOOL_WEAK void otPlatRadioSetMacKey(otInstance * aInstance, OT_UNUSED_VARIABLE(aCurrKey); OT_UNUSED_VARIABLE(aNextKey); } + +OT_TOOL_WEAK void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aMacFrameCounter); +} diff --git a/src/core/thread/key_manager.cpp b/src/core/thread/key_manager.cpp index 7ff564e0a..bbbf1f738 100644 --- a/src/core/thread/key_manager.cpp +++ b/src/core/thread/key_manager.cpp @@ -69,7 +69,6 @@ const otMasterKey KeyManager::kDefaultMasterKey = {{ KeyManager::KeyManager(Instance &aInstance) : InstanceLocator(aInstance) , mKeySequence(0) - , mMacFrameCounter(0) , mMleFrameCounter(0) , mStoredMacFrameCounter(0) , mStoredMleFrameCounter(0) @@ -193,7 +192,7 @@ void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence) mKeySequence = aKeySequence; UpdateKeyMaterial(); - mMacFrameCounter = 0; + SetMacFrameCounter(0); mMleFrameCounter = 0; Get().Signal(OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER); @@ -212,11 +211,19 @@ const Mle::Key &KeyManager::GetTemporaryMleKey(uint32_t aKeySequence) return mTemporaryMleKey; } -void KeyManager::IncrementMacFrameCounter(void) +uint32_t KeyManager::GetMacFrameCounter(void) const { - mMacFrameCounter++; + return Get().GetFrameCounter(); +} - if (mMacFrameCounter >= mStoredMacFrameCounter) +void KeyManager::SetMacFrameCounter(uint32_t aMacFrameCounter) +{ + Get().SetFrameCounter(aMacFrameCounter); +} + +void KeyManager::MacFrameCounterUpdated(uint32_t aMacFrameCounter) +{ + if (aMacFrameCounter >= mStoredMacFrameCounter) { IgnoreError(Get().Store()); } diff --git a/src/core/thread/key_manager.hpp b/src/core/thread/key_manager.hpp index be6bd1690..c9c82d607 100644 --- a/src/core/thread/key_manager.hpp +++ b/src/core/thread/key_manager.hpp @@ -211,7 +211,7 @@ public: * @returns The current MAC Frame Counter value. * */ - uint32_t GetMacFrameCounter(void) const { return mMacFrameCounter; } + uint32_t GetMacFrameCounter(void) const; /** * This method sets the current MAC Frame Counter value. @@ -219,7 +219,7 @@ public: * @param[in] aMacFrameCounter The MAC Frame Counter value. * */ - void SetMacFrameCounter(uint32_t aMacFrameCounter) { mMacFrameCounter = aMacFrameCounter; } + void SetMacFrameCounter(uint32_t aMacFrameCounter); /** * This method sets the MAC Frame Counter value which is stored in non-volatile memory. @@ -229,12 +229,6 @@ public: */ void SetStoredMacFrameCounter(uint32_t aStoredMacFrameCounter) { mStoredMacFrameCounter = aStoredMacFrameCounter; } - /** - * This method increments the current MAC Frame Counter value. - * - */ - void IncrementMacFrameCounter(void); - /** * This method returns the current MLE Frame Counter value. * @@ -430,6 +424,14 @@ public: */ void UpdateKeyMaterial(void); + /** + * This method handles MAC frame counter change. + * + * @param[in] aMacFrameCounter The MAC frame counter value. + * + */ + void MacFrameCounterUpdated(uint32_t aMacFrameCounter); + private: enum { @@ -467,7 +469,6 @@ private: Mle::Key mMleKey; Mle::Key mTemporaryMleKey; - uint32_t mMacFrameCounter; uint32_t mMleFrameCounter; uint32_t mStoredMacFrameCounter; uint32_t mStoredMleFrameCounter; diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 651c1856f..dfed95b67 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -617,6 +617,14 @@ public: const otMacKey &aCurrKey, const otMacKey &aNextKey); + /** + * This method sets the current MAC Frame Counter value. + * + * @param[in] aMacFrameCounter The MAC Frame Counter value. + * + */ + otError SetMacFrameCounter(uint32_t aMacFrameCounter); + /** * This method checks whether the spinel interface is radio-only * @@ -751,7 +759,7 @@ private: spinel_tid_t tid, const char * pack_format, va_list args); - otError ParseRadioFrame(otRadioFrame &aFrame, const uint8_t *aBuffer, uint16_t aLength); + otError ParseRadioFrame(otRadioFrame &aFrame, const uint8_t *aBuffer, uint16_t aLength, spinel_ssize_t &aUnpacked); otError ThreadDatasetHandler(const uint8_t *aBuffer, uint16_t aLength); /** diff --git a/src/lib/spinel/radio_spinel_impl.hpp b/src/lib/spinel/radio_spinel_impl.hpp index 7870fc387..ff5334422 100644 --- a/src/lib/spinel/radio_spinel_impl.hpp +++ b/src/lib/spinel/radio_spinel_impl.hpp @@ -739,17 +739,17 @@ void RadioSpinel::HandleValueIs(spinel_prop_k const uint8_t * aBuffer, uint16_t aLength) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + spinel_ssize_t unpacked; if (aKey == SPINEL_PROP_STREAM_RAW) { - SuccessOrExit(error = ParseRadioFrame(mRxRadioFrame, aBuffer, aLength)); + SuccessOrExit(error = ParseRadioFrame(mRxRadioFrame, aBuffer, aLength, unpacked)); RadioReceive(); } else if (aKey == SPINEL_PROP_LAST_STATUS) { spinel_status_t status = SPINEL_STATUS_OK; - spinel_ssize_t unpacked; unpacked = spinel_datatype_unpack(aBuffer, aLength, "i", &status); VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); @@ -769,9 +769,8 @@ void RadioSpinel::HandleValueIs(spinel_prop_k } else if (aKey == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT) { - uint8_t scanChannel; - int8_t maxRssi; - spinel_ssize_t unpacked; + uint8_t scanChannel; + int8_t maxRssi; unpacked = spinel_datatype_unpack(aBuffer, aLength, "Cc", &scanChannel, &maxRssi); @@ -780,9 +779,8 @@ void RadioSpinel::HandleValueIs(spinel_prop_k } else if (aKey == SPINEL_PROP_STREAM_DEBUG) { - char logStream[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE + 1]; - unsigned int len = sizeof(logStream); - spinel_ssize_t unpacked; + char logStream[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE + 1]; + unsigned int len = sizeof(logStream); unpacked = spinel_datatype_unpack_in_place(aBuffer, aLength, SPINEL_DATATYPE_DATA_S, logStream, &len); assert(len < sizeof(logStream)); @@ -792,9 +790,8 @@ void RadioSpinel::HandleValueIs(spinel_prop_k } else if ((aKey == SPINEL_PROP_STREAM_LOG) && mSupportsLogStream) { - const char * logString; - spinel_ssize_t unpacked; - uint8_t logLevel; + const char *logString; + uint8_t logLevel; unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, &logString); VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE); @@ -837,9 +834,10 @@ exit: } template -otError RadioSpinel::ParseRadioFrame(otRadioFrame & aFrame, - const uint8_t *aBuffer, - uint16_t aLength) +otError RadioSpinel::ParseRadioFrame(otRadioFrame & aFrame, + const uint8_t * aBuffer, + uint16_t aLength, + spinel_ssize_t &aUnpacked) { otError error = OT_ERROR_NONE; uint16_t flags = 0; @@ -848,29 +846,35 @@ otError RadioSpinel::ParseRadioFrame(otRadioF unsigned int receiveError = 0; spinel_ssize_t unpacked; - unpacked = spinel_datatype_unpack_in_place(aBuffer, aLength, - SPINEL_DATATYPE_DATA_WLEN_S // Frame - SPINEL_DATATYPE_INT8_S // RSSI - SPINEL_DATATYPE_INT8_S // Noise Floor - SPINEL_DATATYPE_UINT16_S // Flags - SPINEL_DATATYPE_STRUCT_S( // PHY-data - SPINEL_DATATYPE_UINT8_S // 802.15.4 channel - SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI - SPINEL_DATATYPE_UINT64_S // Timestamp (us). - ) SPINEL_DATATYPE_STRUCT_S( // Vendor-data - SPINEL_DATATYPE_UINT_PACKED_S // Receive error - ), - aFrame.mPsdu, &size, &aFrame.mInfo.mRxInfo.mRssi, &noiseFloor, &flags, - &aFrame.mChannel, &aFrame.mInfo.mRxInfo.mLqi, - &aFrame.mInfo.mRxInfo.mTimestamp, &receiveError); + unpacked = spinel_datatype_unpack_in_place( + aBuffer, aLength, + SPINEL_DATATYPE_DATA_WLEN_S // Frame + SPINEL_DATATYPE_INT8_S // RSSI + SPINEL_DATATYPE_INT8_S // Noise Floor + SPINEL_DATATYPE_UINT16_S // Flags + SPINEL_DATATYPE_STRUCT_S( // PHY-data + SPINEL_DATATYPE_UINT8_S // 802.15.4 channel + SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI + SPINEL_DATATYPE_UINT64_S // Timestamp (us). + ) SPINEL_DATATYPE_STRUCT_S( // Vendor-data + SPINEL_DATATYPE_UINT_PACKED_S // Receive error + ) SPINEL_DATATYPE_STRUCT_S( // MAC-data + SPINEL_DATATYPE_UINT8_S // Security key index + SPINEL_DATATYPE_UINT32_S // Security frame counter + ), + aFrame.mPsdu, &size, &aFrame.mInfo.mRxInfo.mRssi, &noiseFloor, &flags, &aFrame.mChannel, + &aFrame.mInfo.mRxInfo.mLqi, &aFrame.mInfo.mRxInfo.mTimestamp, &receiveError, &aFrame.mInfo.mRxInfo.mAckKeyId, + &aFrame.mInfo.mRxInfo.mAckFrameCounter); VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + aUnpacked = unpacked; if (receiveError == OT_ERROR_NONE) { aFrame.mLength = static_cast(size); aFrame.mInfo.mRxInfo.mAckedWithFramePending = ((flags & SPINEL_MD_FLAG_ACKED_FP) != 0); + aFrame.mInfo.mRxInfo.mAckedWithSecEnhAck = ((flags & SPINEL_MD_FLAG_ACKED_SEC) != 0); } else if (receiveError < OT_NUM_ERRORS) { @@ -1030,6 +1034,17 @@ exit: return error; } +template +otError RadioSpinel::SetMacFrameCounter(uint32_t aMacFrameCounter) +{ + otError error; + + SuccessOrExit(error = Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S, aMacFrameCounter)); + +exit: + return error; +} + template otError RadioSpinel::GetIeeeEui64(uint8_t *aIeeeEui64) { @@ -1433,8 +1448,9 @@ void RadioSpinel::HandleTransmitDone(uint32_t const uint8_t * aBuffer, uint16_t aLength) { - otError error = OT_ERROR_NONE; - spinel_status_t status = SPINEL_STATUS_OK; + otError error = OT_ERROR_NONE; + spinel_status_t status = SPINEL_STATUS_OK; + bool framePending = false; spinel_ssize_t unpacked; VerifyOrExit(aCommand == SPINEL_CMD_PROP_VALUE_IS && aKey == SPINEL_PROP_LAST_STATUS, error = OT_ERROR_FAILED); @@ -1445,30 +1461,36 @@ void RadioSpinel::HandleTransmitDone(uint32_t aBuffer += unpacked; aLength -= static_cast(unpacked); + unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_BOOL_S, &framePending); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + aBuffer += unpacked; + aLength -= static_cast(unpacked); + if (status == SPINEL_STATUS_OK) { - bool framePending = false; - unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_BOOL_S, &framePending); - OT_UNUSED_VARIABLE(framePending); - VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); - + SuccessOrExit(error = ParseRadioFrame(mAckRadioFrame, aBuffer, aLength, unpacked)); aBuffer += unpacked; - aLength -= static_cast(unpacked); - - if (aLength > 0) - { - SuccessOrExit(error = ParseRadioFrame(mAckRadioFrame, aBuffer, aLength)); - } - else - { - mAckRadioFrame.mLength = 0; - } + aLength -= static_cast(unpacked); } else { error = SpinelStatusToOtError(status); } + if ((mRadioCaps & OT_RADIO_CAPS_TRANSMIT_SEC) && static_cast(mTransmitFrame)->GetSecurityEnabled()) + { + uint8_t keyId; + uint32_t frameCounter; + + // Replace transmit frame security key index and frame counter with the one filled by RCP + unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT32_S, &keyId, + &frameCounter); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + static_cast(mTransmitFrame)->SetKeyId(keyId); + static_cast(mTransmitFrame)->SetFrameCounter(frameCounter); + } + exit: mState = kStateTransmitDone; mTxError = error; diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index 6b978c60f..9db58800b 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -692,11 +692,12 @@ typedef uint8_t spinel_tid_t; enum { - SPINEL_MD_FLAG_TX = 0x0001, //!< Packet was transmitted, not received. - SPINEL_MD_FLAG_BAD_FCS = 0x0004, //!< Packet was received with bad FCS - SPINEL_MD_FLAG_DUPE = 0x0008, //!< Packet seems to be a duplicate - SPINEL_MD_FLAG_ACKED_FP = 0x0010, //!< Packet was acknowledged with frame pending set - SPINEL_MD_FLAG_RESERVED = 0xFFE2, //!< Flags reserved for future use. + SPINEL_MD_FLAG_TX = 0x0001, //!< Packet was transmitted, not received. + SPINEL_MD_FLAG_BAD_FCS = 0x0004, //!< Packet was received with bad FCS + SPINEL_MD_FLAG_DUPE = 0x0008, //!< Packet seems to be a duplicate + SPINEL_MD_FLAG_ACKED_FP = 0x0010, //!< Packet was acknowledged with frame pending set + SPINEL_MD_FLAG_ACKED_SEC = 0x0020, //!< Packet was acknowledged with secure enhance ACK + SPINEL_MD_FLAG_RESERVED = 0xFFC2, //!< Flags reserved for future use. }; enum @@ -3952,6 +3953,16 @@ enum */ SPINEL_PROP_RCP_MAC_KEY = SPINEL_PROP_RCP__BEGIN + 0, + /// MAC Frame Counter + /** Format: `L`. + * + * `L`: MAC frame counter + * + * The Spinel property is used to set MAC frame counter to RCP. + * + */ + SPINEL_PROP_RCP_MAC_FRAME_COUNTER = SPINEL_PROP_RCP__BEGIN + 1, + SPINEL_PROP_RCP__END = 0x900, SPINEL_PROP_NEST__BEGIN = 0x3BC0, diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index 4285c2310..97ca3e06c 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -267,6 +267,7 @@ protected: otError DecodeChannelMask(uint32_t &aChannelMask); #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE + otError PackRadioFrame(otRadioFrame *aFrame, otError aError); static void LinkRawReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError); void LinkRawReceiveDone(otRadioFrame *aFrame, otError aError); diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index 0cc987049..8987134db 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -414,6 +414,7 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_RCP_MAC_KEY), + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_RCP_MAC_FRAME_COUNTER), #endif #if OPENTHREAD_MTD || OPENTHREAD_FTD OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_UNSOL_UPDATE_FILTER), diff --git a/src/ncp/ncp_base_radio.cpp b/src/ncp/ncp_base_radio.cpp index f6743095d..6fdbeca64 100644 --- a/src/ncp/ncp_base_radio.cpp +++ b/src/ncp/ncp_base_radio.cpp @@ -51,20 +51,12 @@ namespace Ncp { // MARK: Raw Link-Layer Datapath Glue // ---------------------------------------------------------------------------- -void NcpBase::LinkRawReceiveDone(otInstance *, otRadioFrame *aFrame, otError aError) +otError NcpBase::PackRadioFrame(otRadioFrame *aFrame, otError aError) { - sNcpInstance->LinkRawReceiveDone(aFrame, aError); -} + otError error = OT_ERROR_FAILED; + uint16_t flags = 0; -void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError) -{ - uint16_t flags = 0; - uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; - - // Append frame header - SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_STREAM_RAW)); - - if (aError == OT_ERROR_NONE) + if (aFrame != NULL && aError == OT_ERROR_NONE) { // Append the frame contents SuccessOrExit(mEncoder.WriteDataWithLen(aFrame->mPsdu, aFrame->mLength)); @@ -76,28 +68,61 @@ void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError) } // Append metadata (rssi, etc) - SuccessOrExit(mEncoder.WriteInt8(aFrame->mInfo.mRxInfo.mRssi)); // RSSI - SuccessOrExit(mEncoder.WriteInt8(-128)); // Noise Floor (Currently unused) + SuccessOrExit(mEncoder.WriteInt8(aFrame ? aFrame->mInfo.mRxInfo.mRssi : 0)); // RSSI + SuccessOrExit(mEncoder.WriteInt8(-128)); // Noise Floor (Currently unused) - if (aFrame->mInfo.mRxInfo.mAckedWithFramePending) + if (aFrame != NULL) { - flags |= SPINEL_MD_FLAG_ACKED_FP; + if (aFrame->mInfo.mRxInfo.mAckedWithFramePending) + { + flags |= SPINEL_MD_FLAG_ACKED_FP; + } + + if (aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck) + { + flags |= SPINEL_MD_FLAG_ACKED_SEC; + } } SuccessOrExit(mEncoder.WriteUint16(flags)); // Flags - SuccessOrExit(mEncoder.OpenStruct()); // PHY-data - SuccessOrExit(mEncoder.WriteUint8(aFrame->mChannel)); // 802.15.4 channel (Receive channel) - SuccessOrExit(mEncoder.WriteUint8(aFrame->mInfo.mRxInfo.mLqi)); // 802.15.4 LQI - - SuccessOrExit(mEncoder.WriteUint64(aFrame->mInfo.mRxInfo.mTimestamp)); // The timestamp in microseconds + SuccessOrExit(mEncoder.OpenStruct()); // PHY-data + SuccessOrExit(mEncoder.WriteUint8(aFrame ? aFrame->mChannel : 0)); // 802.15.4 channel (Receive channel) + SuccessOrExit(mEncoder.WriteUint8(aFrame ? aFrame->mInfo.mRxInfo.mLqi + : static_cast(OT_RADIO_LQI_NONE))); // 802.15.4 LQI + SuccessOrExit(mEncoder.WriteUint64(aFrame ? aFrame->mInfo.mRxInfo.mTimestamp : 0)); // The timestamp in microseconds SuccessOrExit(mEncoder.CloseStruct()); SuccessOrExit(mEncoder.OpenStruct()); // Vendor-data SuccessOrExit(mEncoder.WriteUintPacked(aError)); // Receive error SuccessOrExit(mEncoder.CloseStruct()); + SuccessOrExit(mEncoder.OpenStruct()); // MAC-data + SuccessOrExit(mEncoder.WriteUint8(aFrame ? aFrame->mInfo.mRxInfo.mAckKeyId : 0)); // The ACK auxiliary key ID + SuccessOrExit( + mEncoder.WriteUint32(aFrame ? aFrame->mInfo.mRxInfo.mAckFrameCounter : 0)); // The ACK auxiliary frame counter + SuccessOrExit(mEncoder.CloseStruct()); + + error = OT_ERROR_NONE; + +exit: + return error; +} + +void NcpBase::LinkRawReceiveDone(otInstance *, otRadioFrame *aFrame, otError aError) +{ + sNcpInstance->LinkRawReceiveDone(aFrame, aError); +} + +void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError) +{ + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; + + // Append frame header + SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_STREAM_RAW)); + + SuccessOrExit(PackRadioFrame(aFrame, aError)); SuccessOrExit(mEncoder.EndFrame()); exit: @@ -125,25 +150,22 @@ void NcpBase::LinkRawTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, SuccessOrExit(mEncoder.WriteUintPacked(ThreadErrorToSpinelStatus(aError))); SuccessOrExit(mEncoder.WriteBool(framePending)); - if (aAckFrame && aError == OT_ERROR_NONE) + if (aError == OT_ERROR_NONE) { - SuccessOrExit(mEncoder.WriteUint16(aAckFrame->mLength)); - SuccessOrExit(mEncoder.WriteData(aAckFrame->mPsdu, aAckFrame->mLength)); + SuccessOrExit(PackRadioFrame(aAckFrame, aError)); + } - SuccessOrExit(mEncoder.WriteInt8(aAckFrame->mInfo.mRxInfo.mRssi)); // RSSI - SuccessOrExit(mEncoder.WriteInt8(-128)); // Noise Floor (Currently unused) - SuccessOrExit(mEncoder.WriteUint16(0)); // Flags + if (static_cast(aFrame)->GetSecurityEnabled()) + { + uint8_t keyId; + uint32_t frameCounter; - SuccessOrExit(mEncoder.OpenStruct()); // PHY-data - SuccessOrExit(mEncoder.WriteUint8(aAckFrame->mChannel)); // Receive channel - SuccessOrExit(mEncoder.WriteUint8(aAckFrame->mInfo.mRxInfo.mLqi)); // Link Quality Indicator - SuccessOrExit(mEncoder.WriteUint64(aAckFrame->mInfo.mRxInfo.mTimestamp)); // The timestamp in microseconds + // Transmit frame auxiliary key index and frame counter + SuccessOrExit(static_cast(aFrame)->GetKeyId(keyId)); + SuccessOrExit(static_cast(aFrame)->GetFrameCounter(frameCounter)); - SuccessOrExit(mEncoder.CloseStruct()); - - SuccessOrExit(mEncoder.OpenStruct()); // Vendor-data - SuccessOrExit(mEncoder.WriteUintPacked(aError)); // Receive error - SuccessOrExit(mEncoder.CloseStruct()); + SuccessOrExit(mEncoder.WriteUint8(keyId)); + SuccessOrExit(mEncoder.WriteUint32(frameCounter)); } SuccessOrExit(mEncoder.EndFrame()); @@ -461,6 +483,19 @@ exit: return error; } +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error = OT_ERROR_NONE; + uint32_t frameCounter; + + SuccessOrExit(error = mDecoder.ReadUint32(frameCounter)); + + error = otLinkRawSetMacFrameCounter(mInstance, frameCounter); + +exit: + return error; +} + } // namespace Ncp } // namespace ot diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index 17c41c290..819315827 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -489,3 +489,9 @@ void otPlatRadioSetMacKey(otInstance * aInstance, SuccessOrDie(sRadioSpinel.SetMacKey(aKeyIdMode, aKeyId, *aPrevKey, *aCurrKey, *aNextKey)); OT_UNUSED_VARIABLE(aInstance); } + +void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter) +{ + SuccessOrDie(sRadioSpinel.SetMacFrameCounter(aMacFrameCounter)); + OT_UNUSED_VARIABLE(aInstance); +} diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index 63f34e62d..a0b25c9b7 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -157,10 +157,11 @@ EXTRA_DIST = \ test_lowpan.py \ test_mac802154.py \ test_mle.py \ - test_service.py \ test_network_data.py \ test_network_layer.py \ test_reed_address_solicit_rejected.py \ + test_reset.py \ + test_service.py \ thread_cert.py \ tlvs_parsing.py \ $(NULL) @@ -181,10 +182,11 @@ check_SCRIPTS = \ test_lowpan.py \ test_mac802154.py \ test_mle.py \ - test_service.py \ test_network_data.py \ test_network_layer.py \ test_reed_address_solicit_rejected.py \ + test_reset.py \ + test_service.py \ Cert_5_1_01_RouterAttach.py \ Cert_5_1_02_ChildAddressTimeout.py \ Cert_5_1_03_RouterAddressReallocation.py \ diff --git a/tests/scripts/thread-cert/test_reset.py b/tests/scripts/thread-cert/test_reset.py new file mode 100755 index 000000000..e32208b85 --- /dev/null +++ b/tests/scripts/thread-cert/test_reset.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2020, 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. +# + +import unittest +import thread_cert + +LEADER = 1 +ROUTER = 2 +ED = 3 +SED = 4 + + +class TestReset(thread_cert.TestCase): + topology = { + LEADER: { + 'mode': 'rsdn', + 'panid': 0xface, + 'whitelist': [ROUTER] + }, + ROUTER: { + 'mode': 'rsdn', + 'panid': 0xface, + 'router_selection_jitter': 1, + 'whitelist': [LEADER, ED] + }, + ED: { + 'is_mtd': True, + 'mode': 'rsn', + 'panid': 0xface, + 'whitelist': [ROUTER] + }, + } + + def test(self): + self.nodes[LEADER].start() + self.simulator.go(5) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + self.nodes[ROUTER].start() + self.simulator.go(7) + self.assertEqual(self.nodes[ROUTER].get_state(), 'router') + + self.nodes[ED].start() + self.simulator.go(7) + self.assertEqual(self.nodes[ED].get_state(), 'child') + + leader_addrs = self.nodes[LEADER].get_addrs() + router_addrs = self.nodes[ROUTER].get_addrs() + + for i in range(0, 1010): + self.assertTrue(self.nodes[ED].ping(leader_addrs[0])) + self.simulator.go(1) + + # 1 - Leader + self.nodes[LEADER].reset() + self.nodes[LEADER].start() + self.simulator.go(7) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + for addr in router_addrs: + self.assertTrue(self.nodes[LEADER].ping(addr)) + + # 2 - Router + self.nodes[ROUTER].reset() + self.nodes[ROUTER].start() + self.simulator.go(7) + self.assertEqual(self.nodes[ROUTER].get_state(), 'router') + + for addr in leader_addrs: + self.assertTrue(self.nodes[ROUTER].ping(addr)) + + # 3 - Child + self.nodes[ED].reset() + self.nodes[ED].start() + self.simulator.go(7) + self.assertEqual(self.nodes[ED].get_state(), 'child') + + for addr in router_addrs: + self.assertTrue(self.nodes[ED].ping(addr)) + + +if __name__ == '__main__': + unittest.main()