Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restyle Mdns: Allow multiple operational advertisements #8789

Closed
wants to merge 13 commits into from
94 changes: 76 additions & 18 deletions src/lib/mdns/Advertiser_ImplMinimalMdns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ class AdvertiserMinMdns : public ServiceAdvertiser,
AdvertiserMinMdns() : mResponseSender(&GlobalMinimalMdnsServer::Server())
{
GlobalMinimalMdnsServer::Instance().SetQueryDelegate(this);
mResponseSender.AddQueryResponder(mQueryResponderAllocatorOperational.GetQueryResponder());
for (auto & allocator : mQueryResponderAllocatorOperational)
{
mResponseSender.AddQueryResponder(allocator.GetQueryResponder());
}
mResponseSender.AddQueryResponder(mQueryResponderAllocatorCommissionable.GetQueryResponder());
mResponseSender.AddQueryResponder(mQueryResponderAllocatorCommissioner.GetQueryResponder());
}
Expand Down Expand Up @@ -139,13 +142,17 @@ class AdvertiserMinMdns : public ServiceAdvertiser,
FullQName GetCommisioningTextEntries(const CommissionAdvertisingParameters & params);

// Max number of records for operational = PTR, SRV, TXT, A, AAAA, no subtypes.
static constexpr size_t kMaxOperationalRecords = 5;
QueryResponderAllocator<kMaxOperationalRecords> mQueryResponderAllocatorOperational;
static constexpr size_t kMaxOperationalRecords = 5;
static constexpr size_t kMaxOperationalNetworks = 5;
QueryResponderAllocator<kMaxOperationalRecords> mQueryResponderAllocatorOperational[kMaxOperationalNetworks];
// 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;

QueryResponderAllocator<kMaxOperationalRecords> * FindOperationalAllocator(const FullQName & qname);
QueryResponderAllocator<kMaxOperationalRecords> * FindEmptyOperationalAllocator();

