Configuring mDNS for Easy Access to Devices
Zeroconf consists of a number of related concepts with the common objective of making simple networked services "just work". Initially it was implemented by Apple as Bonjour, but on Linux the re-implementation Avahi is usually used to provide Zeroconf. Although initially intended for discovering networked devices like printers or speakers, it can also be useful for accessing other hosts or smart home devices on the local network without hard-coding their address or hosting a name server.
The relevant concept in Zeroconf that makes this work is multicast DNS (mDNS) which can resolve hostnames inside a local network by using multicast broadcasts. The mDNS client sends a multicast broadcast asking for <name>.local, and the host with the given name (typically the device's hostname) answers it. My goal was to make various devices on my home network accessible over their mDNS address. This works unreliably out of the box, I've collected the steps I took to get it working more reliably.
MDNS is easily conflated with DNS service discovery (DNS-SD), which allows discovering specific services (e.g. print service, web server, etc.) once a host is known. Both are relevant, however technically these are two separate concepts under Zeroconf.
Analyzing the Network
To get a more or less complete list of DNS-SD services on the network, use the command avahi-browse --resolve --terminate --all. This uses both mDNS and DNS-SD via the Avahi daemon. For more details, see https://askubuntu.com/a/1105895.
To use mDNS to resolve a hostname, use avahi-resolve-host-name <name>.local. This returns only a single resolved address, even if the host has multiple. To at least reliably get an IPv4 or an IPv6 address, pass -4 or -6.
Becoming Discoverable
While some devices are easily made accessible to mDNS queries, some need more work.
Avahi (Linux)
The Avahi Daemon, as typically configured on Linux distributions, does not answer mDNS queries, because it is not configured to provide any services. To publish a "workstation" service, effectively advertising only that the host exists, a change is required in the config file (probably /etc/avahi/avahi-daemon.conf):
publish-workstation=yes
This supposedly used to be the default in earlier versions, but was changed for security reasons. Using a firewall on untrusted networks is a good idea anyway!
Restart the Daemon afterwards with sudo systemctl restart avahi-daemon.service.
Android
Support is poor: the address is hard-coded to Android.local and the daemon only starts if some service is registered.
Discovering Others
Linux (Avahi/NSS)
Resolution of mDNS addresses on Linux is provided by Avahi via a plugin to the Nameserver Switch (NSS). Multicast DNS is distinct from plain DNS, and is thus enabled by its own entry under hosts: in /etc/nsswitch.conf. On my Fedora system, this contains mdns4_minimal [NOTFOUND=return], which means mDNS is consulted over IPv4. Most applications will consult the NSS (via glibc) and thus be able to resolve .local addresses.
As a matter of principle, I would also like mDNS over IPv6. This can be accomplished by changing mdns4_minimal [NOTFOUND=return] to mdns_minimal [NOTFOUND=return], which tries both IPv6 and IPv4.
On my system, nsswitch.conf is managed by authselect (man(8) authselect, man(5) authselect-profiles). From authselect current, the current profile is called local, while authselect list-features local lists the available feature with-mdns6. Enabling the feature with authselect enable-feature with-mdns6 makes the appropriate change to nsswitch.conf.
Caveat on IPv6 Support
Consulting man(5) nsswitch.conf, additional services are implemented as shared libraries named libnss_SERVICE.so.X. Looking in the appropriate places (man(8) ldconfig), we can list available services:
find /lib/ /lib64/ /usr/lib/ /usr/lib64/ -name 'libnss_*.so.*' -print0 | xargs --null basename --multiple | sort | uniq
Among other things, this finds the library /lib64/libnss_mdns4.so.2 which on my system is provided by the package nss-mdns. From the README, IPv6 support is as simple as changing mdns4_minimal to mdns_minimal.
However, it comes with the following warning:
Due to the fact that most mDNS responders only register local IPv4 addresses via mDNS, most people will want to use
libnss_mdns4.so.2exclusively. Usinglibnss_mdns.so.2orlibnss_mdns6.so.2in such a situation causes long timeouts when resolving hosts since most modern Unix/Linux applications check for IPv6 addresses first, followed by a lookup for IPv4.
Indeed, the only hosts advertising an IPv6 address on my home network are my Linux laptop and my Linux server. However, by using the libnss_mdns_minimal libraries, non-mDNS addresses will by immediately rejected, so that the timeout won't affect us when not resolving mDNS addresses.
mDNS and Android
As of November 2021, Android supports resolving mDNS addresses directly. https://source.android.com/docs/core/ota/modular-system/dns-resolver#mdns-local-resolution