diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index 735f6177e63..acf4bc029d4 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -382,6 +382,15 @@ void otPlatRadioSetShortAddress(otInstance *aInstance, otShortAddress aShortAddr sRadioContext.mShortAddress = aShortAddress; } +void otPlatRadioSetAlternateShortAddress(otInstance *aInstance, otShortAddress aShortAddress) +{ + OT_UNUSED_VARIABLE(aInstance); + + assert(aInstance != NULL); + + sRadioContext.mAlternateShortAddress = aShortAddress; +} + void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) { OT_UNUSED_VARIABLE(aInstance); @@ -849,9 +858,9 @@ void radioProcessFrame(otInstance *aInstance) otEXPECT(sPromiscuous == false); - otEXPECT_ACTION( - otMacFrameDoesAddrMatch(&sReceiveFrame, sPanid, sRadioContext.mShortAddress, &sRadioContext.mExtAddress), - error = OT_ERROR_ABORT); + otEXPECT_ACTION(otMacFrameDoesAddrMatchAny(&sReceiveFrame, sPanid, sRadioContext.mShortAddress, + sRadioContext.mAlternateShortAddress, &sRadioContext.mExtAddress), + error = OT_ERROR_ABORT); #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE otEXPECT_ACTION(otMacFrameGetSrcAddr(&sReceiveFrame, &macAddress) == OT_ERROR_NONE, error = OT_ERROR_PARSE); diff --git a/examples/platforms/utils/mac_frame.cpp b/examples/platforms/utils/mac_frame.cpp index 860b4996e94..1356c7e4b5f 100644 --- a/examples/platforms/utils/mac_frame.cpp +++ b/examples/platforms/utils/mac_frame.cpp @@ -41,6 +41,15 @@ bool otMacFrameDoesAddrMatch(const otRadioFrame *aFrame, otPanId aPanId, otShortAddress aShortAddress, const otExtAddress *aExtAddress) +{ + return otMacFrameDoesAddrMatchAny(aFrame, aPanId, aShortAddress, Mac::kShortAddrInvalid, aExtAddress); +} + +bool otMacFrameDoesAddrMatchAny(const otRadioFrame *aFrame, + otPanId aPanId, + otShortAddress aShortAddress, + otShortAddress aAltShortAddress, + const otExtAddress *aExtAddress) { const Mac::Frame &frame = *static_cast(aFrame); bool rval = true; @@ -52,7 +61,9 @@ bool otMacFrameDoesAddrMatch(const otRadioFrame *aFrame, switch (dst.GetType()) { case Mac::Address::kTypeShort: - VerifyOrExit(dst.GetShort() == Mac::kShortAddrBroadcast || dst.GetShort() == aShortAddress, rval = false); + VerifyOrExit(dst.GetShort() == Mac::kShortAddrBroadcast || dst.GetShort() == aShortAddress || + (aAltShortAddress != Mac::kShortAddrInvalid && dst.GetShort() == aAltShortAddress), + rval = false); break; case Mac::Address::kTypeExtended: diff --git a/examples/platforms/utils/mac_frame.h b/examples/platforms/utils/mac_frame.h index 62ac959a842..5501f0acd53 100644 --- a/examples/platforms/utils/mac_frame.h +++ b/examples/platforms/utils/mac_frame.h @@ -132,6 +132,25 @@ bool otMacFrameDoesAddrMatch(const otRadioFrame *aFrame, otShortAddress aShortAddress, const otExtAddress *aExtAddress); +/** + * Check if @p aFrame matches the @p aPandId and @p aShortAddress, or @p aAltShortAddress or @p aExtAddress. + * + * @param[in] aFrame A pointer to the frame. + * @param[in] aPanId The PAN id to match with. + * @param[in] aShortAddress The short address to match with. + * @param[in] aAltShortAddress The alternate short address to match with. Can be `OT_RADIO_INVALID_SHORT_ADDR` if + * there is no alternate address. + * @param[in] aExtAddress The extended address to match with. + * + * @retval true It is a broadcast or matches with the PAN id and one of the addresses. + * @retval false It doesn't match. + */ +bool otMacFrameDoesAddrMatchAny(const otRadioFrame *aFrame, + otPanId aPanId, + otShortAddress aShortAddress, + otShortAddress aAltShortAddress, + const otExtAddress *aExtAddress); + /** * Get source MAC address. * @@ -319,6 +338,7 @@ typedef struct otRadioContext uint32_t mCslSampleTime; ///< The sample time based on the microsecond timer. uint16_t mCslPeriod; ///< In unit of 10 symbols. otShortAddress mShortAddress; + otShortAddress mAlternateShortAddress; otRadioKeyType mKeyType; uint8_t mKeyId; otMacKeyMaterial mPrevKey; diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 4686ce00155..e8576df40f4 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (460) +#define OPENTHREAD_API_VERSION (461) /** * @addtogroup api-instance diff --git a/include/openthread/link.h b/include/openthread/link.h index d74a710379f..ccdc2c7aa35 100644 --- a/include/openthread/link.h +++ b/include/openthread/link.h @@ -588,10 +588,19 @@ otError otLinkSetPollPeriod(otInstance *aInstance, uint32_t aPollPeriod); * * @param[in] aInstance A pointer to an OpenThread instance. * - * @returns A pointer to the IEEE 802.15.4 Short Address. + * @returns The IEEE 802.15.4 Short Address. */ otShortAddress otLinkGetShortAddress(otInstance *aInstance); +/** + * Get the IEEE 802.15.4 alternate short address. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The alternate short address, or `OT_RADIO_INVALID_SHORT_ADDR` (0xfffe) if there is no alternate address. + */ +otShortAddress otLinkGetAlternateShortAddress(otInstance *aInstance); + /** * Returns the maximum number of frame retries during direct transmission. * diff --git a/include/openthread/link_raw.h b/include/openthread/link_raw.h index 5bea2a04183..92c7f9b3b18 100644 --- a/include/openthread/link_raw.h +++ b/include/openthread/link_raw.h @@ -115,6 +115,26 @@ otError otLinkRawSetPromiscuous(otInstance *aInstance, bool aEnable); */ otError otLinkRawSetShortAddress(otInstance *aInstance, uint16_t aShortAddress); +/** + * Set the alternate short address. + * + * This is an optional API. Support for this is indicated by including the capability `OT_RADIO_CAPS_ALT_SHORT_ADDR` in + * `otLinkRawGetCaps()`. + * + * When supported, the radio will accept received frames destined to the specified alternate short address in addition + * to the short address provided in `otLinkRawSetShortAddress()`. + * + * The @p aShortAddress can be set to `OT_RADIO_INVALID_SHORT_ADDR` (0xfffe) to clear any previously set alternate + * short address. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aShortAddress The alternate short address. `OT_RADIO_INVALID_SHORT_ADDR` to clear. + * + * @retval OT_ERROR_NONE Successfully set the alternate short address. + * @retval OT_ERROR_INVALID_STATE The raw link-layer is not enabled. + */ +otError otLinkRawSetAlternateShortAddress(otInstance *aInstance, otShortAddress aShortAddress); + /** * Transition the radio from Receive to Sleep. * Turn off the radio. diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index bd7d24fe295..c793bd748a5 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -82,6 +82,9 @@ enum OT_RADIO_LQI_NONE = 0, ///< LQI measurement not supported OT_RADIO_RSSI_INVALID = 127, ///< Invalid or unknown RSSI value OT_RADIO_POWER_INVALID = 127, ///< Invalid or unknown power value + + OT_RADIO_INVALID_SHORT_ADDR = 0xfffe, ///< Invalid short address. + OT_RADIO_BROADCAST_SHORT_ADDR = 0xffff, ///< Broadcast short address. }; /** @@ -120,17 +123,18 @@ typedef uint16_t otRadioCaps; */ enum { - OT_RADIO_CAPS_NONE = 0, ///< Radio supports no capability. - OT_RADIO_CAPS_ACK_TIMEOUT = 1 << 0, ///< Radio supports AckTime event. - OT_RADIO_CAPS_ENERGY_SCAN = 1 << 1, ///< Radio supports Energy Scans. - OT_RADIO_CAPS_TRANSMIT_RETRIES = 1 << 2, ///< Radio supports tx retry logic with collision avoidance (CSMA). - OT_RADIO_CAPS_CSMA_BACKOFF = 1 << 3, ///< Radio supports CSMA backoff for frame transmission (but no retry). - OT_RADIO_CAPS_SLEEP_TO_TX = 1 << 4, ///< Radio supports direct transition from sleep to TX with CSMA. - OT_RADIO_CAPS_TRANSMIT_SEC = 1 << 5, ///< Radio supports tx security. - OT_RADIO_CAPS_TRANSMIT_TIMING = 1 << 6, ///< Radio supports tx at specific time. - OT_RADIO_CAPS_RECEIVE_TIMING = 1 << 7, ///< Radio supports rx at specific time. - OT_RADIO_CAPS_RX_ON_WHEN_IDLE = 1 << 8, ///< Radio supports RxOnWhenIdle handling. - OT_RADIO_CAPS_TRANSMIT_FRAME_POWER = 1 << 9, ///< Radio supports setting per-frame transmit power. + OT_RADIO_CAPS_NONE = 0, ///< Radio supports no capability. + OT_RADIO_CAPS_ACK_TIMEOUT = 1 << 0, ///< Radio supports AckTime event. + OT_RADIO_CAPS_ENERGY_SCAN = 1 << 1, ///< Radio supports Energy Scans. + OT_RADIO_CAPS_TRANSMIT_RETRIES = 1 << 2, ///< Radio supports tx retry logic with collision avoidance (CSMA). + OT_RADIO_CAPS_CSMA_BACKOFF = 1 << 3, ///< Radio supports CSMA backoff for frame tx (but no retry). + OT_RADIO_CAPS_SLEEP_TO_TX = 1 << 4, ///< Radio supports direct transition from sleep to TX with CSMA. + OT_RADIO_CAPS_TRANSMIT_SEC = 1 << 5, ///< Radio supports tx security. + OT_RADIO_CAPS_TRANSMIT_TIMING = 1 << 6, ///< Radio supports tx at specific time. + OT_RADIO_CAPS_RECEIVE_TIMING = 1 << 7, ///< Radio supports rx at specific time. + OT_RADIO_CAPS_RX_ON_WHEN_IDLE = 1 << 8, ///< Radio supports RxOnWhenIdle handling. + OT_RADIO_CAPS_TRANSMIT_FRAME_POWER = 1 << 9, ///< Radio supports setting per-frame transmit power. + OT_RADIO_CAPS_ALT_SHORT_ADDR = 1 << 10, ///< Radio supports setting alternate short address. }; #define OT_PANID_BROADCAST 0xffff ///< IEEE 802.15.4 Broadcast PAN ID @@ -517,6 +521,26 @@ void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aE */ void otPlatRadioSetShortAddress(otInstance *aInstance, otShortAddress aShortAddress); +/** + * Set the alternate short address. + * + * This is an optional radio platform API. The radio platform MUST indicate support for this API by including the + * capability `OT_RADIO_CAPS_ALT_SHORT_ADDR` in `otPlatRadioGetCaps()`. + * + * When supported, the radio should accept received frames destined to the specified alternate short address in + * addition to the short address provided in `otPlatRadioSetShortAddress()`. + * + * The @p aShortAddress can be set to `OT_RADIO_INVALID_SHORT_ADDR` (0xfffe) to clear any previously set alternate + * short address. + * + * This function is used by OpenThread stack during child-to-router role transitions, allowing the device to continue + * receiving frames addressed to its previous short address for a short period. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aShortAddress The alternate IEEE 802.15.4 short address. `OT_RADIO_INVALID_SHORT_ADDR` to clear. + */ +void otPlatRadioSetAlternateShortAddress(otInstance *aInstance, otShortAddress aShortAddress); + /** * Get the radio's transmit power in dBm. * diff --git a/src/cli/README.md b/src/cli/README.md index b6c610fc619..ad8dd647d60 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -70,7 +70,7 @@ Done - [linkmetricsmgr](#linkmetricsmgr-disable) - [locate](#locate) - [log](#log-filename-filename) -- [mac](#mac-retries-direct) +- [mac](#mac-altshortaddr) - [macfilter](#macfilter) - [meshdiag](#meshdiag-topology-ip6-addrs-children) - [mliid](#mliid-iid) @@ -4196,6 +4196,16 @@ Print API version number. Done ``` +### mac altshortaddr + +Get the alternate short address used by MAC layer. Can be `0xfffe` if not set. + +```bash +> mac altshortaddr +0x4801 +Done +``` + ### mac retries direct Get the number of direct TX retries on the MAC layer. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 78339215872..fbb4bd79be5 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -7306,7 +7306,21 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; - if (aArgs[0] == "retries") + /** + * @cli mac altshortaddr + * @code + * mac altshortaddr + * 0x4802 + * Done + * @endcode + * @par api_copy + * otLinkGetAlternateShortAddress + */ + if (aArgs[0] == "altshortaddr") + { + OutputLine("0x%04x", otLinkGetAlternateShortAddress(GetInstancePtr())); + } + else if (aArgs[0] == "retries") { /** * @cli mac retries direct (get,set) diff --git a/src/core/api/link_api.cpp b/src/core/api/link_api.cpp index f8ca7c12c71..1a46d51aaf0 100644 --- a/src/core/api/link_api.cpp +++ b/src/core/api/link_api.cpp @@ -182,6 +182,11 @@ otShortAddress otLinkGetShortAddress(otInstance *aInstance) return AsCoreType(aInstance).Get().GetShortAddress(); } +otShortAddress otLinkGetAlternateShortAddress(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetAlternateShortAddress(); +} + uint8_t otLinkGetMaxFrameRetriesDirect(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetMaxFrameRetriesDirect(); diff --git a/src/core/api/link_raw_api.cpp b/src/core/api/link_raw_api.cpp index 4eb4712148e..cf8f0caf039 100644 --- a/src/core/api/link_raw_api.cpp +++ b/src/core/api/link_raw_api.cpp @@ -53,6 +53,11 @@ otError otLinkRawSetShortAddress(otInstance *aInstance, uint16_t aShortAddress) return AsCoreType(aInstance).Get().SetShortAddress(aShortAddress); } +otError otLinkRawSetAlternateShortAddress(otInstance *aInstance, otShortAddress aShortAddress) +{ + return AsCoreType(aInstance).Get().SetAlternateShortAddress(aShortAddress); +} + bool otLinkRawGetPromiscuous(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetPromiscuous(); } otError otLinkRawSetPromiscuous(otInstance *aInstance, bool aEnable) diff --git a/src/core/mac/link_raw.cpp b/src/core/mac/link_raw.cpp index fdccd79f5ff..db5d5c86621 100644 --- a/src/core/mac/link_raw.cpp +++ b/src/core/mac/link_raw.cpp @@ -161,6 +161,17 @@ Error LinkRaw::SetShortAddress(ShortAddress aShortAddress) return error; } +Error LinkRaw::SetAlternateShortAddress(ShortAddress aShortAddress) +{ + Error error = kErrorNone; + + VerifyOrExit(IsEnabled(), error = kErrorInvalidState); + mSubMac.SetAlternateShortAddress(aShortAddress); + +exit: + return error; +} + Error LinkRaw::Receive(void) { Error error = kErrorNone; diff --git a/src/core/mac/link_raw.hpp b/src/core/mac/link_raw.hpp index 1f0f2533532..9f01e0f8eea 100644 --- a/src/core/mac/link_raw.hpp +++ b/src/core/mac/link_raw.hpp @@ -183,6 +183,16 @@ class LinkRaw : public InstanceLocator, private NonCopyable */ Error SetShortAddress(ShortAddress aShortAddress); + /** + * Sets the alternate short address. + * + * @param[in] aShortAddress The short address. Use `kShortAddrInvalid` to clear it. + * + * @retval kErrorNone If successful. + * @retval kErrorInvalidState If the raw link-layer isn't enabled. + */ + Error SetAlternateShortAddress(ShortAddress aShortAddress); + /** * Returns PANID. * diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index eb6fdfe0cf6..820e37e4741 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -118,6 +118,9 @@ Mac::Mac(Instance &aInstance) SetPanId(mPanId); SetExtAddress(randomExtAddress); SetShortAddress(GetShortAddress()); +#if OPENTHREAD_FTD + SetAlternateShortAddress(kShortAddrInvalid); +#endif mMode2KeyMaterial.SetFrom(AsCoreType(&sMode2Key)); } @@ -1766,6 +1769,33 @@ Error Mac::ProcessEnhAckSecurity(TxFrame &aTxFrame, RxFrame &aAckFrame) } #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 +Error Mac::FilterDestShortAddress(ShortAddress aDestAddress) const +{ + Error error = kErrorNone; + + if (aDestAddress == GetShortAddress()) + { + ExitNow(); + } + +#if OPENTHREAD_FTD + if ((GetAlternateShortAddress() != kShortAddrInvalid) && (aDestAddress == GetAlternateShortAddress())) + { + ExitNow(); + } +#endif + + if (mRxOnWhenIdle && (aDestAddress == kShortAddrBroadcast)) + { + ExitNow(); + } + + error = kErrorDestinationAddressFiltered; + +exit: + return error; +} + void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) { Address srcaddr; @@ -1795,8 +1825,7 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) break; case Address::kTypeShort: - VerifyOrExit((mRxOnWhenIdle && dstaddr.IsBroadcast()) || dstaddr.GetShort() == GetShortAddress(), - error = kErrorDestinationAddressFiltered); + SuccessOrExit(error = FilterDestShortAddress(dstaddr.GetShort())); #if OPENTHREAD_FTD // Allow multicasts from neighbor routers if FTD diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index 4a1c0d1953c..8a5e78ec268 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -251,6 +251,20 @@ class Mac : public InstanceLocator, private NonCopyable */ void SetShortAddress(ShortAddress aShortAddress) { mLinks.SetShortAddress(aShortAddress); } + /** + * Gets the alternate short address. + * + * @returns The alternate short address, or `kShortAddrInvalid` if there is no alternate address. + */ + ShortAddress GetAlternateShortAddress(void) const { return mLinks.GetAlternateShortAddress(); } + + /** + * Sets the alternate short address. + * + * @param[in] aShortAddress The alternate short address. Use `kShortAddrInvalid` to clear the alternate address. + */ + void SetAlternateShortAddress(ShortAddress aShortAddress) { mLinks.SetAlternateShortAddress(aShortAddress); } + /** * Returns the IEEE 802.15.4 PAN Channel. * @@ -807,6 +821,7 @@ class Mac : public InstanceLocator, private NonCopyable bool ShouldSendBeacon(void) const; bool IsJoinable(void) const; void BeginTransmit(void); + Error FilterDestShortAddress(ShortAddress aDestAddress) const; void UpdateNeighborLinkInfo(Neighbor &aNeighbor, const RxFrame &aRxFrame); bool HandleMacCommand(RxFrame &aFrame); void HandleTimer(void); diff --git a/src/core/mac/mac_links.cpp b/src/core/mac/mac_links.cpp index 4b8c1ec5b17..60b7aee5aad 100644 --- a/src/core/mac/mac_links.cpp +++ b/src/core/mac/mac_links.cpp @@ -127,6 +127,7 @@ Links::Links(Instance &aInstance) , mTxFrames(aInstance) #if !OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE , mShortAddress(kShortAddrInvalid) + , mAlternateShortAddress(kShortAddrInvalid) #endif { #if !OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE diff --git a/src/core/mac/mac_links.hpp b/src/core/mac/mac_links.hpp index 978f199aae7..13b89a22916 100644 --- a/src/core/mac/mac_links.hpp +++ b/src/core/mac/mac_links.hpp @@ -330,6 +330,35 @@ class Links : public InstanceLocator #endif } + /** + * Gets the alternate MAC short address. + * + * @returns The alternate MAC short address, or `kShortAddrInvalid` if there is no alternate address. + */ + ShortAddress GetAlternateShortAddress(void) const + { + return +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.GetAlternateShortAddress(); +#else + mAlternateShortAddress; +#endif + } + + /** + * Sets the alternate MAC short address. + * + * @param[in] aShortAddress The alternate short address. Use `kShortAddrInvalid` to clear it. + */ + void SetAlternateShortAddress(ShortAddress aShortAddress) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.SetAlternateShortAddress(aShortAddress); +#else + mAlternateShortAddress = aShortAddress; +#endif + } + /** * Gets the MAC Extended Address. * @@ -677,6 +706,7 @@ class Links : public InstanceLocator #if !OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE ShortAddress mShortAddress; + ShortAddress mAlternateShortAddress; ExtAddress mExtAddress; #endif }; diff --git a/src/core/mac/mac_types.hpp b/src/core/mac/mac_types.hpp index d33c4ee394b..7b504ad5ec6 100644 --- a/src/core/mac/mac_types.hpp +++ b/src/core/mac/mac_types.hpp @@ -70,8 +70,8 @@ constexpr PanId kPanIdBroadcast = 0xffff; ///< Broadcast PAN ID. */ typedef otShortAddress ShortAddress; -constexpr ShortAddress kShortAddrBroadcast = 0xffff; ///< Broadcast Short Address. -constexpr ShortAddress kShortAddrInvalid = 0xfffe; ///< Invalid Short Address. +constexpr ShortAddress kShortAddrBroadcast = OT_RADIO_BROADCAST_SHORT_ADDR; ///< Broadcast Short Address. +constexpr ShortAddress kShortAddrInvalid = OT_RADIO_INVALID_SHORT_ADDR; ///< Invalid Short Address. /** * Generates a random IEEE 802.15.4 PAN ID. diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 0d1a6557097..fbe243d75ef 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -68,10 +68,11 @@ SubMac::SubMac(Instance &aInstance) void SubMac::Init(void) { - mState = kStateDisabled; - mCsmaBackoffs = 0; - mTransmitRetries = 0; - mShortAddress = kShortAddrInvalid; + mState = kStateDisabled; + mCsmaBackoffs = 0; + mTransmitRetries = 0; + mShortAddress = kShortAddrInvalid; + mAlternateShortAddress = kShortAddrInvalid; mExtAddress.Clear(); mRxOnWhenIdle = true; mEnergyScanMaxRssi = Radio::kInvalidRssi; @@ -165,6 +166,18 @@ void SubMac::SetShortAddress(ShortAddress aShortAddress) LogDebg("RadioShortAddress: 0x%04x", mShortAddress); } +void SubMac::SetAlternateShortAddress(ShortAddress aShortAddress) +{ + VerifyOrExit(mAlternateShortAddress != aShortAddress); + + mAlternateShortAddress = aShortAddress; + Get().SetAlternateShortAddress(mAlternateShortAddress); + LogDebg("RadioAlternateShortAddress: 0x%04x", mAlternateShortAddress); + +exit: + return; +} + void SubMac::SetExtAddress(const ExtAddress &aExtAddress) { ExtAddress address; diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 45448801630..4bef9381881 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -232,6 +232,20 @@ class SubMac : public InstanceLocator, private NonCopyable */ void SetShortAddress(ShortAddress aShortAddress); + /** + * Gets the alternate short address. + * + * @returns The alternate short address, or `kShortAddrInvalid` if there is no alternate address. + */ + ShortAddress GetAlternateShortAddress(void) const { return mAlternateShortAddress; } + + /** + * Sets the alternate short address. + * + * @param[in] aShortAddress The short address. Use `kShortAddrInvalid` to clear it. + */ + void SetAlternateShortAddress(ShortAddress aShortAddress); + /** * Gets the extended address. * @@ -627,6 +641,7 @@ class SubMac : public InstanceLocator, private NonCopyable uint8_t mCsmaBackoffs; uint8_t mTransmitRetries; ShortAddress mShortAddress; + ShortAddress mAlternateShortAddress; ExtAddress mExtAddress; bool mRxOnWhenIdle : 1; #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index 5cd35b42750..8bf6993934e 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -330,6 +330,13 @@ class Radio : public InstanceLocator, private NonCopyable */ void SetShortAddress(Mac::ShortAddress aShortAddress); + /** + * Set the altrnate short address. + * + * @param[in] aShortAddress The alternate short address. + */ + void SetAlternateShortAddress(Mac::ShortAddress aShortAddress); + /** * Sets MAC key and key ID. * @@ -840,6 +847,11 @@ inline int8_t Radio::GetReceiveSensitivity(void) const { return otPlatRadioGetRe inline void Radio::SetPanId(Mac::PanId aPanId) { otPlatRadioSetPanId(GetInstancePtr(), aPanId); } +inline void Radio::SetAlternateShortAddress(Mac::ShortAddress aShortAddress) +{ + otPlatRadioSetAlternateShortAddress(GetInstancePtr(), aShortAddress); +} + inline void Radio::SetMacKey(uint8_t aKeyIdMode, uint8_t aKeyId, const Mac::KeyMaterial &aPrevKey, @@ -1003,6 +1015,8 @@ inline void Radio::SetExtendedAddress(const Mac::ExtAddress &) {} inline void Radio::SetShortAddress(Mac::ShortAddress) {} +inline void Radio::SetAlternateShortAddress(Mac::ShortAddress) {} + inline void Radio::SetMacKey(uint8_t, uint8_t, const Mac::KeyMaterial &, diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp index e3fd35d9d76..1db6af755c9 100644 --- a/src/core/radio/radio_platform.cpp +++ b/src/core/radio/radio_platform.cpp @@ -183,6 +183,12 @@ extern "C" void otPlatDiagRadioTransmitDone(otInstance *, otRadioFrame *, otErro //--------------------------------------------------------------------------------------------------------------------- // Default/weak implementation of radio platform APIs +extern "C" OT_TOOL_WEAK void otPlatRadioSetAlternateShortAddress(otInstance *aInstance, otShortAddress aShortAddress) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aShortAddress); +} + extern "C" OT_TOOL_WEAK uint32_t otPlatRadioGetSupportedChannelMask(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index dd8b035110b..76bf74cd3ce 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -693,6 +693,7 @@ void Mle::SetStateDetached(void) Get().SetRxOnWhenIdle(true); Get().SetBeaconEnabled(false); #if OPENTHREAD_FTD + Get().ClearAlternateRloc16(); Get().HandleDetachStart(); #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE @@ -816,6 +817,13 @@ Error Mle::SetDeviceMode(DeviceMode aDeviceMode) IgnoreError(Store()); +#if OPENTHREAD_FTD + if (!aDeviceMode.IsFullThreadDevice()) + { + Get().ClearAlternateRloc16(); + } +#endif + if (IsAttached()) { bool shouldReattach = false; @@ -953,6 +961,12 @@ void Mle::SetRloc16(uint16_t aRloc16) Get().AddUnicastAddress(mMeshLocalRloc); #if OPENTHREAD_FTD Get().RestartAddressQueries(); +#endif + } + else + { +#if OPENTHREAD_FTD + Get().ClearAlternateRloc16(); #endif } } diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 930db620525..1fd2ae30894 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -56,6 +56,7 @@ MleRouter::MleRouter(Instance &aInstance) , mPreviousPartitionRouterIdSequence(0) , mPreviousPartitionIdTimeout(0) , mChildRouterLinks(kChildRouterLinks) + , mAlternateRloc16Timeout(0) #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE , mMaxChildIpAddresses(0) #endif @@ -88,6 +89,30 @@ MleRouter::MleRouter(Instance &aInstance) #endif } +void MleRouter::SetAlternateRloc16(uint16_t aRloc16) +{ + VerifyOrExit(aRloc16 != Mac::kShortAddrInvalid); + + LogInfo("Setting alternate RLOC16 0x%04x", aRloc16); + + Get().SetAlternateShortAddress(aRloc16); + mAlternateRloc16Timeout = kAlternateRloc16Timeout; + +exit: + return; +} + +void MleRouter::ClearAlternateRloc16(void) +{ + VerifyOrExit(Get().GetAlternateShortAddress() != Mac::kShortAddrInvalid); + + LogInfo("Clearing alternate RLOC16"); + Get().SetAlternateShortAddress(Mac::kShortAddrInvalid); + +exit: + mAlternateRloc16Timeout = 0; +} + void MleRouter::HandlePartitionChange(void) { mPreviousPartitionId = mLeaderData.GetPartitionId(); @@ -1474,6 +1499,16 @@ void MleRouter::HandleTimeTick(void) mPreviousPartitionIdTimeout--; } + if (mAlternateRloc16Timeout > 0) + { + mAlternateRloc16Timeout--; + + if (mAlternateRloc16Timeout == 0) + { + ClearAlternateRloc16(); + } + } + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Role transitions @@ -3278,6 +3313,8 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Message *aMessage, SuccessOrExit(Tlv::FindTlv(*aMessage, routerMaskTlv)); VerifyOrExit(routerMaskTlv.IsValid()); + SetAlternateRloc16(GetRloc16()); + SetRouterId(routerId); SetStateRouter(Rloc16FromRouterId(mRouterId)); diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index e66e968da6c..772c9863c4e 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -515,6 +515,7 @@ class MleRouter : public Mle static constexpr uint16_t kUnsolicitedDataResponseJitter = 500; // Max delay for unsol Data Response (in msec). static constexpr uint8_t kLeaderDowngradeExtraDelay = 10; // Extra delay to downgrade leader (in sec). static constexpr uint8_t kDefaultLeaderWeight = 64; + static constexpr uint8_t kAlternateRloc16Timeout = 8; // Time to use alternate RLOC16 (in sec). // Threshold to accept a router upgrade request with reason // `kBorderRouterRequest` (number of BRs acting as router in @@ -587,6 +588,8 @@ class MleRouter : public Mle //------------------------------------------------------------------------------------------------------------------ // Methods + void SetAlternateRloc16(uint16_t aRloc16); + void ClearAlternateRloc16(void); void HandleDetachStart(void); void HandleChildStart(AttachMode aMode); void HandleSecurityPolicyChanged(void); @@ -680,6 +683,7 @@ class MleRouter : public Mle uint8_t mPreviousPartitionRouterIdSequence; uint8_t mPreviousPartitionIdTimeout; uint8_t mChildRouterLinks; + uint8_t mAlternateRloc16Timeout; #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE uint8_t mMaxChildIpAddresses; #endif diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 3299208a306..82a82922629 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -846,6 +846,17 @@ otError RadioSpinel::SetShortAddress(uint16_t aAddress) return error; } +otError RadioSpinel::SetAlternateShortAddress(uint16_t aAddress) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(sRadioCaps & OT_RADIO_CAPS_ALT_SHORT_ADDR); + error = Set(SPINEL_PROP_MAC_15_4_ALT_SADDR, SPINEL_DATATYPE_UINT16_S, aAddress); + +exit: + return error; +} + #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE otError RadioSpinel::ReadMacKey(const otMacKeyMaterial &aKeyMaterial, otMacKey &aKey) diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 482f01d9d8f..a83db5f037a 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -223,6 +223,17 @@ class RadioSpinel : private Logger */ otError SetShortAddress(uint16_t aAddress); + /** + * Sets the alternate short address. + * + * @param[in] aShortAddress The alternate short address. + * + * @retval OT_ERROR_NONE Succeeded. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + */ + otError SetAlternateShortAddress(uint16_t aAddress); + /** * Gets the factory-assigned IEEE EUI-64 for this transceiver. * diff --git a/src/lib/spinel/spinel.c b/src/lib/spinel/spinel.c index 60e4f0c5977..ae6fc63229f 100644 --- a/src/lib/spinel/spinel.c +++ b/src/lib/spinel/spinel.c @@ -1263,6 +1263,7 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) {SPINEL_PROP_MAC_ENERGY_SCAN_RESULT, "MAC_ENERGY_SCAN_RESULT"}, {SPINEL_PROP_MAC_DATA_POLL_PERIOD, "MAC_DATA_POLL_PERIOD"}, {SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE, "MAC_RX_ON_WHEN_IDLE_MODE"}, + {SPINEL_PROP_MAC_15_4_ALT_SADDR, "SPINEL_PROP_MAC_15_4_ALT_SADDR"}, {SPINEL_PROP_MAC_ALLOWLIST, "MAC_ALLOWLIST"}, {SPINEL_PROP_MAC_ALLOWLIST_ENABLED, "MAC_ALLOWLIST_ENABLED"}, {SPINEL_PROP_MAC_EXTENDED_ADDR, "MAC_EXTENDED_ADDR"}, diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index 4f914c61a71..526e57037db 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -419,7 +419,7 @@ * * Please see section "Spinel definition compatibility guideline" for more details. */ -#define SPINEL_RCP_API_VERSION 10 +#define SPINEL_RCP_API_VERSION 11 /** * @def SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION @@ -2093,6 +2093,14 @@ enum */ SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE = SPINEL_PROP_MAC__BEGIN + 11, + /// MAC Alternate Short Address + /** Format: `S` + * + * The 802.15.4 alternate short address. + * + */ + SPINEL_PROP_MAC_15_4_ALT_SADDR = SPINEL_PROP_MAC__BEGIN + 12, + SPINEL_PROP_MAC__END = 0x40, SPINEL_PROP_MAC_EXT__BEGIN = 0x1300, diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index a1884e3c28c..5b137e4d77f 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -448,6 +448,9 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_DATA_POLL_PERIOD), #endif OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE), +#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_15_4_ALT_SADDR), +#endif #if OPENTHREAD_MTD || OPENTHREAD_FTD OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_IF_UP), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_STACK_UP), diff --git a/src/ncp/ncp_base_radio.cpp b/src/ncp/ncp_base_radio.cpp index 7da914be72c..0b701d06d83 100644 --- a/src/ncp/ncp_base_radio.cpp +++ b/src/ncp/ncp_base_radio.cpp @@ -407,6 +407,19 @@ template <> otError NcpBase::HandlePropertySet(void) return error; } +template <> otError NcpBase::HandlePropertySet(void) +{ + uint16_t shortAddress; + otError error = OT_ERROR_NONE; + + SuccessOrExit(error = mDecoder.ReadUint16(shortAddress)); + + error = otLinkRawSetAlternateShortAddress(mInstance, shortAddress); + +exit: + return error; +} + #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE template <> otError NcpBase::HandlePropertySet(void) { diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index 210f6a66c3c..a0a6597ae31 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -256,6 +256,12 @@ void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress) SuccessOrDie(GetRadioSpinel().SetShortAddress(aAddress)); } +void otPlatRadioSetAlternateShortAddress(otInstance *aInstance, uint16_t aAddress) +{ + OT_UNUSED_VARIABLE(aInstance); + SuccessOrDie(GetRadioSpinel().SetAlternateShortAddress(aAddress)); +} + void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) { OT_UNUSED_VARIABLE(aInstance); diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index 8236ab3b5be..d86d4655003 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -312,6 +312,9 @@ def thread_stop(self): def get_rloc16(self): return self._cli_single_output('rloc16') + def get_mac_alt_short_addr(self): + return self._cli_single_output('mac altshortaddr') + def get_ip_addrs(self, verbose=None): return self.cli('ipaddr', verbose) diff --git a/tests/toranj/cli/test-033-alt-short-addr-role-transition.py b/tests/toranj/cli/test-033-alt-short-addr-role-transition.py new file mode 100755 index 00000000000..27406cbd9ff --- /dev/null +++ b/tests/toranj/cli/test-033-alt-short-addr-role-transition.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024, 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: +# +# Validate the use alternate short address after role transition from child to router. +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +leader = cli.Node() +node = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Test implementation + +INVALID_SHORT_ADDR = '0xfffe' +ALT_SHORT_ADDR_TIMEOUT = 10 + +verify(leader.get_mac_alt_short_addr() == INVALID_SHORT_ADDR) + +# Form topology + +leader.form('alt-shrt-addr') +node.join(leader, cli.JOIN_TYPE_REED) + +verify(leader.get_state() == 'leader') +verify(node.get_state() == 'child') + +verify(len(leader.get_child_table()) == 1) + +# Check the short address and alternate short address + +node_rloc16_as_child = '0x' + node.get_rloc16() + +verify(node.get_mac_alt_short_addr() == INVALID_SHORT_ADDR) + +# Allow `node` to transition from child to router role + +node.set_router_selection_jitter(1) +node.set_router_eligible('enable') + + +def check_node_become_router(): + verify(node.get_state() == 'router') + + +verify_within(check_node_become_router, 10) + +# Make sure the old short address is now being used as +# the alternate short address. + +node_rloc16_as_router = '0x' + node.get_rloc16() +verify(node_rloc16_as_router != node_rloc16_as_child) +verify(node.get_mac_alt_short_addr() == node_rloc16_as_child) + +# Make sure the old short address is removed after the +# timeout + +time.sleep(ALT_SHORT_ADDR_TIMEOUT / speedup) + +verify(node.get_mac_alt_short_addr() == INVALID_SHORT_ADDR) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh index 5451c51bffc..ee1c74b4e7e 100755 --- a/tests/toranj/start.sh +++ b/tests/toranj/start.sh @@ -197,6 +197,7 @@ if [ "$TORANJ_CLI" = 1 ]; then run cli/test-030-anycast-forwarding.py run cli/test-031-service-aloc-route-lookup.py run cli/test-032-leader-take-over.py + run cli/test-033-alt-short-addr-role-transition.py run cli/test-035-context-id-change-addr-reg.py run cli/test-400-srp-client-server.py run cli/test-401-srp-server-address-cache-snoop.py