mirror of
https://github.com/espressif/openthread.git
synced 2026-08-12 21:57:47 +00:00
[srp-server] notify advertising proxy of host/service expiration (#6248)
There is an issue that the SRP host or service are not deregistered by the Advertising Proxy when the host or service is expired. This is because we didn't notifies the Advertising Proxy of the host/service expiration. This commit fixes this issue and adds tests for it.
This commit is contained in:
+102
-54
@@ -132,7 +132,7 @@ otError Server::SetLeaseRange(uint32_t aMinLease, uint32_t aMaxLease, uint32_t a
|
||||
|
||||
// TODO: Support longer LEASE.
|
||||
// We use milliseconds timer for LEASE & KEY-LEASE, this is to avoid overflow.
|
||||
VerifyOrExit(aMaxKeyLease <= TimerMilli::kMaxDelay / 1000, error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(aMaxKeyLease <= Time::MsecToSec(TimerMilli::kMaxDelay), error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(aMinLease <= aMaxLease, error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(aMinKeyLease <= aMaxKeyLease, error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(aMinLease <= aMinKeyLease, error = OT_ERROR_INVALID_ARGS);
|
||||
@@ -214,11 +214,41 @@ void Server::AddHost(Host *aHost)
|
||||
IgnoreError(mHosts.Add(*aHost));
|
||||
}
|
||||
|
||||
void Server::RemoveAndFreeHost(Host *aHost)
|
||||
void Server::RemoveHost(Host *aHost, bool aRetainName, bool aNotifyServiceHandler)
|
||||
{
|
||||
otLogInfoSrp("[server] fully remove host %s", aHost->GetFullName());
|
||||
IgnoreError(mHosts.Remove(*aHost));
|
||||
aHost->Free();
|
||||
VerifyOrExit(aHost != nullptr);
|
||||
|
||||
aHost->mLease = 0;
|
||||
aHost->ClearResources();
|
||||
|
||||
if (aRetainName)
|
||||
{
|
||||
otLogInfoSrp("[server] remove host '%s' (but retain its name)", aHost->mFullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
aHost->mKeyLease = 0;
|
||||
IgnoreError(mHosts.Remove(*aHost));
|
||||
otLogInfoSrp("[server] fully remove host '%s'", aHost->mFullName);
|
||||
}
|
||||
|
||||
if (aNotifyServiceHandler && mServiceUpdateHandler != nullptr)
|
||||
{
|
||||
mServiceUpdateHandler(aHost, kDefaultEventsHandlerTimeout, mServiceUpdateHandlerContext);
|
||||
// We don't wait for the reply from the service update handler,
|
||||
// but always remove the host (and its services) regardless of
|
||||
// host/service update result. Because removing a host should fail
|
||||
// only when there is system failure of the platform mDNS implementation
|
||||
// and in which case the host is not expected to be still registered.
|
||||
}
|
||||
|
||||
if (!aRetainName)
|
||||
{
|
||||
aHost->Free();
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
const Server::Service *Server::FindService(const char *aFullName) const
|
||||
@@ -321,25 +351,17 @@ void Server::CommitSrpUpdate(otError aError,
|
||||
if (aHost.GetKeyLease() == 0)
|
||||
{
|
||||
otLogInfoSrp("[server] remove key of host %s", aHost.GetFullName());
|
||||
|
||||
if (existingHost != nullptr)
|
||||
{
|
||||
RemoveAndFreeHost(existingHost);
|
||||
}
|
||||
RemoveHost(existingHost, /* aRetainName */ false, /* aNotifyServiceHandler */ false);
|
||||
}
|
||||
else if (existingHost != nullptr)
|
||||
{
|
||||
Service *service = nullptr;
|
||||
|
||||
existingHost->SetLease(aHost.GetLease());
|
||||
existingHost->SetKeyLease(aHost.GetKeyLease());
|
||||
|
||||
// Clear all resources associated to this host and its services.
|
||||
existingHost->ClearResources();
|
||||
otLogInfoSrp("[server] remove host '%s' (but retain its name)", existingHost->GetFullName());
|
||||
RemoveHost(existingHost, /* aRetainName */ true, /* aNotifyServiceHandler */ false);
|
||||
while ((service = existingHost->GetNextService(service)) != nullptr)
|
||||
{
|
||||
service->DeleteResourcesButRetainName();
|
||||
existingHost->RemoveService(service, /* aRetainName */ true, /* aNotifyServiceHandler */ false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,10 +382,7 @@ void Server::CommitSrpUpdate(otError aError,
|
||||
|
||||
if (service->mIsDeleted)
|
||||
{
|
||||
if (existingService != nullptr)
|
||||
{
|
||||
existingService->DeleteResourcesButRetainName();
|
||||
}
|
||||
existingHost->RemoveService(existingService, /* aRetainName */ true, /* aNotifyServiceHandler */ false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -428,9 +447,11 @@ void Server::Stop(void)
|
||||
|
||||
while (!mHosts.IsEmpty())
|
||||
{
|
||||
mHosts.Pop()->Free();
|
||||
RemoveHost(mHosts.GetHead(), /* aRetainName */ false, /* aNotifyServiceHandler */ true);
|
||||
}
|
||||
|
||||
// TODO: We should cancel any oustanding service updates, but current
|
||||
// OTBR mDNS publisher cannot properly handle it.
|
||||
while (!mOutstandingUpdates.IsEmpty())
|
||||
{
|
||||
mOutstandingUpdates.Pop()->Free();
|
||||
@@ -530,7 +551,7 @@ void Server::HandleDnsUpdate(Message & aMessage,
|
||||
// Per 2.3.2 of SRP draft 6, no prerequisites should be included in a SRP update.
|
||||
VerifyOrExit(aDnsHeader.GetPrerequisiteRecordCount() == 0, error = OT_ERROR_FAILED);
|
||||
|
||||
host = Host::New();
|
||||
host = Host::New(GetInstance());
|
||||
VerifyOrExit(host != nullptr, error = OT_ERROR_NO_BUFS);
|
||||
SuccessOrExit(error = ProcessUpdateSection(*host, aMessage, aDnsHeader, zone, aOffset));
|
||||
|
||||
@@ -1147,7 +1168,7 @@ void Server::HandleLeaseTimer(void)
|
||||
otLogInfoSrp("[server] KEY LEASE of host %s expired", host->GetFullName());
|
||||
|
||||
// Removes the whole host and all services if the KEY RR expired.
|
||||
RemoveAndFreeHost(host);
|
||||
RemoveHost(host, /* aRetainName */ false, /* aNotifyServiceHandler */ true);
|
||||
}
|
||||
else if (host->IsDeleted())
|
||||
{
|
||||
@@ -1168,7 +1189,7 @@ void Server::HandleLeaseTimer(void)
|
||||
if (service->GetKeyExpireTime() <= now)
|
||||
{
|
||||
otLogInfoSrp("[server] KEY LEASE of service %s expired", service->mFullName);
|
||||
host->RemoveAndFreeService(service);
|
||||
host->RemoveService(service, /* aRetainName */ false, /* aNotifyServiceHandler */ true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1185,10 +1206,10 @@ void Server::HandleLeaseTimer(void)
|
||||
otLogInfoSrp("[server] LEASE of host %s expired", host->GetFullName());
|
||||
|
||||
// If the host expired, delete all resources of this host and its services.
|
||||
host->DeleteResourcesButRetainName();
|
||||
RemoveHost(host, /* aRetainName */ true, /* aNotifyServiceHandler */ true);
|
||||
while ((service = host->GetNextService(service)) != nullptr)
|
||||
{
|
||||
service->DeleteResourcesButRetainName();
|
||||
host->RemoveService(service, /* aRetainName */ true, /* aNotifyServiceHandler */ true);
|
||||
}
|
||||
|
||||
earliestExpireTime = OT_MIN(earliestExpireTime, host->GetKeyExpireTime());
|
||||
@@ -1216,8 +1237,8 @@ void Server::HandleLeaseTimer(void)
|
||||
{
|
||||
otLogInfoSrp("[server] LEASE of service %s expired", service->mFullName);
|
||||
|
||||
// The service gets expired, delete it.
|
||||
service->DeleteResourcesButRetainName();
|
||||
// The service is expired, delete it.
|
||||
host->RemoveService(service, /* aRetainName */ true, /* aNotifyServiceHandler */ true);
|
||||
earliestExpireTime = OT_MIN(earliestExpireTime, service->GetKeyExpireTime());
|
||||
}
|
||||
else
|
||||
@@ -1236,7 +1257,7 @@ void Server::HandleLeaseTimer(void)
|
||||
{
|
||||
if (!mLeaseTimer.IsRunning() || earliestExpireTime <= mLeaseTimer.GetFireTime())
|
||||
{
|
||||
otLogInfoSrp("[server] lease timer is scheduled for %u seconds", (earliestExpireTime - now) / 1000);
|
||||
otLogInfoSrp("[server] lease timer is scheduled for %u seconds", Time::MsecToSec(earliestExpireTime - now));
|
||||
mLeaseTimer.StartAt(earliestExpireTime, 0);
|
||||
}
|
||||
}
|
||||
@@ -1384,13 +1405,6 @@ void Server::Service::ClearResources(void)
|
||||
mTxtLength = 0;
|
||||
}
|
||||
|
||||
void Server::Service::DeleteResourcesButRetainName(void)
|
||||
{
|
||||
mIsDeleted = true;
|
||||
ClearResources();
|
||||
otLogInfoSrp("[server] remove service '%s' (but retain its name)", mFullName);
|
||||
}
|
||||
|
||||
otError Server::Service::CopyResourcesFrom(const Service &aService)
|
||||
{
|
||||
otError error;
|
||||
@@ -1426,7 +1440,7 @@ bool Server::Service::MatchesServiceName(const char *aServiceName) const
|
||||
return j == 0 && i > 0 && mFullName[i - 1] == '.';
|
||||
}
|
||||
|
||||
Server::Host *Server::Host::New(void)
|
||||
Server::Host *Server::Host::New(Instance &aInstance)
|
||||
{
|
||||
void *buf;
|
||||
Host *host = nullptr;
|
||||
@@ -1434,7 +1448,7 @@ Server::Host *Server::Host::New(void)
|
||||
buf = Instance::HeapCAlloc(1, sizeof(Host));
|
||||
VerifyOrExit(buf != nullptr);
|
||||
|
||||
host = new (buf) Host();
|
||||
host = new (buf) Host(aInstance);
|
||||
|
||||
exit:
|
||||
return host;
|
||||
@@ -1442,13 +1456,14 @@ exit:
|
||||
|
||||
void Server::Host::Free(void)
|
||||
{
|
||||
RemoveAndFreeAllServices();
|
||||
FreeAllServices();
|
||||
Instance::HeapFree(mFullName);
|
||||
Instance::HeapFree(this);
|
||||
}
|
||||
|
||||
Server::Host::Host(void)
|
||||
: mFullName(nullptr)
|
||||
Server::Host::Host(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mFullName(nullptr)
|
||||
, mAddressesNum(0)
|
||||
, mNext(nullptr)
|
||||
, mLease(0)
|
||||
@@ -1526,20 +1541,61 @@ exit:
|
||||
return service;
|
||||
}
|
||||
|
||||
void Server::Host::RemoveAndFreeService(Service *aService)
|
||||
void Server::Host::RemoveService(Service *aService, bool aRetainName, bool aNotifyServiceHandler)
|
||||
{
|
||||
if (aService != nullptr)
|
||||
const Server &server = Get<Server>();
|
||||
|
||||
VerifyOrExit(aService != nullptr);
|
||||
|
||||
aService->mIsDeleted = true;
|
||||
|
||||
if (aRetainName)
|
||||
{
|
||||
aService->ClearResources();
|
||||
otLogInfoSrp("[server] remove service '%s' (but retain its name)", aService->mFullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
otLogInfoSrp("[server] fully remove service '%s'", aService->mFullName);
|
||||
}
|
||||
|
||||
IgnoreError(mServices.Remove(*aService));
|
||||
|
||||
if (aNotifyServiceHandler && server.mServiceUpdateHandler != nullptr)
|
||||
{
|
||||
LinkedList<Service> remainingServices = mServices;
|
||||
|
||||
mServices.Clear();
|
||||
IgnoreError(mServices.Add(*aService));
|
||||
|
||||
server.mServiceUpdateHandler(this, kDefaultEventsHandlerTimeout, server.mServiceUpdateHandlerContext);
|
||||
// We don't wait for the reply from the service update handler,
|
||||
// but always remove the service regardless of service update result.
|
||||
// Because removing a service should fail only when there is system
|
||||
// failure of the platform mDNS implementation and in which case the
|
||||
// service is not expected to be still registered.
|
||||
|
||||
mServices = remainingServices;
|
||||
}
|
||||
|
||||
if (aRetainName)
|
||||
{
|
||||
IgnoreError(mServices.Add(*aService));
|
||||
}
|
||||
else
|
||||
{
|
||||
IgnoreError(mServices.Remove(*aService));
|
||||
aService->Free();
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Server::Host::RemoveAndFreeAllServices(void)
|
||||
void Server::Host::FreeAllServices(void)
|
||||
{
|
||||
while (!mServices.IsEmpty())
|
||||
{
|
||||
RemoveAndFreeService(mServices.GetHead());
|
||||
RemoveService(mServices.GetHead(), /* aRetainName */ false, /* aNotifyServiceHandler */ false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1548,14 +1604,6 @@ void Server::Host::ClearResources(void)
|
||||
mAddressesNum = 0;
|
||||
}
|
||||
|
||||
void Server::Host::DeleteResourcesButRetainName(void)
|
||||
{
|
||||
// Mark the host as deleted.
|
||||
mLease = 0;
|
||||
ClearResources();
|
||||
otLogInfoSrp("[server] remove host '%s' (but retain its name)", mFullName);
|
||||
}
|
||||
|
||||
void Server::Host::CopyResourcesFrom(const Host &aHost)
|
||||
{
|
||||
memcpy(mAddresses, aHost.mAddresses, aHost.mAddressesNum * sizeof(mAddresses[0]));
|
||||
|
||||
@@ -217,7 +217,6 @@ public:
|
||||
otError SetTxtDataFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength);
|
||||
otError CopyResourcesFrom(const Service &aService);
|
||||
void ClearResources(void);
|
||||
void DeleteResourcesButRetainName(void);
|
||||
|
||||
char * mFullName;
|
||||
uint16_t mPriority;
|
||||
@@ -235,7 +234,7 @@ public:
|
||||
* This class implements the Host which registers services on the SRP server.
|
||||
*
|
||||
*/
|
||||
class Host : public LinkedListEntry<Host>, private NonCopyable
|
||||
class Host : public LinkedListEntry<Host>, public InstanceLocator, private NonCopyable
|
||||
{
|
||||
friend class LinkedListEntry<Host>;
|
||||
friend class Server;
|
||||
@@ -244,11 +243,13 @@ public:
|
||||
/**
|
||||
* This method creates a new Host object.
|
||||
*
|
||||
* @param[in] aInstance A reference to the OpenThread instance.
|
||||
*
|
||||
* @returns A pointer to the newly created Host object, nullptr if
|
||||
* cannot allocate memory for the object.
|
||||
*
|
||||
*/
|
||||
static Host *New(void);
|
||||
static Host *New(Instance &aInstance);
|
||||
|
||||
/**
|
||||
* This method Frees the Host object.
|
||||
@@ -359,17 +360,16 @@ public:
|
||||
kMaxAddressesNum = OPENTHREAD_CONFIG_SRP_SERVER_MAX_ADDRESSES_NUM,
|
||||
};
|
||||
|
||||
explicit Host(void);
|
||||
explicit Host(Instance &aInstance);
|
||||
otError SetFullName(const char *aFullName);
|
||||
void SetKey(Dns::Ecdsa256KeyRecord &aKey);
|
||||
void SetLease(uint32_t aLease);
|
||||
void SetKeyLease(uint32_t aKeyLease);
|
||||
Service *GetNextService(Service *aService) { return aService ? aService->GetNext() : mServices.GetHead(); }
|
||||
Service *AddService(const char *aFullName);
|
||||
void RemoveAndFreeService(Service *aService);
|
||||
void RemoveAndFreeAllServices(void);
|
||||
void RemoveService(Service *aService, bool aRetainName, bool aNotifyServiceHandler);
|
||||
void FreeAllServices(void);
|
||||
void ClearResources(void);
|
||||
void DeleteResourcesButRetainName(void);
|
||||
void CopyResourcesFrom(const Host &aHost);
|
||||
Service *FindService(const char *aFullName);
|
||||
const Service *FindService(const char *aFullName) const;
|
||||
@@ -597,7 +597,7 @@ private:
|
||||
|
||||
void HandleUpdate(const Dns::UpdateHeader &aDnsHeader, Host *aHost, const Ip6::MessageInfo &aMessageInfo);
|
||||
void AddHost(Host *aHost);
|
||||
void RemoveAndFreeHost(Host *aHost);
|
||||
void RemoveHost(Host *aHost, bool aRetainName, bool aNotifyServiceHandler);
|
||||
bool HasNameConflictsWith(Host &aHost) const;
|
||||
void SendResponse(const Dns::UpdateHeader & aHeader,
|
||||
Dns::UpdateHeader::Response aResponseCode,
|
||||
|
||||
@@ -47,6 +47,8 @@ import thread_cert
|
||||
BR = 1
|
||||
ROUTER = 2
|
||||
HOST = 3
|
||||
LEASE = 10 # Seconds
|
||||
KEY_LEASE = 20 # Seconds
|
||||
|
||||
|
||||
class SingleHostAndService(thread_cert.TestCase):
|
||||
@@ -81,6 +83,7 @@ class SingleHostAndService(thread_cert.TestCase):
|
||||
self.simulator.go(5)
|
||||
|
||||
server.srp_server_set_enabled(True)
|
||||
server.srp_server_set_lease_range(LEASE, LEASE, KEY_LEASE, KEY_LEASE)
|
||||
server.start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('leader', server.get_state())
|
||||
@@ -96,7 +99,7 @@ class SingleHostAndService(thread_cert.TestCase):
|
||||
client.srp_client_set_host_name('my-host')
|
||||
client.srp_client_set_host_address('2001::1')
|
||||
client.srp_client_add_service('my-service', '_ipps._tcp', 12345)
|
||||
client.srp_client_start(server.get_addrs()[0], client.get_srp_server_port())
|
||||
client.srp_client_enable_auto_start_mode()
|
||||
self.simulator.go(2)
|
||||
|
||||
self.check_host_and_service(server, client, '2001::1')
|
||||
@@ -142,6 +145,32 @@ class SingleHostAndService(thread_cert.TestCase):
|
||||
self.check_host_and_service(server, client, '2001::2')
|
||||
self.host_check_mdns_service(host, '2001::2')
|
||||
|
||||
#
|
||||
# 6. Check if the service is removed by the Advertising Proxy when the SRP server is stopped.
|
||||
#
|
||||
|
||||
server.srp_server_set_enabled(False)
|
||||
self.simulator.go(2)
|
||||
|
||||
self.assertEqual(len(server.srp_server_get_hosts()), 0)
|
||||
self.assertEqual(len(server.srp_server_get_services()), 0)
|
||||
self.assertIsNone(host.discover_mdns_service('my-service', '_ipps._tcp', 'my-host'))
|
||||
|
||||
server.srp_server_set_enabled(True)
|
||||
self.simulator.go(LEASE)
|
||||
|
||||
self.check_host_and_service(server, client, '2001::2')
|
||||
self.host_check_mdns_service(host, '2001::2')
|
||||
|
||||
#
|
||||
# 7. Check if the expired service is removed by the Advertising Proxy.
|
||||
#
|
||||
|
||||
client.srp_client_stop()
|
||||
self.simulator.go(LEASE + 2)
|
||||
|
||||
self.assertIsNone(host.discover_mdns_service('my-service', '_ipps._tcp', 'my-host'))
|
||||
|
||||
def host_check_mdns_service(self, host, host_addr):
|
||||
service = host.discover_mdns_service('my-service', '_ipps._tcp', 'my-host')
|
||||
self.assertIsNotNone(service)
|
||||
|
||||
@@ -2741,9 +2741,10 @@ class LinuxHost():
|
||||
#
|
||||
for line in self.bash(f'cat /tmp/{host_name}'):
|
||||
elements = line.split()
|
||||
if not elements or len(elements) < 6 or not elements[4].startswith(host_name):
|
||||
fullname = f'{host_name}.local.'
|
||||
if fullname not in elements:
|
||||
continue
|
||||
addresses.append(elements[5].split('%')[0])
|
||||
addresses.append(elements[elements.index(fullname) + 1].split('%')[0])
|
||||
|
||||
logging.debug(f'addresses of {host_name}: {addresses}')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user