Skip to content

Commit b6caecf

Browse files
authored
[Linux] DnssdImpl: rework avahi implementation (#26397)
* InetInterface: add IsLoopback() to InterfaceIterator and InterfaceAddressIterator Required for improving dns-sd avahi based implementation * [Linux] DnssdImpl: rework avahi implementation This commit fixes two problems with the previous avahi based dns-sd implementation: - Publishing more than one service at the same time did not work. This needs to be possible e.g. when a node is commissioned into multiple fabrics. The previous implementation falsely assumed that additional services can be added to already committed (=published) AvahiEntryGroup, which is not the case. An AvahiEntryGroup can only publish multiple services ALL AT ONCE. The new implementation creates a new AvahiEntryGroup per service, on demand. - The previous implementation took ownership of the platform-global default hostname, (by overwriting it). This is not a good idea because the default hostname is usually of relevance for other non-matter services on a given Linux platform. The new implementation establishes the matter-mandated MAC-derived hostname separately and explicitly adds interface addresses. * DnssdImpl.cpp: avoid shadowing local vars to prevent warning/error * DnssdImpl.cpp: make work without INET_CONFIG_ENABLE_IPV4 * DnssdImpl.cpp: fix missing error variable assignment in SuccessOrExit() (found by code-lints)
1 parent 9c4ddfa commit b6caecf

File tree

4 files changed

+156
-77
lines changed

4 files changed

+156
-77
lines changed

src/inet/InetInterface.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,11 @@ bool InterfaceIterator::IsUp()
562562
return (GetFlags() & IFF_UP) != 0;
563563
}
564564

565+
bool InterfaceIterator::IsLoopback()
566+
{
567+
return (GetFlags() & IFF_LOOPBACK) != 0;
568+
}
569+
565570
bool InterfaceIterator::SupportsMulticast()
566571
{
567572
return (GetFlags() & IFF_MULTICAST) != 0;
@@ -706,6 +711,11 @@ bool InterfaceAddressIterator::IsUp()
706711
return HasCurrent() && (mCurAddr->ifa_flags & IFF_UP) != 0;
707712
}
708713

