[srp-client] update how auto-start mode prefers netdata entries (#7503)
This commit updates the auto-start behavior in `Srp::Client` which
monitors the Thread Network Data entries to discover and select SRP
sever to register with. There are three types of entries in Network
Data and are selected in the order below:
1) Preferred unicast entries with address in service data,
2) Anycast entries (each having a seq number,
3) Unicast entries with address info included in server data.
This commit also defines `AutoStart` class which includes all the
state and data variables related to auto-start feature. It also
updates `test_srp_auto_start_mode` test to cover the newly added
behaviors.
diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp
index 7eeb2db..0667d30 100644
--- a/src/core/net/srp_client.cpp
+++ b/src/core/net/srp_client.cpp
@@ -35,12 +35,9 @@
#include "common/debug.hpp"
#include "common/instance.hpp"
#include "common/locator_getters.hpp"
-
-#include "common/numeric_limits.hpp"
#include "common/random.hpp"
#include "common/settings.hpp"
#include "common/string.hpp"
-#include "thread/network_data_service.hpp"
/**
* @file
@@ -156,6 +153,83 @@
}
//---------------------------------------------------------------------
+// Client::AutoStart
+
+#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
+
+Client::AutoStart::AutoStart(void)
+{
+ Clear();
+ mState = kDefaultMode ? kSelectedNone : kDisabled;
+}
+
+bool Client::AutoStart::HasSelectedServer(void) const
+{
+ bool hasSelected = false;
+
+ switch (mState)
+ {
+ case kDisabled:
+ case kSelectedNone:
+ break;
+
+ case kSelectedUnicastPreferred:
+ case kSelectedUnicast:
+ case kSelectedAnycast:
+ hasSelected = true;
+ break;
+ }
+
+ return hasSelected;
+}
+
+void Client::AutoStart::SetState(State aState)
+{
+ if (mState != aState)
+ {
+ LogInfo("AutoStartState %s -> %s", StateToString(mState), StateToString(aState));
+ mState = aState;
+ }
+}
+
+void Client::AutoStart::SetCallback(AutoStartCallback aCallback, void *aContext)
+{
+ mCallback = aCallback;
+ mContext = aContext;
+}
+
+void Client::AutoStart::InvokeCallback(const Ip6::SockAddr *aServerSockAddr) const
+{
+ if (mCallback != nullptr)
+ {
+ mCallback(aServerSockAddr, mContext);
+ }
+}
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
+const char *Client::AutoStart::StateToString(State aState)
+{
+ static const char *const kStateStrings[] = {
+ "Disabled", // (0) kDisabled
+ "Idle", // (1) kSelectedNone
+ "Unicast-prf", // (2) kSelectedUnicastPreferred
+ "Anycast", // (3) kSelectedAnycast
+ "Unicast", // (4) kSelectedUnicast
+ };
+
+ static_assert(0 == kDisabled, "kDisabled value is incorrect");
+ static_assert(1 == kSelectedNone, "kSelectedNone value is incorrect");
+ static_assert(2 == kSelectedUnicastPreferred, "kSelectedUnicastPreferred value is incorrect");
+ static_assert(3 == kSelectedAnycast, "kSelectedAnycast value is incorrect");
+ static_assert(4 == kSelectedUnicast, "kSelectedUnicast value is incorrect");
+
+ return kStateStrings[aState];
+}
+#endif
+
+#endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
+
+//---------------------------------------------------------------------
// Client
const char Client::kDefaultDomainName[] = "default.service.arpa";
@@ -165,11 +239,6 @@
, mState(kStateStopped)
, mTxFailureRetryCount(0)
, mShouldRemoveKeyLease(false)
-#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
- , mAutoStartModeEnabled(kAutoStartDefaultMode)
- , mAutoStartDidSelectServer(false)
- , mAutoStartIsUsingAnycastAddress(false)
-#endif
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
, mServiceKeyRecordEnabled(false)
#endif
@@ -181,14 +250,6 @@
, mSocket(aInstance)
, mCallback(nullptr)
, mCallbackContext(nullptr)
-#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
- , mAutoStartCallback(nullptr)
- , mAutoStartContext(nullptr)
- , mServerSequenceNumber(0)
-#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
- , mTimoutFailureCount(0)
-#endif
-#endif
, mDomainName(kDefaultDomainName)
, mTimer(aInstance, Client::HandleTimer)
{
@@ -226,18 +287,12 @@
Resume();
#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
- mAutoStartDidSelectServer = (aRequester == kRequesterAuto);
-
- if (mAutoStartDidSelectServer)
+ if (aRequester == kRequesterAuto)
{
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE
Get<Dns::Client>().UpdateDefaultConfigAddress();
#endif
-
- if (mAutoStartCallback != nullptr)
- {
- mAutoStartCallback(&aServerSockAddr, mAutoStartContext);
- }
+ mAutoStart.InvokeCallback(&aServerSockAddr);
}
#endif
@@ -290,14 +345,11 @@
#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
- mTimoutFailureCount = 0;
+ mAutoStart.ResetTimoutFailureCount();
#endif
-
- mAutoStartDidSelectServer = false;
-
- if ((aRequester == kRequesterAuto) && (mAutoStartCallback != nullptr))
+ if (aRequester == kRequesterAuto)
{
- mAutoStartCallback(nullptr, mAutoStartContext);
+ mAutoStart.InvokeCallback(nullptr);
}
#endif
@@ -611,24 +663,29 @@
}
#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
- if (mAutoStartModeEnabled && mAutoStartDidSelectServer && (oldHostState != kRegistered) &&
- (mHostInfo.GetState() == kRegistered))
+ if ((oldHostState != kRegistered) && (mHostInfo.GetState() == kRegistered))
{
- if (mAutoStartIsUsingAnycastAddress)
- {
- IgnoreError(Get<Settings>().Delete<Settings::SrpClientInfo>());
- }
- else
- {
- Settings::SrpClientInfo info;
+ Settings::SrpClientInfo info;
+ switch (mAutoStart.GetState())
+ {
+ case AutoStart::kDisabled:
+ case AutoStart::kSelectedNone:
+ break;
+
+ case AutoStart::kSelectedUnicastPreferred:
+ case AutoStart::kSelectedUnicast:
info.SetServerAddress(GetServerAddress().GetAddress());
info.SetServerPort(GetServerAddress().GetPort());
-
IgnoreError(Get<Settings>().Save(info));
+ break;
+
+ case AutoStart::kSelectedAnycast:
+ IgnoreError(Get<Settings>().Delete<Settings::SrpClientInfo>());
+ break;
}
}
-#endif
+#endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
}
void Client::InvokeCallback(Error aError) const
@@ -1211,7 +1268,7 @@
LogInfo("Received response");
#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
- mTimoutFailureCount = 0;
+ mAutoStart.ResetTimoutFailureCount();
#endif
error = Dns::Header::ResponseCodeToError(header.GetResponseCode());
@@ -1609,12 +1666,9 @@
// callback. It works correctly due to the guard check at the
// top of `SelectNextServer()`.
- if (mTimoutFailureCount < NumericLimits<uint8_t>::kMax)
- {
- mTimoutFailureCount++;
- }
+ mAutoStart.IncrementTimoutFailureCount();
- if (mTimoutFailureCount >= kMaxTimeoutFailuresToSwitchServer)
+ if (mAutoStart.GetTimoutFailureCount() >= kMaxTimeoutFailuresToSwitchServer)
{
SelectNextServer(kDisallowSwitchOnRegisteredHost);
}
@@ -1631,11 +1685,11 @@
void Client::EnableAutoStartMode(AutoStartCallback aCallback, void *aContext)
{
- mAutoStartCallback = aCallback;
- mAutoStartContext = aContext;
+ mAutoStart.SetCallback(aCallback, aContext);
- VerifyOrExit(!mAutoStartModeEnabled);
- mAutoStartModeEnabled = true;
+ VerifyOrExit(mAutoStart.GetState() == AutoStart::kDisabled);
+
+ mAutoStart.SetState(AutoStart::kSelectedNone);
ProcessAutoStart();
exit:
@@ -1644,130 +1698,181 @@
void Client::ProcessAutoStart(void)
{
- Ip6::SockAddr serverSockAddr;
- bool serverIsAnycast = false;
- NetworkData::Service::DnsSrpAnycast::Info anycastInfo;
-#if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
- Settings::SrpClientInfo savedInfo;
- bool hasSavedServerInfo = false;
-#endif
+ Ip6::SockAddr serverSockAddr;
+ DnsSrpAnycast::Info anycastInfo;
+ DnsSrpUnicast::Info unicastInfo;
+ bool shouldRestart = false;
- VerifyOrExit(mAutoStartModeEnabled);
+ // If auto start mode is enabled, we check the Network Data entries
+ // to discover and select the preferred SRP server to register with.
+ // If we currently have a selected server, we ensure that it is
+ // still present in the Network Data and is still the preferred one.
+
+ VerifyOrExit(mAutoStart.GetState() != AutoStart::kDisabled);
+
+ // If SRP client is running, we check to make sure that auto-start
+ // did select the current server, and server was not specified by
+ // user directly.
+
+ if (IsRunning())
+ {
+ VerifyOrExit(mAutoStart.GetState() != AutoStart::kSelectedNone);
+ }
+
+ // There are three types of entries in Network Data:
+ //
+ // 1) Preferred unicast entries with address included in service data.
+ // 2) Anycast entries (each having a seq number).
+ // 3) Unicast entries with address info included in server data.
serverSockAddr.Clear();
- // If the SRP client is not running and auto start mode is
- // enabled, we check if we can find any SRP server info in the
- // Thread Network Data. If it is already running and the server
- // was chosen by the auto-start feature, then we ensure that the
- // selected server is still present in the Network Data.
- //
- // Two types of "DNS/SRP Service" entries can be present in
- // Network Data, "DNS/SRP Service Anycast Address" model and
- // "DNS/SRP Service Unicast" model. The Anycast entries are
- // preferred over the Unicast entries.
+ if (SelectUnicastEntry(DnsSrpUnicast::kFromServiceData, unicastInfo) == kErrorNone)
+ {
+ mAutoStart.SetState(AutoStart::kSelectedUnicastPreferred);
+ serverSockAddr = unicastInfo.mSockAddr;
+ }
+ else if (Get<NetworkData::Service::Manager>().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone)
+ {
+ serverSockAddr.SetAddress(anycastInfo.mAnycastAddress);
+ serverSockAddr.SetPort(kAnycastServerPort);
- VerifyOrExit(!IsRunning() || mAutoStartDidSelectServer);
+ // We check if we are selecting an anycast entry for first
+ // time, or if the seq number has changed. Even if the
+ // anycast address remains the same as before, on a seq
+ // number change, the client still needs to restart to
+ // re-register its info.
- // Now `IsRunning()` implies `mAutoStartDidSelectServer`.
+ if ((mAutoStart.GetState() != AutoStart::kSelectedAnycast) ||
+ (mAutoStart.GetAnycastSeqNum() != anycastInfo.mSequenceNumber))
+ {
+ shouldRestart = true;
+ mAutoStart.SetAnycastSeqNum(anycastInfo.mSequenceNumber);
+ }
+ mAutoStart.SetState(AutoStart::kSelectedAnycast);
+ }
+ else if (SelectUnicastEntry(DnsSrpUnicast::kFromServerData, unicastInfo) == kErrorNone)
+ {
+ mAutoStart.SetState(AutoStart::kSelectedUnicast);
+ serverSockAddr = unicastInfo.mSockAddr;
+ }
+
+ if (IsRunning())
+ {
+ VerifyOrExit((GetServerAddress() != serverSockAddr) || shouldRestart);
+ Stop(kRequesterAuto, kResetRetryInterval);
+ }
+
+ if (!serverSockAddr.GetAddress().IsUnspecified())
+ {
+ IgnoreError(Start(serverSockAddr, kRequesterAuto));
+ }
+ else
+ {
+ mAutoStart.SetState(AutoStart::kSelectedNone);
+ }
+
+exit:
+ return;
+}
+
+Error Client::SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::Info &aInfo) const
+{
+ Error error = kErrorNotFound;
+ DnsSrpUnicast::Info unicastInfo;
+ NetworkData::Service::Manager::Iterator iterator;
+ uint16_t numServers = 0;
#if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
+ Settings::SrpClientInfo savedInfo;
+ bool hasSavedServerInfo = false;
+
if (!IsRunning())
{
hasSavedServerInfo = (Get<Settings>().Read(savedInfo) == kErrorNone);
}
#endif
- if (Get<NetworkData::Service::Manager>().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone)
+ while (Get<NetworkData::Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone)
{
- if (IsRunning() && mAutoStartIsUsingAnycastAddress && (mServerSequenceNumber == anycastInfo.mSequenceNumber) &&
- (GetServerAddress().GetAddress() == anycastInfo.mAnycastAddress))
+ if (unicastInfo.mOrigin != aOrigin)
{
- // Client is already using the same anycast address.
+ continue;
+ }
+
+ if (mAutoStart.HasSelectedServer() && (GetServerAddress() == unicastInfo.mSockAddr))
+ {
+ aInfo = unicastInfo;
+ error = kErrorNone;
ExitNow();
}
- LogInfo("Found anycast server %d", anycastInfo.mSequenceNumber);
-
- serverSockAddr.SetAddress(anycastInfo.mAnycastAddress);
- serverSockAddr.SetPort(kAnycastServerPort);
- mServerSequenceNumber = anycastInfo.mSequenceNumber;
- serverIsAnycast = true;
- }
- else
- {
- uint16_t numServers = 0;
- NetworkData::Service::DnsSrpUnicast::Info unicastInfo;
- NetworkData::Service::Manager::Iterator iterator;
-
- while (Get<NetworkData::Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone)
- {
- if (IsRunning() && !mAutoStartIsUsingAnycastAddress && (GetServerAddress() == unicastInfo.mSockAddr))
- {
- ExitNow();
- }
-
#if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
- if (hasSavedServerInfo && (unicastInfo.mSockAddr.GetAddress() == savedInfo.GetServerAddress()) &&
- (unicastInfo.mSockAddr.GetPort() == savedInfo.GetServerPort()))
- {
- // Stop the search if we see a match for the previously
- // saved server info in the network data entries.
+ if (hasSavedServerInfo && (unicastInfo.mSockAddr.GetAddress() == savedInfo.GetServerAddress()) &&
+ (unicastInfo.mSockAddr.GetPort() == savedInfo.GetServerPort()))
+ {
+ // Stop the search if we see a match for the previously
+ // saved server info in the network data entries.
- serverSockAddr = unicastInfo.mSockAddr;
- serverIsAnycast = false;
- break;
- }
+ aInfo = unicastInfo;
+ error = kErrorNone;
+ ExitNow();
+ }
#endif
+ numServers++;
- 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.
- // 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))
- {
- serverSockAddr = unicastInfo.mSockAddr;
- serverIsAnycast = false;
- }
+ if ((numServers == 1) || (Random::NonCrypto::GetUint16InRange(0, numServers) == 0))
+ {
+ aInfo = unicastInfo;
+ error = kErrorNone;
}
}
- if (IsRunning())
- {
- Stop(kRequesterAuto, kResetRetryInterval);
- }
-
- VerifyOrExit(!serverSockAddr.GetAddress().IsUnspecified());
-
- mAutoStartIsUsingAnycastAddress = serverIsAnycast;
- IgnoreError(Start(serverSockAddr, kRequesterAuto));
-
exit:
- return;
+ return error;
}
#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
void Client::SelectNextServer(bool aDisallowSwitchOnRegisteredHost)
{
- // This method tries to find the next server info entry in the
+ // This method tries to find the next unicast server info entry in the
// Network Data after the current one selected. If found, it
// restarts the client with the new server (keeping the retry wait
// interval as before).
- Ip6::SockAddr serverSockAddr;
- bool selectNext = false;
+ Ip6::SockAddr serverSockAddr;
+ bool selectNext = false;
+ DnsSrpUnicast::Origin origin = DnsSrpUnicast::kFromServiceData;
serverSockAddr.Clear();
// Ensure that client is running, auto-start is enabled and
- // auto-start selected the server.
+ // auto-start selected the server and it is a unicast entry.
- VerifyOrExit(IsRunning() && mAutoStartModeEnabled && mAutoStartDidSelectServer);
+ VerifyOrExit(IsRunning());
+
+ switch (mAutoStart.GetState())
+ {
+ case AutoStart::kSelectedUnicastPreferred:
+ origin = DnsSrpUnicast::kFromServiceData;
+ break;
+
+ case AutoStart::kSelectedUnicast:
+ origin = DnsSrpUnicast::kFromServerData;
+ break;
+
+ case AutoStart::kSelectedAnycast:
+ case AutoStart::kDisabled:
+ case AutoStart::kSelectedNone:
+ ExitNow();
+ }
if (aDisallowSwitchOnRegisteredHost)
{
@@ -1782,36 +1887,19 @@
do
{
- NetworkData::Service::DnsSrpAnycast::Info anycastInfo;
- NetworkData::Service::DnsSrpUnicast::Info unicastInfo;
- NetworkData::Service::Manager::Iterator iterator;
-
- while (Get<NetworkData::Service::Manager>().GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNone)
- {
- if (selectNext)
- {
- serverSockAddr.SetAddress(anycastInfo.mAnycastAddress);
- serverSockAddr.SetPort(kAnycastServerPort);
- mServerSequenceNumber = anycastInfo.mSequenceNumber;
- mAutoStartIsUsingAnycastAddress = true;
- ExitNow();
- }
-
- if (mAutoStartIsUsingAnycastAddress && (GetServerAddress().GetAddress() == anycastInfo.mAnycastAddress) &&
- (GetServerAddress().GetPort() == kAnycastServerPort))
- {
- selectNext = true;
- }
- }
-
- iterator.Reset();
+ DnsSrpUnicast::Info unicastInfo;
+ NetworkData::Service::Manager::Iterator iterator;
while (Get<NetworkData::Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone)
{
+ if (unicastInfo.mOrigin != origin)
+ {
+ continue;
+ }
+
if (selectNext)
{
- serverSockAddr = unicastInfo.mSockAddr;
- mAutoStartIsUsingAnycastAddress = false;
+ serverSockAddr = unicastInfo.mSockAddr;
ExitNow();
}
diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp
index ebec59d..80b381f 100644
--- a/src/core/net/srp_client.hpp
+++ b/src/core/net/srp_client.hpp
@@ -43,11 +43,13 @@
#include "common/message.hpp"
#include "common/non_copyable.hpp"
#include "common/notifier.hpp"
+#include "common/numeric_limits.hpp"
#include "common/timer.hpp"
#include "crypto/ecdsa.hpp"
#include "net/dns_types.hpp"
#include "net/ip6.hpp"
#include "net/udp6.hpp"
+#include "thread/network_data_service.hpp"
/**
* @file
@@ -69,6 +71,9 @@
{
friend class ot::Notifier;
+ using DnsSrpUnicast = NetworkData::Service::DnsSrpUnicast;
+ using DnsSrpAnycast = NetworkData::Service::DnsSrpAnycast;
+
public:
/**
* This enumeration types represents an SRP client item (service or host info) state.
@@ -355,7 +360,7 @@
* Note that a call to `Stop()` will also disable the auto-start mode.
*
*/
- void DisableAutoStartMode(void) { mAutoStartModeEnabled = false; }
+ void DisableAutoStartMode(void) { mAutoStart.SetState(AutoStart::kDisabled); }
/**
* This method indicates the current state of auto-start mode (enabled or disabled).
@@ -363,7 +368,7 @@
* @returns TRUE if the auto-start mode is enabled, FALSE otherwise.
*
*/
- bool IsAutoStartModeEnabled(void) const { return mAutoStartModeEnabled; }
+ bool IsAutoStartModeEnabled(void) const { return mAutoStart.GetState() != AutoStart::kDisabled; }
/**
* This method indicates whether or not the current SRP server's address is selected by auto-start.
@@ -371,7 +376,7 @@
* @returns TRUE if the SRP server's address is selected by auto-start, FALSE otherwise.
*
*/
- bool IsServerSelectedByAutoStart(void) const { return mAutoStartDidSelectServer; }
+ bool IsServerSelectedByAutoStart(void) const { return mAutoStart.HasSelectedServer(); }
#endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
/**
@@ -790,6 +795,54 @@
kKeepRetryInterval,
};
+#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
+ class AutoStart : Clearable<AutoStart>
+ {
+ public:
+ enum State : uint8_t{
+ kDisabled, // AutoStart is disabled.
+ kSelectedNone, // AutoStart is enabled but not yet selected any servers.
+ kSelectedUnicastPreferred, // AutoStart selected a preferred unicast entry (address in service data).
+ kSelectedAnycast, // AutoStart selected an anycast entry with `mAnycastSeqNum`.
+ kSelectedUnicast, // AutoStart selected a unicast entry (address in server data).
+ };
+
+ AutoStart(void);
+ bool HasSelectedServer(void) const;
+ State GetState(void) const { return mState; }
+ void SetState(State aState);
+ uint8_t GetAnycastSeqNum(void) const { return mAnycastSeqNum; }
+ void SetAnycastSeqNum(uint8_t aAnycastSeqNum) { mAnycastSeqNum = aAnycastSeqNum; }
+ void SetCallback(AutoStartCallback aCallback, void *aContext);
+ void InvokeCallback(const Ip6::SockAddr *aServerSockAddr) const;
+
+#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
+ uint8_t GetTimoutFailureCount(void) const { return mTimoutFailureCount; }
+ void ResetTimoutFailureCount(void) { mTimoutFailureCount = 0; }
+ void IncrementTimoutFailureCount(void)
+ {
+ if (mTimoutFailureCount < NumericLimits<uint8_t>::kMax)
+ {
+ mTimoutFailureCount++;
+ }
+ }
+#endif
+
+ private:
+ static constexpr bool kDefaultMode = OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE;
+
+ static const char *StateToString(State aState);
+
+ AutoStartCallback mCallback;
+ void * mContext;
+ State mState;
+ uint8_t mAnycastSeqNum;
+#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
+ uint8_t mTimoutFailureCount; // Number of no-response timeout failures with the currently selected server.
+#endif
+ };
+#endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
+
struct Info : public Clearable<Info>
{
static constexpr uint16_t kUnknownOffset = 0; // Unknown offset value (used when offset is not yet set).
@@ -839,7 +892,8 @@
static void HandleTimer(Timer &aTimer);
void HandleTimer(void);
#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
- void ProcessAutoStart(void);
+ void ProcessAutoStart(void);
+ Error SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::Info &aInfo) const;
#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
void SelectNextServer(bool aDisallowSwitchOnRegisteredHost);
#endif
@@ -859,11 +913,6 @@
State mState;
uint8_t mTxFailureRetryCount : 4;
bool mShouldRemoveKeyLease : 1;
-#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
- bool mAutoStartModeEnabled : 1;
- bool mAutoStartDidSelectServer : 1;
- bool mAutoStartIsUsingAnycastAddress : 1;
-#endif
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
bool mServiceKeyRecordEnabled : 1;
#endif
@@ -878,22 +927,15 @@
Ip6::Udp::Socket mSocket;
- Callback mCallback;
- void * mCallbackContext;
-
-#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
- AutoStartCallback mAutoStartCallback;
- void * mAutoStartContext;
- uint8_t mServerSequenceNumber;
-#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
- uint8_t mTimoutFailureCount;
-#endif
-#endif
-
+ Callback mCallback;
+ void * mCallbackContext;
const char * mDomainName;
HostInfo mHostInfo;
LinkedList<Service> mServices;
TimerMilli mTimer;
+#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
+ AutoStart mAutoStart;
+#endif
};
} // namespace Srp
diff --git a/tests/scripts/thread-cert/test_srp_auto_start_mode.py b/tests/scripts/thread-cert/test_srp_auto_start_mode.py
index 2b18264..9fc4c09 100755
--- a/tests/scripts/thread-cert/test_srp_auto_start_mode.py
+++ b/tests/scripts/thread-cert/test_srp_auto_start_mode.py
@@ -31,23 +31,22 @@
import unittest
import command
+import config
import thread_cert
# Test description:
# This test verifies SRP client auto-start functionality that SRP client can
-# correctly discover and connect to SRP server.
+# correctly discovers and connects to SRP server.
#
# Topology:
#
-# CLIENT (leader) -- SERVER1 (router)
-# |
-# |
-# SERVER2 (router)
+# Four routers, one acting as SRP client, others as SRP server.
#
CLIENT = 1
SERVER1 = 2
SERVER2 = 3
+SERVER3 = 4
class SrpAutoStartMode(thread_cert.TestCase):
@@ -57,17 +56,18 @@
TOPOLOGY = {
CLIENT: {
'name': 'SRP_CLIENT',
- 'networkkey': '00112233445566778899aabbccddeeff',
'mode': 'rdn',
},
SERVER1: {
'name': 'SRP_SERVER1',
- 'networkkey': '00112233445566778899aabbccddeeff',
'mode': 'rdn',
},
SERVER2: {
'name': 'SRP_SERVER2',
- 'networkkey': '00112233445566778899aabbccddeeff',
+ 'mode': 'rdn',
+ },
+ SERVER3: {
+ 'name': 'SRP_SERVER3',
'mode': 'rdn',
},
}
@@ -76,27 +76,38 @@
client = self.nodes[CLIENT]
server1 = self.nodes[SERVER1]
server2 = self.nodes[SERVER2]
+ server3 = self.nodes[SERVER3]
- #
- # 0. Start the server & client devices.
- #
+ #-------------------------------------------------------------------
+ # Form the network.
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()
+ server3.start()
self.simulator.go(5)
self.assertEqual(server1.get_state(), 'router')
self.assertEqual(server2.get_state(), 'router')
+ self.assertEqual(server3.get_state(), 'router')
- #
- # 1. Enable auto start mode on client and check that server1 is used.
- #
+ server1_mleid = server1.get_mleid()
+ server2_mleid = server2.get_mleid()
+ server3_mleid = server3.get_mleid()
+ anycast_port = 53
+
+ #-------------------------------------------------------------------
+ # Enable server1 with unicast address mode
+
+ server1.srp_server_set_addr_mode('unicast')
+ server1.srp_server_set_enabled(True)
+ self.simulator.go(5)
+
+ #-------------------------------------------------------------------
+ # Enable auto start mode on client and check that server1 is selected
self.assertEqual(client.srp_client_get_state(), 'Disabled')
client.srp_client_enable_auto_start_mode()
@@ -104,42 +115,174 @@
self.simulator.go(2)
self.assertEqual(client.srp_client_get_state(), 'Enabled')
- self.assertTrue(server1.has_ipaddr(client.srp_client_get_server_address()))
+ self.assertEqual(client.srp_client_get_server_address(), server1_mleid)
- #
- # 2. Disable server1 and check client is stopped/disabled.
- #
+ #-------------------------------------------------------------------
+ # 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.
- #
+ #-------------------------------------------------------------------
+ # Enable server2 with unicast address mode and check client starts
+ # again.
+ server1.srp_server_set_addr_mode('unicast')
server2.srp_server_set_enabled(True)
self.simulator.go(5)
self.assertEqual(client.srp_client_get_state(), 'Enabled')
- server2_address = client.srp_client_get_server_address()
+ self.assertEqual(client.srp_client_get_server_address(), server2_mleid)
- #
- # 4. Enable both servers and check client stays with server2.
- #
+ #-------------------------------------------------------------------
+ # Enable server1 and check that client stays with server2
server1.srp_server_set_enabled(True)
self.simulator.go(5)
self.assertEqual(client.srp_client_get_state(), 'Enabled')
- self.assertEqual(client.srp_client_get_server_address(), server2_address)
+ self.assertEqual(client.srp_client_get_server_address(), server2_mleid)
- #
- # 5. Disable server2 and check client switches to server1.
- #
+ #-------------------------------------------------------------------
+ # 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_address(), server2_address)
+ self.assertEqual(client.srp_client_get_server_address(), server1_mleid)
+
+ #-------------------------------------------------------------------
+ # Enable server2 with anycast mode seq-num 1, and check that client
+ # switched to it.
+
+ server2.srp_server_set_addr_mode('anycast')
+ server2.srp_server_set_anycast_seq_num(1)
+ server2.srp_server_set_enabled(True)
+ self.simulator.go(5)
+ server2_alocs = server2.get_ip6_address(config.ADDRESS_TYPE.ALOC)
+ self.assertEqual(server2.srp_server_get_anycast_seq_num(), 1)
+ self.assertEqual(client.srp_client_get_state(), 'Enabled')
+ self.assertIn(client.srp_client_get_server_address(), server2_alocs)
+ self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+ #-------------------------------------------------------------------
+ # Enable server3 with anycast mode seq-num 2, and check that client
+ # switched to it since seq number is higher.
+
+ server3.srp_server_set_addr_mode('anycast')
+ server3.srp_server_set_anycast_seq_num(2)
+ server3.srp_server_set_enabled(True)
+ self.simulator.go(5)
+ server3_alocs = server3.get_ip6_address(config.ADDRESS_TYPE.ALOC)
+ self.assertEqual(server3.srp_server_get_anycast_seq_num(), 2)
+ self.assertEqual(client.srp_client_get_state(), 'Enabled')
+ self.assertIn(client.srp_client_get_server_address(), server3_alocs)
+ self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+ #-------------------------------------------------------------------
+ # Disable server3 and check that client goes back to server2.
+
+ server3.srp_server_set_enabled(False)
+ self.simulator.go(5)
+ self.assertEqual(client.srp_client_get_state(), 'Enabled')
+ self.assertIn(client.srp_client_get_server_address(), server2_alocs)
+ self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+ #-------------------------------------------------------------------
+ # Enable server3 with anycast mode seq-num 0 (which is smaller than
+ # server2 seq-num 1) and check that client stays with server2.
+
+ server3.srp_server_set_anycast_seq_num(0)
+ server3.srp_server_set_enabled(True)
+ self.simulator.go(5)
+ self.assertEqual(server3.srp_server_get_anycast_seq_num(), 0)
+ self.assertEqual(client.srp_client_get_state(), 'Enabled')
+ self.assertIn(client.srp_client_get_server_address(), server2_alocs)
+ self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+ #-------------------------------------------------------------------
+ # Disable server2 and check that client goes back to server3.
+
+ server2.srp_server_set_enabled(False)
+ self.simulator.go(5)
+ self.assertEqual(client.srp_client_get_state(), 'Enabled')
+ server3_alocs = server3.get_ip6_address(config.ADDRESS_TYPE.ALOC)
+ self.assertIn(client.srp_client_get_server_address(), server3_alocs)
+ self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+ #-------------------------------------------------------------------
+ # Disable server3 and check that client goes back to server1 with
+ # unicast address.
+
+ server3.srp_server_set_enabled(False)
+ self.simulator.go(5)
+ self.assertEqual(client.srp_client_get_state(), 'Enabled')
+ self.assertEqual(client.srp_client_get_server_address(), server1_mleid)
+
+ #-------------------------------------------------------------------
+ # Enable server2 with anycast mode seq-num 5, and check that client
+ # switched to it.
+
+ server2.srp_server_set_addr_mode('anycast')
+ server2.srp_server_set_anycast_seq_num(5)
+ server2.srp_server_set_enabled(True)
+ self.simulator.go(5)
+ server2_alocs = server2.get_ip6_address(config.ADDRESS_TYPE.ALOC)
+ self.assertEqual(server2.srp_server_get_anycast_seq_num(), 5)
+ self.assertEqual(client.srp_client_get_state(), 'Enabled')
+ self.assertIn(client.srp_client_get_server_address(), server2_alocs)
+ self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+ #-------------------------------------------------------------------
+ # Publish an entry on server3 with specific unicast address
+ # This entry should be now preferred over anycast of server2.
+
+ unicast_addr3 = 'fd00:0:0:0:0:3333:beef:cafe'
+ unicast_port3 = 1234
+ server3.netdata_publish_dnssrp_unicast(unicast_addr3, unicast_port3)
+ self.simulator.go(65)
+ self.assertEqual(client.srp_client_get_state(), 'Enabled')
+ self.assertEqual(client.srp_client_get_server_address(), unicast_addr3)
+ self.assertEqual(client.srp_client_get_server_port(), unicast_port3)
+
+ #-------------------------------------------------------------------
+ # Publish an entry on server1 with specific unicast address
+ # Client should still stay with server3 which it originally selected.
+
+ unicast_addr1 = 'fd00:0:0:0:0:2222:beef:cafe'
+ unicast_port1 = 10203
+ server1.srp_server_set_enabled(False)
+ server1.netdata_publish_dnssrp_unicast(unicast_addr1, unicast_port1)
+ self.simulator.go(65)
+ self.assertEqual(client.srp_client_get_state(), 'Enabled')
+ self.assertEqual(client.srp_client_get_server_address(), unicast_addr3)
+ self.assertEqual(client.srp_client_get_server_port(), unicast_port3)
+
+ #-------------------------------------------------------------------
+ # Unpublish the entry on server3. Now client should switch to entry
+ # from server1.
+
+ server3.netdata_unpublish_dnssrp()
+ self.simulator.go(65)
+ self.assertEqual(client.srp_client_get_state(), 'Enabled')
+ self.assertEqual(client.srp_client_get_server_address(), unicast_addr1)
+ self.assertEqual(client.srp_client_get_server_port(), unicast_port1)
+
+ #-------------------------------------------------------------------
+ # Unpublish the entry on server1 and check client goes back to anycast
+ # entry from server2.
+
+ server1.netdata_unpublish_dnssrp()
+ self.simulator.go(65)
+ self.assertEqual(client.srp_client_get_state(), 'Enabled')
+ self.assertIn(client.srp_client_get_server_address(), server2_alocs)
+ self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+ #-------------------------------------------------------------------
+ # Finally disable server2, and check that client is disabled.
+
+ server2.srp_server_set_enabled(False)
+ self.simulator.go(5)
+ self.assertEqual(client.srp_client_get_state(), 'Disabled')
if __name__ == '__main__':