Resolving mDNS across VLANs with Avahi on OpenWRT

mDNS, or multicast DNS, is a way to discover devices on your network at .local domain without any central DNS configuration (also known as ZeroConf and Bonjour, etc). Fedora Magazine has a good article on setting it up in Fedora, which I won’t repeat here.

If you’re like me, you’re using OpenWRT with multiple VLANs to separate networks. In my case this includes my home automation (HA) network (VLAN 2) from my regular trusted LAN (VLAN 1). Various untrusted home automation products, as well as my own devices, go into the HA network (more on that in a later post).

In my setup, my OpenWRT router acts as my central router, connecting each of my networks and controlling access. My LAN can access everything in my HA network, but generally only establish related TCP traffic is allowed back from HA to LAN. There are some exceptions though, for example my Pi-hole DNS servers which are accessible from all networks, but otherwise that’s the general setup.

With IPv4, mDNS communicates by sending IP multicast UDP packets to 224.0.0.251 with source and destination ports both using 5353. In order to receive requests and responses, your devices need to be running an mDNS service and also allow incoming UDP traffic on port 5353.

As multicast is local only, mDNS doesn’t work natively across routed networks. Therefore, this prevents me from easily talking to my various HA devices from my LAN. In order to support mDNS across routed networks, you need a proxy in the middle to transparently send requests and responses back and forward. There are a few different options for a proxy, such as igmpproxy, but i prefer to use the standard Avahi server on my OpenWRT router.

Keep in mind that doing this will also mean that any device in your untrusted networks will be able to send mDNS requests into your trusted networks. We could stop the mDNS requests with an application layer firewall (which iptables is not), or perhaps with connection tracking, but we’ll leave that for another day. Even if untrusted devices discover addresses in LAN, the firewall is stopping the from actually communicating (at least on my setup).

Set up Avahi

Log onto your OpenWRT router and install Avahi.

opkg update
opkg install avahi-daemon

There is really only one thing that must be set in the /etc/avahi/avahi-daemon.conf config file, and that is to enable reflector (proxy) support. This goes under the [reflector] section and looks like this.

[reflector]
enable-reflector=yes

While technically not required, you can also set which interfaces to listen on. By default it will listen on all networks, which includes WAN and other VLANs, so I prefer to limit this just to the two networks I need.

On my router, my LAN is the br-lan device and my home automation network on VLAN 2 is the eth1.2 device. Your LAN is probably the same, but your other networks will most likely be different. You can find these in your router’s Luci web interface under Network -> Interfaces. The interfaces option goes under the [server] section and looks like this.

[server]
allow-interfaces=br-lan,eth1.2

Now we can start and enable dbus and the Avahi service!

/etc/init.d/dbus start
/etc/init.d/dbus enable
/etc/init.d/avahi-daemon start
/etc/init.d/avahi-daemon enable

OK that’s all we need to do for Avahi. It is now configured to listen on both LAN and HA interfaces and act as a proxy back and forth.

Firewall rules

As mentioned above, devices need to have incoming UDP port 5353 open. In order for our router to act as a proxy, we must enable this on both LAN and HA network interfaces (we’ll just configure for all interfaces). As mDNS multicasts to a specific address with source and destination ports both using 5353, we can lock this rule down a bit more.

Log onto your firewall Luci web interface and go to Network -> Firewall -> Traffic Rules tab. Under Open ports on router add a new rule for mDNS. This will be for UDP on port 5353.

Find the new rule in the list and edit it so we can customise it further. We can set the source to be any zone, source port to be 5353, where destination zone is the Device (input) and the destination address and port are 224.0.0.251 and 5353. Finally, set action should be accept. If you prefer to not allow all interfaces, then create two rules instead and restrict source zone for one to LAN and to your untrusted network for the other. Hit Save & Apply to make the rule!

We should now be able to resolve mDNS from LAN into the untrusted network.

Testing

To test it, ensure your Fedora computer is configured for mDNS and can resolve yourself. Now, try and ping a device in your untrusted network. For me, this will be study.local which is one of my home automation devices in my study (funnily enough).

ping study.local

When my computer in LAN tries to discover the device running in the study, the communication flow looks like this.

  • My computer (192.168.0.125) on LAN tries to ping study.local but needs to resolve it.
  • My computer sends out the mDNS UDP multicast to 224.0.0.251:5353 on the LAN, requesting address of study.local.
  • My router (192.168.0.1) picks up the request on LAN and sends same multicast request out on HA network (10.0.0.1).
  • The study device on HA network picks up the request and multicasts the reply of 10.0.0.202 back to 224.0.0.251:5353 on the HA network.
  • My router picks up the reply on HA network and re-casts it on LAN.
  • My computer picks up the reply on LAN and thus learns the address of the study device on HA network.
  • My computer successfully pings study.local at 10.0.0.202 from LAN by routing through my router to HA network.

This is what a packet capture looks like.