714+
bool InterfaceAddressIterator::IsLoopback()
715+
{
716+
return HasCurrent() && (mCurAddr->ifa_flags & IFF_LOOPBACK) != 0;
717+
}
718+
709719
bool InterfaceAddressIterator::SupportsMulticast()
710720
{
711721
return HasCurrent() && (mCurAddr->ifa_flags & IFF_MULTICAST) != 0;

src/inet/InetInterface.h

+16
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,14 @@ class InterfaceIterator
310310
*/
311311
bool IsUp();
312312

313+
/**
314+
* Returns whether the current network interface is a loopback interface
315+
*
316+
* @return \c true if current network interface is a loopback interface, \c false
317+
* if not, or if the iterator is positioned beyond the end of the list.
318+
*/
319+
bool IsLoopback();
320+
313321
/**
314322
* Returns whether the current network interface supports multicast.
315323
*
@@ -504,6 +512,14 @@ class DLL_EXPORT InterfaceAddressIterator
504512
*/
505513
bool IsUp();
506514

515+
/**
516+
* Returns whether the current network interface is a loopback interface
517+
*
518+
* @return \c true if current network interface is a loopback interface, \c false
519+
* if not, or if the iterator is positioned beyond the end of the list.
520+
*/
521+
bool IsLoopback();
522+
507523
/**
508524
* Returns whether the network interface associated with the current interface address supports multicast.
509525
*

src/platform/Linux/DnssdImpl.cpp

+128-73
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ CHIP_ERROR MdnsAvahi::Init(DnssdAsyncReturnCallback initCallback, DnssdAsyncRetu
332332

333333
VerifyOrExit(initCallback != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT);
334334
VerifyOrExit(errorCallback != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT);
335-
VerifyOrExit(mClient == nullptr && mGroup == nullptr, error = CHIP_ERROR_INCORRECT_STATE);
335+
VerifyOrExit(mClient == nullptr && mPublishedGroups.empty(), error = CHIP_ERROR_INCORRECT_STATE);
336336
mInitCallback = initCallback;
337337
mErrorCallback = errorCallback;
338338
mAsyncReturnContext = context;
@@ -346,11 +346,7 @@ CHIP_ERROR MdnsAvahi::Init(DnssdAsyncReturnCallback initCallback, DnssdAsyncRetu
346346

347347
void MdnsAvahi::Shutdown()
348348
{
349-
if (mGroup)
350-
{
351-
avahi_entry_group_free(mGroup);
352-
mGroup = nullptr;
353-
}
349+
StopPublish();
354350
if (mClient)
355351
{
356352
avahi_client_free(mClient);
@@ -361,19 +357,11 @@ void MdnsAvahi::Shutdown()
361357
CHIP_ERROR MdnsAvahi::SetHostname(const char * hostname)
362358
{
363359
CHIP_ERROR error = CHIP_NO_ERROR;
364-
int avahiRet;
365360

366361
VerifyOrExit(mClient != nullptr, error = CHIP_ERROR_INCORRECT_STATE);
367-
avahiRet = avahi_client_set_host_name(mClient, hostname);
368-
if (avahiRet == AVAHI_ERR_ACCESS_DENIED)
369-
{
370-
ChipLogError(DeviceLayer, "Cannot set hostname on this system, continue anyway...");
371-
}
372-
else if (avahiRet != AVAHI_OK && avahiRet != AVAHI_ERR_NO_CHANGE)
373-
{
374-
error = CHIP_ERROR_INTERNAL;
375-
}
376-
362+
// Note: we do no longer set the primary hostname here, as other services
363+
// on the platform might not be happy with the matter mandated hostname.
364+
// Instead, we'll establish our own hostname when needed (see PublishService())
377365
exit:
378366
return error;
379367
}
@@ -390,16 +378,8 @@ void MdnsAvahi::HandleClientState(AvahiClient * client, AvahiClientState state)
390378
case AVAHI_CLIENT_S_RUNNING:
391379
ChipLogProgress(DeviceLayer, "Avahi client registered");
392380
mClient = client;
393-
mGroup = avahi_entry_group_new(client, HandleGroupState, this);
394-
if (mGroup == nullptr)
395-
{
396-
ChipLogError(DeviceLayer, "Failed to create avahi group: %s", avahi_strerror(avahi_client_errno(client)));
397-
mInitCallback(mAsyncReturnContext, CHIP_ERROR_OPEN_FAILED);
398-
}
399-
else
400-
{
401-
mInitCallback(mAsyncReturnContext, CHIP_NO_ERROR);
402-
}
381+
// no longer create groups here, but on a by-service basis in PublishService()
382+
mInitCallback(mAsyncReturnContext, CHIP_NO_ERROR);
403383
break;
404384
case AVAHI_CLIENT_FAILURE:
405385
ChipLogError(DeviceLayer, "Avahi client failure");
@@ -408,22 +388,8 @@ void MdnsAvahi::HandleClientState(AvahiClient * client, AvahiClientState state)
408388
case AVAHI_CLIENT_S_COLLISION:
409389
case AVAHI_CLIENT_S_REGISTERING:
410390
ChipLogProgress(DeviceLayer, "Avahi re-register required");
411-
if (mGroup != nullptr)
412-
{
413-
avahi_entry_group_reset(mGroup);
414-
avahi_entry_group_free(mGroup);
415-
}
416-
mGroup = avahi_entry_group_new(client, HandleGroupState, this);
417-
mPublishedServices.clear();
418-
if (mGroup == nullptr)
419-
{
420-
ChipLogError(DeviceLayer, "Failed to create avahi group: %s", avahi_strerror(avahi_client_errno(client)));
421-
mErrorCallback(mAsyncReturnContext, CHIP_ERROR_OPEN_FAILED);
422-
}
423-
else
424-
{
425-
mErrorCallback(mAsyncReturnContext, CHIP_ERROR_FORCED_RESET);
426-
}
391+
StopPublish();
392+
mErrorCallback(mAsyncReturnContext, CHIP_ERROR_FORCED_RESET);
427393
break;
428394
case AVAHI_CLIENT_CONNECTING:
429395
ChipLogProgress(DeviceLayer, "Avahi connecting");
@@ -449,7 +415,7 @@ void MdnsAvahi::HandleGroupState(AvahiEntryGroup * group, AvahiEntryGroupState s
449415
break;
450416
case AVAHI_ENTRY_GROUP_FAILURE:
451417
ChipLogError(DeviceLayer, "Avahi group internal failure %s",
452-
avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(mGroup))));
418+
avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(group))));
453419
mErrorCallback(mAsyncReturnContext, CHIP_ERROR_INTERNAL);
454420
break;
455421
case AVAHI_ENTRY_GROUP_UNCOMMITED:
@@ -462,50 +428,130 @@ CHIP_ERROR MdnsAvahi::PublishService(const DnssdService & service, DnssdPublishC
462428
{
463429
std::ostringstream keyBuilder;
464430
std::string key;
465-
std::string type = GetFullType(service.mType, service.mProtocol);
466-
CHIP_ERROR error = CHIP_NO_ERROR;
467-
AvahiStringList * text = nullptr;
431+
std::string type = GetFullType(service.mType, service.mProtocol);
432+
std::string matterHostname;
433+
CHIP_ERROR error = CHIP_NO_ERROR;
434+
AvahiStringList * text = nullptr;
435+
AvahiEntryGroup * group = nullptr;
436+
const char * mainHostname = nullptr;
468437
AvahiIfIndex interface =
469438
service.mInterface.IsPresent() ? static_cast<AvahiIfIndex>(service.mInterface.GetPlatformInterface()) : AVAHI_IF_UNSPEC;
439+
AvahiProtocol protocol = ToAvahiProtocol(service.mAddressType);
470440

471441
keyBuilder << service.mName << "." << type << service.mPort << "." << interface;
472442
key = keyBuilder.str();
473443
ChipLogProgress(DeviceLayer, "PublishService %s", key.c_str());
474-
475-
if (mPublishedServices.find(key) == mPublishedServices.end())
444+
auto publishedgroups_it = mPublishedGroups.find(key);
445+
if (publishedgroups_it != mPublishedGroups.end())
476446
{
477-
SuccessOrExit(error = MakeAvahiStringListFromTextEntries(service.mTextEntries, service.mTextEntrySize, &text));
478-
479-
mPublishedServices.emplace(key);
480-
VerifyOrExit(avahi_entry_group_add_service_strlst(mGroup, interface, ToAvahiProtocol(service.mAddressType),
481-
static_cast<AvahiPublishFlags>(0), service.mName, type.c_str(), nullptr,
482-
nullptr, service.mPort, text) == 0,
483-
error = CHIP_ERROR_INTERNAL);
484-
for (size_t i = 0; i < service.mSubTypeSize; i++)
447+
// same service was already published, we need to de-publish it first
448+
int avahiRet = avahi_entry_group_free(publishedgroups_it->second);
449+
if (avahiRet != AVAHI_OK)
485450
{
486-
std::ostringstream sstream;
451+
ChipLogError(DeviceLayer, "Cannot remove avahi group: %s", avahi_strerror(avahiRet));
452+
ExitNow(error = CHIP_ERROR_INTERNAL);
453+
}
454+
mPublishedGroups.erase(publishedgroups_it);
455+
}
487456

488-
sstream << service.mSubTypes[i] << "._sub." << type;
457+
// create fresh group
458+
group = avahi_entry_group_new(mClient, HandleGroupState, this);
459+
VerifyOrExit(group != nullptr, error = CHIP_ERROR_INTERNAL);
489460

490-
VerifyOrExit(avahi_entry_group_add_service_subtype(mGroup, interface, ToAvahiProtocol(service.mAddressType),
491-
static_cast<AvahiPublishFlags>(0), service.mName, type.c_str(),
492-
nullptr, sstream.str().c_str()) == 0,
493-
error = CHIP_ERROR_INTERNAL);
494-
}
461+
// establish the host name (separately from avahi's default host name that the platform might have,
462+
// unless it matches the matter hostname)
463+
mainHostname = avahi_client_get_host_name(mClient);
464+
if (strcmp(mainHostname, service.mHostName) == 0)
465+
{
466+
// main host name is correct, we can use it
467+
matterHostname = std::string(mainHostname) + ".local";
495468
}
496469
else
497470
{
498-
SuccessOrExit(error = MakeAvahiStringListFromTextEntries(service.mTextEntries, service.mTextEntrySize, &text));
471+
// we need to establish a matter hostname separately from the platform's default hostname
472+
char b[chip::Inet::IPAddress::kMaxStringLength];
473+
SuccessOrExit(error = service.mInterface.GetInterfaceName(b, chip::Inet::IPAddress::kMaxStringLength));
474+
ChipLogDetail(DeviceLayer, "Using addresses from interface id=%d name=%s", service.mInterface.GetPlatformInterface(), b);
475+
matterHostname = std::string(service.mHostName) + ".local";
476+
// find addresses to publish
477+
for (chip::Inet::InterfaceAddressIterator addr_it; addr_it.HasCurrent(); addr_it.Next())
478+
{
479+
// only specific interface?
480+
if (service.mInterface.IsPresent() && addr_it.GetInterfaceId() != service.mInterface)
481+
{
482+
continue;
483+
}
484+
if (addr_it.IsUp())
485+
{
486+
if (addr_it.IsLoopback())
487+
{
488+
// do not advertise loopback interface addresses
489+
continue;
490+
}
491+
chip::Inet::IPAddress addr;
492+
if ((addr_it.GetAddress(addr) == CHIP_NO_ERROR) &&
493+
((service.mAddressType == chip::Inet::IPAddressType::kAny) ||
494+
(addr.IsIPv6() && service.mAddressType == chip::Inet::IPAddressType::kIPv6)
495+
#if INET_CONFIG_ENABLE_IPV4
496+
|| (addr.IsIPv4() && service.mAddressType == chip::Inet::IPAddressType::kIPv4)
497+
#endif
498+
))
499+
{
500+
VerifyOrExit(addr.ToString(b) != nullptr, error = CHIP_ERROR_INTERNAL);
501+
AvahiAddress a;
502+
VerifyOrExit(avahi_address_parse(b, AVAHI_PROTO_UNSPEC, &a) != nullptr, error = CHIP_ERROR_INTERNAL);
503+
AvahiIfIndex thisinterface = static_cast<AvahiIfIndex>(addr_it.GetInterfaceId().GetPlatformInterface());
504+
// Note: NO_REVERSE publish flag is needed because otherwise we can't have more than one hostname
505+
// for reverse resolving IP addresses back to hostnames
506+
VerifyOrExit(avahi_entry_group_add_address(group, // group
507+
thisinterface, // interface
508+
ToAvahiProtocol(addr.Type()), // protocol
509+
AVAHI_PUBLISH_NO_REVERSE, // publish flags
510+
matterHostname.c_str(), // hostname
511+
&a // address
512+
) == 0,
513+
error = CHIP_ERROR_INTERNAL);
514+
}
515+
}
516+
}
517+
}
499518

500-
VerifyOrExit(avahi_entry_group_update_service_txt_strlst(mGroup, interface, ToAvahiProtocol(service.mAddressType),
501-
static_cast<AvahiPublishFlags>(0), service.mName, type.c_str(),
502-
nullptr, text) == 0,
519+
// create the service
520+
SuccessOrExit(error = MakeAvahiStringListFromTextEntries(service.mTextEntries, service.mTextEntrySize, &text));
521+
522+
VerifyOrExit(avahi_entry_group_add_service_strlst(group, interface, protocol, // group, interface, protocol
523+
static_cast<AvahiPublishFlags>(0), // publish flags
524+
service.mName, // service name
525+
type.c_str(), // type
526+
nullptr, // domain
527+
matterHostname.c_str(), // host
528+
service.mPort, // port
529+
text) == 0, // TXT records StringList
530+
error = CHIP_ERROR_INTERNAL);
531+
532+
// add the subtypes
533+
for (size_t i = 0; i < service.mSubTypeSize; i++)
534+
{
535+
std::ostringstream sstream;
536+
537+
sstream << service.mSubTypes[i] << "._sub." << type;
538+
539+
VerifyOrExit(avahi_entry_group_add_service_subtype(group, interface, protocol, static_cast<AvahiPublishFlags>(0),
540+
service.mName, type.c_str(), nullptr, sstream.str().c_str()) == 0,
503541
error = CHIP_ERROR_INTERNAL);
504542
}
543+
VerifyOrExit(avahi_entry_group_commit(group) == 0, error = CHIP_ERROR_INTERNAL);
505544

506-
VerifyOrExit(avahi_entry_group_commit(mGroup) == 0, error = CHIP_ERROR_INTERNAL);
545+
// group is now published, pass it to the service map
546+
mPublishedGroups[key] = group;
547+
group = nullptr;
507548

508549
exit:
550+
if (group != nullptr)
551+
{
552+
avahi_entry_group_free(group);
553+
}
554+
509555
if (text != nullptr)
510556
{
511557
avahi_string_list_free(text);
@@ -521,6 +567,8 @@ CHIP_ERROR MdnsAvahi::PublishService(const DnssdService & service, DnssdPublishC
521567
}
522568
else
523569
{
570+
ChipLogError(DeviceLayer, "PublishService failed: %s",
571+
mClient ? avahi_strerror(avahi_client_errno(mClient)) : "no mClient");
524572
callback(context, nullptr, nullptr, error);
525573
}
526574

@@ -530,12 +578,19 @@ CHIP_ERROR MdnsAvahi::PublishService(const DnssdService & service, DnssdPublishC
530578
CHIP_ERROR MdnsAvahi::StopPublish()
531579
{
532580
CHIP_ERROR error = CHIP_NO_ERROR;
533-
mPublishedServices.clear();
534-
if (mGroup)
581+
for (const auto & group : mPublishedGroups)
535582
{
536-
VerifyOrExit(avahi_entry_group_reset(mGroup) == 0, error = CHIP_ERROR_INTERNAL);
583+
if (group.second)
584+
{
585+
int avahiRet = avahi_entry_group_free(group.second);
586+
if (avahiRet != AVAHI_OK)
587+
{
588+
ChipLogError(DeviceLayer, "Error freeing avahi group: %s", avahi_strerror(avahiRet));
589+
error = CHIP_ERROR_INTERNAL;
590+
}
591+
}
537592
}
538-
exit:
593+
mPublishedGroups.clear();
539594
return error;
540595
}
541596

src/platform/Linux/DnssdImpl.h

+2-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#include <chrono>
2424
#include <map>
2525
#include <memory>
26-
#include <set>
2726
#include <string>
2827
#include <vector>
2928

@@ -142,7 +141,7 @@ class MdnsAvahi
142141
uint8_t mAttempts = 0;
143142
};
144143

145-
MdnsAvahi() : mClient(nullptr), mGroup(nullptr) {}
144+
MdnsAvahi() : mClient(nullptr) {}
146145
static MdnsAvahi sInstance;
147146

148147
static void HandleClientState(AvahiClient * client, AvahiClientState state, void * context);
@@ -163,9 +162,8 @@ class MdnsAvahi
163162
DnssdAsyncReturnCallback mErrorCallback;
164163
void * mAsyncReturnContext;
165164

166-
std::set<std::string> mPublishedServices;
167165
AvahiClient * mClient;
168-
AvahiEntryGroup * mGroup;
166+
std::map<std::string, AvahiEntryGroup *> mPublishedGroups;
169167
Poller mPoller;
170168
};
171169

0 commit comments

Comments
 (0)