ResponseSender mResponseSender;
uint32_t mCommissionInstanceName1;
uint32_t mCommissionInstanceName2;
Expand Down Expand Up @@ -196,6 +203,9 @@ CHIP_ERROR AdvertiserMinMdns::Start(chip::Inet::InetLayer * inetLayer, uint16_t

mCommissionInstanceName1 = GetRandU32();
mCommissionInstanceName2 = GetRandU32();
// Re-set the server in the response sender in case this has been swapped in the
// GlobalMinimalMdnsServer (used for testing).
mResponseSender.SetServer(&GlobalMinimalMdnsServer::Server());

ReturnErrorOnFailure(GlobalMinimalMdnsServer::Instance().StartServer(inetLayer, port));

Expand All @@ -209,36 +219,79 @@ CHIP_ERROR AdvertiserMinMdns::Start(chip::Inet::InetLayer * inetLayer, uint16_t
/// Stops the advertiser.
CHIP_ERROR AdvertiserMinMdns::StopPublishDevice()
{
mQueryResponderAllocatorOperational.Clear();
for (auto & allocator : mQueryResponderAllocatorOperational)
{
allocator.Clear();
}
mQueryResponderAllocatorCommissionable.Clear();
mQueryResponderAllocatorCommissioner.Clear();
return CHIP_NO_ERROR;
}

QueryResponderAllocator<AdvertiserMinMdns::kMaxOperationalRecords> *
AdvertiserMinMdns::FindOperationalAllocator(const FullQName & qname)
{
for (auto & allocator : mQueryResponderAllocatorOperational)
{
if (allocator.GetResponder(QType::SRV, qname) != nullptr)
{
return &allocator;
}
}
return nullptr;
}

QueryResponderAllocator<AdvertiserMinMdns::kMaxOperationalRecords> * AdvertiserMinMdns::FindEmptyOperationalAllocator()
{
for (auto & allocator : mQueryResponderAllocatorOperational)
{
if (allocator.IsEmpty())
{
return &allocator;
}
}
return nullptr;
}

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

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

QNamePart nameCheckParts[] = { nameBuffer, kOperationalServiceName, kOperationalProtocol, kLocalDomain };
FullQName nameCheck = FullQName(nameCheckParts);
QueryResponderAllocator<AdvertiserMinMdns::kMaxOperationalRecords> * operationalAllocator = FindOperationalAllocator(nameCheck);
if (operationalAllocator != nullptr)
{
operationalAllocator->Clear();
}
else
{
operationalAllocator = FindEmptyOperationalAllocator();
if (operationalAllocator == nullptr)
{
ChipLogError(Discovery, "Failed to find an open operational allocator");
return CHIP_ERROR_NO_MEMORY;
}
}

FullQName operationalServiceName =
mQueryResponderAllocatorOperational.AllocateQName(kOperationalServiceName, kOperationalProtocol, kLocalDomain);
operationalAllocator->AllocateQName(kOperationalServiceName, kOperationalProtocol, kLocalDomain);
FullQName operationalServerName =
mQueryResponderAllocatorOperational.AllocateQName(nameBuffer, kOperationalServiceName, kOperationalProtocol, kLocalDomain);
operationalAllocator->AllocateQName(nameBuffer, kOperationalServiceName, kOperationalProtocol, kLocalDomain);

ReturnErrorOnFailure(MakeHostName(nameBuffer, sizeof(nameBuffer), params.GetMac()));
FullQName serverName = mQueryResponderAllocatorOperational.AllocateQName(nameBuffer, kLocalDomain);
FullQName serverName = operationalAllocator->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 (!mQueryResponderAllocatorOperational.AddResponder<PtrResponder>(operationalServiceName, operationalServerName)
if (!operationalAllocator->AddResponder<PtrResponder>(operationalServiceName, operationalServerName)
.SetReportAdditional(operationalServerName)
.SetReportInServiceListing(true)
.IsValid())
Expand All @@ -247,31 +300,30 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const OperationalAdvertisingParameters &
return CHIP_ERROR_NO_MEMORY;
}

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

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

if (params.IsIPv4Enabled())
{
if (!mQueryResponderAllocatorOperational.AddResponder<IPv4Responder>(serverName).IsValid())
if (!operationalAllocator->AddResponder<IPv4Responder>(serverName).IsValid())
{
ChipLogError(Discovery, "Failed to add IPv4 mDNS responder");
return CHIP_ERROR_NO_MEMORY;
Expand Down Expand Up @@ -650,7 +702,10 @@ void AdvertiserMinMdns::AdvertiseRecords()
QueryData queryData(QType::PTR, QClass::IN, false /* unicast */);
queryData.SetIsBootAdvertising(true);

mQueryResponderAllocatorOperational.GetQueryResponder()->ClearBroadcastThrottle();
for (auto & allocator : mQueryResponderAllocatorOperational)
{
allocator.GetQueryResponder()->ClearBroadcastThrottle();
}
mQueryResponderAllocatorCommissionable.GetQueryResponder()->ClearBroadcastThrottle();
mQueryResponderAllocatorCommissioner.GetQueryResponder()->ClearBroadcastThrottle();

Expand All @@ -662,7 +717,10 @@ void AdvertiserMinMdns::AdvertiseRecords()
}

// Once all automatic broadcasts are done, allow immediate replies once.
mQueryResponderAllocatorOperational.GetQueryResponder()->ClearBroadcastThrottle();
for (auto & allocator : mQueryResponderAllocatorOperational)
{
allocator.GetQueryResponder()->ClearBroadcastThrottle();
}
mQueryResponderAllocatorCommissionable.GetQueryResponder()->ClearBroadcastThrottle();
mQueryResponderAllocatorCommissioner.GetQueryResponder()->ClearBroadcastThrottle();
}
Expand Down
78 changes: 54 additions & 24 deletions src/lib/mdns/Advertiser_ImplMinimalMdnsAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ class QueryResponderAllocator
public:
QueryResponderAllocator()
{
for (size_t i = 0; i < kMaxRecords; i++)
for (auto & responder : mAllocatedResponders)
{
mAllocatedResponders[i] = nullptr;
responder = nullptr;
}
for (size_t i = 0; i < kMaxAllocatedQNameData; i++)
for (auto & name : mAllocatedQNameParts)
{
mAllocatedQNameParts[i] = nullptr;
name = nullptr;
}
}
~QueryResponderAllocator() { Clear(); }
Expand Down Expand Up @@ -84,25 +84,55 @@ class QueryResponderAllocator
mQueryResponder.Init();

// Free all allocated data
for (size_t i = 0; i < kMaxRecords; i++)
for (auto & responder : mAllocatedResponders)
{
if (mAllocatedResponders[i] != nullptr)
if (responder != nullptr)
{
chip::Platform::Delete(mAllocatedResponders[i]);
mAllocatedResponders[i] = nullptr;
chip::Platform::Delete(responder);
responder = nullptr;
}
}

for (size_t i = 0; i < kMaxAllocatedQNameData; i++)
for (auto & name : mAllocatedQNameParts)
{
if (mAllocatedQNameParts[i] != nullptr)
if (name != nullptr)
{
chip::Platform::MemoryFree(mAllocatedQNameParts[i]);
mAllocatedQNameParts[i] = nullptr;
chip::Platform::MemoryFree(name);
name = nullptr;
}
}
}
mdns::Minimal::QueryResponder<kMaxRecords + 1> * GetQueryResponder() { return &mQueryResponder; }
const mdns::Minimal::RecordResponder * GetResponder(const mdns::Minimal::QType & qtype,
const mdns::Minimal::FullQName & qname) const
{
for (auto & responder : mAllocatedResponders)
{
if (responder != nullptr && responder->GetQType() == qtype && responder->GetQName() == qname)
{
return responder;
}
}
return nullptr;
}
bool IsEmpty() const
{
for (auto & responder : mAllocatedResponders)
{
if (responder != nullptr)
{
return false;
}
}
for (auto & name : mAllocatedQNameParts)
{
if (name != nullptr)
{
return false;
}
}
return true;
}

protected:
// For testing.
Expand All @@ -118,45 +148,45 @@ class QueryResponderAllocator
// The QueryResponder needs 1 extra space to hold the record for itself.
mdns::Minimal::QueryResponder<kMaxRecords + 1> mQueryResponder;

mdns::Minimal::QueryResponderSettings AddAllocatedResponder(mdns::Minimal::RecordResponder * responder)
mdns::Minimal::QueryResponderSettings AddAllocatedResponder(mdns::Minimal::RecordResponder * newResponder)
{
if (responder == nullptr)
if (newResponder == nullptr)
{
ChipLogError(Discovery, "Responder memory allocation failed");
return mdns::Minimal::QueryResponderSettings(); // failed
}

for (size_t i = 0; i < kMaxRecords; i++)
for (auto & responder : mAllocatedResponders)
{
if (mAllocatedResponders[i] != nullptr)
if (responder != nullptr)
{
continue;
}

mAllocatedResponders[i] = responder;
return mQueryResponder.AddResponder(mAllocatedResponders[i]);
responder = newResponder;
return mQueryResponder.AddResponder(responder);
}

Platform::Delete(responder);
Platform::Delete(newResponder);
ChipLogError(Discovery, "Failed to find free slot for adding a responder");
return mdns::Minimal::QueryResponderSettings();
}

void * AllocateQNameSpace(size_t size)
{
for (size_t i = 0; i < kMaxAllocatedQNameData; i++)
for (auto & name : mAllocatedQNameParts)
{
if (mAllocatedQNameParts[i] != nullptr)
if (name != nullptr)
{
continue;
}

mAllocatedQNameParts[i] = chip::Platform::MemoryAlloc(size);
if (mAllocatedQNameParts[i] == nullptr)
name = chip::Platform::MemoryAlloc(size);
if (name == nullptr)
{
ChipLogError(Discovery, "QName memory allocation failed");
}
return mAllocatedQNameParts[i];
return name;
}
ChipLogError(Discovery, "Failed to find free slot for adding a qname");
return nullptr;
Expand Down
25 changes: 25 additions & 0 deletions src/lib/mdns/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.

import("//build_overrides/chip.gni")
import("//build_overrides/nlunit_test.gni")
import("${chip_root}/src/platform/device.gni")

source_set("platform_header") {
Expand Down Expand Up @@ -58,3 +59,27 @@ static_library("mdns") {
assert(false, "Unknown mDNS advertiser implementation.")
}
}

static_library("minimal_mdns_tests") {
public_deps = [
":platform_header",
"${chip_root}/src/lib/core",
"${chip_root}/src/lib/support",
]
defines = [ "MDNS_MINIMAL_TEST_SERVER=true" ]
sources = [
"Advertiser.h",
"Advertiser_ImplMinimalMdns.cpp",
"MinimalMdnsServer.cpp",
"MinimalMdnsServer.h",
"Resolver.h",
"ServiceNaming.cpp",
"ServiceNaming.h",
"TxtFields.cpp",
"TxtFields.h",
]
public_deps += [
"${chip_root}/src/lib/mdns/minimal",
"${nlunit_test_root}:nlunit-test",
]
}
Loading