First thing you need to know is how UPnP IGD protocol is working. You choose random local UDP port and from it you send discovery request to well-known multicast address 239.255.255.250 and UDP port 1900. UPnP IGD server (running on your router) listen for those multicast queries and send you back unicast UDP reply from randomly chosen port to your ip address and port from which discovery request was sent. But such reply is not paired by conntrack iptables module to your sent request so, received reply is dropped by iptables. This is why enabling all UDP ports or disabling firewall helped. In that UDP reply is location of your UPnP IGD server and client then establish classic TCP connection with UPnP IGD server. So the only problem is how to write a rule for receiving UDP reply to that multicast discovery request.
Via ipset it is possible. I described it in answer at https://serverfault.com/a/911286:
$ 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
In IPv6 are UPnP packets sent to multicast address ff02::c or ff05::c. So rules would look like:
$ ipset create upnp6 hash:ip,port timeout 3 family inet6
$ ip6tables -A OUTPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist
$ ip6tables -A OUTPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist
$ ip6tables -A INPUT -p udp -m set --match-set upnp6 dst,dst -j ACCEPT
Some UPnP servers (but not all) periodically (e.g. every 30s) announce theirself via multicast UDP packet to well-known address/port. If you have such server and also client which is listening for these multicast packets, then iptables rule very is simple:
$ iptables -A INPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j ACCEPT
And equivalent for IPv6:
$ ip6tables -A INPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j ACCEPT
$ ip6tables -A INPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j ACCEPT
In your question you described something similar to above iptables rule, but you have did one big mistake: You specified source port, instead of destination: --sport 1900
. UPnP UDP packets are sent from random source ports to fixed destination port 1900.
You also described that upnp-inspector can detect your UPnP IGD router after you added port 1900 to trusted (probably both source and destination), but it was slower as disabling firewall. This perfectly matches above description of periodic announcement as upnp-inspector was waiting when your router send next announce packet.
firewalld
for the NAT-PMP connection. You only have to open the Transmission port infirewalld
, as normal (port 51413, by default) – sourcejedi Sep 24 '19 at 10:09