From c9665886fa62a540d9f8875f8885c3bd09c5438f Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 12 Jan 2026 12:13:42 -0800 Subject: [PATCH] [joiner] add build config for for experimental Joiner Adv feature (#12276) This change introduces a new build-time configuration flag, `OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE`, to control the inclusion of the experimental Joiner Advertisement feature. This behavior was first added in PR #5299. It allows optional inclusion of a newly proposed Joiner Adv TLV in an MLE Discovery Scan Request message. This is an experimental feature and is not part of the Thread specification. OpenThread's implementation is limited and partial: it only provides the mechanism for a Joiner to include a new Joiner Adv TLV in its emitted Discovery Scan Request messages, but does not include the corresponding logic for the receiver of Scan Request to read or parse this TLV. The new flag conditionally compiles this behavior. It allows for the entire feature to be compiled out, reducing code size and memory usage for devices that do not require this experimental functionality. Additionally, this change clarifies the experimental status of the feature in the documentation. By default, this is now disabled. It is enabled under posix build for `toranj` tests so that it is covered in GitHub CI. --- include/openthread/dataset.h | 2 +- include/openthread/instance.h | 2 +- include/openthread/thread.h | 17 ++++++--- src/core/api/thread_api.cpp | 2 ++ src/core/config/joiner.h | 12 +++++++ src/core/meshcop/meshcop_tlvs.hpp | 4 +++ src/core/thread/discover_scanner.cpp | 36 +++++++++++-------- src/core/thread/discover_scanner.hpp | 18 +++++++--- .../openthread-core-toranj-config-posix.h | 2 ++ 9 files changed, 68 insertions(+), 27 deletions(-) diff --git a/include/openthread/dataset.h b/include/openthread/dataset.h index 6dbd9e68b..8bb7e574b 100644 --- a/include/openthread/dataset.h +++ b/include/openthread/dataset.h @@ -306,7 +306,7 @@ typedef enum otMeshcopTlvType OT_MESHCOP_TLV_WAKEUP_CHANNEL = 74, ///< meshcop Wake-up Channel TLV OT_MESHCOP_TLV_DISCOVERYREQUEST = 128, ///< meshcop Discovery Request TLV OT_MESHCOP_TLV_DISCOVERYRESPONSE = 129, ///< meshcop Discovery Response TLV - OT_MESHCOP_TLV_JOINERADVERTISEMENT = 241, ///< meshcop Joiner Advertisement TLV + OT_MESHCOP_TLV_JOINERADVERTISEMENT = 241, ///< meshcop Joiner Advertisement TLV (experimental) } otMeshcopTlvType; /** diff --git a/include/openthread/instance.h b/include/openthread/instance.h index b52977f01..0043a27d8 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 (569) +#define OPENTHREAD_API_VERSION (570) /** * @addtogroup api-instance diff --git a/include/openthread/thread.h b/include/openthread/thread.h index 5cbbad805..ba6f6e909 100644 --- a/include/openthread/thread.h +++ b/include/openthread/thread.h @@ -303,17 +303,24 @@ otError otThreadDiscover(otInstance *aInstance, bool otThreadIsDiscoverInProgress(otInstance *aInstance); /** - * Sets the Thread Joiner Advertisement when discovering Thread network. + * Sets the Thread Joiner Advertisement used when discovering a Thread network. * - * Thread Joiner Advertisement is used to allow a Joiner to advertise its own application-specific information - * (such as Vendor ID, Product ID, Discriminator, etc.) via a newly-proposed Joiner Advertisement TLV, - * and to make this information available to Commissioners or Commissioner Candidates without human interaction. + * Requires `OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE`. + * + * @note This is an experimental feature and is not part of the Thread specification. OpenThread's implementation is + * partial: it provides the mechanism for a Joiner to include a new Joiner Adv TLV in its emitted Discovery Scan + * Request messages, but does not include the corresponding logic for the receiver of Scan Request to read or + * parse this TLV. + * + * A Joiner can use this to advertise its own application-specific information (such as Vendor ID, Product ID, + * Discriminator, etc.) using a newly proposed Joiner Advertisement TLV (`OT_MESHCOP_TLV_JOINERADVERTISEMENT`). + * This TLV is appended as a sub-TLV within the MLE Discovery TLV in an MLE Discovery Scan Request message. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aOui The Vendor IEEE OUI value that will be included in the Joiner Advertisement. Only the * least significant 3 bytes will be used, and the most significant byte will be ignored. * @param[in] aAdvData A pointer to the AdvData that will be included in the Joiner Advertisement. - * @param[in] aAdvDataLength The length of AdvData in bytes. + * @param[in] aAdvDataLength The length of AdvData in bytes. Must not exceed `OT_JOINER_ADVDATA_MAX_LENGTH`. * * @retval OT_ERROR_NONE Successfully set Joiner Advertisement. * @retval OT_ERROR_INVALID_ARGS Invalid AdvData. diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp index 568b7e390..20052eae3 100644 --- a/src/core/api/thread_api.cpp +++ b/src/core/api/thread_api.cpp @@ -419,6 +419,7 @@ otError otThreadDiscover(otInstance *aInstance, /* aFilterIndexes (use hash of factory EUI64) */ nullptr, aCallback, aCallbackContext); } +#if OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE otError otThreadSetJoinerAdvertisement(otInstance *aInstance, uint32_t aOui, const uint8_t *aAdvData, @@ -426,6 +427,7 @@ otError otThreadSetJoinerAdvertisement(otInstance *aInstance, { return AsCoreType(aInstance).Get().SetJoinerAdvertisement(aOui, aAdvData, aAdvDataLength); } +#endif bool otThreadIsDiscoverInProgress(otInstance *aInstance) { diff --git a/src/core/config/joiner.h b/src/core/config/joiner.h index b428d2930..3ce4d4f08 100644 --- a/src/core/config/joiner.h +++ b/src/core/config/joiner.h @@ -61,6 +61,18 @@ #define OPENTHREAD_CONFIG_JOINER_MAX_CANDIDATES 2 #endif +/** + * @def OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE + * + * Define as 1 to enable including Joiner Advertisement TLV during discovery scan by the Joiner. + * + * This is an experimental feature. It is not part of the Thread specification. See `otThreadSetJoinerAdvertisement` + * for more details. + */ +#ifndef OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE +#define OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE 0 +#endif + /** * @} */ diff --git a/src/core/meshcop/meshcop_tlvs.hpp b/src/core/meshcop/meshcop_tlvs.hpp index d422a8c77..edff0b87c 100644 --- a/src/core/meshcop/meshcop_tlvs.hpp +++ b/src/core/meshcop/meshcop_tlvs.hpp @@ -1120,6 +1120,8 @@ private: uint8_t mReserved; } OT_TOOL_PACKED_END; +#if OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE + /** * Implements Joiner Advertisement TLV generation and parsing. */ @@ -1193,6 +1195,8 @@ private: uint8_t mAdvData[kAdvDataMaxLength]; } OT_TOOL_PACKED_END; +#endif // OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE + } // namespace MeshCoP } // namespace ot diff --git a/src/core/thread/discover_scanner.cpp b/src/core/thread/discover_scanner.cpp index 5fe360f09..1c21ec089 100644 --- a/src/core/thread/discover_scanner.cpp +++ b/src/core/thread/discover_scanner.cpp @@ -45,9 +45,11 @@ DiscoverScanner::DiscoverScanner(Instance &aInstance) , mFilterIndexes() , mState(kStateIdle) , mScanChannel(0) - , mAdvDataLength(0) , mEnableFiltering(false) , mShouldRestorePanId(false) +#if OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE + , mAdvDataLength(0) +#endif { } @@ -59,12 +61,11 @@ Error DiscoverScanner::Discover(const Mac::ChannelMask &aScanChannels, Handler aCallback, void *aContext) { - Error error = kErrorNone; - Mle::TxMessage *message = nullptr; - Tlv::Bookmark tlvBookmark; - Ip6::Address destination; - MeshCoP::DiscoveryRequestTlv discoveryRequest; - MeshCoP::JoinerAdvertisementTlv joinerAdvertisement; + Error error = kErrorNone; + Mle::TxMessage *message = nullptr; + Tlv::Bookmark tlvBookmark; + Ip6::Address destination; + MeshCoP::DiscoveryRequestTlv discoveryRequest; VerifyOrExit(Get().IsUp(), error = kErrorInvalidState); @@ -109,13 +110,17 @@ Error DiscoverScanner::Discover(const Mac::ChannelMask &aScanChannels, discoveryRequest.SetJoiner(aJoiner); SuccessOrExit(error = discoveryRequest.AppendTo(*message)); +#if OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE if (mAdvDataLength != 0) { - joinerAdvertisement.Init(); - joinerAdvertisement.SetOui(mOui); - joinerAdvertisement.SetAdvData(mAdvData, mAdvDataLength); - SuccessOrExit(error = joinerAdvertisement.AppendTo(*message)); + MeshCoP::JoinerAdvertisementTlv joinerAdvTlv; + + joinerAdvTlv.Init(); + joinerAdvTlv.SetOui(mOui); + joinerAdvTlv.SetAdvData(mAdvData, mAdvDataLength); + SuccessOrExit(error = joinerAdvTlv.AppendTo(*message)); } +#endif SuccessOrExit(error = Tlv::EndTlv(*message, tlvBookmark)); @@ -153,22 +158,23 @@ exit: return error; } +#if OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE Error DiscoverScanner::SetJoinerAdvertisement(uint32_t aOui, const uint8_t *aAdvData, uint8_t aAdvDataLength) { Error error = kErrorNone; - VerifyOrExit((aAdvData != nullptr) && (aAdvDataLength != 0) && - (aAdvDataLength <= MeshCoP::JoinerAdvertisementTlv::kAdvDataMaxLength) && (aOui <= kMaxOui), - error = kErrorInvalidArgs); + VerifyOrExit(aAdvData != nullptr, error = kErrorInvalidArgs); + VerifyOrExit(IsValueInRange(aAdvDataLength, kMinAdvDataLength, kMaxAdvDataLength), error = kErrorInvalidArgs); + VerifyOrExit(aOui <= kMaxOui, error = kErrorInvalidArgs); mOui = aOui; mAdvDataLength = aAdvDataLength; - memcpy(mAdvData, aAdvData, aAdvDataLength); exit: return error; } +#endif Mac::TxFrame *DiscoverScanner::PrepareDiscoveryRequestFrame(Mac::TxFrame &aFrame) { diff --git a/src/core/thread/discover_scanner.hpp b/src/core/thread/discover_scanner.hpp index 9baf27102..43cf017e8 100644 --- a/src/core/thread/discover_scanner.hpp +++ b/src/core/thread/discover_scanner.hpp @@ -132,6 +132,7 @@ public: */ bool IsInProgress(void) const { return (mState != kStateIdle); } +#if OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE /** * Sets Joiner Advertisement. * @@ -143,8 +144,15 @@ public: * @retval kErrorInvalidArgs Invalid AdvData. */ Error SetJoinerAdvertisement(uint32_t aOui, const uint8_t *aAdvData, uint8_t aAdvDataLength); +#endif private: +#if OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE + static constexpr uint32_t kMaxOui = 0xffffff; + static constexpr uint8_t kMinAdvDataLength = 1; + static constexpr uint8_t kMaxAdvDataLength = MeshCoP::JoinerAdvertisementTlv::kAdvDataMaxLength; +#endif + enum State : uint8_t { kStateIdle, @@ -152,8 +160,6 @@ private: kStateScanDone, }; - static constexpr uint32_t kMaxOui = 0xffffff; - // Methods used by `MeshForwarder` Mac::TxFrame *PrepareDiscoveryRequestFrame(Mac::TxFrame &aFrame); void Stop(void) { HandleDiscoverComplete(); } @@ -176,12 +182,14 @@ private: FilterIndexes mFilterIndexes; Mac::ChannelMask mScanChannels; State mState; - uint32_t mOui; uint8_t mScanChannel; - uint8_t mAdvDataLength; - uint8_t mAdvData[MeshCoP::JoinerAdvertisementTlv::kAdvDataMaxLength]; bool mEnableFiltering : 1; bool mShouldRestorePanId : 1; +#if OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE + uint8_t mAdvDataLength; + uint8_t mAdvData[kMaxAdvDataLength]; + uint32_t mOui; +#endif }; } // namespace Mle diff --git a/tests/toranj/openthread-core-toranj-config-posix.h b/tests/toranj/openthread-core-toranj-config-posix.h index f66f1e725..e203aad02 100644 --- a/tests/toranj/openthread-core-toranj-config-posix.h +++ b/tests/toranj/openthread-core-toranj-config-posix.h @@ -60,6 +60,8 @@ #define OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE 1 +#define OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE 1 + #define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED #define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1