16:38:12.489582 IP 192.168.0.125.5353 > 224.0.0.251.5353: 0 A (QM)? study.local. (35)
16:38:12.489820 IP 10.0.0.1.5353 > 224.0.0.251.5353: 0 A (QM)? study.local. (35)
16:38:12.696894 IP 10.0.0.202.5353 > 224.0.0.251.5353: 0*- [0q] 1/0/0 (Cache flush) A 10.0.0.202 (45)
16:38:12.697037 IP 192.168.0.1.5353 > 224.0.0.251.5353: 0*- [0q] 1/0/0 (Cache flush) A 10.0.0.202 (45)

And that’s it! Now we can use mDNS to resolve devices in an untrusted network from a trusted network with zeroconf.

29 thoughts on “Resolving mDNS across VLANs with Avahi on OpenWRT

  1. tx for the guide
    I had to start /etc/init.d/dbus start first

    syslog said
    daemon.warn avahi-daemon[14927]: WARNING: Failed to contact D-Bus daemon.

  2. OK, I have the avahi-daemon at least working to resolve LEDE.local. I changed the parameter:

    publish-workstation=yes

  3. Nice thanks. I believe this is disabled by default as it is can leak information cross your network, but if it’s a trusted network then should be OK.

  4. The guide’s not working for me, followed everything but my devices on my lan network still can’t ping to the devices on teh IOT network. And when I reload the firewall, it just says this

    * Rule ‘Allow mDNS’
    ! Skipping due to different family of ip address
    I enabled reflector in avahi and I also added allowed interfaces (br-lan and br-iot), but it’s not working still

    Config for Avahi firewall rules

    config rule
    option src_port ‘5353’
    option src ‘lan’
    option name ‘Allow mDNS for LAN’
    option target ‘ACCEPT’
    list dest_ip ‘224.0.0.251’
    option dest_port ‘5353’
    list proto ‘udp’
    option dest ‘iot’

    config rule
    option src_port ‘5353’
    option src ‘iot’
    option name ‘Allow mDNS for IOT’
    option target ‘ACCEPT’
    list dest_ip ‘224.0.0.251’
    option dest_port ‘5353’
    list proto ‘udp’
    option dest ‘lan’

    config rule
    option src_port ‘5353’
    option src ‘*’
    option name ‘Allow mDNS’
    option target ‘ACCEPT’
    option dest_port ‘5353’
    list proto ‘udp’
    list dest_ip ‘224.0.0.251’
    option enabled ‘0’

  5. FYI in OpenWRT 18.06 and later (if not before), there is no package avahi-daemon but instead avahi-dbus-daemon and avahi-nodbus-daemon.

  6. Thanks Hamish, I just checked on 19.07.7 and it does seem to have avahi-daemon which I think is an alias for avahi-dbus-daemon… Here’s what an install looks like.

    root@router:~# opkg install avahi-daemon
    Installing avahi-dbus-daemon (0.8-1) to root...
    Downloading http://downloads.openwrt.org/releases/19.07.7/packages/arm_cortex-a9/packages/avahi-dbus-daemon_0.8-1_arm_cortex-a9.ipk
    Installing libexpat (2.2.9-1) to root...
    Downloading http://downloads.openwrt.org/releases/19.07.7/packages/arm_cortex-a9/packages/libexpat_2.2.9-1_arm_cortex-a9.ipk
    Installing libdbus (1.12.12-1) to root...
    Downloading http://downloads.openwrt.org/releases/19.07.7/packages/arm_cortex-a9/packages/libdbus_1.12.12-1_arm_cortex-a9.ipk
    Installing dbus (1.12.12-1) to root...
    Downloading http://downloads.openwrt.org/releases/19.07.7/packages/arm_cortex-a9/packages/dbus_1.12.12-1_arm_cortex-a9.ipk
    Installing libavahi-dbus-support (0.8-1) to root...
    Downloading http://downloads.openwrt.org/releases/19.07.7/packages/arm_cortex-a9/packages/libavahi-dbus-support_0.8-1_arm_cortex-a9.ipk
    Installing librt (1.1.24-2) to root...
    Downloading http://downloads.openwrt.org/releases/19.07.7/targets/bcm53xx/generic/packages/librt_1.1.24-2_arm_cortex-a9.ipk
    Installing libdaemon (0.14-5) to root...
    Downloading http://downloads.openwrt.org/releases/19.07.7/packages/arm_cortex-a9/packages/libdaemon_0.14-5_arm_cortex-a9.ipk
    Configuring libexpat.
    Configuring libdbus.
    Configuring dbus.
    dbus[7527]: Unknown group "netdev" in message bus configuration file
    Configuring libavahi-dbus-support.
    Configuring librt.
    Configuring libdaemon.
    Configuring avahi-dbus-daemon.

  7. I had trouble making this work with the “avhai-daemon” but it worked right way using the avahi-nodbus-daemon version.
    If the above isn’t working for anyone, try again with the nodbus version.

  8. Hey Nova, I’ve read your post in the forum. I think the confusing this is that mDNS is just for resolving hosts, that is, it’s what makes it resolve things like “mycomputer.local” but it does not allow any traffic otherwise. As mDNS will only resolve hosts on a single network, it won’t work trying to resolve hosts across two networks like you have with lan and iot. However, my blog post shows you how you can make resolution work across networks by getting the openwrt router to pass traffic back and forth between the networks. However this is only about name resolution, it does not permit any other traffic. Therefore, once resolution is working, you’d still need a firewall rule allowing ICMP traffic between the networks.

  9. Do you have a link to which post shows how to resolve name resolution across networks?

  10. Oh I see! So, I just follow this post to get name resolution working and then I create a ICMP traffic rule that allows the traffic between the networks, right?

  11. A very strange thing is happening.. I can only ping to one of my devices with mDNS, the rest however I can’t ping to them using the domain address I have saved on the router’s side (nor on the devices side) across networks. Even odder is when I connect to the network that they’re on, I can only ping to that one device but not the rest using their mDNS name

  12. Might be a long shot since this post is a bit old..
    I’m trying to set this up now. Avahi on openWRT is able to resolve the ip in the different subnet, but i’m not able to get a response.

    Any chance you can share your zone settings in the firewall tab?

  13. I only have this on the router.

    config rule
    option target 'ACCEPT'
    option proto 'udp'
    option dest_port '5353'
    option src '*'
    option src_port '5353'
    option family 'ipv4'
    list dest_ip '224.0.0.251'
    option name 'allow-mdns'

    Maybe try without `allow-interfaces` set, if you haven’t already?

    -c

  14. Thanks for a great guide Chirs!
    I’m running on 21.02 and I’ve been following your guide. Airplay is working fine but I can’t unfortunately get the chromecasting function working from my iPhone Netflix or Youtube casting to the TV. All ports on the TV are open from the iot network to the private network. Any ideas on how I can get chromecasting working?

  15. Hmmm.. it works for me with an Android phone, perhaps casting on iPhones works differently? Do you have a friend or someone who as an Android you could test with to see if it works? Perhaps airplay doesn’t use mdns at all? I think the way casting is meant to to work is the phone uses mdns to discover chromecast, talks to it and then the chromecast itself goes out directly to fetch the stream while I think airplay streams directly. So I expect it’s only the first bit that requires mdns. I guess the first thing is to make sure mdns is working. Can you ping any device on the two networks that has mdns running by name? You could do a packet capture with tcpdump or something and see what is being requested and test manually. If nothing works, double check the avahi configuration file and the firewall rules…

  16. Just stopping in to say “Thanks” for this straightforward guide. I have been searching all over to try and understand how to make my devices (smart speakers, Home Minis, Chromecast Audios) show up in Spotify when they are in a segmented interface in OpenWRT.

    Following this guide, in particular the firewall rules for port 5353 and Avahi configuration, lets this work perfectly on my network now so I can now cast Spotify across subnets! Thanks

  17. This was great info and helped me getting HomeKit compatible devices talking to my HomePod across multiple firewall’d VLANS. At first I was running in to intermittent problems though. On occasion I wasn’t able to resolve .local hostnames across the vlans. I’d restart all of my devices and it would work for a bit but then resolving would start to fail again. Then it would randomly start working again without any configuration changed. I eventually figured out I had to turn off IGMP snooping on my switch witch my access point was connected through. My basic network structure is : —- —- — . Figure this might help someone having the same problem as me.

  18. Chris, I’ve read your article multiple times and on multiple occasions. Around 2 years ago, I was able to get Chromecast/Google Nest Mini Speakers working across different vlans and subnets, and I am quite confident this article helped achieve that. Unfortunately, my recent attempts have all resulted in failure. I’ve set up avahi and can confirm that Bounjour/zeroconf works, and I can list all the devices from vlan1 (iot) In vlan2 (lan). The firewall also allows all types of traffic iot=>lan and lan=>iot (to simplify). I can’t get chromecast/spotify casting/nest mini casting to work, though. Did you by any chance get it working with avahi?

  19. Hey Luis, yes I believe so. Did you also open up the traffic to allow into the router (Device input)? I think that was also needed…

  20. Yap Chris, everything open. Input, output, forward. I just made it that way to make sure it wasn’t a firewall issue at all. Did you have to use any kind of multicast routing as well? I’ve been looking into smcroute but no luck either.

  21. Worked perfectly on newest OpenWrt 22.03.2 (asus RT-AX53U).
    The only extra step I needed was to reboot whole router (apparently restarting services wasn’t enough).

  22. Thank you! This worked for me. Adding context for future visitors:

    Firmware: OpenWrt 23.05.02
    Platform: ipq806x (Netgear R7800)
    Package installed: avahi-dbus-daemon

    PS: Don’t forget to restart the router after starting avahi daemon

  23. I’ve tried your tutorial but it doesn’t work.
    I am in another case. I have an openwrt router for the trust network, connected to my Internet router and another openwrt router for the iot network.
    I did the first part on the trust router without the open port on router.
    I’m wondering how to configure my iot router to accept mdns traffic.
    The networks are 192.168.30.0 (trusted)->192.168.1.0-> 192.168.35.0 (iot)

  24. This works great! 🙂 Thank you!

    This did not work:
    /etc/init.d/dbus start
    /etc/init.d/dbus enable

Leave a Reply

Your email address will not be published. Required fields are marked *