Stateful matching of multicast responses in iptables

Solution 1:

That's the problem with multicast: netfilter can never be sure whether it's related or not.

The only way you can allow UPnP SSDP will therefore be:

-A INPUT -p udp --sport 1900 -j ACCEPT

In addition to the existing ESTABLISHED,RELATED rule.

Solution 2:

With recent Linux kernels (>= 2.6.39) you can use kernel's ipset to workaround limitation of connection tracking. You do not need to write any userspace or kernel helper. For UPnP SSDP it can be written as:

$ ipset create upnp hash:ip,port timeout 3
$ iptables -A OUTPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j SET --add-set upnp src,src --exist
$ iptables -A INPUT -p udp -m set --match-set upnp dst,dst -j ACCEPT

First command creates a new ipset called upnp which stores tuple (ip address, ip protocol, ip port) and every inserted record expires in 3 seconds.

Second command matches outgoing UPnP SSDP packet (destination is multicast address 239.255.255.250 on udp port 1900) and stores source ip address and source udp port of packet into ipset upnp. First keyword src means source ip address and second keyword src means source port as ipset of type hash:ip,port always needs such pair. Keyword --exists means that for existing record is timer reseted. This stored record is automatically removed in 3 seconds.

Third command matches incoming udp packet and if its destination address and destination port matches some record in ipset upnp then this packet is accepted. Syntax dst,dst means destination ip address and destination port.

UPnP clients normally sends udp packet to 239.255.255.250:1090 and wait just 2 seconds for response. So autoexpiration in 3 seconds in ipset is enough.

I have not found on internet any working firewall/iptables configuration for UPnP clients which is not too relax (e.g. accept all incoming UDP packet) or without some userspace tracking or need to patch kernel. Therefore I hope this example helps you.