diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 9832c2dfb..9c206631a 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 (73) +#define OPENTHREAD_API_VERSION (74) /** * @addtogroup api-instance diff --git a/include/openthread/srp_client.h b/include/openthread/srp_client.h index d0a94c54f..2f7b9df79 100644 --- a/include/openthread/srp_client.h +++ b/include/openthread/srp_client.h @@ -171,6 +171,21 @@ typedef void (*otSrpClientCallback)(otError aError, const otSrpClientService * aRemovedServices, void * aContext); +/** + * This function pointer type defines the callback used by SRP client to notify user when it is auto-started or stopped. + * + * This is only used when auto-start feature `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` is enabled. + * + * This callback is invoked when auto-start mode is enabled and the SRP client is either automatically started or + * stopped. + * + * @param[in] aSeverSockAddress A non-NULL pointer indicates SRP sever was started and pointer will give the + * selected server socket address. A NULL pointer indicates SRP sever was stopped. + * @param[in] aContext A pointer to an arbitrary context (provided when callback was registered). + * + */ +typedef void (*otSrpClientAutoStartCallback)(const otSockAddr *aServerSockAddr, void *aContext); + /** * This function starts the SRP client operation. * @@ -245,6 +260,60 @@ const otSockAddr *otSrpClientGetServerAddress(otInstance *aInstance); */ void otSrpClientSetCallback(otInstance *aInstance, otSrpClientCallback aCallback, void *aContext); +/** + * This function enables the auto-start mode. + * + * This is only available when auto-start feature `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` is enabled. + * + * Config option `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE` specifies the default auto-start mode (whether + * it is enabled or disabled at the start of OT stack). + * + * When auto-start is enabled, the SRP client will monitor the Thread Network Data for SRP Server Service entries + * and automatically start and stop the client when an SRP server is detected. + * + * If multiple SRP servers are found, a random one will be selected. If the selected SRP server is no longer + * detected (not longer present in the Thread Network Data), the SRP client will be stopped and then it may switch + * to another SRP server (if available). + * + * When the SRP client is explicitly started through a successful call to `otSrpClientStart()`, the given SRP server + * address in `otSrpClientStart()` will continue to be used regardless of the state of auto-start mode and whether the + * same SRP server address is discovered or not in the Thread Network Data. In this case, only an explicit + * `otSrpClientStop()` call will stop the client. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in] aCallback A callback to notify when client is auto-started/stopped. Can be NULL if not needed. + * @param[in] aContext A context to be passed when invoking @p aCallback. + * + */ +void otSrpClientEnableAutoStartMode(otInstance *aInstance, otSrpClientAutoStartCallback aCallback, void *aContext); + +/** + * This function disables the auto-start mode. + * + * This is only available when auto-start feature `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` is enabled. + * + * Disabling the auto-start mode will not stop the client if it is already running but the client stops monitoring + * the Thread Network Data to verify that the selected SRP server is still present in it. + * + * Note that a call to `otSrpClientStop()` will also disable the auto-start mode. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + */ +void otSrpClientDisableAutoStartMode(otInstance *aInstance); + +/** + * This function indicates the current state of auto-start mode (enabled or disabled). + * + * This is only available when auto-start feature `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + * @returns TRUE if the auto-start mode is enabled, FALSE otherwise. + * + */ +bool otSrpClientIsAutoStartModeEnabled(otInstance *aInstance); + /** * This function gets the lease interval used in SRP update requests. * diff --git a/src/cli/README_SRP.md b/src/cli/README_SRP.md index 6d928676e..08c0b6f63 100644 --- a/src/cli/README_SRP.md +++ b/src/cli/README_SRP.md @@ -57,16 +57,8 @@ fe80:0:0:0:a8cd:6e23:df3d:4193 Done > srp server enable Done -> netdata show -Prefixes: -Routes: -Services: -44970 5d c002 s 8400 -Done ``` -The SRP Server listening UDP port which is `c002`(`49154`) is included in the Server Data (listed by the `netdata show` command). - ### Start SRP Client Start the SRP Client node: @@ -100,12 +92,29 @@ Done Done > srp client service add my-service _ipps._tcp 12345 Done -srp client start fded:5114:8263:1fe1:68bc:ec03:c1ad:9325 49154 +> srp client autostart enable Done ``` +The last command enables the auto-start mode on the client which then monitors the network data to discover available SRP servers within the Thread network and automatically starts the client. + +Alternatively, the client can be started manually using the `srp client start`. + +The SRP Server listening UDP port (which is `c002`(`49154`) in the example below) can be found from the Server Data (listed by the `netdata show` command). + Make sure the SRP Server address & port are used for the `srp client start` command. +```bash +> netdata show +Prefixes: +Routes: +Services: +44970 5d c002 s 8400 +Done +srp client start fded:5114:8263:1fe1:68bc:ec03:c1ad:9325 49154 +Done +``` + ### Verify the service status Check if the host and service has been successfully registered on the client node: diff --git a/src/cli/README_SRP_CLIENT.md b/src/cli/README_SRP_CLIENT.md index adf6a106f..0baa1f561 100644 --- a/src/cli/README_SRP_CLIENT.md +++ b/src/cli/README_SRP_CLIENT.md @@ -5,6 +5,7 @@ Usage : `srp client [command] ...` - [help](#help) +- [autostart](#autostart) - [callback](#callback) - [host](#host) - [keyleaseinterval](#keyleaseinterval) @@ -12,6 +13,7 @@ Usage : `srp client [command] ...` - [server](#server) - [service](#service) - [start](#start) +- [state](#state) - [stop](#stop) ## Command Details @@ -24,6 +26,7 @@ Print SRP client help menu. ```bash > srp client help +autostart callback help host @@ -31,10 +34,36 @@ keyleaseinterval leaseinterval service start +state stop Done ``` +### autostart + +Usage `srp client autostart [enable|disable]` + +Enable/Disable auto start mode in SRP client. This command requires `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` feature to be enabled. + +Get the current autostart mode. + +```bash +> srp client autostart +Disabled +Done +``` + +Set the autostart mode. + +```bash +> srp client autostart enable +Done + +> srp client autostart +Enabled +Done +``` + ### callback Usage `srp client callback [enable|disable]` @@ -303,13 +332,25 @@ Done Usage: `srp client start ` -Start the SRP client with a given server IPv6 address and port number +Start the SRP client with a given server IPv6 address and port number. ```bash > srp client start fd00::d88a:618b:384d:e760 4724 Done ``` +### state + +Usage: `srp client state` + +Indicates the state of SRP client, i.e., whether it is enabled or disabled. + +```bash +> srp client state +Enabled +Done +``` + ### stop Usage: `srp client stop` diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp index c57d82a91..db9ba1465 100644 --- a/src/cli/cli_srp_client.cpp +++ b/src/cli/cli_srp_client.cpp @@ -96,6 +96,39 @@ exit: return error; } +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + +otError SrpClient::ProcessAutoStart(uint8_t aArgsLength, char *aArgs[]) +{ + otError error = OT_ERROR_NONE; + + if (aArgsLength == 0) + { + mInterpreter.OutputEnabledDisabledStatus(otSrpClientIsAutoStartModeEnabled(mInterpreter.mInstance)); + ExitNow(); + } + + VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); + + if (strcmp(aArgs[0], "enable") == 0) + { + otSrpClientEnableAutoStartMode(mInterpreter.mInstance, /* aCallback */ nullptr, /* aContext */ nullptr); + } + else if (strcmp(aArgs[0], "disable") == 0) + { + otSrpClientDisableAutoStartMode(mInterpreter.mInstance); + } + else + { + error = OT_ERROR_INVALID_COMMAND; + } + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + otError SrpClient::ProcessCallback(uint8_t aArgsLength, char *aArgs[]) { otError error = OT_ERROR_NONE; @@ -463,6 +496,20 @@ exit: return error; } +otError SrpClient::ProcessState(uint8_t aArgsLength, char *aArgs[]) +{ + OT_UNUSED_VARIABLE(aArgs); + + otError error = OT_ERROR_NONE; + + VerifyOrExit(aArgsLength == 0, error = OT_ERROR_INVALID_ARGS); + + mInterpreter.OutputEnabledDisabledStatus(otSrpClientIsRunning(mInterpreter.mInstance)); + +exit: + return error; +} + otError SrpClient::ProcessStop(uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgs); diff --git a/src/cli/cli_srp_client.hpp b/src/cli/cli_srp_client.hpp index 4ce70ed88..ef541c10e 100644 --- a/src/cli/cli_srp_client.hpp +++ b/src/cli/cli_srp_client.hpp @@ -100,6 +100,7 @@ private: otError (SrpClient::*mHandler)(uint8_t aArgsLength, char *aArgs[]); }; + otError ProcessAutoStart(uint8_t aArgsLength, char *aArgs[]); otError ProcessCallback(uint8_t aArgsLength, char *aArgs[]); otError ProcessHelp(uint8_t aArgsLength, char *aArgs[]); otError ProcessHost(uint8_t aArgsLength, char *aArgs[]); @@ -108,6 +109,7 @@ private: otError ProcessServer(uint8_t aArgsLength, char *aArgs[]); otError ProcessService(uint8_t aArgsLength, char *aArgs[]); otError ProcessStart(uint8_t aArgsLength, char *aArgs[]); + otError ProcessState(uint8_t aArgsLength, char *aArgs[]); otError ProcessStop(uint8_t aArgsLength, char *aArgs[]); void OutputHostInfo(uint8_t aIndentSize, const otSrpClientHostInfo &aHostInfo); @@ -125,6 +127,7 @@ private: const otSrpClientService * aRemovedServices); static constexpr Command sCommands[] = { + {"autostart", &SrpClient::ProcessAutoStart}, {"callback", &SrpClient::ProcessCallback}, {"help", &SrpClient::ProcessHelp}, {"host", &SrpClient::ProcessHost}, @@ -133,6 +136,7 @@ private: {"server", &SrpClient::ProcessServer}, {"service", &SrpClient::ProcessService}, {"start", &SrpClient::ProcessStart}, + {"state", &SrpClient::ProcessState}, {"stop", &SrpClient::ProcessStop}, }; diff --git a/src/core/api/srp_client_api.cpp b/src/core/api/srp_client_api.cpp index e9c88810e..0417d7587 100644 --- a/src/core/api/srp_client_api.cpp +++ b/src/core/api/srp_client_api.cpp @@ -78,6 +78,29 @@ void otSrpClientSetCallback(otInstance *aInstance, otSrpClientCallback aCallback instance.Get().SetCallback(aCallback, aContext); } +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE +void otSrpClientEnableAutoStartMode(otInstance *aInstance, otSrpClientAutoStartCallback aCallback, void *aContext) +{ + Instance &instance = *static_cast(aInstance); + + instance.Get().EnableAutoStartMode(aCallback, aContext); +} + +void otSrpClientDisableAutoStartMode(otInstance *aInstance) +{ + Instance &instance = *static_cast(aInstance); + + instance.Get().DisableAutoStartMode(); +} + +bool otSrpClientIsAutoStartModeEnabled(otInstance *aInstance) +{ + Instance &instance = *static_cast(aInstance); + + return instance.Get().IsAutoStartModeEnabled(); +} +#endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + uint32_t otSrpClientGetLeaseInterval(otInstance *aInstance) { Instance &instance = *static_cast(aInstance); diff --git a/src/core/config/srp_client.h b/src/core/config/srp_client.h index 09c7856e4..e25115177 100644 --- a/src/core/config/srp_client.h +++ b/src/core/config/srp_client.h @@ -45,6 +45,31 @@ #define OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + * + * Define to 1 to enable SRP Client auto-start feature and its APIs. + * + * When enabled, the SRP client can be configured to automatically start when it detects the presence of an SRP server + * (by monitoring the Thread Network Data for SRP Server Service entries). + * + */ +#ifndef OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE +#define OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE + * + * Define the default mode (enabled or disabled) of auto-start mode. + * + * This config is applicable/used only when `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` is enabled. + * + */ +#ifndef OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE +#define OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE 0 +#endif + /** * @def OPENTHREAD_CONFIG_SRP_CLIENT_DOMAIN_NAME_API_ENABLE * diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index dcccd2593..5b471e872 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -33,8 +33,10 @@ #include "common/instance.hpp" #include "common/locator-getters.hpp" #include "common/logging.hpp" +#include "common/random.hpp" #include "common/settings.hpp" #include "common/string.hpp" +#include "thread/network_data_service.hpp" #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE @@ -135,6 +137,10 @@ Client::Client(Instance &aInstance) , mState(kStateStopped) , mTxFailureRetryCount(0) , mShouldRemoveKeyLease(false) +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + , mAutoStartModeEnabled(kAutoStartDefaultMode) + , mAutoStartDidSelectServer(false) +#endif , mUpdateMessageId(0) , mRetryWaitInterval(kMinRetryWaitInterval) , mAcceptedLeaseInterval(0) @@ -143,6 +149,10 @@ Client::Client(Instance &aInstance) , mSocket(aInstance) , mCallback(nullptr) , mCallbackContext(nullptr) +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + , mAutoStartCallback(nullptr) + , mAutoStartContext(nullptr) +#endif , mDomainName(kDefaultDomainName) , mTimer(aInstance, Client::HandleTimer) { @@ -164,28 +174,33 @@ Client::Client(Instance &aInstance) static_assert(kRemoved == 7, "kRemoved value is not correct"); } -otError Client::Start(const Ip6::SockAddr &aServerSockAddr) +otError Client::Start(const Ip6::SockAddr &aServerSockAddr, Requester aRequester) { - otError error = OT_ERROR_NONE; + otError error; - if (GetState() != kStateStopped) - { - VerifyOrExit(aServerSockAddr == mSocket.GetPeerName(), error = OT_ERROR_BUSY); - ExitNow(); - } + VerifyOrExit(GetState() == kStateStopped, + error = (aServerSockAddr == GetServerAddress()) ? OT_ERROR_NONE : OT_ERROR_BUSY); SuccessOrExit(error = mSocket.Open(Client::HandleUdpReceive, this)); SuccessOrExit(error = mSocket.Connect(aServerSockAddr)); - otLogInfoSrp("[client] Starting, server %s", aServerSockAddr.ToString().AsCString()); + otLogInfoSrp("[client] %starting, server %s", (aRequester == kRequesterUser) ? "S" : "Auto-s", + aServerSockAddr.ToString().AsCString()); Resume(); +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + mAutoStartDidSelectServer = (aRequester == kRequesterAuto); + + VerifyOrExit((aRequester == kRequesterAuto) && (mAutoStartCallback != nullptr)); + mAutoStartCallback(&aServerSockAddr, mAutoStartContext); +#endif + exit: return error; } -void Client::Stop(void) +void Client::Stop(Requester aRequester) { // Change the state of host info and services so that they are // added/removed again once the client is started back. In the @@ -222,8 +237,18 @@ void Client::Stop(void) ResetRetryWaitInterval(); SetState(kStateStopped); +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + VerifyOrExit((aRequester == kRequesterAuto) && (mAutoStartCallback != nullptr)); + mAutoStartCallback(nullptr, mAutoStartContext); +#endif + exit: - return; +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + if (aRequester == kRequesterUser) + { + DisableAutoStartMode(); + } +#endif } void Client::SetCallback(Callback aCallback, void *aContext) @@ -267,14 +292,21 @@ void Client::Pause(void) void Client::HandleNotifierEvents(Events aEvents) { - VerifyOrExit(aEvents.Contains(kEventThreadRoleChanged)); - - if (Get().IsDisabled()) + if (aEvents.Contains(kEventThreadRoleChanged)) { - Stop(); - ExitNow(); + HandleRoleChanged(); } +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + if (aEvents.Contains(kEventThreadNetdataChanged)) + { + ProcessAutoStart(); + } +#endif +} + +void Client::HandleRoleChanged(void) +{ if (Get().IsAttached()) { VerifyOrExit(GetState() == kStatePaused); @@ -1428,6 +1460,75 @@ void Client::HandleTimer(void) } } +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + +void Client::EnableAutoStartMode(AutoStartCallback aCallback, void *aContext) +{ + mAutoStartCallback = aCallback; + mAutoStartContext = aContext; + + VerifyOrExit(!mAutoStartModeEnabled); + mAutoStartModeEnabled = true; + ProcessAutoStart(); + +exit: + return; +} + +void Client::ProcessAutoStart(void) +{ + uint16_t numServers = 0; + NetworkData::Service::SrpServer::Info selectedServer; + NetworkData::Service::SrpServer::Info server; + NetworkData::Service::Manager::Iterator iterator; + + VerifyOrExit(mAutoStartModeEnabled); + + // If the client is not running we check if there is any SRP sever + // info in Network Data and select one randomly and then start the + // client. If the client is already running with a server that was + // selected by the auto-start feature, we verify that the selected + // server is still present in the Network Data. + + VerifyOrExit(!IsRunning() || mAutoStartDidSelectServer); + + while (Get().GetNextSrpServerInfo(iterator, server) == OT_ERROR_NONE) + { + numServers++; + + // Choose a server randomly (with uniform distribution) from + // the list of servers. As we iterate through server entries, + // with probability `1/numServers`, we choose to switch the + // current selected server with the new entry. This approach + // results in a uniform/same probability of selection among + // all server entries. + + if ((numServers == 1) || (Random::NonCrypto::GetUint16InRange(0, numServers) == 0)) + { + selectedServer = server; + } + + if (IsRunning() && mAutoStartDidSelectServer && (GetServerAddress() == server.mSockAddr)) + { + ExitNow(); + } + } + + if (IsRunning()) + { + otLogInfoSrp("[client] Server %s is no longer present in net data", GetServerAddress().ToString().AsCString()); + Stop(kRequesterAuto); + } + + VerifyOrExit(numServers > 0); + IgnoreError(Start(selectedServer.mSockAddr, kRequesterAuto)); + +exit: + return; +} + +#endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + const char *Client::ItemStateToString(ItemState aState) { static const char *const kItemStateStrings[] = { diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index 809dd9991..86c321c16 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -276,7 +276,7 @@ public: * @retval OT_ERROR_FAILED Failed to open/connect the client's UDP socket. * */ - otError Start(const Ip6::SockAddr &aServerSockAddr); + otError Start(const Ip6::SockAddr &aServerSockAddr) { return Start(aServerSockAddr, kRequesterUser); } /** * This method stops the SRP client operation. @@ -284,8 +284,63 @@ public: * This method stops any further interactions with the SRP server. Note that it does not remove or clear host info * and/or list of services. It marks all services to be added/removed again once the client is started again. * + * If `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE` (auto-start feature) is enabled, a call to this method + * also disables the auto-start mode. + * */ - void Stop(void); + void Stop(void) { Stop(kRequesterUser); } + +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + /** + * This function pointer type defines the callback used by SRP client to notify user when it is auto-started or + * stopped. + * + */ + typedef otSrpClientAutoStartCallback AutoStartCallback; + + /** + * This method enables the auto-start mode. + * + * Config option `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE` specifies the default auto-start mode + * (whether it is enabled or disabled at the start of OT stack). + * + * When auto-start is enabled, the SRP client will monitor the Thread Network Data for SRP Server Service entries + * and automatically start and stop the client when an SRP server is detected. + * + * If multiple SRP servers are found, a random one will be selected. If the selected SRP server is no longer + * detected (not longer present in the Thread Network Data), the SRP client will be stopped and then it may switch + * to another SRP server (if available). + * + * When the SRP client is explicitly started through a successful call to `Start()`, the given SRP server address + * in `Start()` will continue to be used regardless of the state of auto-start mode and whether the same SRP + * server address is discovered or not in the Thread Network Data. In this case, only an explicit `Stop()` call + * will stop the client. + * + * @param[in] aCallback A callback to notify when client is auto-started/stopped. Can be `nullptr` if not needed. + * @param[in] aContext A context to be passed when invoking @p aCallback. + * + */ + void EnableAutoStartMode(AutoStartCallback aCallback, void *aContext); + + /** + * This method disables the auto-start mode. + * + * Disabling the auto-start mode will not stop the client if it is already running but the client stops monitoring + * the Thread Network Data to verify that the selected SRP server is still present in it. + * + * Note that a call to `Stop()` will also disable the auto-start mode. + * + */ + void DisableAutoStartMode(void) { mAutoStartModeEnabled = false; } + + /** + * This method indicates the current state of auto-start mode (enabled or disabled). + * + * @returns TRUE if the auto-start mode is enabled, FALSE otherwise. + * + */ + bool IsAutoStartModeEnabled(void) const { return mAutoStartModeEnabled; } +#endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE /** * This method indicates whether the SRP client is running or not. @@ -623,6 +678,22 @@ private: kStateToRetry, // SRP update tx failed, waiting to retry. }; + enum : bool + { + kAutoStartDefaultMode = OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE, + }; + + // This enumeration type is used by the private `Start()` and + // `Stop()` methods to indicate whether it is being requested by the + // user or by the auto-start feature. + enum Requester + { + kRequesterUser, +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + kRequesterAuto, +#endif + }; + struct Info : public Clearable { enum : uint16_t @@ -636,9 +707,12 @@ private: Crypto::Ecdsa::P256::KeyPair mKeyPair; // The ECDSA key pair. }; + otError Start(const Ip6::SockAddr &aServerSockAddr, Requester aRequester); + void Stop(Requester aRequester); void Resume(void); void Pause(void); void HandleNotifierEvents(Events aEvents); + void HandleRoleChanged(void); void UpdateServiceStateToRemove(Service &aService); State GetState(void) const { return mState; } void SetState(State aState); @@ -671,6 +745,9 @@ private: bool ShouldRenewEarly(const Service &aService) const; static void HandleTimer(Timer &aTimer); void HandleTimer(void); +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + void ProcessAutoStart(void); +#endif #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_SRP == 1) static const char *StateToString(State aState); @@ -681,11 +758,15 @@ private: static const char kDefaultDomainName[]; - static_assert(kMaxTxFailureRetries < 128, "kMaxTxFailureRetries exceed the range of mTxFailureRetryCount (7-bit)"); + static_assert(kMaxTxFailureRetries < 16, "kMaxTxFailureRetries exceed the range of mTxFailureRetryCount (4-bit)"); State mState; - uint8_t mTxFailureRetryCount : 7; + uint8_t mTxFailureRetryCount : 4; bool mShouldRemoveKeyLease : 1; +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + bool mAutoStartModeEnabled : 1; + bool mAutoStartDidSelectServer : 1; +#endif uint16_t mUpdateMessageId; uint32_t mRetryWaitInterval; @@ -697,8 +778,14 @@ private: Ip6::Udp::Socket mSocket; - Callback mCallback; - void * mCallbackContext; + Callback mCallback; + void * mCallbackContext; + +#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE + AutoStartCallback mAutoStartCallback; + void * mAutoStartContext; +#endif + const char * mDomainName; HostInfo mHostInfo; LinkedList mServices; diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index fe9b35a90..4088a2e62 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -172,6 +172,7 @@ EXTRA_DIST = \ test_route_table.py \ test_router_reattach.py \ test_service.py \ + test_srp_auto_start_mode.py \ test_srp_lease.py \ test_srp_name_conflicts.py \ test_srp_register_single_service.py \ @@ -225,6 +226,7 @@ check_SCRIPTS = \ test_route_table.py \ test_router_reattach.py \ test_service.py \ + test_srp_auto_start_mode.py \ test_srp_lease.py \ test_srp_name_conflicts.py \ test_srp_register_single_service.py \ diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 9cec245db..1facf847a 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -928,6 +928,34 @@ class NodeImpl: self.send_command(f'srp client stop') self._expect_done() + def srp_client_get_state(self): + cmd = 'srp client state' + self.send_command(cmd) + return self._expect_command_output(cmd)[0] + + def srp_client_get_auto_start_mode(self): + cmd = 'srp client autostart' + self.send_command(cmd) + return self._expect_command_output(cmd)[0] + + def srp_client_enable_auto_start_mode(self): + self.send_command(f'srp client autostart enable') + self._expect_done() + + def srp_client_disable_auto_start_mode(self): + self.send_command(f'srp client autostart able') + self._expect_done() + + def srp_client_get_server_address(self): + cmd = 'srp client server address' + self.send_command(cmd) + return self._expect_command_output(cmd)[0] + + def srp_client_get_server_port(self): + cmd = 'srp client server port' + self.send_command(cmd) + return int(self._expect_command_output(cmd)[0]) + def srp_client_get_host_state(self): cmd = 'srp client host state' self.send_command(cmd) diff --git a/tests/scripts/thread-cert/test_srp_auto_start_mode.py b/tests/scripts/thread-cert/test_srp_auto_start_mode.py new file mode 100755 index 000000000..f0a9c140b --- /dev/null +++ b/tests/scripts/thread-cert/test_srp_auto_start_mode.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021, 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 ipaddress +import unittest + +import command +import thread_cert + +# Test description: +# This test verifies SRP client auto-start functionality that SRP client can +# correctly discover and connect to SRP server. +# +# Topology: +# +# CLIENT (leader) -- SERVER1 (router) +# | +# | +# SERVER2 (router) +# + +CLIENT = 1 +SERVER1 = 2 +SERVER2 = 3 + + +class SrpAutoStartMode(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + SUPPORT_NCP = False + + TOPOLOGY = { + CLIENT: { + 'name': 'SRP_CLIENT', + 'masterkey': '00112233445566778899aabbccddeeff', + 'mode': 'rdn', + 'panid': 0xface + }, + SERVER1: { + 'name': 'SRP_SERVER1', + 'masterkey': '00112233445566778899aabbccddeeff', + 'mode': 'rdn', + 'panid': 0xface, + 'router_selection_jitter': 1 + }, + SERVER2: { + 'name': 'SRP_SERVER2', + 'masterkey': '00112233445566778899aabbccddeeff', + 'mode': 'rdn', + 'panid': 0xface, + 'router_selection_jitter': 1 + }, + } + + def test(self): + client = self.nodes[CLIENT] + server1 = self.nodes[SERVER1] + server2 = self.nodes[SERVER2] + + # + # 0. Start the server & client devices. + # + + client.srp_server_set_enabled(False) + client.start() + self.simulator.go(5) + self.assertEqual(client.get_state(), 'leader') + + server1.srp_server_set_enabled(True) + server2.srp_server_set_enabled(False) + server1.start() + server2.start() + self.simulator.go(5) + self.assertEqual(server1.get_state(), 'router') + self.assertEqual(server2.get_state(), 'router') + + # + # 1. Enable auto start mode on client and check that server1 is used. + # + + self.assertEqual(client.srp_client_get_state(), 'Disabled') + client.srp_client_enable_auto_start_mode() + self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled') + self.simulator.go(2) + + self.assertEqual(client.srp_client_get_state(), 'Enabled') + self.assertEqual(client.srp_client_get_server_port(), client.get_srp_server_port()) + + # + # 2. Disable server1 and check client is stopped/disabled. + # + + server1.srp_server_set_enabled(False) + self.simulator.go(5) + self.assertEqual(client.srp_client_get_state(), 'Disabled') + + # + # 3. Enable server2 and check client starts again. + # + + server2.srp_server_set_enabled(True) + self.simulator.go(5) + self.assertEqual(client.srp_client_get_state(), 'Enabled') + prev_port = client.srp_client_get_server_port() + + # + # 4. Enable both servers and check client stays with server2. + # + + server1.srp_server_set_enabled(True) + self.simulator.go(2) + self.assertEqual(client.srp_client_get_state(), 'Enabled') + self.assertEqual(client.srp_client_get_server_port(), prev_port) + + # + # 5. Disable server2 and check client switches to server1. + # + + server2.srp_server_set_enabled(False) + self.simulator.go(5) + self.assertEqual(client.srp_client_get_state(), 'Enabled') + self.assertNotEqual(client.srp_client_get_server_port(), prev_port) + + +if __name__ == '__main__': + unittest.main()