Multicast routing debugging
Setting up multicast reception on Fedora 34 requires using
firewall-cmd
to allow multicast traffic through firewalld via rich
rules. The following is a description of a debugging session (or two)
that helped identify the problem.
The basic problem was that multicast traffic was not received by a userland program on Fedora 34 x86_64 machine. All of the tests below use the excellent mcjoin multicast test program.
1. Checking iptables
My first hypothesis was that multicast was blocked by the standard
firewall. As I was familiar only with ipchains
and iptables
, I
verified first that ipchains
was not in use. From the iptables
man page:
This iptables is very similar to ipchains by Rusty Russell. The main difference is that the chains INPUT and OUTPUT are only traversed for packets coming into the local host and originating from the local host respectively. Hence every packet only passes through one of the three chains (except loopback traffic, which involves both INPUT and OUTPUT chains); previously a forwarded packet would pass through all three.
Trusting the quote above, the simplest way to test whether the INPUT and OUTPUT chains inhibit multicast reception is to use loopback. Done as follows:
sudo ifconfig lo multicast # loopback normally does not allow multicast sudo route add -host 224.12.12.12 dev lo # cargo-cult, but let's leave it here for now
With the setup above, the route table and the lo
interface are
ready for multicast testing:
[ravi@rcvr ~]$ ifconfig lo
lo: flags=4169<UP,LOOPBACK,RUNNING,MULTICAST> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 28 bytes 2986 (2.9 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 28 bytes 2986 (2.9 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[ravi@rcvr ~]$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 10.100.66.1 0.0.0.0 UG 100 0 0 enp4s0
10.100.66.0 0.0.0.0 255.255.255.0 U 100 0 0 enp4s0
192.168.211.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0
224.12.12.12 0.0.0.0 255.255.255.255 UH 0 0 0 lo
Multicast published to 224.12.12.12 is received appropriately by
the receiver bound to lo
:
mcjoin -s 224.12.12.12 -p 1234 -i lo # sender mcjoin -j 224.12.12.12 -i lo # receiver
The next step is to remove the multicast address from the route table:
sudo route del -host 224.12.12.12 dev lo
which results in
[ravi@rcvr ~]$ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default 10.100.66.1 0.0.0.0 UG 100 0 0 enp4s0 10.100.66.0 0.0.0.0 255.255.255.0 U 100 0 0 enp4s0 192.168.211.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0
Multicast transmission and reception work as expected again.
My conclusion is that iptables
does not block multicast. In case
I am mistaken, here are the iptables
rules:
ravi@rcvr ~]$ for table in filter nat; do echo "------ $table ------"; sudo iptables -t $table -L -n; done ------ filter ------ Chain INPUT (policy ACCEPT) target prot opt source destination LIBVIRT_INP all -- 0.0.0.0/0 0.0.0.0/0 Chain FORWARD (policy ACCEPT) target prot opt source destination LIBVIRT_FWX all -- 0.0.0.0/0 0.0.0.0/0 LIBVIRT_FWI all -- 0.0.0.0/0 0.0.0.0/0 LIBVIRT_FWO all -- 0.0.0.0/0 0.0.0.0/0 Chain OUTPUT (policy ACCEPT) target prot opt source destination LIBVIRT_OUT all -- 0.0.0.0/0 0.0.0.0/0 Chain LIBVIRT_FWI (1 references) target prot opt source destination ACCEPT all -- 0.0.0.0/0 192.168.211.0/24 ctstate RELATED,ESTABLISHED REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable Chain LIBVIRT_FWO (1 references) target prot opt source destination ACCEPT all -- 192.168.211.0/24 0.0.0.0/0 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable Chain LIBVIRT_FWX (1 references) target prot opt source destination ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 Chain LIBVIRT_INP (1 references) target prot opt source destination ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:53 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:53 ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:67 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:67 Chain LIBVIRT_OUT (1 references) target prot opt source destination ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:53 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:53 ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:68 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:68 ------ nat ------ Chain PREROUTING (policy ACCEPT) target prot opt source destination Chain INPUT (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination LIBVIRT_PRT all -- 0.0.0.0/0 0.0.0.0/0 Chain LIBVIRT_PRT (1 references) target prot opt source destination RETURN all -- 192.168.211.0/24 224.0.0.0/24 RETURN all -- 192.168.211.0/24 255.255.255.255 MASQUERADE tcp -- 192.168.211.0/24 !192.168.211.0/24 masq ports: 1024-65535 MASQUERADE udp -- 192.168.211.0/24 !192.168.211.0/24 masq ports: 1024-65535 MASQUERADE all -- 192.168.211.0/24 !192.168.211.0/24
All of the libvirt
and 192.168.211.0
stuff above is due to the
presence of a virtual machine used for testing. In the end, those
pieces turned out to be irrelevant, but I left them in to show
acutal debugging values.
2. Sending multicast from a separate host
Sender: sndr | Receiver: rcvr | |
---|---|---|
IP address | 10.100.66.68 | 10.100.66.67 |
Interface | enp1s0 | enp4s0 |
Kernel version | 5.14.16-201.fc34.x86_64 | 5.15.11-100.fc34.x86_64 |
- Both hosts can ping each other.
- Traffic between them does not have any VLAN tagging, since they are on the same VLAN.
- Each of them has a wireless interface without an assigned IP address or an associated access point.
- Sender sends multicast on 224.17.17.17 port 1234.
Focusing on the receiver:
[ravi@rcvr ~]$ sudo sysctl -ar '\.rp_filter'
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.enp4s0.rp_filter = 2
net.ipv4.conf.lo.rp_filter = 2
net.ipv4.conf.virbr0.rp_filter = 2
net.ipv4.conf.wlp3s0.rp_filter = 2
Packets are sent from the transmitter at 1pps:
./mcjoin -f 1000 -s -t 4 224.17.17.17
The receiver does not see the data in userland both with and without the useless multicast route:
sudo route add -host 224.17.17.17 dev enp4s0 # for testing sudo route del -host 224.17.17.17 dev enp4s0 # remove testing
Packets are visible on the interface, regardless of whether there has been a multicast join, since they are on the same VLAN and the switch floods those packets:
[ravi@rcvr ~]$ sudo tcpdump -i enp4s0 -nn -e -vv -c 5 multicast and not broadcast dropped privs to tcpdump tcpdump: listening on enp4s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 09:24:36.838722 48:9a:e3:30:5c:74 > 01:00:5e:11:11:11, ethertype IPv4 (0x0800), length 142: (tos 0x0, ttl 4, id 25384, offset 0, flags [DF], proto UDP (17), length 128) 10.100.66.68.36682 > 224.17.17.17.1234: [udp sum ok] UDP, length 100 09:24:37.839498 48:9a:e3:30:5c:74 > 01:00:5e:11:11:11, ethertype IPv4 (0x0800), length 142: (tos 0x0, ttl 4, id 25701, offset 0, flags [DF], proto UDP (17), length 128) 10.100.66.68.36682 > 224.17.17.17.1234: [udp sum ok] UDP, length 100 09:24:38.840155 48:9a:e3:30:5c:74 > 01:00:5e:11:11:11, ethertype IPv4 (0x0800), length 142: (tos 0x0, ttl 4, id 25798, offset 0, flags [DF], proto UDP (17), length 128) 10.100.66.68.36682 > 224.17.17.17.1234: [udp sum ok] UDP, length 100 09:24:39.840319 48:9a:e3:30:5c:74 > 01:00:5e:11:11:11, ethertype IPv4 (0x0800), length 142: (tos 0x0, ttl 4, id 26349, offset 0, flags [DF], proto UDP (17), length 128) 10.100.66.68.36682 > 224.17.17.17.1234: [udp sum ok] UDP, length 100 09:24:40.840963 48:9a:e3:30:5c:74 > 01:00:5e:11:11:11, ethertype IPv4 (0x0800), length 142: (tos 0x0, ttl 4, id 27016, offset 0, flags [DF], proto UDP (17), length 128) 10.100.66.68.36682 > 224.17.17.17.1234: [udp sum ok] UDP, length 100 5 packets captured 5 packets received by filter 0 packets dropped by kernel
Note that the TTL is 4 at the receiver, which should be more than sufficient.
When the receiver is running, packets do appear:
[ravi@rcvr ~]$ netstat -gn -4 Active Internet connections (w/o servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 10.100.66.67:42580 10.100.66.68:22 ESTABLISHED [... other irrelevant connections redacted ...] udp 0 0 10.100.66.67:68 10.100.66.1:67 ESTABLISHED IPv6/IPv4 Group Memberships Interface RefCnt Group --------------- ------ --------------------- lo 1 224.0.0.251 lo 1 224.0.0.1 enp4s0 1 224.17.17.17 enp4s0 1 224.0.0.251 enp4s0 1 224.0.0.252 enp4s0 1 224.0.0.1 virbr0 1 224.0.0.251 virbr0 1 224.0.0.1 virbr0 1 224.0.0.106 [ravi@rcvr ~]$ netstat -sgu; sleep 10; netstat -sgu IcmpMsg: InType3: 22 OutType3: 22 Udp: 2552 packets received 0 packets to unknown port received 0 packet receive errors 2203 packets sent 0 receive buffer errors 0 send buffer errors UdpLite: IpExt: InMcastPkts: 754 OutMcastPkts: 1105 InBcastPkts: 2 OutBcastPkts: 4 InOctets: 3587966 OutOctets: 757004 InMcastOctets: 83946 OutMcastOctets: 127091 InBcastOctets: 4202 OutBcastOctets: 4242 InNoECTPkts: 5968 MPTcpExt: IcmpMsg: InType3: 22 OutType3: 22 Udp: 2552 packets received 0 packets to unknown port received 0 packet receive errors 2203 packets sent 0 receive buffer errors 0 send buffer errors UdpLite: IpExt: InMcastPkts: 764 OutMcastPkts: 1105 InBcastPkts: 2 OutBcastPkts: 4 InOctets: 3594625 OutOctets: 757719 InMcastOctets: 85226 OutMcastOctets: 127091 InBcastOctets: 4202 OutBcastOctets: 4242 InNoECTPkts: 5990 MPTcpExt: [ravi@rcvr ~]$ ip maddr 1: lo inet 224.0.0.251 inet 224.0.0.1 inet6 ff02::fb inet6 ff02::1 inet6 ff01::1 2: enp4s0 link 01:00:5e:00:00:01 link 33:33:00:00:00:01 link 33:33:00:00:00:fb link 33:33:ff:13:a0:aa link 33:33:00:01:00:03 link 01:00:5e:00:00:fc link 01:00:5e:00:00:fb link 01:00:5e:11:11:11 inet 224.17.17.17 inet 224.0.0.251 inet 224.0.0.252 inet 224.0.0.1 inet6 ff02::1:3 inet6 ff02::1:ff13:a0aa inet6 ff02::fb inet6 ff02::1 inet6 ff01::1 3: wlp3s0 inet6 ff02::1 inet6 ff01::1 4: virbr0 link 01:00:5e:00:00:6a link 33:33:00:00:00:6a link 01:00:5e:00:00:01 link 01:00:5e:00:00:fb inet 224.0.0.251 inet 224.0.0.1 inet 224.0.0.106 inet6 ff02::6a inet6 ff02::1 inet6 ff01::1
Since packets are sent at the rate of 1 packet per second from the
sender, the number of packets matches expectations in netstat
-sgu
.
Repeating the same experiment above with the following changes yields the same results.
[ravi@rcvr ~]$ sudo sysctl -w net.ipv4.conf.enp4s0.rp_filter=0
net.ipv4.conf.enp4s0.rp_filter = 0
[ravi@rcvr ~]$ sudo sysctl -w net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.default.rp_filter = 0
[ravi@rcvr ~]$ sudo sysctl -w net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.all.rp_filter = 0
[ravi@rcvr ~]$ sudo sysctl -ar '\.rp_filter'
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.enp4s0.rp_filter = 0
net.ipv4.conf.lo.rp_filter = 2
net.ipv4.conf.virbr0.rp_filter = 2
net.ipv4.conf.wlp3s0.rp_filter = 2
3. Solution
At this point, I started looking through any documentation for firewalls, as there was nothing else I could see. The man page for firewalld hinted vaguely at a debugging facility:
–set-log-denied=value
Add logging rules right before reject and drop rules in the INPUT, FORWARD and OUTPUT chains for the default rules and also final reject and drop rules in zones for the configured link-layer packet type. The possible values are: all, unicast, broadcast, multicast and off. The default setting is off, which disables the logging.
Useful debugging information came from
sudo firewall-cmd --set-log-denied=multicast
whose output could be observed via journalctl
. We still need to
figure out the exact set of multicast destinations to allow.
The problem is in netfilter; even though iptables
did not return
anything useful, firewalld
(using nft
) was still setting up
rejections. All multicast except mDNS
was blocked. The solution
was to allow multicast via firewalld
:
sudo firewall-cmd --add-rich-rule='rule family=ipv4 destination address="224.0.0.0/4" accept'
sudo firewall-cmd --add-protocol=igmp
sudo firewall-cmd --reload
It's annoying that there was no obvious way to query nftables
rules directly without dropping into the interactive nft
shell,
but that's probably only a lack of knowledge on my part. As I was
testing this during some VyOS SSDP/DLNA debugging, isolating this
as a problem with the local host also took additional time.