Skip to content

Commit

Permalink
Mdns: separate query responders for advertisers (project-chip#7615)
Browse files Browse the repository at this point in the history
* Mdns: separate query responders for advertisers

The current setup uses a single query responder for commissionable
and operational and all the records are cleared together and added
in a large group. This makes it challenging to run commissionable
and operational at the same time since they can't be cleared
individually and because there are duplicate records between
operational and commissionable. This commit changes the code to
use one query responder per advertisement type and gets the
ResponseSender to loop through the query responders to filter
for answers.

* Apply suggestions from code review

Co-authored-by: chrisdecenzo <[email protected]>

* Restyled by clang-format

* Fix tests - they were not testing right.

* Restyled by clang-format

* Update number of responders

Represents 5 multi-admin fabrics for operational, commissionable
and commissioner.

* Whoops - broke a test with my last "minor" change.

* Lighten the stack load for the failing test.

Co-authored-by: chrisdecenzo <[email protected]>
Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
3 people authored and Nikita committed Sep 23, 2021
1 parent 9b8090c commit 5bb4f70
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 89 deletions.
123 changes: 78 additions & 45 deletions src/lib/mdns/Advertiser_ImplMinimalMdns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,12 @@ class AdvertiserMinMdns : public ServiceAdvertiser,
public ParserDelegate // parses queries
{
public:
AdvertiserMinMdns() : mResponseSender(&GlobalMinimalMdnsServer::Server(), mQueryResponderAllocator.GetQueryResponder())
AdvertiserMinMdns() : mResponseSender(&GlobalMinimalMdnsServer::Server())
{
GlobalMinimalMdnsServer::Instance().SetQueryDelegate(this);
mResponseSender.AddQueryResponder(mQueryResponderAllocatorOperational.GetQueryResponder());
mResponseSender.AddQueryResponder(mQueryResponderAllocatorCommissionable.GetQueryResponder());
mResponseSender.AddQueryResponder(mQueryResponderAllocatorCommissioner.GetQueryResponder());
}
~AdvertiserMinMdns() {}

Expand Down Expand Up @@ -134,8 +137,13 @@ class AdvertiserMinMdns : public ServiceAdvertiser,

FullQName GetCommisioningTextEntries(const CommissionAdvertisingParameters & params);

static constexpr size_t kMaxRecords = 32;
QueryResponderAllocator<kMaxRecords> mQueryResponderAllocator;
// Max number of records for operational = PTR, SRV, TXT, A, AAAA, no subtypes.
static constexpr size_t kMaxOperationalRecords = 5;
QueryResponderAllocator<kMaxOperationalRecords> mQueryResponderAllocatorOperational;
// Max number of records for commissionable = 7 x PTR (base + 6 sub types - _S, _L, _D, _T, _C, _A), SRV, TXT, A, AAAA
static constexpr size_t kMaxCommissionRecords = 11;
QueryResponderAllocator<kMaxCommissionRecords> mQueryResponderAllocatorCommissionable;
QueryResponderAllocator<kMaxCommissionRecords> mQueryResponderAllocatorCommissioner;

ResponseSender mResponseSender;

Expand Down Expand Up @@ -195,33 +203,36 @@ CHIP_ERROR AdvertiserMinMdns::Start(chip::Inet::InetLayer * inetLayer, uint16_t
/// Stops the advertiser.
CHIP_ERROR AdvertiserMinMdns::StopPublishDevice()
{
mQueryResponderAllocator.Clear();
mQueryResponderAllocatorOperational.Clear();
mQueryResponderAllocatorCommissionable.Clear();
mQueryResponderAllocatorCommissioner.Clear();
return CHIP_NO_ERROR;
}

CHIP_ERROR AdvertiserMinMdns::Advertise(const OperationalAdvertisingParameters & params)
{
mQueryResponderAllocator.Clear();
// TODO: When multi-admin is enabled, commissionable does not need to be cleared here.
mQueryResponderAllocatorOperational.Clear();
char nameBuffer[64] = "";

/// need to set server name
ReturnErrorOnFailure(MakeInstanceName(nameBuffer, sizeof(nameBuffer), params.GetPeerId()));

FullQName operationalServiceName =
mQueryResponderAllocator.AllocateQName(kOperationalServiceName, kOperationalProtocol, kLocalDomain);
mQueryResponderAllocatorOperational.AllocateQName(kOperationalServiceName, kOperationalProtocol, kLocalDomain);
FullQName operationalServerName =
mQueryResponderAllocator.AllocateQName(nameBuffer, kOperationalServiceName, kOperationalProtocol, kLocalDomain);
mQueryResponderAllocatorOperational.AllocateQName(nameBuffer, kOperationalServiceName, kOperationalProtocol, kLocalDomain);

ReturnErrorOnFailure(MakeHostName(nameBuffer, sizeof(nameBuffer), params.GetMac()));
FullQName serverName = mQueryResponderAllocator.AllocateQName(nameBuffer, kLocalDomain);
FullQName serverName = mQueryResponderAllocatorOperational.AllocateQName(nameBuffer, kLocalDomain);

if ((operationalServiceName.nameCount == 0) || (operationalServerName.nameCount == 0) || (serverName.nameCount == 0))
{
ChipLogError(Discovery, "Failed to allocate QNames.");
return CHIP_ERROR_NO_MEMORY;
}

if (!mQueryResponderAllocator.AddResponder<PtrResponder>(operationalServiceName, operationalServerName)
if (!mQueryResponderAllocatorOperational.AddResponder<PtrResponder>(operationalServiceName, operationalServerName)
.SetReportAdditional(operationalServerName)
.SetReportInServiceListing(true)
.IsValid())
Expand All @@ -230,30 +241,31 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const OperationalAdvertisingParameters &
return CHIP_ERROR_NO_MEMORY;
}

if (!mQueryResponderAllocator.AddResponder<SrvResponder>(SrvResourceRecord(operationalServerName, serverName, params.GetPort()))
if (!mQueryResponderAllocatorOperational
.AddResponder<SrvResponder>(SrvResourceRecord(operationalServerName, serverName, params.GetPort()))
.SetReportAdditional(serverName)
.IsValid())
{
ChipLogError(Discovery, "Failed to add SRV record mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}
if (!mQueryResponderAllocator.AddResponder<TxtResponder>(TxtResourceRecord(operationalServerName, mEmptyTextEntries))
if (!mQueryResponderAllocatorOperational.AddResponder<TxtResponder>(TxtResourceRecord(operationalServerName, mEmptyTextEntries))
.SetReportAdditional(serverName)
.IsValid())
{
ChipLogError(Discovery, "Failed to add TXT record mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}

if (!mQueryResponderAllocator.AddResponder<IPv6Responder>(serverName).IsValid())
if (!mQueryResponderAllocatorOperational.AddResponder<IPv6Responder>(serverName).IsValid())
{
ChipLogError(Discovery, "Failed to add IPv6 mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}

if (params.IsIPv4Enabled())
{
if (!mQueryResponderAllocator.AddResponder<IPv4Responder>(serverName).IsValid())
if (!mQueryResponderAllocatorOperational.AddResponder<IPv4Responder>(serverName).IsValid())
{
ChipLogError(Discovery, "Failed to add IPv4 mDNS responder");
return CHIP_ERROR_NO_MEMORY;
Expand All @@ -267,30 +279,43 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const OperationalAdvertisingParameters &

CHIP_ERROR AdvertiserMinMdns::Advertise(const CommissionAdvertisingParameters & params)
{
mQueryResponderAllocator.Clear();
// TODO: When multi-admin is enabled, operational does not need to be cleared here.
if (params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode)
{
mQueryResponderAllocatorCommissionable.Clear();
}
else
{
mQueryResponderAllocatorCommissioner.Clear();
}

// TODO: need to detect colisions here
char nameBuffer[64] = "";
size_t len = snprintf(nameBuffer, sizeof(nameBuffer), ChipLogFormatX64, GetRandU32(), GetRandU32());
if (len >= sizeof(nameBuffer))
{
return CHIP_ERROR_NO_MEMORY;
}
QueryResponderAllocator<kMaxCommissionRecords> * allocator =
params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode ? &mQueryResponderAllocatorCommissionable
: &mQueryResponderAllocatorCommissioner;
const char * serviceType = params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode
? kCommissionableServiceName
: kCommissionerServiceName;
FullQName serviceName = mQueryResponderAllocator.AllocateQName(serviceType, kCommissionProtocol, kLocalDomain);
FullQName instanceName = mQueryResponderAllocator.AllocateQName(nameBuffer, serviceType, kCommissionProtocol, kLocalDomain);

FullQName serviceName = allocator->AllocateQName(serviceType, kCommissionProtocol, kLocalDomain);
FullQName instanceName = allocator->AllocateQName(nameBuffer, serviceType, kCommissionProtocol, kLocalDomain);

ReturnErrorOnFailure(MakeHostName(nameBuffer, sizeof(nameBuffer), params.GetMac()));
FullQName hostName = mQueryResponderAllocator.AllocateQName(nameBuffer, kLocalDomain);
FullQName hostName = allocator->AllocateQName(nameBuffer, kLocalDomain);

if ((serviceName.nameCount == 0) || (instanceName.nameCount == 0) || (hostName.nameCount == 0))
{
ChipLogError(Discovery, "Failed to allocate QNames.");
return CHIP_ERROR_NO_MEMORY;
}

if (!mQueryResponderAllocator.AddResponder<PtrResponder>(serviceName, instanceName)
if (!allocator->AddResponder<PtrResponder>(serviceName, instanceName)
.SetReportAdditional(instanceName)
.SetReportInServiceListing(true)
.IsValid())
Expand All @@ -299,22 +324,22 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const CommissionAdvertisingParameters &
return CHIP_ERROR_NO_MEMORY;
}

if (!mQueryResponderAllocator.AddResponder<SrvResponder>(SrvResourceRecord(instanceName, hostName, params.GetPort()))
if (!allocator->AddResponder<SrvResponder>(SrvResourceRecord(instanceName, hostName, params.GetPort()))
.SetReportAdditional(hostName)
.IsValid())
{
ChipLogError(Discovery, "Failed to add SRV record mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}
if (!mQueryResponderAllocator.AddResponder<IPv6Responder>(hostName).IsValid())
if (!allocator->AddResponder<IPv6Responder>(hostName).IsValid())
{
ChipLogError(Discovery, "Failed to add IPv6 mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}

if (params.IsIPv4Enabled())
{
if (!mQueryResponderAllocator.AddResponder<IPv4Responder>(hostName).IsValid())
if (!allocator->AddResponder<IPv4Responder>(hostName).IsValid())
{
ChipLogError(Discovery, "Failed to add IPv4 mDNS responder");
return CHIP_ERROR_NO_MEMORY;
Expand All @@ -325,11 +350,11 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const CommissionAdvertisingParameters &
{
MakeServiceSubtype(nameBuffer, sizeof(nameBuffer),
DiscoveryFilter(DiscoveryFilterType::kVendor, params.GetVendorId().Value()));
FullQName vendorServiceName = mQueryResponderAllocator.AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType,
kCommissionProtocol, kLocalDomain);
FullQName vendorServiceName =
allocator->AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType, kCommissionProtocol, kLocalDomain);
ReturnErrorCodeIf(vendorServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);

if (!mQueryResponderAllocator.AddResponder<PtrResponder>(vendorServiceName, instanceName)
if (!allocator->AddResponder<PtrResponder>(vendorServiceName, instanceName)
.SetReportAdditional(instanceName)
.SetReportInServiceListing(true)
.IsValid())
Expand All @@ -343,11 +368,11 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const CommissionAdvertisingParameters &
{
MakeServiceSubtype(nameBuffer, sizeof(nameBuffer),
DiscoveryFilter(DiscoveryFilterType::kDeviceType, params.GetDeviceType().Value()));
FullQName vendorServiceName = mQueryResponderAllocator.AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType,
kCommissionProtocol, kLocalDomain);
FullQName vendorServiceName =
allocator->AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType, kCommissionProtocol, kLocalDomain);
ReturnErrorCodeIf(vendorServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);

if (!mQueryResponderAllocator.AddResponder<PtrResponder>(vendorServiceName, instanceName)
if (!allocator->AddResponder<PtrResponder>(vendorServiceName, instanceName)
.SetReportAdditional(instanceName)
.SetReportInServiceListing(true)
.IsValid())
Expand All @@ -363,11 +388,11 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const CommissionAdvertisingParameters &
{
MakeServiceSubtype(nameBuffer, sizeof(nameBuffer),
DiscoveryFilter(DiscoveryFilterType::kShort, params.GetShortDiscriminator()));
FullQName shortServiceName = mQueryResponderAllocator.AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType,
kCommissionProtocol, kLocalDomain);
FullQName shortServiceName =
allocator->AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType, kCommissionProtocol, kLocalDomain);
ReturnErrorCodeIf(shortServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);

if (!mQueryResponderAllocator.AddResponder<PtrResponder>(shortServiceName, instanceName)
if (!allocator->AddResponder<PtrResponder>(shortServiceName, instanceName)
.SetReportAdditional(instanceName)
.SetReportInServiceListing(true)
.IsValid())
Expand All @@ -380,10 +405,10 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const CommissionAdvertisingParameters &
{
MakeServiceSubtype(nameBuffer, sizeof(nameBuffer),
DiscoveryFilter(DiscoveryFilterType::kLong, params.GetLongDiscriminator()));
FullQName longServiceName = mQueryResponderAllocator.AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType,
kCommissionProtocol, kLocalDomain);
FullQName longServiceName =
allocator->AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType, kCommissionProtocol, kLocalDomain);
ReturnErrorCodeIf(longServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);
if (!mQueryResponderAllocator.AddResponder<PtrResponder>(longServiceName, instanceName)
if (!allocator->AddResponder<PtrResponder>(longServiceName, instanceName)
.SetReportAdditional(instanceName)
.SetReportInServiceListing(true)
.IsValid())
Expand All @@ -396,10 +421,10 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const CommissionAdvertisingParameters &
{
MakeServiceSubtype(nameBuffer, sizeof(nameBuffer),
DiscoveryFilter(DiscoveryFilterType::kCommissioningMode, params.GetCommissioningMode() ? 1 : 0));
FullQName longServiceName = mQueryResponderAllocator.AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType,
kCommissionProtocol, kLocalDomain);
FullQName longServiceName =
allocator->AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType, kCommissionProtocol, kLocalDomain);
ReturnErrorCodeIf(longServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);
if (!mQueryResponderAllocator.AddResponder<PtrResponder>(longServiceName, instanceName)
if (!allocator->AddResponder<PtrResponder>(longServiceName, instanceName)
.SetReportAdditional(instanceName)
.SetReportInServiceListing(true)
.IsValid())
Expand All @@ -413,10 +438,10 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const CommissionAdvertisingParameters &
{
MakeServiceSubtype(nameBuffer, sizeof(nameBuffer),
DiscoveryFilter(DiscoveryFilterType::kCommissioningModeFromCommand, 1));
FullQName longServiceName = mQueryResponderAllocator.AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType,
kCommissionProtocol, kLocalDomain);
FullQName longServiceName =
allocator->AllocateQName(nameBuffer, kSubtypeServiceNamePart, serviceType, kCommissionProtocol, kLocalDomain);
ReturnErrorCodeIf(longServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);
if (!mQueryResponderAllocator.AddResponder<PtrResponder>(longServiceName, instanceName)
if (!allocator->AddResponder<PtrResponder>(longServiceName, instanceName)
.SetReportAdditional(instanceName)
.SetReportInServiceListing(true)
.IsValid())
Expand All @@ -427,7 +452,7 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const CommissionAdvertisingParameters &
}
}

if (!mQueryResponderAllocator.AddResponder<TxtResponder>(TxtResourceRecord(instanceName, GetCommisioningTextEntries(params)))
if (!allocator->AddResponder<TxtResponder>(TxtResourceRecord(instanceName, GetCommisioningTextEntries(params)))
.SetReportAdditional(hostName)
.IsValid())
{
Expand All @@ -454,6 +479,10 @@ FullQName AdvertiserMinMdns::GetCommisioningTextEntries(const CommissionAdvertis
const char * txtFields[kMaxTxtFields];
size_t numTxtFields = 0;

QueryResponderAllocator<kMaxCommissionRecords> * allocator =
params.GetCommissionAdvertiseMode() == CommssionAdvertiseMode::kCommissionableNode ? &mQueryResponderAllocatorCommissionable
: &mQueryResponderAllocatorCommissioner;

char txtVidPid[chip::Mdns::kKeyVendorProductMaxLength + 4];
if (params.GetProductId().HasValue() && params.GetVendorId().HasValue())
{
Expand Down Expand Up @@ -490,7 +519,7 @@ FullQName AdvertiserMinMdns::GetCommisioningTextEntries(const CommissionAdvertis

if (!params.GetVendorId().HasValue())
{
return mQueryResponderAllocator.AllocateQName(txtDiscriminator);
return allocator->AllocateQName(txtDiscriminator);
}

char txtCommissioningMode[chip::Mdns::kKeyCommissioningModeMaxLength + 4];
Expand Down Expand Up @@ -527,11 +556,11 @@ FullQName AdvertiserMinMdns::GetCommisioningTextEntries(const CommissionAdvertis
}
if (numTxtFields == 0)
{
return mQueryResponderAllocator.AllocateQNameFromArray(mEmptyTextEntries, 1);
return allocator->AllocateQNameFromArray(mEmptyTextEntries, 1);
}
else
{
return mQueryResponderAllocator.AllocateQNameFromArray(txtFields, numTxtFields);
return allocator->AllocateQNameFromArray(txtFields, numTxtFields);
}
} // namespace

Expand Down Expand Up @@ -603,7 +632,9 @@ void AdvertiserMinMdns::AdvertiseRecords()
QueryData queryData(QType::PTR, QClass::IN, false /* unicast */);
queryData.SetIsBootAdvertising(true);

mQueryResponderAllocator.GetQueryResponder()->ClearBroadcastThrottle();
mQueryResponderAllocatorOperational.GetQueryResponder()->ClearBroadcastThrottle();
mQueryResponderAllocatorCommissionable.GetQueryResponder()->ClearBroadcastThrottle();
mQueryResponderAllocatorCommissioner.GetQueryResponder()->ClearBroadcastThrottle();

CHIP_ERROR err = mResponseSender.Respond(0, queryData, &packetInfo);
if (err != CHIP_NO_ERROR)
Expand All @@ -613,7 +644,9 @@ void AdvertiserMinMdns::AdvertiseRecords()
}

// Once all automatic broadcasts are done, allow immediate replies once.
mQueryResponderAllocator.GetQueryResponder()->ClearBroadcastThrottle();
mQueryResponderAllocatorOperational.GetQueryResponder()->ClearBroadcastThrottle();
mQueryResponderAllocatorCommissionable.GetQueryResponder()->ClearBroadcastThrottle();
mQueryResponderAllocatorCommissioner.GetQueryResponder()->ClearBroadcastThrottle();
}

AdvertiserMinMdns gAdvertiser;
Expand Down
Loading

0 comments on commit 5bb4f70

Please sign in to comment.