mDNS, hostname.local, and WSL2

Modern computers can often be reached by the DNS name hostname.local. This is implemented via Multicast DNS, mDNS, which originated in the early 2000s as part of the zeroconf work to make networked computers usable without manual configuration. Apple’s Bonjour was the first implementation of it I saw working widely. Linux machines tend to use Avahi. For Debain and Ubuntu systems apt install avahi-daemon is generally enough to get the system announcing its name.

The magic of multicast

It works via multicast. mDNS-aware name resolvers know that if a domain name ends in .local, the way to resolve it is to send a UDP multicast request to 224.0.0.251:5353 or and see if anyone answers. (There’s an IPv6 equivalent). Multicast generally propagates across your whole LAN; if a host sees a query for their own name it answers back to 224.0.0.251 (not the requester directly) with a DNS record. It’s not exactly standard DNS packets, some slight modifications in place, but the basics are the same.

It works great! As long as you have a reliable LAN. I’m using it on some Raspberry Pi devices with a flaky WiFi connection and that’s not a good experience.

As an aside, can I say how much I love multicast? Ethernet broadcast too, and even the less common anycast. Being able to send a packet to the local network without being exactly sure who the recipient is can be very useful in all sorts of applications. A lot of stuff we do like NTP would work better if it regularly used multicast. Unfortunately multicast over the routed public Internet is pretty much a dead-end technology; no one has ever figured out how to manage it scalably.

mDNS services

I mostly use mDNS myself for .local domain name resolution. But mDNS is a full service discovery framework. You can use it to ask for things like “is there a printer on the network?”

Avahi comes with a discovery tool to get a quick dump of services on the network.
avahi-browse --all --ignore-local --resolve --terminate
Here’s an example of the services my printer offers

+   eno1 IPv4 Samsung M267x 287x Series (SEC...)   _privet._tcp         local
+   eno1 IPv4 Samsung M267x 287x Series (SEC...)   _scanner._tcp        local
+   eno1 IPv4 Samsung M267x 287x Series (SEC...)   UNIX Printer         local
+   eno1 IPv4 Samsung M267x 287x Series (SEC...)   PDL Printer          local
+   eno1 IPv4 Samsung M267x 287x Series (SEC...)   Internet Printer     local
+   eno1 IPv4 Samsung M267x 287x Series (SEC...)   Web Site             local

On my LAN I have services for Sonos, Roku Airplay, the printer, my Samba fileshare, and some things Home Assistant has published.

I believe the service discovery is all implemented via mDNS queries, with some extra information placed in TXT records. It all looks fairly elaborate, RFC 6763 is one reference for how it works. Now I’m curious how carefully specified the metadata for things like printers are. Is there a clear standard or is it all freeform and customary publication?

WSL and mDNS

WSL2, Microsoft’s Linux VM for Windows, also supports .local domain names. It’s a little weird. My windows machine is named “Nelson-Win10”. On the rest of my network nelson-win10.local resolves to 192.168.3.114, the DHCP-assigned address for the host Windows system. Just like you’d expect. But inside a WSL2 shell it resolves to Nelson-Win10.mshome.net (172.27.48.1). That’s a private-use address and the domain name is something Microsoft has been doing for years as part of their Internet connection sharing stuff. This address is reachable from the host Windows system. It’s not accessible on the rest of my LAN because I don’t have routes for that subnet, I wonder if it would work if I added one? Anyway this is all a workaround for the fact that localhost doesn’t work very well (or at all) across the host Windows and WSL2). Honestly all the networking in WSL2 to the outside world is kind of confusing and maybe buggy. But hostname.local works on the Windows machine itself.

(Note that in a command shell on Windows itself, nelson-win10.local also works. It resolves to an IPv6 link local address in the fe80::/10 block.)