How block multicast to specific bridge port

What's the best way to configure an nftables-based OpenWrt Ethernet bridge such that it forwards multicast traffic between most physical ports, but blocks multicast traffic going to one, designated port?
Note that broadcast traffic should be bridges across all ports.

Maybe

echo 1 > /sys/devices/virtual/net/br-lan/brif/lan1/broadcast_flood
echo 0 > /sys/devices/virtual/net/br-lan/brif/lan1/multicast_flood

Just a guess, haven't tried it & not sure what you are trying to do/accomplish.

We have a network segment that goes to an underwater vehicle.
That segment has limited bandwidth.
Unfortunately, it's on the same subnet as the higher bandwidth segments.
We want to block all multicast traffic from the low-bandwidth network segment attached to a specific physical port on the openwrt router.

I don't think the *_flood kernel parameters will do want we seek here.

Ah, makes sense, thank you.

Hmm, IP multicast has corresponding MAC multicast addresses to make sure that the traffic does not get filtered away on the ethernet port.

How about just modifying the firewall rules, adding a MAC filter for multicast MAC addresses as they arrive ingress on the port from the underwater vehicle, to drop those?

Love that you specified "underwater vehicle" and not just "vehicle" :slight_smile: I'm imagining things now

That makes sense, but...
I cannot modify the configuration of the underwater vehicle.
Has to be done "topside"

Let me help your imagination:

Hmm or use meta pkttype in nftables?

Keyword Description Data Type
pkttype packet type (unicast, broadcast, multicast, other) pkt_type

From here

Yep that's what I meant - when the frames arrive topside, they will have a multicast MAC address as the destination-MAC.

Match on that - and drop the frames in the firewall.

The goal is to drop multicast coming from underwater right?

LOL, nice, thanks :smiley:

Sorry. I should have made that more clear.
The goal is to drop multicast traffic that's "topside" from entering the network segment that goes to the vehicle, underwater.

nftables matching by pkttype sounds promising. I'd love to see an example...

Not that promising, just tested it and couldn't get it to work haha.

Let me try the MAC address thing and see if that works, one moment.

EDIT: nft syntax is annoying and hard

Ingress:

# nft 'add table netdev bob'
# nft 'add chain netdev bob lan1_filter { type filter hook ingress device lan1 priority 0; policy accept; }'
# nft 'add rule netdev bob lan1_filter meta pkttype multicast counter drop'
# nft list ruleset
table netdev bob {
        chain lan1_filter {
                type filter hook ingress device "lan1" priority filter; policy accept;
                meta pkttype multicast counter packets 0 bytes 0 drop
        }
}

meta pkttype seems to be fine here.


Egress:

nft 'add table bridge carla'
nft 'add chain bridge carla mac_filter { type filter hook postrouting priority 0; policy accept; }'
nft 'add rule bridge carla mac_filter ether daddr == 11:22:33:44:55:66 counter drop'
nft list ruleset
table bridge carla {
        chain mac_filter {
                type filter hook postrouting priority 0; policy accept;
                ether daddr 11:22:33:44:55:66 counter packets 0 bytes 0 drop
        }
}

Here I couldn't get it to eat meta pkttype but it will accept a destination-mac just fine.

Will need a bunch more MACs than just one, though...

This should work in kernel 5.16 according to docs, but my box is still on 5.15 so cannot test:

# nft 'add table netdev sinclair'
# nft 'add chain netdev sinclair lan1_egress_filter { type filter hook egress device lan1 priority 0; policy accept; }'
# nft 'add rule netdev sinclair lan1_egress_filter meta pkttype multicast counter drop'

What kernel version is the topside running?

How about this:

tc qdisc add dev eth0 root handle 1: pfifo
tc filter add dev eth0 protocol ip parent 1: prio 1 u32 \
    match ip protocol 17 0xff \
    match ip dst 224.0.0.0/20 \
    action drop

Nice :+1: completely forgot about tc !

So, does the tc qdisc/tc filter trick require kernel >5.15?
Is this an alternative to the nft bridge table rules you suggested previously?

Just trying to wrap my head around all this.
What does tc stand for in the above?

no, tc been here since forever

Yep i think its a better choice (but does only block IP multicast so if you have an appletalk daemon multicasting it will not block that ho-hum)

