diff --git a/include/openthread/instance.h b/include/openthread/instance.h
index 02e5b89b7..e0ff78f8b 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 (217)
+#define OPENTHREAD_API_VERSION (218)
/**
* @addtogroup api-instance
diff --git a/include/openthread/srp_client.h b/include/openthread/srp_client.h
index 1dc0c9e3e..bb291d414 100644
--- a/include/openthread/srp_client.h
+++ b/include/openthread/srp_client.h
@@ -75,8 +75,9 @@ typedef enum
typedef struct otSrpClientHostInfo
{
const char * mName; ///< Host name (label) string (NULL if not yet set).
- const otIp6Address * mAddresses; ///< Pointer to an array of host IPv6 addresses (NULL if not yet set).
+ const otIp6Address * mAddresses; ///< Array of host IPv6 addresses (NULL if not set or auto address is enabled).
uint8_t mNumAddresses; ///< Number of IPv6 addresses in `mAddresses` array.
+ bool mAutoAddress; ///< Indicates whether auto address mode is enabled or not.
otSrpClientItemState mState; ///< Host info state.
} otSrpClientHostInfo;
@@ -428,6 +429,27 @@ const otSrpClientHostInfo *otSrpClientGetHostInfo(otInstance *aInstance);
*/
otError otSrpClientSetHostName(otInstance *aInstance, const char *aName);
+/**
+ * This function enables auto host address mode.
+ *
+ * When enabled host IPv6 addresses are automatically set by SRP client using all the unicast addresses on Thread netif
+ * excluding all link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID address is
+ * added. The SRP client will automatically re-register when/if addresses on Thread netif are updated (new addresses
+ * are added or existing addresses are removed).
+ *
+ * The auto host address mode can be enabled before start or during operation of SRP client except when the host info
+ * is being removed (client is busy handling a remove request from an call to `otSrpClientRemoveHostAndServices()` and
+ * host info still being in either `STATE_TO_REMOVE` or `STATE_REMOVING` states).
+ *
+ * After auto host address mode is enabled, it can be disabled by a call to `otSrpClientSetHostAddresses()` which
+ * then explicitly sets the host addresses.
+ *
+ * @retval OT_ERROR_NONE Successfully enabled auto host address mode.
+ * @retval OT_ERROR_INVALID_STATE Host is being removed and therefore cannot enable auto host address mode.
+ *
+ */
+otError otSrpClientEnableAutoHostAddress(otInstance *aInstance);
+
/**
* This function sets/updates the list of host IPv6 address.
*
@@ -442,6 +464,9 @@ otError otSrpClientSetHostName(otInstance *aInstance, const char *aName);
* After a successful call to this function, `otSrpClientCallback` will be called to report the status of the address
* registration with SRP server.
*
+ * Calling this function disables auto host address mode if it was previously enabled from a successful call to
+ * `otSrpClientEnableAutoHostAddress()`.
+ *
* @param[in] aInstance A pointer to the OpenThread instance.
* @param[in] aIp6Addresses A pointer to the an array containing the host IPv6 addresses.
* @param[in] aNumAddresses The number of addresses in the @p aIp6Addresses array.
diff --git a/src/cli/README_SRP_CLIENT.md b/src/cli/README_SRP_CLIENT.md
index ab5dcdb77..ae48fbf1e 100644
--- a/src/cli/README_SRP_CLIENT.md
+++ b/src/cli/README_SRP_CLIENT.md
@@ -139,6 +139,14 @@ name:"dev4312", state:Registered, addrs:[fd00:0:0:0:0:0:0:1234, fd00:0:0:0:0:0:0
Done
```
+When auto host address mode is enabled.
+
+```bash
+srp client host
+name:"dev1234", state:Registered, addrs:auto
+Done
+```
+
### host name
Usage: `srp client host name [name]`
@@ -160,9 +168,17 @@ Done
### host address
-Usage : `srp client host address [
...]`
+Usage : `srp client host address [auto | ...]`
-Get the list of host addresses.
+Indicate auto address mode is enabled.
+
+```bash
+> srp client host address
+auto
+Done
+```
+
+Get the list of host addresses (when auto host address is not enabled).
```bash
> srp client host address
@@ -171,7 +187,14 @@ fd00:0:0:0:0:0:0:beef
Done
```
-Set the list of host addresses (can be set while client is running to update the host addresses)
+Enable auto host address mode. When enabled client will automatically use all Thread netif unicast addresses excluding all link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID address is added. SRP client will automatically re-register if/when addresses on Thread netif get changed (e.g., new address is added or existing address is removed).
+
+```bash
+> srp client host address auto
+Done
+```
+
+Explicitly set the list of host addresses (can be set while client is running to update the host addresses), also disabled auto host address mode.
```bash
> srp client host address fd00::cafe
diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp
index 4d81cd901..0732ac2e4 100644
--- a/src/cli/cli_srp_client.cpp
+++ b/src/cli/cli_srp_client.cpp
@@ -160,10 +160,21 @@ template <> otError SrpClient::Process(Arg aArgs[])
{
const otSrpClientHostInfo *hostInfo = otSrpClientGetHostInfo(GetInstancePtr());
- for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++)
+ if (hostInfo->mAutoAddress)
{
- OutputIp6AddressLine(hostInfo->mAddresses[index]);
+ OutputLine("auto");
}
+ else
+ {
+ for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++)
+ {
+ OutputIp6AddressLine(hostInfo->mAddresses[index]);
+ }
+ }
+ }
+ else if (aArgs[1] == "auto")
+ {
+ error = otSrpClientEnableAutoHostAddress(GetInstancePtr());
}
else
{
@@ -447,19 +458,28 @@ void SrpClient::OutputHostInfo(uint8_t aIndentSize, const otSrpClientHostInfo &a
OutputFormat("(null)");
}
- OutputFormat(", state:%s, addrs:[", otSrpClientItemStateToString(aHostInfo.mState));
+ OutputFormat(", state:%s, addrs:", otSrpClientItemStateToString(aHostInfo.mState));
- for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++)
+ if (aHostInfo.mAutoAddress)
{
- if (index > 0)
+ OutputLine("auto");
+ }
+ else
+ {
+ OutputFormat("[");
+
+ for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++)
{
- OutputFormat(", ");
+ if (index > 0)
+ {
+ OutputFormat(", ");
+ }
+
+ OutputIp6Address(aHostInfo.mAddresses[index]);
}
- OutputIp6Address(aHostInfo.mAddresses[index]);
+ OutputLine("]");
}
-
- OutputLine("]");
}
void SrpClient::OutputServiceList(uint8_t aIndentSize, const otSrpClientService *aServices)
diff --git a/src/core/api/srp_client_api.cpp b/src/core/api/srp_client_api.cpp
index a0c16e3d9..3ec356f34 100644
--- a/src/core/api/srp_client_api.cpp
+++ b/src/core/api/srp_client_api.cpp
@@ -124,6 +124,11 @@ otError otSrpClientSetHostName(otInstance *aInstance, const char *aName)
return AsCoreType(aInstance).Get().SetHostName(aName);
}
+otError otSrpClientEnableAutoHostAddress(otInstance *aInstance)
+{
+ return AsCoreType(aInstance).Get().EnableAutoHostAddress();
+}
+
otError otSrpClientSetHostAddresses(otInstance *aInstance, const otIp6Address *aIp6Addresses, uint8_t aNumAddresses)
{
return AsCoreType(aInstance).Get().SetHostAddresses(AsCoreTypePtr(aIp6Addresses), aNumAddresses);
diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp
index e148d659a..a65fe9251 100644
--- a/src/core/net/srp_client.cpp
+++ b/src/core/net/srp_client.cpp
@@ -76,10 +76,20 @@ void Client::HostInfo::SetState(ItemState aState)
}
}
+void Client::HostInfo::EnableAutoAddress(void)
+{
+ mAddresses = nullptr;
+ mNumAddresses = 0;
+ mAutoAddress = true;
+
+ LogInfo("HostInfo enabled auto address", GetNumAddresses());
+}
+
void Client::HostInfo::SetAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses)
{
mAddresses = aAddresses;
mNumAddresses = aNumAddresses;
+ mAutoAddress = false;
LogInfo("HostInfo set %d addrs", GetNumAddresses());
@@ -239,6 +249,7 @@ Client::Client(Instance &aInstance)
, mState(kStateStopped)
, mTxFailureRetryCount(0)
, mShouldRemoveKeyLease(false)
+ , mAutoHostAddressAddedMeshLocal(false)
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
, mServiceKeyRecordEnabled(false)
#endif
@@ -415,6 +426,22 @@ void Client::HandleNotifierEvents(Events aEvents)
ProcessAutoStart();
}
#endif
+
+ if (mHostInfo.IsAutoAddressEnabled())
+ {
+ Events::Flags eventFlags = (kEventIp6AddressAdded | kEventIp6AddressRemoved);
+
+ if (mAutoHostAddressAddedMeshLocal)
+ {
+ eventFlags |= kEventThreadMeshLocalAddrChanged;
+ }
+
+ if (aEvents.ContainsAny(eventFlags))
+ {
+ IgnoreError(UpdateHostInfoStateOnAddressChange());
+ UpdateState();
+ }
+ }
}
void Client::HandleRoleChanged(void)
@@ -466,11 +493,37 @@ exit:
return error;
}
+Error Client::EnableAutoHostAddress(void)
+{
+ Error error = kErrorNone;
+
+ VerifyOrExit(!mHostInfo.IsAutoAddressEnabled());
+ SuccessOrExit(error = UpdateHostInfoStateOnAddressChange());
+
+ mHostInfo.EnableAutoAddress();
+ UpdateState();
+
+exit:
+ return error;
+}
+
Error Client::SetHostAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses)
{
Error error = kErrorNone;
VerifyOrExit((aAddresses != nullptr) && (aNumAddresses > 0), error = kErrorInvalidArgs);
+ SuccessOrExit(error = UpdateHostInfoStateOnAddressChange());
+
+ mHostInfo.SetAddresses(aAddresses, aNumAddresses);
+ UpdateState();
+
+exit:
+ return error;
+}
+
+Error Client::UpdateHostInfoStateOnAddressChange(void)
+{
+ Error error = kErrorNone;
VerifyOrExit((mHostInfo.GetState() != kToRemove) && (mHostInfo.GetState() != kRemoving),
error = kErrorInvalidState);
@@ -484,9 +537,6 @@ Error Client::SetHostAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddre
mHostInfo.SetState(kToRefresh);
}
- mHostInfo.SetAddresses(aAddresses, aNumAddresses);
- UpdateState();
-
exit:
return error;
}
@@ -1008,10 +1058,9 @@ exit:
return error;
}
-Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) const
+Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo)
{
- Error error = kErrorNone;
- Dns::ResourceRecord rr;
+ Error error = kErrorNone;
//----------------------------------
// Host Description Instruction
@@ -1024,16 +1073,37 @@ Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) c
// AAAA RRs
- rr.Init(Dns::ResourceRecord::kTypeAaaa);
- rr.SetTtl(GetTtl());
- rr.SetLength(sizeof(Ip6::Address));
-
- for (uint8_t index = 0; index < mHostInfo.GetNumAddresses(); index++)
+ if (mHostInfo.IsAutoAddressEnabled())
{
- SuccessOrExit(error = AppendHostName(aMessage, aInfo));
- SuccessOrExit(error = aMessage.Append(rr));
- SuccessOrExit(error = aMessage.Append(mHostInfo.GetAddress(index)));
- aInfo.mRecordCount++;
+ // Append all addresses on Thread netif excluding link-local and
+ // mesh-local addresses. If no address is appended, we include
+ // the mesh local address.
+
+ mAutoHostAddressAddedMeshLocal = true;
+
+ for (const Ip6::Netif::UnicastAddress &unicastAddress : Get().GetUnicastAddresses())
+ {
+ if (unicastAddress.GetAddress().IsLinkLocal() ||
+ Get().IsMeshLocalAddress(unicastAddress.GetAddress()))
+ {
+ continue;
+ }
+
+ SuccessOrExit(error = AppendAaaaRecord(unicastAddress.GetAddress(), aMessage, aInfo));
+ mAutoHostAddressAddedMeshLocal = false;
+ }
+
+ if (mAutoHostAddressAddedMeshLocal)
+ {
+ SuccessOrExit(error = AppendAaaaRecord(Get().GetMeshLocal64(), aMessage, aInfo));
+ }
+ }
+ else
+ {
+ for (uint8_t index = 0; index < mHostInfo.GetNumAddresses(); index++)
+ {
+ SuccessOrExit(error = AppendAaaaRecord(mHostInfo.GetAddress(index), aMessage, aInfo));
+ }
}
// KEY RR
@@ -1045,6 +1115,24 @@ exit:
return error;
}
+Error Client::AppendAaaaRecord(const Ip6::Address &aAddress, Message &aMessage, Info &aInfo) const
+{
+ Error error;
+ Dns::ResourceRecord rr;
+
+ rr.Init(Dns::ResourceRecord::kTypeAaaa);
+ rr.SetTtl(GetTtl());
+ rr.SetLength(sizeof(Ip6::Address));
+
+ SuccessOrExit(error = AppendHostName(aMessage, aInfo));
+ SuccessOrExit(error = aMessage.Append(rr));
+ SuccessOrExit(error = aMessage.Append(aAddress));
+ aInfo.mRecordCount++;
+
+exit:
+ return error;
+}
+
Error Client::AppendKeyRecord(Message &aMessage, Info &aInfo) const
{
Error error;
@@ -1519,7 +1607,7 @@ void Client::UpdateState(void)
// host address, otherwise no need to send SRP update message.
// The exception is when removing host info where we allow
// for empty service list.
- VerifyOrExit(!mServices.IsEmpty() && (mHostInfo.GetNumAddresses() > 0));
+ VerifyOrExit(!mServices.IsEmpty() && (mHostInfo.IsAutoAddressEnabled() || (mHostInfo.GetNumAddresses() > 0)));
// Fall through
diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp
index 8e7cca7dc..4a3502bb6 100644
--- a/src/core/net/srp_client.hpp
+++ b/src/core/net/srp_client.hpp
@@ -103,7 +103,7 @@ public:
* This type represents an SRP client host info.
*
*/
- class HostInfo : public otSrpClientHostInfo, public Clearable
+ class HostInfo : public otSrpClientHostInfo, private Clearable
{
friend class Client;
@@ -128,6 +128,15 @@ public:
*/
const char *GetName(void) const { return mName; }
+ /**
+ * This method indicates whether or not the host auto address mode is enabled.
+ *
+ * @retval TRUE If the auto address mode is enabled.
+ * @retval FALSE If the auto address mode is disabled.
+ *
+ */
+ bool IsAutoAddressEnabled(void) const { return mAutoAddress; }
+
/**
* This method gets the number of host IPv6 addresses.
*
@@ -158,6 +167,7 @@ public:
void SetName(const char *aName) { mName = aName; }
void SetState(ItemState aState);
void SetAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses);
+ void EnableAutoAddress(void);
};
/**
@@ -489,16 +499,16 @@ public:
const HostInfo &GetHostInfo(void) const { return mHostInfo; }
/**
- * This function sets the host name label.
+ * This method sets the host name label.
*
- * After a successful call to this function, `Callback` will be called to report the status of host info
+ * After a successful call to this method, `Callback` will be called to report the status of host info
* registration with SRP server.
*
* The host name can be set before client is started or after start but before host info is registered with server
* (host info should be in either `kToAdd` or `kRemoved`).
*
* @param[in] aName A pointer to host name label string (MUST NOT be NULL). Pointer the string buffer MUST
- * persist and remain valid and constant after return from this function.
+ * persist and remain valid and constant after return from this method.
*
* @retval kErrorNone The host name label was set successfully.
* @retval kErrorInvalidArgs The @p aName is NULL.
@@ -507,6 +517,27 @@ public:
*/
Error SetHostName(const char *aName);
+ /**
+ * This method enables auto host address mode.
+ *
+ * When enabled host IPv6 addresses are automatically set by SRP client using all the unicast addresses on Thread
+ * netif excluding the link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID
+ * address is added. The SRP client will automatically re-register when/if addresses on Thread netif are updated
+ * (new addresses are added or existing addresses are removed).
+ *
+ * The auto host address mode can be enabled before start or during operation of SRP client except when the host
+ * info is being removed (client is busy handling a remove request from an call to `RemoveHostAndServices()` and
+ * host info still being in either `kStateToRemove` or `kStateRemoving` states).
+ *
+ * After auto host address mode is enabled, it can be disabled by a call to `SetHostAddresses()` which then
+ * explicitly sets the host addresses.
+ *
+ * @retval kErrorNone Successfully enabled auto host address mode.
+ * @retval kErrorInvalidState Host is being removed and therefore cannot enable auto host address mode.
+ *
+ */
+ Error EnableAutoHostAddress(void);
+
/**
* This method sets/updates the list of host IPv6 address.
*
@@ -518,6 +549,9 @@ public:
* After a successful call to this method, `Callback` will be called to report the status of the address
* registration with SRP server.
*
+ * Calling this method disables auto host address mode if it was previously enabled from a successful call to
+ * `EnableAutoHostAddress()`.
+ *
* @param[in] aAddresses A pointer to the an array containing the host IPv6 addresses.
* @param[in] aNumAddresses The number of addresses in the @p aAddresses array.
*
@@ -884,6 +918,7 @@ private:
void Pause(void);
void HandleNotifierEvents(Events aEvents);
void HandleRoleChanged(void);
+ Error UpdateHostInfoStateOnAddressChange(void);
void UpdateServiceStateToRemove(Service &aService);
State GetState(void) const { return mState; }
void SetState(State aState);
@@ -895,10 +930,11 @@ private:
Error PrepareUpdateMessage(Message &aMessage);
Error ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair);
Error AppendServiceInstructions(Service &aService, Message &aMessage, Info &aInfo);
- Error AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) const;
+ Error AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo);
Error AppendKeyRecord(Message &aMessage, Info &aInfo) const;
Error AppendDeleteAllRrsets(Message &aMessage) const;
Error AppendHostName(Message &aMessage, Info &aInfo, bool aDoNotCompress = false) const;
+ Error AppendAaaaRecord(const Ip6::Address &aAddress, Message &aMessage, Info &aInfo) const;
Error AppendUpdateLeaseOptRecord(Message &aMessage) const;
Error AppendSignature(Message &aMessage, Info &aInfo);
void UpdateRecordLengthInMessage(Dns::ResourceRecord &aRecord, uint16_t aOffset, Message &aMessage) const;
@@ -938,6 +974,7 @@ private:
State mState;
uint8_t mTxFailureRetryCount : 4;
bool mShouldRemoveKeyLease : 1;
+ bool mAutoHostAddressAddedMeshLocal : 1;
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
bool mServiceKeyRecordEnabled : 1;
#endif
diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am
index 72906e699..200c46a3c 100644
--- a/tests/scripts/thread-cert/Makefile.am
+++ b/tests/scripts/thread-cert/Makefile.am
@@ -188,6 +188,7 @@ EXTRA_DIST = \
test_router_upgrade.py \
test_service.py \
test_set_mliid.py \
+ test_srp_auto_host_address.py \
test_srp_auto_start_mode.py \
test_srp_client_remove_host.py \
test_srp_client_save_server_info.py \
@@ -264,6 +265,7 @@ check_SCRIPTS = \
test_router_reattach.py \
test_router_upgrade.py \
test_service.py \
+ test_srp_auto_host_address.py \
test_srp_auto_start_mode.py \
test_srp_client_remove_host.py \
test_srp_client_save_server_info.py \
diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py
index 9b317bac5..ccc400ed2 100755
--- a/tests/scripts/thread-cert/node.py
+++ b/tests/scripts/thread-cert/node.py
@@ -1146,6 +1146,10 @@ class NodeImpl:
self.send_command(f'srp client host clear')
self._expect_done()
+ def srp_client_enable_auto_host_address(self):
+ self.send_command(f'srp client host address auto')
+ self._expect_done()
+
def srp_client_set_host_address(self, *addrs: str):
self.send_command(f'srp client host address {" ".join(addrs)}')
self._expect_done()
diff --git a/tests/scripts/thread-cert/test_srp_auto_host_address.py b/tests/scripts/thread-cert/test_srp_auto_host_address.py
new file mode 100755
index 000000000..bb9ac6e1e
--- /dev/null
+++ b/tests/scripts/thread-cert/test_srp_auto_host_address.py
@@ -0,0 +1,258 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2022, 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 config
+import thread_cert
+
+# Test description:
+# This test verifies SRP client auto host address mode.
+#
+# Topology:
+# SRP client (leader)
+# |
+# |
+# SRP server (router)
+#
+
+CLIENT = 1
+SERVER = 2
+
+
+class SrpAutoHostAddress(thread_cert.TestCase):
+ USE_MESSAGE_FACTORY = False
+ SUPPORT_NCP = False
+
+ TOPOLOGY = {
+ CLIENT: {
+ 'name': 'SRP_CLIENT',
+ 'mode': 'rdn',
+ },
+ SERVER: {
+ 'name': 'SRP_SERVER',
+ 'mode': 'rdn',
+ },
+ }
+
+ def test(self):
+ client = self.nodes[CLIENT]
+ server = self.nodes[SERVER]
+
+ #-------------------------------------------------------------------
+ # Form the network.
+
+ client.srp_server_set_enabled(False)
+ client.start()
+ self.simulator.go(15)
+ self.assertEqual(client.get_state(), 'leader')
+
+ server.start()
+ self.simulator.go(5)
+ self.assertEqual(server.get_state(), 'router')
+
+ #-------------------------------------------------------------------
+ # Enable SRP server
+
+ server.srp_server_set_enabled(True)
+ self.simulator.go(5)
+
+ #-------------------------------------------------------------------
+ # Enable auto start mode on SRP client
+
+ 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')
+
+ #-------------------------------------------------------------------
+ # Set host name and enable auto host address on client
+
+ client.srp_client_set_host_name('host')
+ client.srp_client_enable_auto_host_address()
+
+ #-------------------------------------------------------------------
+ # Register a service on client
+
+ client.srp_client_add_service('test_srv', '_test._udo', 12345, 0, 0)
+ self.simulator.go(2)
+ self.check_registered_addresses(client, server)
+
+ #-------------------------------------------------------------------
+ # Add an address and check the SRP client re-registered and updated
+ # server with new address.
+
+ client.add_ipaddr('fd00:1:2:3:4:5:6:7')
+
+ self.simulator.go(5)
+ client_addresses = [addr.strip() for addr in client.get_addrs()]
+ self.assertIn('fd00:1:2:3:4:5:6:7', client_addresses)
+ self.check_registered_addresses(client, server)
+
+ #-------------------------------------------------------------------
+ # Remove the address and check the SRP client re-registered and updated
+ # server.
+
+ client.del_ipaddr('fd00:1:2:3:4:5:6:7')
+
+ self.simulator.go(5)
+ client_addresses = [addr.strip() for addr in client.get_addrs()]
+ self.assertNotIn('fd00:1:2:3:4:5:6:7', client_addresses)
+ self.check_registered_addresses(client, server)
+
+ #-------------------------------------------------------------------
+ # Add an SLAAC on-mesh prefix (which will trigger an address to be
+ # added) and check that the SRP client re-registered and updated
+ # server with the new address.
+
+ client.add_prefix('fd00:abba:cafe:bee::/64', 'paos')
+ client.register_netdata()
+ self.simulator.go(5)
+
+ slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:abba:cafe:bee:')]
+ self.assertEqual(len(slaac_addr), 1)
+ self.check_registered_addresses(client, server)
+
+ #-------------------------------------------------------------------
+ # Add another SLAAC on-mesh prefix and check that the SRP client
+ # re-registered and updated server with all address.
+
+ client.add_prefix('fd00:9:8:7::/64', 'paos')
+ client.register_netdata()
+ self.simulator.go(5)
+
+ slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:9:8:7:')]
+ self.assertEqual(len(slaac_addr), 1)
+ self.check_registered_addresses(client, server)
+
+ #-------------------------------------------------------------------
+ # Remove the on-mesh prefix (which will trigger an address to be
+ # removed) and check that the SRP client re-registered and updated
+ # server with the remaining address.
+
+ client.remove_prefix('fd00:abba:cafe:bee::/64')
+ client.register_netdata()
+ self.simulator.go(5)
+
+ slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:abba:cafe:bee:')]
+ self.assertEqual(len(slaac_addr), 0)
+ self.check_registered_addresses(client, server)
+
+ #-------------------------------------------------------------------
+ # Remove the next on-mesh prefix. Check that SRP client re-registered
+ # now with only ML-EID.
+
+ client.remove_prefix('fd00:9:8:7::/64')
+ client.register_netdata()
+ self.simulator.go(5)
+
+ slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:9:8:7:')]
+ self.assertEqual(len(slaac_addr), 0)
+ self.check_registered_addresses(client, server)
+
+ #-------------------------------------------------------------------
+ # Explicitly set the host addresses (which disables the auto host
+ # address mode) and check that only the new addresses are registered.
+
+ client.srp_client_set_host_address('fd00:f:e:d:c:b:a:9')
+ self.simulator.go(5)
+
+ self.assertEqual(client.srp_client_get_host_state(), 'Registered')
+ server_hosts = server.srp_server_get_hosts()
+ self.assertEqual(len(server_hosts), 1)
+ server_host = server_hosts[0]
+ self.assertEqual(server_host['deleted'], 'false')
+ self.assertEqual(server_host['fullname'], 'host.default.service.arpa.')
+ host_addresses = [addr.strip() for addr in server_host['addresses']]
+ self.assertEqual(len(host_addresses), 1)
+ self.assertEqual(host_addresses[0], 'fd00:f:e:d:c:b:a:9')
+
+ #-------------------------------------------------------------------
+ # Re-enable auto host address mode and check that addresses are
+ # updated and registered properly.
+
+ client.srp_client_enable_auto_host_address()
+ self.simulator.go(5)
+ self.check_registered_addresses(client, server)
+
+ def check_registered_addresses(self, client, server):
+ # Ensure client has registered successfully.
+ self.assertEqual(client.srp_client_get_host_state(), 'Registered')
+
+ # Check the host info on server.
+ server_hosts = server.srp_server_get_hosts()
+ self.assertEqual(len(server_hosts), 1)
+ server_host = server_hosts[0]
+ self.assertEqual(server_host['deleted'], 'false')
+ self.assertEqual(server_host['fullname'], 'host.default.service.arpa.')
+
+ # Check the host addresses on server to match client.
+
+ host_addresses = [addr.strip() for addr in server_host['addresses']]
+ client_addresses = [addr.strip() for addr in client.get_addrs()]
+
+ # All registered addresses must be in client list of addresses.
+
+ for addr in host_addresses:
+ self.assertIn(addr, client_addresses)
+
+ # All addresses on client excluding link-local and mesh-local
+ # addresses must be seen on server side. But if there was
+ # no address, then mesh-local address should be the only
+ # one registered.
+
+ client_mleid = client.get_mleid()
+ checked_address = False
+
+ for addr in client_addresses:
+ if not self.is_address_link_local(addr) and not self.is_address_locator(addr) and addr != client_mleid:
+ self.assertIn(addr, host_addresses)
+ checked_address = True
+
+ if not checked_address:
+ self.assertEqual(len(host_addresses), 1)
+ self.assertIn(client_mleid, host_addresses)
+
+ def is_address_locator(self, addr):
+ # Checks if an IPv6 address is a locator (IID should match `0:ff:fe00:xxxx`)
+ u32s = addr.split(':')
+ self.assertEqual(len(u32s), 8)
+ return ':'.join(u32s[4:]).startswith('0:ff:fe00:')
+
+ def is_address_link_local(self, addr):
+ # Checks if an IPv6 address is link-local
+ return addr.startswith('fe80:')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py
index 5e24bedec..3bfb7a830 100644
--- a/tests/toranj/cli/cli.py
+++ b/tests/toranj/cli/cli.py
@@ -329,6 +329,9 @@ class Node(object):
def srp_client_clear_host(self):
self._cli_no_output('srp client host clear')
+ def srp_client_enable_auto_host_address(self):
+ self._cli_no_output('srp client host address auto')
+
def srp_client_set_host_address(self, *addrs):
self._cli_no_output('srp client host address', *addrs)