Skip to content

Commit

Permalink
Add operational discovery and continuous query support in linux platf…
Browse files Browse the repository at this point in the history
…orm mdns
  • Loading branch information
su-shanka committed May 10, 2024
1 parent 0d67568 commit 948e46e
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 10 deletions.
54 changes: 49 additions & 5 deletions src/lib/dnssd/Discovery_ImplPlatform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,17 @@ static void HandleNodeResolve(void * context, DnssdService * result, const Span<
}

DiscoveredNodeData nodeData;
result->ToDiscoveredNodeData(addresses, nodeData);

if (0 == strncmp(result->mType, kOperationalServiceName, sizeof(kOperationalServiceName)))
{
result->ToDiscoveredOperationalNodeBrowseData(nodeData);

nodeData.Get<OperationalNodeBrowseData>().LogDetail();
discoveryContext->OnNodeDiscovered(nodeData);
return;
}

result->ToDiscoveredCommissionNodeData(addresses, nodeData);

nodeData.Get<CommissionNodeData>().LogDetail();
discoveryContext->OnNodeDiscovered(nodeData);
Expand All @@ -75,8 +85,9 @@ static void HandleNodeBrowse(void * context, DnssdService * services, size_t ser

auto & ipAddress = services[i].mAddress;

// Check if SRV, TXT and AAAA records were received in DNS responses
if (strlen(services[i].mHostName) == 0 || services[i].mTextEntrySize == 0 || !ipAddress.has_value())
// Check that this is not operational browse result and if SRV, TXT and AAAA records were received in DNS responses
if (strncmp(services[i].mType, kOperationalServiceName, sizeof(kOperationalServiceName)) != 0
&& (strlen(services[i].mHostName) == 0 || services[i].mTextEntrySize == 0 || !ipAddress.has_value()))
{
ChipDnssdResolve(&services[i], services[i].mInterface, HandleNodeResolve, context);
}
Expand Down Expand Up @@ -340,7 +351,15 @@ void DiscoveryImplPlatform::HandleNodeIdResolve(void * context, DnssdService * r
impl->mOperationalDelegate->OnOperationalNodeResolved(nodeData);
}

void DnssdService::ToDiscoveredNodeData(const Span<Inet::IPAddress> & addresses, DiscoveredNodeData & nodeData)
void DnssdService::ToDiscoveredOperationalNodeBrowseData(DiscoveredNodeData & nodeData)
{
nodeData.Set<OperationalNodeBrowseData>();

ExtractIdFromInstanceName(mName, &nodeData.Get<OperationalNodeBrowseData>().peerId);
nodeData.Get<OperationalNodeBrowseData>().hasZeroTTL = (mTtlSeconds == 0);
}

void DnssdService::ToDiscoveredCommissionNodeData(const Span<Inet::IPAddress> & addresses, DiscoveredNodeData & nodeData)
{
nodeData.Set<CommissionNodeData>();
auto & discoveredData = nodeData.Get<CommissionNodeData>();
Expand Down Expand Up @@ -746,6 +765,31 @@ CHIP_ERROR DiscoveryImplPlatform::DiscoverCommissioners(DiscoveryFilter filter,
return error;
}

CHIP_ERROR DiscoveryImplPlatform::DiscoverOperational(DiscoveryFilter filter, DiscoveryContext & context)
{
ReturnErrorOnFailure(InitImpl());
StopDiscovery(context);

char serviceName[kMaxCommissionerServiceNameSize];
ReturnErrorOnFailure(MakeServiceTypeName(serviceName, sizeof(serviceName), filter, DiscoveryType::kOperational));

intptr_t browseIdentifier;
// Increase the reference count of the context to keep it alive until HandleNodeBrowse is called back.
CHIP_ERROR error = ChipDnssdBrowse(serviceName, DnssdServiceProtocol::kDnssdProtocolTcp, Inet::IPAddressType::kAny,
Inet::InterfaceId::Null(), HandleNodeBrowse, context.Retain(), &browseIdentifier);

if (error == CHIP_NO_ERROR)
{
context.SetBrowseIdentifier(browseIdentifier);
}
else
{
context.Release();
}

return error;
}

CHIP_ERROR DiscoveryImplPlatform::StartDiscovery(DiscoveryType type, DiscoveryFilter filter, DiscoveryContext & context)
{
switch (type)
Expand All @@ -755,7 +799,7 @@ CHIP_ERROR DiscoveryImplPlatform::StartDiscovery(DiscoveryType type, DiscoveryFi
case DiscoveryType::kCommissionerNode:
return DiscoverCommissioners(filter, context);
case DiscoveryType::kOperational:
return CHIP_ERROR_NOT_IMPLEMENTED;
return DiscoverOperational(filter, context);
default:
return CHIP_ERROR_INVALID_ARGUMENT;
}
Expand Down
1 change: 1 addition & 0 deletions src/lib/dnssd/Discovery_ImplPlatform.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class DiscoveryImplPlatform : public ServiceAdvertiser, public Resolver
void NodeIdResolutionNoLongerNeeded(const PeerId & peerId) override;
CHIP_ERROR DiscoverCommissionableNodes(DiscoveryFilter filter, DiscoveryContext & context);
CHIP_ERROR DiscoverCommissioners(DiscoveryFilter filter, DiscoveryContext & context);
CHIP_ERROR DiscoverOperational(DiscoveryFilter filter, DiscoveryContext & context);
CHIP_ERROR StartDiscovery(DiscoveryType type, DiscoveryFilter filter, DiscoveryContext & context) override;
CHIP_ERROR StopDiscovery(DiscoveryContext & context) override;
CHIP_ERROR ReconfirmRecord(const char * hostname, Inet::IPAddress address, Inet::InterfaceId interfaceId) override;
Expand Down
4 changes: 4 additions & 0 deletions src/lib/dnssd/ServiceNaming.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ CHIP_ERROR MakeServiceTypeName(char * buffer, size_t bufferLen, DiscoveryFilter
{
requiredSize = snprintf(buffer, bufferLen, kCommissionerServiceName);
}
else if (type == DiscoveryType::kOperational)
{
requiredSize = snprintf(buffer, bufferLen, kOperationalServiceName);
}
else
{
return CHIP_ERROR_NOT_IMPLEMENTED;
Expand Down
3 changes: 2 additions & 1 deletion src/lib/dnssd/platform/Dnssd.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ struct DnssdService
// Time to live in seconds. Per rfc6762 section 10, because we have a hostname, our default TTL is 120 seconds
uint32_t mTtlSeconds = 120;

void ToDiscoveredNodeData(const Span<Inet::IPAddress> & addresses, DiscoveredNodeData & nodeData);
void ToDiscoveredCommissionNodeData(const Span<Inet::IPAddress> & addresses, DiscoveredNodeData & nodeData);
void ToDiscoveredOperationalNodeBrowseData(DiscoveredNodeData & nodeData);
};

/**
Expand Down
14 changes: 12 additions & 2 deletions src/lib/shell/commands/Dns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,13 @@ CHIP_ERROR BrowseOperationalHandler(int argc, char ** argv)
return sResolverProxy.DiscoverOperationalNodes(filter);
}

CHIP_ERROR BrowseStopHandler(int argc, char ** argv)
{
streamer_printf(streamer_get(), "Stopping browse...\r\n");

return sResolverProxy.StopDiscovery();
}

CHIP_ERROR BrowseHandler(int argc, char ** argv)
{
if (argc == 0)
Expand Down Expand Up @@ -289,14 +296,17 @@ void RegisterDnsCommands()
"Browse Matter commissionable nodes. Usage: dns browse commissionable [subtype]" },
{ &BrowseCommissionerHandler, "commissioner",
"Browse Matter commissioner nodes. Usage: dns browse commissioner [subtype]" },
{ &BrowseOperationalHandler, "operational", "Browse Matter operational nodes. Usage: dns browse operational" },
{ &BrowseOperationalHandler, "operational",
"Browse Matter operational nodes. Usage: dns browse operational" },
{ &BrowseStopHandler, "stop",
"Stop ongoing browse. USage: dns browse stop"},
};

static const shell_command_t sDnsSubCommands[] = {
{ &ResolveHandler, "resolve",
"Resolve the DNS service. Usage: dns resolve <fabric-id> <node-id> (e.g. dns resolve 5544332211 1)" },
{ &BrowseHandler, "browse",
"Browse DNS services published by Matter nodes. Usage: dns browse <commissionable|commissioner|operational>" },
"Browse DNS services published by Matter nodes. Usage: dns browse <commissionable|commissioner|operational>|stop" },
};

static const shell_command_t sDnsCommand = { &DnsHandler, "dns", "Dns client commands" };
Expand Down
2 changes: 1 addition & 1 deletion src/platform/Darwin/DnssdContexts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ bool ResolveContext::TryReportingResultsForInterfaceIndex(uint32_t interfaceInde
{
auto delegate = static_cast<DiscoverNodeDelegate *>(context);
DiscoveredNodeData nodeData;
service.ToDiscoveredNodeData(addresses, nodeData);
service.ToDiscoveredCommissionNodeData(addresses, nodeData);
delegate->OnNodeDiscovered(nodeData);
}
else
Expand Down
43 changes: 42 additions & 1 deletion src/platform/Linux/DnssdImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,11 @@ void CopyTypeWithoutProtocol(char (&dest)[N], const char * typeAndProtocol)
void MdnsAvahi::BrowseRetryCallback(chip::System::Layer * aLayer, void * appState)
{
BrowseContext * context = static_cast<BrowseContext *>(appState);

// free existing browser handle
avahi_service_browser_free(context->mBrowser);
context->mBrowser = nullptr;

// Don't schedule anything new if we've stopped.
if (context->mStopped.load())
{
Expand Down Expand Up @@ -743,20 +748,39 @@ void MdnsAvahi::HandleBrowse(AvahiServiceBrowser * browser, AvahiIfIndex interfa
case AVAHI_BROWSER_ALL_FOR_NOW: {
ChipLogProgress(DeviceLayer, "Avahi browse: all for now");
bool needRetries = context->mBrowseRetries++ < kMaxBrowseRetries && !context->mStopped.load();

// If we were already asked to stop, no need to send a callback - no one is listening.
if (!context->mStopped.load())
{
context->mCallback(context->mContext, context->mServices.data(), context->mServices.size(), !needRetries,
CHIP_NO_ERROR);

// Clearing records/services already passed to application through delegate. Keeping it may cause
// duplicates in next query / retry attempt as currently found will also come again from cache.
context->mServices.clear();
}
avahi_service_browser_free(browser);
// hold on to browser handle so we don't lose out any message, this will be freed just before next query
context->mBrowser = browser;

if (needRetries)
{
context->mNextRetryDelay *= 2;
// Hand the ownership of the context over to the timer. It will either schedule a new browse on the context,
// triggering this function, or it will delete and not reschedule (if stopped).
DeviceLayer::SystemLayer().StartTimer(context->mNextRetryDelay / 2, BrowseRetryCallback, context);
}
else if (!context->mStopped.load())
{
// Re-trigger query until StopBrowse() is called

// Clear retry info and services.
context->mNextRetryDelay = chip::System::Clock::Seconds16(1);
context->mBrowseRetries = 0;
context->mServices.clear();
// Hand the ownership of the context over to the timer. It will either schedule a new browse on the context,
// triggering this function, or it will delete and not reschedule (if stopped).
DeviceLayer::SystemLayer().StartTimer(context->mNextQueryDelay, BrowseRetryCallback, context);
}
else
{
// We didn't schedule a timer, so we're responsible for deleting the context
Expand All @@ -772,7 +796,24 @@ void MdnsAvahi::HandleBrowse(AvahiServiceBrowser * browser, AvahiIfIndex interfa
std::remove_if(context->mServices.begin(), context->mServices.end(), [name, type](const DnssdService & service) {
return strcmp(name, service.mName) == 0 && type == GetFullType(service.mType, service.mProtocol);
}));

DnssdService service = {};

Platform::CopyString(service.mName, name);
CopyTypeWithoutProtocol(service.mType, type);
service.mProtocol = GetProtocolInType(type);
service.mAddressType = context->mAddressType;
service.mTransportType = ToAddressType(protocol);
service.mInterface = Inet::InterfaceId::Null();
if (interface != AVAHI_IF_UNSPEC)
{
service.mInterface = static_cast<chip::Inet::InterfaceId>(interface);
}
service.mType[kDnssdTypeMaxSize] = 0;
service.mTtlSeconds = 0;
context->mCallback(context->mContext, &service, 1, false, CHIP_NO_ERROR);
}

break;
case AVAHI_BROWSER_CACHE_EXHAUSTED:
ChipLogProgress(DeviceLayer, "Avahi browse: cache exhausted");
Expand Down
2 changes: 2 additions & 0 deletions src/platform/Linux/DnssdImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ class MdnsAvahi
AvahiIfIndex mInterface;
std::string mProtocol;
chip::System::Clock::Timeout mNextRetryDelay = chip::System::Clock::Seconds16(1);
chip::System::Clock::Timeout mNextQueryDelay = chip::System::Clock::Seconds16(60);
std::atomic_bool mStopped{ false };
AvahiServiceBrowser *mBrowser;
};

struct ResolveContext
Expand Down

0 comments on commit 948e46e

Please sign in to comment.