Skip to content

Commit 8a7e198

Browse files
authored
Merge pull request #3 from SiliconLabs/cherry-pick/mdns-changes
Operational Discovery with Continuous Query Support for Linux (project-chip#33402)
2 parents 8fceb32 + 387ab05 commit 8a7e198

File tree

8 files changed

+153
-50
lines changed

8 files changed

+153
-50
lines changed

src/lib/dnssd/Discovery_ImplPlatform.cpp

+66-5
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,33 @@ static void HandleNodeResolve(void * context, DnssdService * result, const Span<
4949
}
5050

5151
DiscoveredNodeData nodeData;
52-
result->ToDiscoveredNodeData(addresses, nodeData);
52+
53+
result->ToDiscoveredCommissionNodeData(addresses, nodeData);
5354

5455
nodeData.Get<CommissionNodeData>().LogDetail();
5556
discoveryContext->OnNodeDiscovered(nodeData);
5657
discoveryContext->Release();
5758
}
5859

60+
static void HandleNodeOperationalBrowse(void * context, DnssdService * result, CHIP_ERROR error)
61+
{
62+
DiscoveryContext * discoveryContext = static_cast<DiscoveryContext *>(context);
63+
64+
if (error != CHIP_NO_ERROR)
65+
{
66+
discoveryContext->Release();
67+
return;
68+
}
69+
70+
DiscoveredNodeData nodeData;
71+
72+
result->ToDiscoveredOperationalNodeBrowseData(nodeData);
73+
74+
nodeData.Get<OperationalNodeBrowseData>().LogDetail();
75+
discoveryContext->OnNodeDiscovered(nodeData);
76+
discoveryContext->Release();
77+
}
78+
5979
static void HandleNodeBrowse(void * context, DnssdService * services, size_t servicesSize, bool finalBrowse, CHIP_ERROR error)
6080
{
6181
DiscoveryContext * discoveryContext = static_cast<DiscoveryContext *>(context);
@@ -72,8 +92,16 @@ static void HandleNodeBrowse(void * context, DnssdService * services, size_t ser
7292
discoveryContext->Retain();
7393
// For some platforms browsed services are already resolved, so verify if resolve is really needed or call resolve callback
7494

75-
// Check if SRV, TXT and AAAA records were received in DNS responses
76-
if (strlen(services[i].mHostName) == 0 || services[i].mTextEntrySize == 0 || !services[i].mAddress.HasValue())
95+
// mType(service name) exactly matches with operational service name
96+
bool isOperationalBrowse = strcmp(services[i].mType, kOperationalServiceName) == 0;
97+
98+
// For operational browse result we currently don't need IP address hence skip resolution and handle differently.
99+
if (isOperationalBrowse)
100+
{
101+
HandleNodeOperationalBrowse(context, &services[i], error);
102+
}
103+
// check whether SRV, TXT and AAAA records were received in DNS responses
104+
else if (strlen(services[i].mHostName) == 0 || services[i].mTextEntrySize == 0 || !services[i].mAddress.HasValue())
77105
{
78106
ChipDnssdResolve(&services[i], services[i].mInterface, HandleNodeResolve, context);
79107
}
@@ -337,7 +365,15 @@ void DiscoveryImplPlatform::HandleNodeIdResolve(void * context, DnssdService * r
337365
impl->mOperationalDelegate->OnOperationalNodeResolved(nodeData);
338366
}
339367

340-
void DnssdService::ToDiscoveredNodeData(const Span<Inet::IPAddress> & addresses, DiscoveredNodeData & nodeData)
368+
void DnssdService::ToDiscoveredOperationalNodeBrowseData(DiscoveredNodeData & nodeData)
369+
{
370+
nodeData.Set<OperationalNodeBrowseData>();
371+
372+
ExtractIdFromInstanceName(mName, &nodeData.Get<OperationalNodeBrowseData>().peerId);
373+
nodeData.Get<OperationalNodeBrowseData>().hasZeroTTL = (mTtlSeconds == 0);
374+
}
375+
376+
void DnssdService::ToDiscoveredCommissionNodeData(const Span<Inet::IPAddress> & addresses, DiscoveredNodeData & nodeData)
341377
{
342378
nodeData.Set<CommissionNodeData>();
343379
auto & discoveredData = nodeData.Get<CommissionNodeData>();
@@ -743,6 +779,31 @@ CHIP_ERROR DiscoveryImplPlatform::DiscoverCommissioners(DiscoveryFilter filter,
743779
return error;
744780
}
745781

782+
CHIP_ERROR DiscoveryImplPlatform::DiscoverOperational(DiscoveryFilter filter, DiscoveryContext & context)
783+
{
784+
ReturnErrorOnFailure(InitImpl());
785+
StopDiscovery(context);
786+
787+
char serviceName[kMaxOperationalServiceNameSize];
788+
ReturnErrorOnFailure(MakeServiceTypeName(serviceName, sizeof(serviceName), filter, DiscoveryType::kOperational));
789+
790+
intptr_t browseIdentifier;
791+
// Increase the reference count of the context to keep it alive until HandleNodeBrowse is called back.
792+
CHIP_ERROR error = ChipDnssdBrowse(serviceName, DnssdServiceProtocol::kDnssdProtocolTcp, Inet::IPAddressType::kAny,
793+
Inet::InterfaceId::Null(), HandleNodeBrowse, context.Retain(), &browseIdentifier);
794+
795+
if (error == CHIP_NO_ERROR)
796+
{
797+
context.SetBrowseIdentifier(browseIdentifier);
798+
}
799+
else
800+
{
801+
context.Release();
802+
}
803+
804+
return error;
805+
}
806+
746807
CHIP_ERROR DiscoveryImplPlatform::StartDiscovery(DiscoveryType type, DiscoveryFilter filter, DiscoveryContext & context)
747808
{
748809
switch (type)
@@ -752,7 +813,7 @@ CHIP_ERROR DiscoveryImplPlatform::StartDiscovery(DiscoveryType type, DiscoveryFi
752813
case DiscoveryType::kCommissionerNode:
753814
return DiscoverCommissioners(filter, context);
754815
case DiscoveryType::kOperational:
755-
return CHIP_ERROR_NOT_IMPLEMENTED;
816+
return DiscoverOperational(filter, context);
756817
default:
757818
return CHIP_ERROR_INVALID_ARGUMENT;
758819
}

src/lib/dnssd/Discovery_ImplPlatform.h

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class DiscoveryImplPlatform : public ServiceAdvertiser, public Resolver
5555
void NodeIdResolutionNoLongerNeeded(const PeerId & peerId) override;
5656
CHIP_ERROR DiscoverCommissionableNodes(DiscoveryFilter filter, DiscoveryContext & context);
5757
CHIP_ERROR DiscoverCommissioners(DiscoveryFilter filter, DiscoveryContext & context);
58+
CHIP_ERROR DiscoverOperational(DiscoveryFilter filter, DiscoveryContext & context);
5859
CHIP_ERROR StartDiscovery(DiscoveryType type, DiscoveryFilter filter, DiscoveryContext & context) override;
5960
CHIP_ERROR StopDiscovery(DiscoveryContext & context) override;
6061
CHIP_ERROR ReconfirmRecord(const char * hostname, Inet::IPAddress address, Inet::InterfaceId interfaceId) override;

src/lib/dnssd/ServiceNaming.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ CHIP_ERROR MakeServiceTypeName(char * buffer, size_t bufferLen, DiscoveryFilter
162162
{
163163
requiredSize = snprintf(buffer, bufferLen, kCommissionerServiceName);
164164
}
165+
else if (type == DiscoveryType::kOperational)
166+
{
167+
requiredSize = snprintf(buffer, bufferLen, kOperationalServiceName);
168+
}
165169
else
166170
{
167171
return CHIP_ERROR_NOT_IMPLEMENTED;

src/lib/dnssd/platform/Dnssd.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ struct DnssdService
8181
// Time to live in seconds. Per rfc6762 section 10, because we have a hostname, our default TTL is 120 seconds
8282
uint32_t mTtlSeconds = 120;
8383

84-
void ToDiscoveredNodeData(const Span<Inet::IPAddress> & addresses, DiscoveredNodeData & nodeData);
84+
void ToDiscoveredCommissionNodeData(const Span<Inet::IPAddress> & addresses, DiscoveredNodeData & nodeData);
85+
void ToDiscoveredOperationalNodeBrowseData(DiscoveredNodeData & nodeData);
8586
};
8687

8788
/**

src/lib/shell/commands/Dns.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,13 @@ CHIP_ERROR DnsHandler(int argc, char ** argv)
282282
return sShellDnsSubcommands.ExecCommand(argc, argv);
283283
}
284284

285+
CHIP_ERROR BrowseStopHandler(int argc, char ** argv)
286+
{
287+
streamer_printf(streamer_get(), "Stopping browse...\r\n");
288+
289+
return sResolverProxy.StopDiscovery();
290+
}
291+
285292
} // namespace
286293

287294
void RegisterDnsCommands()
@@ -292,6 +299,8 @@ void RegisterDnsCommands()
292299
{ &BrowseCommissionerHandler, "commissioner",
293300
"Browse Matter commissioner nodes. Usage: dns browse commissioner [subtype]" },
294301
{ &BrowseOperationalHandler, "operational", "Browse Matter operational nodes. Usage: dns browse operational" },
302+
{ &BrowseStopHandler, "stop", "Stop ongoing browse. Usage: dns browse stop" },
303+
295304
};
296305

297306
static const shell_command_t sDnsSubCommands[] = {

src/platform/Darwin/DnssdContexts.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,16 @@ bool ResolveContext::TryReportingResultsForInterfaceIndex(uint32_t interfaceInde
585585
{
586586
auto delegate = static_cast<DiscoverNodeDelegate *>(context);
587587
DiscoveredNodeData nodeData;
588-
service.ToDiscoveredNodeData(addresses, nodeData);
588+
589+
// Check whether mType (service name) exactly matches with operational service name
590+
if (strcmp(service.mType, kOperationalServiceName) == 0)
591+
{
592+
service.ToDiscoveredOperationalNodeBrowseData(nodeData);
593+
}
594+
else
595+
{
596+
service.ToDiscoveredCommissionNodeData(addresses, nodeData);
597+
}
589598
delegate->OnNodeDiscovered(nodeData);
590599
}
591600
else

src/platform/Linux/DnssdImpl.cpp

+58-40
Original file line numberDiff line numberDiff line change
@@ -610,9 +610,9 @@ CHIP_ERROR MdnsAvahi::Browse(const char * type, DnssdServiceProtocol protocol, c
610610
{
611611
avahiInterface = AVAHI_IF_UNSPEC;
612612
}
613-
browseContext->mInterface = avahiInterface;
614-
browseContext->mProtocol = GetFullType(type, protocol);
615-
browseContext->mBrowseRetries = 0;
613+
browseContext->mInterface = avahiInterface;
614+
browseContext->mProtocol = GetFullType(type, protocol);
615+
browseContext->mReceivedAllCached = false;
616616
browseContext->mStopped.store(false);
617617

618618
browser = avahi_service_browser_new(mClient, avahiInterface, AVAHI_PROTO_UNSPEC, browseContext->mProtocol.c_str(), nullptr,
@@ -685,23 +685,22 @@ void CopyTypeWithoutProtocol(char (&dest)[N], const char * typeAndProtocol)
685685
}
686686
}
687687

688-
void MdnsAvahi::BrowseRetryCallback(chip::System::Layer * aLayer, void * appState)
688+
void MdnsAvahi::InvokeDelegateOrCleanUp(BrowseContext * context, AvahiServiceBrowser * browser)
689689
{
690-
BrowseContext * context = static_cast<BrowseContext *>(appState);
691-
// Don't schedule anything new if we've stopped.
692-
if (context->mStopped.load())
690+
// If we were already asked to stop, no need to send a callback - no one is listening.
691+
if (!context->mStopped.load())
693692
{
694-
chip::Platform::Delete(context);
695-
return;
693+
// since this is continuous browse, finalBrowse will always be false.
694+
context->mCallback(context->mContext, context->mServices.data(), context->mServices.size(), false, CHIP_NO_ERROR);
695+
696+
// Clearing records/services already passed to application through delegate. Keeping it may cause
697+
// duplicates in next query / retry attempt as currently found will also come again from cache.
698+
context->mServices.clear();
696699
}
697-
AvahiServiceBrowser * newBrowser =
698-
avahi_service_browser_new(context->mInstance->mClient, context->mInterface, AVAHI_PROTO_UNSPEC, context->mProtocol.c_str(),
699-
nullptr, static_cast<AvahiLookupFlags>(0), HandleBrowse, context);
700-
if (newBrowser == nullptr)
700+
else
701701
{
702-
// If we failed to create the browser, this browse context is effectively done. We need to call the final callback and
703-
// delete the context.
704-
context->mCallback(context->mContext, context->mServices.data(), context->mServices.size(), true, CHIP_NO_ERROR);
702+
// browse is stopped, so free browse handle and context
703+
avahi_service_browser_free(browser);
705704
chip::Platform::Delete(context);
706705
}
707706
}
@@ -721,6 +720,13 @@ void MdnsAvahi::HandleBrowse(AvahiServiceBrowser * browser, AvahiIfIndex interfa
721720
break;
722721
case AVAHI_BROWSER_NEW:
723722
ChipLogProgress(DeviceLayer, "Avahi browse: cache new");
723+
if (context->mStopped.load())
724+
{
725+
// browse is stopped, so free browse handle and context
726+
avahi_service_browser_free(browser);
727+
chip::Platform::Delete(context);
728+
break;
729+
}
724730
if (strcmp("local", domain) == 0)
725731
{
726732
DnssdService service = {};
@@ -737,41 +743,53 @@ void MdnsAvahi::HandleBrowse(AvahiServiceBrowser * browser, AvahiIfIndex interfa
737743
}
738744
service.mType[kDnssdTypeMaxSize] = 0;
739745
context->mServices.push_back(service);
746+
if (context->mReceivedAllCached)
747+
{
748+
InvokeDelegateOrCleanUp(context, browser);
749+
}
740750
}
741751
break;
742752
case AVAHI_BROWSER_ALL_FOR_NOW: {
743753
ChipLogProgress(DeviceLayer, "Avahi browse: all for now");
744-
bool needRetries = context->mBrowseRetries++ < kMaxBrowseRetries && !context->mStopped.load();
745-
// If we were already asked to stop, no need to send a callback - no one is listening.
746-
if (!context->mStopped.load())
747-
{
748-
context->mCallback(context->mContext, context->mServices.data(), context->mServices.size(), !needRetries,
749-
CHIP_NO_ERROR);
750-
}
751-
avahi_service_browser_free(browser);
752-
if (needRetries)
753-
{
754-
context->mNextRetryDelay *= 2;
755-
// Hand the ownership of the context over to the timer. It will either schedule a new browse on the context,
756-
// triggering this function, or it will delete and not reschedule (if stopped).
757-
DeviceLayer::SystemLayer().StartTimer(context->mNextRetryDelay / 2, BrowseRetryCallback, context);
758-
}
759-
else
760-
{
761-
// We didn't schedule a timer, so we're responsible for deleting the context
762-
chip::Platform::Delete(context);
763-
}
754+
context->mReceivedAllCached = true;
755+
756+
InvokeDelegateOrCleanUp(context, browser);
764757
break;
765758
}
766759
case AVAHI_BROWSER_REMOVE:
767760
ChipLogProgress(DeviceLayer, "Avahi browse: remove");
768761
if (strcmp("local", domain) == 0)
769762
{
770-
context->mServices.erase(
771-
std::remove_if(context->mServices.begin(), context->mServices.end(), [name, type](const DnssdService & service) {
772-
return strcmp(name, service.mName) == 0 && type == GetFullType(service.mType, service.mProtocol);
773-
}));
763+
// don't attempt to erase if vector has been cleared
764+
if (context->mServices.size())
765+
{
766+
context->mServices.erase(std::remove_if(
767+
context->mServices.begin(), context->mServices.end(), [name, type](const DnssdService & service) {
768+
return strcmp(name, service.mName) == 0 && type == GetFullType(service.mType, service.mProtocol);
769+
}));
770+
}
771+
772+
if (context->mReceivedAllCached)
773+
{
774+
DnssdService service = {};
775+
776+
Platform::CopyString(service.mName, name);
777+
CopyTypeWithoutProtocol(service.mType, type);
778+
service.mProtocol = GetProtocolInType(type);
779+
service.mAddressType = context->mAddressType;
780+
service.mTransportType = ToAddressType(protocol);
781+
service.mInterface = Inet::InterfaceId::Null();
782+
if (interface != AVAHI_IF_UNSPEC)
783+
{
784+
service.mInterface = static_cast<chip::Inet::InterfaceId>(interface);
785+
}
786+
service.mTtlSeconds = 0;
787+
788+
context->mServices.push_back(service);
789+
InvokeDelegateOrCleanUp(context, browser);
790+
}
774791
}
792+
775793
break;
776794
case AVAHI_BROWSER_CACHE_EXHAUSTED:
777795
ChipLogProgress(DeviceLayer, "Avahi browse: cache exhausted");

src/platform/Linux/DnssdImpl.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,11 @@ class MdnsAvahi
131131
void * mContext;
132132
Inet::IPAddressType mAddressType;
133133
std::vector<DnssdService> mServices;
134-
size_t mBrowseRetries;
134+
bool mReceivedAllCached;
135135
AvahiIfIndex mInterface;
136136
std::string mProtocol;
137-
chip::System::Clock::Timeout mNextRetryDelay = chip::System::Clock::Seconds16(1);
138137
std::atomic_bool mStopped{ false };
138+
AvahiServiceBrowser * mBrowser;
139139
};
140140

141141
struct ResolveContext
@@ -181,7 +181,7 @@ class MdnsAvahi
181181
static void HandleBrowse(AvahiServiceBrowser * broswer, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event,
182182
const char * name, const char * type, const char * domain, AvahiLookupResultFlags flags,
183183
void * userdata);
184-
static void BrowseRetryCallback(chip::System::Layer * aLayer, void * appState);
184+
static void InvokeDelegateOrCleanUp(BrowseContext * context, AvahiServiceBrowser * browser);
185185
static void HandleResolve(AvahiServiceResolver * resolver, AvahiIfIndex interface, AvahiProtocol protocol,
186186
AvahiResolverEvent event, const char * name, const char * type, const char * domain,
187187
const char * host_name, const AvahiAddress * address, uint16_t port, AvahiStringList * txt,

0 commit comments

Comments
 (0)