Updating iptables to allow Chromecast discovery and casting

I occassionally find that I want to cast a browser tab from my laptop - usually to put a chart onto a display so that I can periodically glance at it.

However, my laptop runs a fairly strict set of firewall rules - after all, it's a portable device and, on occasion, will connect to networks that aren't mine.

Strict firewall rules can prevent Chromecast discovery from working, mean that no devices will appear available to be casted to.

This post details adding rules to iptables to allow casting.


Why Discovery Gets Broken

Discovery of chromecast devices is, effectively, a two stage operation:

  • Send a discovery packet asking screen devices to contact your device
  • Receive packets from any responding chromecasts (or other compatible devices)

The discovery packet uses Simple Service Discovery Protocol sent to a broadcast address, with a packet sent out of each active network interface.

Compatible devices respond by sending a UDP payload like the following back:

HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
DATE: Wed, 07 Feb 2024 13:32:56 GMT
EXT:
LOCATION: http://192.168.3.18:8008/ssdp/device-desc.xml
OPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01
01-NLS: 1471f99c-1dd2-11b2-9e7a-87edc9383c72
SERVER: Linux/3.8.13+, UPnP/1.0, Portable SDK for UPnP devices/1.6.18
X-User-Agent: redsonic
ST: urn:dial-multiscreen-org:service:dial:1
USN: uuid:797c4dd2-97e9-1340-25b7-359aad405ce4::urn:dial-multiscreen-org:service:dial:1
BOOTID.UPNP.ORG: 5
CONFIGID.UPNP.ORG: 13

In wireshark, it looks a bit like this (there are 2 interfaces on my laptop and we can see that 3 chromecasts respond):

Screenshot of wireshark having captured the discovery phase

With strict ingress rules, the problem is that the packets coming from the Chromecast(s) appear to be entirely unsolicited - they don't originate from an IP or port that the kernel has seen us send packets to, so won't benefit from a RELATED,ESTABLISHED rule (if we even have one).

With strict egress rules, of course, that discovery packet might also never even make it out onto the network.


Adding Rules

The bad news is that, to allow casting, we need to allow these unsolicited packets. The worse news is that the potential range of ports is pretty massive (32768 - 61000).

In my case, though, it's possible to restrict that to specific IPs: I've allocated a set of contiguous static IPs to the Chromecasts and so can limit the rule to a range of IPs by using the iprange module:

iptables \
-I INPUT \
-p udp \
-m iprange \
--src-range 192.168.3.40-192.168.3.45 \
-m multiport --sports 32768:61000 \
-m multiport --dports 32768:61000 \
-m comment \
--comment "Allow Chromecast UDP in" \
-j ACCEPT

With restrictive outbound rules, we also need to add some output rules.

The first corresponds to the input rule, allowing mDNS and the like:

iptables \
-A OUTPUT \
-p udp \
-m iprange \
--dest-range 192.168.3.40-192.168.3.45 \
-m multiport --sports 32768:61000 \
-m multiport --dports 32768:61000 \
-m comment \
--comment "Allow Chromecast UDP data out" \
-j ACCEPT

The second allows the laptop to connect to a chromecast in order to cast

iptables \
-A OUTPUT \
-p tcp \
-m iprange \
--dest-range 192.168.3.40-192.168.3.45 \
-m multiport --dports 8008:8009 \
-m comment --comment "Allow Chromecast TCP out" \
-j ACCEPT

And, of course, we also need to allow the initial SSDP packet to be sent

iptables \
-A OUTPUT \
-d 239.255.255.250/32 \
-p udp \
--dport 1900 \
-m comment --comment "Allow SSDP" \
-j ACCEPT

Devices should now show up in the "cast" menu and, once you choose to cast to a device you should see a TCP connection get established to it

Screenshot of wireshark showing the packet flow when casting to a device. It's TCP to port 8009