Filter mDNS replies (deep packet inspection)

Hello, I recently bought a WRT3200ACM to replace my spotty AC1900 and, of course, the first thing I did was install OpenWRT on it! I've got it set up nicely, but I've run into a snag.

Firstly, let me explain my setup a little bit:
I have four wireless networks:

  • Regular 2.4G
  • Guest 2.4G
  • Regular 5G
  • Guest 5G

I've split these network (as well a two of each of the ethernet ports) into separate VLANs as I have now hooked up all my IoT devices to the guest network. I'm allowing specific one-to-one connections across the barrier such as SSH to my Raspberry Pi from my laptiop, etc.

One thing I've grown accustomed to since all of my workhorse machines run Linux is Avahi/Bonjour/mDNS. Honestly, I just love the convenience of 'ssh raspberrypi.local' rather than using the static IP (I know, it's silly). However, other devices on my guest VLAN, such as my Google Home Mini also use mDNS in some form or fashion. I've managed to get mDNS working between VLANS using the reflector mode of avahi-daemon, however, I would like to filter mDNS responses from my LAN zone to my guest zone. If I can accomplish this, then I can still utilize the mDNS functionality of the devices on the guest network without exposing any information about computers on my LAN zone to the guest zone.

Reflecting only on my br-guest interface allows my LAN computers to see the mDNS-enabled computers on the guest network, however something like 'ssh raspberrypi.local' does not allow a connection to the advertised device due to the fact that the mDNS request is not reflected from br-lan to br-guest.

At this point I think I need some kind of deep packet inspection so that I can filter mDNS replys only from the LAN zone to the guest zone, while allowing mDNS requests to pass through unhindered.

I did a little reading here on the forums and I ran across nDPI, however, it appears this functionality has been removed from iptables (or, I can't find an easy way to enable it anyway) with the only nDPI package I've been able to locate being libndpi.

Is there any way to accomplish this easily? That being said, I don't mind taking a hard way as I see everything as a learning experience!

mDNS is typically an on-link kind of thing. It can be hacked to sort-of work across networks, with avahi typically being the inter-network relay mechanism. To do the filtering you want, yes, you'll either need iptables or nftables to do protocol-level filtering, or something that has knowledge of the packet format to do the filtering within avahi or around it.

A quick look with wireshark at a mDNS packet suggests that there is a field in the header indicating if a request or response and what type. If this is at a fixed offset, there might be a way that iptables can "key' off those bytes or bits.

_Edit: For one set of ideas on how to examine specific bytes within a packet "natively" with iptables, see http://www.stearns.org/doc/iptables-u32.current.html_

As do I, especially with IPv6. I run a local instance of unbound for several local-only TLDs (though I explicitly don't use "local" as one of them to avoid confusion with mDNS).

I found a way to build the nDPI iptables module, but I wasn't able to simply build and opkg install it, since, for some reason kernal version didn't match up... Even though I used the 18.06 branch and not daily. So, I built the whole image and.... Bricked my router. Luckily I have the tools to diagnose/reflash it, so should have that done by the end of the day fingers crossed.

Alright, so got it "unbricked." Once I hooked up the serial cable I got a Linux console, so it was up and running -- just no network connectivity what-so-ever. I reverted it back to the version from the Router's page and I'm back up and running. Now to determine what caused the problem to begin with. You'd think that using the config file given on the router's page would have resulted in a fully functional like-distributed build but apparently that's not the case. The nDPI functionality was supposed to be added later as packages, but never got that far.

Anyway, I'll report back if I get nDPI working and if so, how I got it all working as well as posting my rules for future reference of anybody who might stumble across this.

OK, well I finally got it working after building about 10 times and learning a little more about how the build system works. Here is how I got it working:

wget https://downloads.openwrt.org/releases/18.06.1/targets/mvebu/cortexa9/config.seed -O .config #router-dependent
./scripts/packages update -a
./scripts/packages install -a
make defconfig
make #sanity build
git clone https://github.com/hisham2630/openwrt-ndpi-netfilter packages/openwrt-ndpi-netfilter
./scripts/packages update -a
./scripts/packages install -a
make menuconfig #Enable Network->Firewall->iptables-mod-ndpi, Network->Firewall->Connlabel support, and Kernel modules->Netfilter extensions->kmod-ipt-ndpi
make

However, this doesn't solve the problem either. Apparently, all that nDPI does is flag the traffic as a specific protocol. While I was hoping to get filtering sort of like Wireshark where I can use protocol.field it only identifies the protocol without doing further filtering. As such, it can only filter or allow all traffic for mDNS (port 5353, ip adddress 224.0.0.251 or ff02::fb) but not filter it further into request/response-type packets. Maybe I can hack something in, but I must say I am pretty disappointed after all that work.

In doing a little more reasearch, I ran across the u32 netfilter extension. This extension allows you to match bytes in a packet up to 4 bytes at a time. I thought this would be the solution to my problem, but it appears (at least on my network after a Wireshark analysis) that mDNS doesn't really follow a clean-cut query/response pattern that typical DNS does.

Instead, you have devices that will actually provide unsolicited responses within queries. Something like "Hey, where are the web servers? Oh, by the way I have a webserver, an ftp server, a Samba network, etc." This means, that simply filtering mDNS responses with u32 packet matching simply doesn't cut it. I did filter packets that contain responses (whether solicited or not) using the u32 filter extension, however, for some reason, that caused all of the mDNS functionality to quit working.

I'm still doing a little research here, but it's beginning to look more and more like for my particular issue I will have to write a program that will interact with a netfilter queue, removing specific parts of the mDNS packet altogether before allowing them to continue through.

Aha! I've figured it out. I had a closer look at the OpenWRT firewall chains and I found that I can actually use u32 to prevent these mDNS messages propagating to the guest network. Here is a snippet from my /etc/firewall.user:

iptables -A output_guest_rule -p udp ! -f --dport 5353 -d 224.0.0.251 -m u32 --u32 "0>>22&0x3C@8>>15&0x01=1" -j DROP #Drop mDNS reponse packets going to guest
iptables -A output_guest_rule -p udp ! -f --dport 5353 -d 224.0.0.251 -m u32 --u32 "0>>22&0x3C@12&0xFFFF=0x01:0xFFFF" -j DROP #Drop mDNS packets with answer RRs going to guest
iptables -A output_guest_rule -p udp ! -f --dport 5353 -d 224.0.0.251 -m u32 --u32 "0>>22&0x3C@14&0xFFFF=0x01:0xFFFF" -j DROP #Drop mDNS packets with authoritative RRs going to guest

ip6tables -A output_guest_rule -p udp --dport 5353 -d ff02::fb -m u32 --u32 "48>>15&0x01=1" -j DROP #Drop mDNS responses going to guest
ip6tables -A output_guest_rule -p udp --dport 5353 -d ff02::fb -m u32 --u32 "52&0xFFFF=0x01:0xFFFF" -j DROP #Drop mDNS packets with answer RRs going to guest
ip6tables -A output_guest_rule -p udp --dport 5353 -d ff02::fb -m u32 --u32 "54&0xFFFF=0x01:0xFFFF" -j DROP #Drop mDNS packets with authoritative RRs going to guest

With the firewall configured in this way, I achieve all of my goals:

  • I can see all of my mDNS-enabled devices on the guest network
  • mDNS-enabled devices on my guest network cannot see devices on my lan network

This is with avahi-daemon set in reflector mode.

4 Likes

you dont need mdns to have local nameresolution.
just sayin