Skip to content

Commit

Permalink
Mdns: Allow multiple operational advertisements (#8610)
Browse files Browse the repository at this point in the history
* Mdns: Allow multiple operational advertisements

- moved CheckOnlyServer into its own file and created a minimal
  mdns implementation that uses the CheckOnlyServer
- Added a () constructor to the CheckOnlyServer with an option
  to set the test suite after.
- fixed a bug where the truncated flag was not being set properly
- Added a function to the response allocator to determine if
  given ResourceRecord is there
- Added a check at the start of operational to check for the given
  SRV record in the allocator - these records are unique to each
  fabric / node. If a different fabric/node is identified, the
  Advertise feature will start a new query responder to append
  records rather than replacing.
- added tests for operational mdns records with single and multiple
  fabrics.

* Restyled by whitespace

* Restyled by gn

* Don't build Advertiser test for zephyr.

Zephyr uses a monolothic lib that already links in a version of
the mdns library.

* Restyled by gn

* Change style.

* Missed two ranged fors.

* Remove :mdns from deps.

Apparently the default is the same as the directory name. TIL.

https://gn.googlesource.com/gn/+/main/docs/reference.md#implicit-names

* New way of doing name.

Flat allocator is overkill for a local variable because the individual
peices don't go out of scope before the QName.

* brute force server swap.

* Restyled by gn

* BUILD file rule isn't used anymore.

* Add RAII-style server swapper class.

* Remove include.

* Change explicit variable type to auto.

Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
cecille and restyled-commits authored Aug 4, 2021
1 parent 55e5f19 commit f679d45
Show file tree
Hide file tree
Showing 13 changed files with 668 additions and 175 deletions.
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);
auto * 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
1 change: 1 addition & 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
21 changes: 17 additions & 4 deletions src/lib/mdns/MinimalMdnsServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once
#include <mdns/minimal/Server.h>

namespace chip {
Expand Down Expand Up @@ -76,7 +76,17 @@ class GlobalMinimalMdnsServer : public mdns::Minimal::ServerDelegate
GlobalMinimalMdnsServer() { mServer.SetDelegate(this); }

static GlobalMinimalMdnsServer & Instance();
static ServerType & Server() { return Instance().mServer; }
static mdns::Minimal::ServerBase & Server()
{
if (Instance().mReplacementServer != nullptr)
{
return *Instance().mReplacementServer;
}
else
{
return Instance().mServer;
}
}

/// Calls Server().Listen() on all available interfaces
CHIP_ERROR StartServer(chip::Inet::InetLayer * inetLayer, uint16_t port);
Expand All @@ -101,10 +111,13 @@ class GlobalMinimalMdnsServer : public mdns::Minimal::ServerDelegate
}
}

void SetReplacementServer(mdns::Minimal::ServerBase * server) { mReplacementServer = server; }

private:
ServerType mServer;
MdnsPacketDelegate * mQueryDelegate = nullptr;
MdnsPacketDelegate * mResponseDelegate = nullptr;
mdns::Minimal::ServerBase * mReplacementServer = nullptr;
MdnsPacketDelegate * mQueryDelegate = nullptr;
MdnsPacketDelegate * mResponseDelegate = nullptr;
};

} // namespace Mdns
Expand Down
4 changes: 1 addition & 3 deletions src/lib/mdns/ServiceNaming.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ uint8_t HexToInt(char c)

CHIP_ERROR MakeInstanceName(char * buffer, size_t bufferLen, const PeerId & peerId)
{
constexpr size_t kServiceNameLen = 16 + 1 + 16; // 2 * 64-bit value in HEX + hyphen

ReturnErrorCodeIf(bufferLen <= kServiceNameLen, CHIP_ERROR_BUFFER_TOO_SMALL);
ReturnErrorCodeIf(bufferLen <= kOperationalServiceNamePrefix, CHIP_ERROR_BUFFER_TOO_SMALL);

NodeId nodeId = peerId.GetNodeId();
FabricId fabricId = peerId.GetFabricId();
Expand Down
Loading

0 comments on commit f679d45

Please sign in to comment.