From 3fec404effd231648f018b2ecab0f7d5611c84d9 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 4 Feb 2026 15:54:54 -0800 Subject: [PATCH] [seeker] introduce new `otSeeker` APIs (#12357) This commit introduces Seeker APIs in OpenThread. An earlier commit extracted the discovery and candidate selection logic from the `Joiner` role into a new, standalone `Seeker` module. The `Seeker` is responsible for performing MLE Discover Scans to find nearby Joiner Router candidates. It prioritizes these candidates based on RSSI and steering data (indicating whether the Joiner is preferred) and manages the list of candidates for connection attempts. This separation allows the `Seeker` functionality to be utilized independently of the full `Joiner` role, enabling the development of custom joining mechanisms over Thread. A new configuration option `OPENTHREAD_CONFIG_SEEKER_ENABLE` has been added to control the presence of `otSeeker` APIs. --- doc/ot_api_doc.h | 1 + etc/cmake/options.cmake | 1 + include/openthread/BUILD.gn | 1 + include/openthread/instance.h | 2 +- include/openthread/seeker.h | 192 ++++++++++++++++++ src/core/BUILD.gn | 1 + src/core/CMakeLists.txt | 1 + src/core/api/seeker_api.cpp | 63 ++++++ src/core/config/joiner.h | 14 ++ src/core/instance/instance.cpp | 4 +- src/core/instance/instance.hpp | 8 +- src/core/meshcop/joiner.cpp | 2 +- src/core/meshcop/seeker.cpp | 26 ++- src/core/meshcop/seeker.hpp | 58 ++++-- tests/nexus/openthread-core-nexus-config.h | 1 + .../openthread-core-toranj-config-posix.h | 4 + ...openthread-core-toranj-config-simulation.h | 4 + tests/toranj/openthread-core-toranj-config.h | 2 - 18 files changed, 351 insertions(+), 34 deletions(-) create mode 100644 include/openthread/seeker.h create mode 100644 src/core/api/seeker_api.cpp diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h index 8ca1b254e..07ebf90d1 100644 --- a/doc/ot_api_doc.h +++ b/doc/ot_api_doc.h @@ -128,6 +128,7 @@ * @brief Includes functions for the Operational Dataset API. * @defgroup api-thread-router Router/Leader * @brief This module includes functions for Thread Routers and Leaders. + * @defgroup api-seeker Seeker * @defgroup api-server Server * @defgroup api-steering-data Steering Data * diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index cd6488564..49b63b498 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -252,6 +252,7 @@ ot_option(OT_PLATFORM_NETIF OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE "platform ne ot_option(OT_PLATFORM_POWER_CALIBRATION OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE "power calibration") ot_option(OT_PLATFORM_UDP OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE "platform UDP") ot_option(OT_REFERENCE_DEVICE OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE "test harness reference device") +ot_option(OT_SEEKER OPENTHREAD_CONFIG_SEEKER_ENABLE "seeker") ot_option(OT_SERVICE OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE "Network Data service") ot_option(OT_SETTINGS_RAM OPENTHREAD_SETTINGS_RAM "volatile-only storage of settings") ot_option(OT_SLAAC OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE "SLAAC address") diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn index 94ffbd4d1..bba5c3573 100644 --- a/include/openthread/BUILD.gn +++ b/include/openthread/BUILD.gn @@ -120,6 +120,7 @@ source_set("openthread") { "radio_stats.h", "random_crypto.h", "random_noncrypto.h", + "seeker.h", "server.h", "sntp.h", "srp_client.h", diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 3f8af477e..287813296 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 (576) +#define OPENTHREAD_API_VERSION (577) /** * @addtogroup api-instance diff --git a/include/openthread/seeker.h b/include/openthread/seeker.h new file mode 100644 index 000000000..d125c7b8b --- /dev/null +++ b/include/openthread/seeker.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2026, 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. + */ + +/** + * @file + * @brief + * This file implements the Thread Seeker role. + * + * The Seeker is a part of the Thread MeshCoP process. It is responsible for discovering nearby Joiner Router + * candidates, prioritizing them, and iterating through the list to select the best candidate for connection. + * It also operates as a sub-system of the `Joiner`, delegating control to the next layer to enable the + * implementation of alternative and custom joining protocols. + */ + +#ifndef OPENTHREAD_SEEKER_H_ +#define OPENTHREAD_SEEKER_H_ + +#include +#include + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-seeker + * + * @brief + * This module includes functions for the Thread Seeker role. + * + * @note + * The functions in this module require `OPENTHREAD_CONFIG_SEEKER_ENABLE`. + * + * @{ + */ + +/** + * Represents a Discover Scan result. + */ +typedef otActiveScanResult otSeekerScanResult; + +/** + * Represents a verdict returned from the `otSeekerScanEvaluator` callback when evaluating a Discover Scan result. + */ +typedef enum otSeekerVerdict +{ + OT_SEEKER_ACCEPT, ///< The scan result is acceptable. + OT_SEEKER_ACCEPT_PREFERRED, ///< The scan result is acceptable and preferred. + OT_SEEKER_IGNORE, ///< The scan result should be ignored. +} otSeekerVerdict; + +/** + * Represents the callback function type used to evaluate a scan result or report the end of a scan. + * + * @param[in] aContext A pointer to the callback context. + * @param[in] aResult A pointer to the scan result to evaluate, or `NULL` to indicate scan completion. + * + * @returns The verdict for the scan result (Accept, AcceptPreferred, or Ignore). + * If @p aResult is `NULL` (scan complete), the return value is ignored. + */ +typedef otSeekerVerdict (*otSeekerScanEvaluator)(void *aContext, const otSeekerScanResult *aResult); + +/** + * Starts the Seeker operation. + * + * The Seeker generates and sets a random MAC address for anonymity, then initiates an MLE Discover Scan to find + * Joiner Router candidates. + * + * Found candidates are reported to the @p aScanEvaluator callback. Based on the returned `otSeekerVerdict`, the Seeker + * maintains a prioritized list of candidates for future connection attempts. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aScanEvaluator The callback function to evaluate scan results. + * @param[in] aContext An arbitrary context pointer to use with @p aScanEvaluator. + * + * @retval OT_ERROR_NONE Successfully started the Seeker. + * @retval OT_ERROR_BUSY The Seeker is already active (scanning or connecting). + * @retval OT_ERROR_INVALID_STATE The IPv6 interface is not enabled, or MLE is enabled. + */ +otError otSeekerStart(otInstance *aInstance, otSeekerScanEvaluator aScanEvaluator, void *aContext); + +/** + * Stops the Seeker operation. + * + * This function stops any ongoing discovery or connection process, unregisters the unsecure Joiner/Seeker UDP port, and + * clears internal state. If the Seeker is already stopped, this method has no effect. + * + * If the join process succeeds after a call to `otSeekerSetUpNextConnection()`, the caller MUST call this method to + * stop the Seeker and, importantly, unregister the Seeker UDP port as an unsecure port. + * + * @note If `otSeekerSetUpNextConnection()` returns `OT_ERROR_NOT_FOUND` (indicating the candidate list is exhausted), + * the Seeker stops automatically. + * + * @param[in] aInstance The OpenThread instance. + */ +void otSeekerStop(otInstance *aInstance); + +/** + * Indicates whether or not the Seeker is running. + * + * @param[in] aInstance The OpenThread instance. + * + * @retval TRUE The Seeker is active and running. + * @retval FALSE The Seeker is stopped. + */ +bool otSeekerIsRunning(otInstance *aInstance); + +/** + * Gets the Seeker UDP port (unsecure port). + * + * @param[in] aInstance The OpenThread instance. + * + * @returns The Seeker UDP port. + */ +uint16_t otSeekerGetUdpPort(otInstance *aInstance); + +/** + * Sets the Seeker UDP port (unsecure port). + * + * This UDP port can only be changed when the Seeker is not running. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aUdpPort The Seeker UDP port. + * + * @retval OT_ERROR_NONE Successfully set the Joiner/Seeker UDP port. + * @retval OT_ERROR_INVALID_STATE The Seeker is already running. + */ +otError otSeekerSetUdpPort(otInstance *aInstance, uint16_t aUdpPort); + +/** + * Selects the next best candidate and prepares the connection. + * + * This function MUST be called after the discovery scan has completed (indicated by the `otSeekerScanEvaluator` + * callback receiving `NULL`). Calling it before scan completion will result in `OT_ERROR_INVALID_STATE`. + * + * This function iterates through the discovered Joiner Router candidates in order of priority. For the selected + * candidate, it configures the radio channel and PAN ID, and populates @p aSockAddr with the candidate's address. + * It also registers the Seeker UDP port `otSeekerGetUdpPort()` as an unsecure port to allow a UDP connection to the + * candidate. The next layer code can start sending UDP messages to the given `otSockAddr` ensuring to use the unsecure + * Seeker UDP port as the source port. These messages are then forwarded by the Joiner Router onward to a + * Commissioner/Enroller connected via a Border Agent/Admitter. + * + * If the list is exhausted, this function returns `OT_ERROR_NOT_FOUND` and automatically calls `otSeekerStop()`, + * which removes the unsecure port and clears internal state. + * + * @param[out] aSockAddr A pointer to a socket address to output the candidate's address. + * + * @retval OT_ERROR_NONE Successfully set up the connection to the next candidate. + * @retval OT_ERROR_NOT_FOUND No more candidates are available (list exhausted). + * @retval OT_ERROR_INVALID_STATE The Seeker is not in a valid state (e.g. scan not yet completed). + */ +otError otSeekerSetUpNextConnection(otInstance *aInstance, otSockAddr *aSockAddr); + +/** + * @} + */ + +#ifdef __cplusplus +} // end of extern "C" +#endif + +#endif // OPENTHREAD_SEEKER_H_ diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 36cd2c487..ca3039ce9 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -370,6 +370,7 @@ openthread_core_files = [ "api/radio_stats_api.cpp", "api/random_crypto_api.cpp", "api/random_noncrypto_api.cpp", + "api/seeker_api.cpp", "api/server_api.cpp", "api/sntp_api.cpp", "api/srp_client_api.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c53798fc8..6f49a728d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -80,6 +80,7 @@ set(COMMON_SOURCES api/radio_stats_api.cpp api/random_crypto_api.cpp api/random_noncrypto_api.cpp + api/seeker_api.cpp api/server_api.cpp api/sntp_api.cpp api/srp_client_api.cpp diff --git a/src/core/api/seeker_api.cpp b/src/core/api/seeker_api.cpp new file mode 100644 index 000000000..24d76bedd --- /dev/null +++ b/src/core/api/seeker_api.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026, 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. + */ + +/** + * @file + * This file implements the OpenThread Seeker API. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_SEEKER_ENABLE + +#include "instance/instance.hpp" + +using namespace ot; + +otError otSeekerStart(otInstance *aInstance, otSeekerScanEvaluator aScanEvaluator, void *aContext) +{ + return AsCoreType(aInstance).Get().Start(aScanEvaluator, aContext); +} + +void otSeekerStop(otInstance *aInstance) { AsCoreType(aInstance).Get().Stop(); } + +bool otSeekerIsRunning(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsRunning(); } + +uint16_t otSeekerGetUdpPort(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetUdpPort(); } + +otError otSeekerSetUdpPort(otInstance *aInstance, uint16_t aUdpPort) +{ + return AsCoreType(aInstance).Get().SetUdpPort(aUdpPort); +} + +otError otSeekerSetUpNextConnection(otInstance *aInstance, otSockAddr *aSockAddr) +{ + return AsCoreType(aInstance).Get().SetUpNextConnection(AsCoreType(aSockAddr)); +} + +#endif // OPENTHREAD_CONFIG_SEEKER_ENABLE diff --git a/src/core/config/joiner.h b/src/core/config/joiner.h index 3ce4d4f08..55d19f119 100644 --- a/src/core/config/joiner.h +++ b/src/core/config/joiner.h @@ -73,6 +73,20 @@ #define OPENTHREAD_CONFIG_JOINER_ADV_EXPERIMENTAL_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_SEEKER_ENABLE + * + * Define as 1 to enable Seeker functionality and `otSeeker` APIs. + * + * The Seeker is a part of the Thread MeshCoP process. It is responsible for discovering nearby Joiner Router + * candidates, prioritizing them, and iterating through the list to select the best candidate for connection. + * It also operates as a sub-system of the `Joiner`, delegating control to the next layer to enable the + * implementation of alternative and custom joining protocols. + */ +#ifndef OPENTHREAD_CONFIG_SEEKER_ENABLE +#define OPENTHREAD_CONFIG_SEEKER_ENABLE OPENTHREAD_CONFIG_JOINER_ENABLE +#endif + /** * @} */ diff --git a/src/core/instance/instance.cpp b/src/core/instance/instance.cpp index bd5678864..3f4ce2668 100644 --- a/src/core/instance/instance.cpp +++ b/src/core/instance/instance.cpp @@ -187,8 +187,10 @@ Instance::Instance(void) #if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE , mTmfSecureAgent(*this) #endif -#if OPENTHREAD_CONFIG_JOINER_ENABLE +#if OPENTHREAD_CONFIG_SEEKER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE , mSeeker(*this) +#endif +#if OPENTHREAD_CONFIG_JOINER_ENABLE , mJoiner(*this) #endif #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE diff --git a/src/core/instance/instance.hpp b/src/core/instance/instance.hpp index ffc96e94e..285bda172 100644 --- a/src/core/instance/instance.hpp +++ b/src/core/instance/instance.hpp @@ -618,8 +618,10 @@ private: Tmf::SecureAgent mTmfSecureAgent; #endif -#if OPENTHREAD_CONFIG_JOINER_ENABLE +#if OPENTHREAD_CONFIG_SEEKER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE MeshCoP::Seeker mSeeker; +#endif +#if OPENTHREAD_CONFIG_JOINER_ENABLE MeshCoP::Joiner mJoiner; #endif @@ -959,9 +961,11 @@ template <> inline PanIdQueryClient &Instance::Get(void) { return mCommissioner. template <> inline Dnssd &Instance::Get(void) { return mDnssd; } #endif -#if OPENTHREAD_CONFIG_JOINER_ENABLE +#if OPENTHREAD_CONFIG_SEEKER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE template <> inline MeshCoP::Seeker &Instance::Get(void) { return mSeeker; } +#endif +#if OPENTHREAD_CONFIG_JOINER_ENABLE template <> inline MeshCoP::Joiner &Instance::Get(void) { return mJoiner; } #endif diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp index 9d48e546c..cfb067615 100644 --- a/src/core/meshcop/joiner.cpp +++ b/src/core/meshcop/joiner.cpp @@ -132,7 +132,7 @@ Error Joiner::Start(const char *aPskd, // (free allocated message, stop seeker, close agent, etc). shouldCleanup = true; - SuccessOrExit(error = Get().Bind(Seeker::kUdpPort)); + SuccessOrExit(error = Get().Bind(Get().GetUdpPort())); Get().SetConnectCallback(HandleSecureCoapClientConnect, this); Get().SetPsk(joinerPskd); diff --git a/src/core/meshcop/seeker.cpp b/src/core/meshcop/seeker.cpp index 333d1facc..d384f6f0f 100644 --- a/src/core/meshcop/seeker.cpp +++ b/src/core/meshcop/seeker.cpp @@ -33,7 +33,7 @@ #include "seeker.hpp" -#if OPENTHREAD_CONFIG_JOINER_ENABLE +#if OPENTHREAD_CONFIG_SEEKER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE #include "instance/instance.hpp" @@ -45,6 +45,7 @@ RegisterLogModule("Seeker"); Seeker::Seeker(Instance &aInstance) : InstanceLocator(aInstance) , mState(kStateStopped) + , mUdpPort(kDefaultUdpPort) , mCandidateIndex(0) { } @@ -87,13 +88,28 @@ void Seeker::Stop(void) case kStateDiscoverDone: break; case kStateConnecting: - IgnoreError(Get().RemoveUnsecurePort(kUdpPort)); + IgnoreError(Get().RemoveUnsecurePort(mUdpPort)); break; } SetState(kStateStopped); } +Error Seeker::SetUdpPort(uint16_t aUdpPort) +{ + Error error = kErrorNone; + + VerifyOrExit(!IsRunning(), error = kErrorInvalidState); + + VerifyOrExit(aUdpPort != mUdpPort); + LogInfo("UDP port changed: %u -> %u", mUdpPort, aUdpPort); + + mUdpPort = aUdpPort; + +exit: + return error; +} + void Seeker::HandleDiscoverResult(ScanResult *aResult, void *aContext) { static_cast(aContext)->HandleDiscoverResult(aResult); @@ -228,9 +244,9 @@ Error Seeker::SetUpNextConnection(Ip6::SockAddr &aSockAddr) Get().SetPanId(candidate->mPanId); SuccessOrExit(error = Get().SetPanChannel(candidate->mChannel)); - if (!Get().IsUnsecurePort(kUdpPort)) + if (!Get().IsUnsecurePort(mUdpPort)) { - SuccessOrExit(error = Get().AddUnsecurePort(kUdpPort)); + SuccessOrExit(error = Get().AddUnsecurePort(mUdpPort)); } SetState(kStateConnecting); @@ -246,4 +262,4 @@ exit: } // namespace MeshCoP } // namespace ot -#endif // OPENTHREAD_CONFIG_JOINER_ENABLE +#endif // OPENTHREAD_CONFIG_SEEKER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE diff --git a/src/core/meshcop/seeker.hpp b/src/core/meshcop/seeker.hpp index a0b5aabf4..5ef1b0bda 100644 --- a/src/core/meshcop/seeker.hpp +++ b/src/core/meshcop/seeker.hpp @@ -40,8 +40,11 @@ #include "openthread-core-config.h" -#if OPENTHREAD_CONFIG_JOINER_ENABLE +#if OPENTHREAD_CONFIG_SEEKER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE +#include + +#include "common/as_core_type.hpp" #include "common/callback.hpp" #include "common/error.hpp" #include "common/locator.hpp" @@ -59,30 +62,20 @@ namespace MeshCoP { class Seeker : public InstanceLocator, private NonCopyable { public: - static constexpr uint16_t kUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; ///< The default Joiner UDP port. + typedef otSeekerScanResult ScanResult; ///< Discover Scan result. - typedef Mle::DiscoverScanner::ScanResult ScanResult; ///< Discover Scan result. + typedef otSeekerVerdict Verdict; ///< A verdict returned from `ScanEvaluator` evaluating a Discover Scan result. - /** - * Represents a verdict returned from `ScanEvaluator` when evaluating a Discover Scan result. - */ - enum Verdict : uint8_t - { - kAccept, ///< The scan result is acceptable. - kAcceptPreferred, ///< The scan result is acceptable and preferred. - kIgnore, ///< The scan result should be ignored. - }; + static constexpr Verdict kAccept = OT_SEEKER_ACCEPT; ///< Scan result is acceptable. + static constexpr Verdict kAcceptPreferred = OT_SEEKER_ACCEPT_PREFERRED; ///< Scan result is acceptable & preferred. + static constexpr Verdict kIgnore = OT_SEEKER_IGNORE; ///< Scan result should be ignored. /** * Represents the callback function type used to evaluate a scan result or report the end of a scan. * - * @param[in] aContext A pointer to the callback context. - * @param[in] aResult A pointer to the scan result to evaluate, or `nullptr` to indicate scan completion. - * - * @returns The verdict for the scan result (`kAccept`, `kAcceptPreferred`, or `kIgnore`). - * If @p aResult is `nullptr` (scan complete), the return value is ignored. + * See `otSeekerScanEvaluator` for more details. */ - typedef Verdict (*ScanEvaluator)(void *aContext, const ScanResult *aResult); + typedef otSeekerScanEvaluator ScanEvaluator; /** * Initializes the `Seeker` @@ -131,6 +124,25 @@ public: */ bool IsRunning(void) const { return GetState() != kStateStopped; } + /** + * Gets the Seeker UDP port. + * + * @returns The Seeker UDP port. + */ + uint16_t GetUdpPort(void) const { return mUdpPort; } + + /** + * Sets the Seeker UDP port. + * + * This method can only be called when the Seeker is not running. + * + * @param[in] aUdpPort The Seeker UDP port. + * + * @retval kErrorNone Successfully set the Seeker UDP port. + * @retval kErrorInvalidState The Seeker is already running. + */ + Error SetUdpPort(uint16_t aUdpPort); + /** * Selects the next best candidate and prepares the connection. * @@ -139,8 +151,8 @@ public: * * This method iterates through the discovered Joiner Router candidates in order of priority. For the selected * candidate, it configures the radio channel and PAN ID, and populates @p aSockAddr with the candidate's address. - * It also registers the Joiner UDP port `kUdpPort` as an unsecure port to allow UDP - * connection to the candidate. + * It also registers the Seeker UDP port (`GetUdpPort()`) as an unsecure port to allow UDP connection to the + * candidate. * * If the list is exhausted, this method returns `kErrorNotFound` and automatically calls `Stop()`, which removes * the unsecure port and clears internal state. @@ -154,7 +166,8 @@ public: Error SetUpNextConnection(Ip6::SockAddr &aSockAddr); private: - static constexpr uint16_t kMaxCandidates = OPENTHREAD_CONFIG_JOINER_MAX_CANDIDATES; + static constexpr uint16_t kDefaultUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; + static constexpr uint16_t kMaxCandidates = OPENTHREAD_CONFIG_JOINER_MAX_CANDIDATES; enum State : uint8_t { @@ -183,6 +196,7 @@ private: static uint8_t CalculatePriority(int8_t aRssi, bool aPreferred); State mState; + uint16_t mUdpPort; Callback mScanEvaluator; Candidate mCandidates[kMaxCandidates]; uint16_t mCandidateIndex; @@ -191,6 +205,6 @@ private: } // namespace MeshCoP } // namespace ot -#endif // OPENTHREAD_CONFIG_JOINER_ENABLE +#endif // OPENTHREAD_CONFIG_SEEKER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE #endif // OT_CORE_MESHCOP_SEEKER_HPP_ diff --git a/tests/nexus/openthread-core-nexus-config.h b/tests/nexus/openthread-core-nexus-config.h index 644d43066..db5ad273d 100644 --- a/tests/nexus/openthread-core-nexus-config.h +++ b/tests/nexus/openthread-core-nexus-config.h @@ -118,6 +118,7 @@ #define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE 0 #define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 0 #define OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE 1 +#define OPENTHREAD_CONFIG_SEEKER_ENABLE 1 #define OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE 0 #define OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE 1 #define OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE 0 diff --git a/tests/toranj/openthread-core-toranj-config-posix.h b/tests/toranj/openthread-core-toranj-config-posix.h index e203aad02..7324a9556 100644 --- a/tests/toranj/openthread-core-toranj-config-posix.h +++ b/tests/toranj/openthread-core-toranj-config-posix.h @@ -48,6 +48,10 @@ #define OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE 0 +#define OPENTHREAD_CONFIG_JOINER_ENABLE 0 + +#define OPENTHREAD_CONFIG_SEEKER_ENABLE 1 + #define OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE 1 #ifdef __linux__ diff --git a/tests/toranj/openthread-core-toranj-config-simulation.h b/tests/toranj/openthread-core-toranj-config-simulation.h index 1de6eea92..f60a2d61c 100644 --- a/tests/toranj/openthread-core-toranj-config-simulation.h +++ b/tests/toranj/openthread-core-toranj-config-simulation.h @@ -71,6 +71,10 @@ #define OPENTHREAD_CONFIG_MULTICAST_DEFAULT_DNS_VERBOSE_LOGGING_STATE 0 +#define OPENTHREAD_CONFIG_JOINER_ENABLE 1 + +#define OPENTHREAD_CONFIG_SEEKER_ENABLE 1 + #define OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX 1 #define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE 0 diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h index 7a5739e5c..6bd406479 100644 --- a/tests/toranj/openthread-core-toranj-config.h +++ b/tests/toranj/openthread-core-toranj-config.h @@ -95,8 +95,6 @@ #define OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE 1 -#define OPENTHREAD_CONFIG_JOINER_ENABLE 1 - #define OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE 1 #define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE 1