traffic classification maybe ? cant remember

that's fine. We have not got any appletalk on our network.

tc stands for 'traffic control'
qdisc = queuing discipline

Thanks. I'll fiddle with the in the coming couple wks and let you know how it works.

by the way -- did you actually test this somehow?

me or @ppmm ?

I did test- just SSH'd to the nearest access point and added the rules

  • with "accept" instead of "drop" to not disturb traffic
  • with "counter" to see if traffic hit the rule

I think nftables filtering would work just fine to filter multicast at the MAC level - you can just filter away anything that is not destined for <rover-unicast-mac> or ff:ff:ff:ff:ff:ff

But it seems that you need kernel 5.16 or higher for some of the functionality and keywords to work. For example "table=netdev hook=egress" to hook at the device-level (same as tc) in the egress direction.

Same seems to apply for matching "oifname lan3" in "table=bridge hook=forward" and "table=bridge hook=postrouting".

My guess would be that both netdev egress filtering and bridge filtering is a somewhat freshly spawned beast in the nftables firewall?...

(tc has been with us for a long time, and is very close to the interface, and was originally designed to handle egress stuff, and I am a little sad that I didn't come up with that solution :smiley:)

Here is some stuff I played around with but could not get nftables in openwrt 23.05.5 to eat:


This might work in the future...: #1

root@OpenWrt:~# cat <<EOF >/etc/rover-unicast-filter.nft
table bridge custom_filter_1 {
   chain my_lladdr_filter {
      type filter hook postrouting priority 0; policy accept;
      oifname lan5 ether daddr != { ff:ff:ff:ff:ff:ff, 11:22:33:44:55:66 } counter accept
   }
}
EOF

root@OpenWrt:~# cat <<EOF >>/etc/config/firewall
config include
	option type 'nftables'
	option path '/etc/rover-unicast-filter.nft'
	option position 'ruleset-post'
EOF

Replace the interface name + rover's MAC + accept with drop.
Could also use "hook forward" instead of "hook postrouting".


This might work in the future...: #2

root@OpenWrt:~# cat <<EOF >/etc/rover-pkttype-filter.nft
table netdev custom_filter_2 {
   chain my_pkttype_filter {
      type filter hook egress device "lan5" priority 0; policy accept;
      meta pkttype multicast counter accept
   }
}
EOF

root@OpenWrt:~# cat <<EOF >>/etc/config/firewall
config include
	option type 'nftables'
	option path '/etc/rover-pkttype-filter.nft'
	option position 'ruleset-post'
EOF

Replace the interface name + accept with drop.


This might work in the future...: #3

root@OpenWrt:~# cat <<EOF >/etc/rover-ipmcast-filter.nft
table netdev custom_filter_3 {
   chain my_egress_filter {
      type filter hook egress device "lan5" priority 0; policy accept;
      ip daddr 224.0.0.0/20 counter accept
   }
}
EOF

root@OpenWrt:~# cat <<EOF >>/etc/config/firewall
config include
	option type 'nftables'
	option path '/etc/rover-ipmcast-filter.nft'
	option position 'ruleset-post'
EOF

Replace the interface name + accept with drop.


Hard to say if any of the above could work, until someone tests on an upgraded OpenWrt box.

So would definitely recommend going with the tc solution by @ppmm :upside_down_face: :slightly_smiling_face: :upside_down_face: :slightly_smiling_face:

Also a thought - if the rover was in its own separate vlan + subnet, and openwrt topside the router in that subnet, couldn't a simple Traffic Rule in the Firewall with dstip=224.0.0.0/20 action=drop work?

Perhaps with another rule to drop ipv6 multicast in similar fashion?

Openwrt might still generate its own multicast but reducing that should be relatively easy, ie. remove ipv6 from the interface, don't install avahi / lldp / etc.

Just throwing an idea around

I did work with the tc command many years back.
I, too, forgot about it.
This is what I'll try first, as I can implement it on any (old) openwrt router.

Yes, of course it would be more straightforward if the underwater segment were on a different subnet. But, it's not and that would be a big architectural change. The nft solution is certainly cleaner. Might try it after I install a recent enough OpenWRT. Thanks for all the time you put in on this!