From 64ad16993dc423e29ed59effb6d4784e27ca8c31 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Linus=20L=C3=BCssing?= Date: Sat, 10 May 2025 05:22:22 +0200 Subject: [PATCH] realtek: fix flooding of unsnoopable multicast addresses MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit RFC4541, section 2.1.2 says: Packets with a destination IP (DIP) address in the 224.0.0.X range which are not IGMP must be forwarded on all ports. And section 3 says: In IPv6, the data forwarding rules are more straight forward because MLD is mandated for addresses with scope 2 (link-scope) or greater. The only exception is the address FF02::1 which is the all hosts link-scope address for which MLD messages are never sent. Packets with the all hosts link-scope address should be forwarded on all ports. However, currently when a listener on FF12::1 or FF12::1234:0:1 for example joins then not only packets to these addresses but also for FF02::1 won't be flooded to all ports anymore, too. Which violates RFC4541. This happens because A): They all map to the same ethernet multicast address, that is 33:33:00:00:00:01. And B) the VLAN profile L2 unknown MC flood setting will only apply flooding of 33:33:00:00:00:01 if there is no specific listener registered for it. So to fix this, avoid registering an MDB entry in the switch for 33:33:00:00:00:01 at all. The downside of this is that FF12::1, FF12::1234:0:1 etc. will always be flooded, too. However fixing the handling of 224.0.0.X and FF02::1 and adhering to RFC4541 must have priority to avoid undesired packetloss, to avoid breaking IPv4/IPv6. Tested-on: ZyXEL GS1900-24HP v1 Fixes: cde31976e375 ("realtek: Add support for Layer 2 Multicast") Signed-off-by: Linus Lüssing Link: https://github.com/openwrt/openwrt/pull/18769 Signed-off-by: Robert Marko --- .../files-6.6/drivers/net/dsa/rtl83xx/dsa.c | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c index 46683cbb142..fd018108ced 100644 --- a/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c +++ b/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c @@ -7,6 +7,15 @@ #include "rtl83xx.h" +static const u8 ipv4_ll_mcast_addr_base[ETH_ALEN] = +{ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 }; +static const u8 ipv4_ll_mcast_addr_mask[ETH_ALEN] = +{ 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; +static const u8 ipv6_all_hosts_mcast_addr_base[ETH_ALEN] = +{ 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 }; +static const u8 ipv6_all_hosts_mcast_addr_mask[ETH_ALEN] = +{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + extern struct rtl83xx_soc_info soc_info; static void rtl83xx_init_stats(struct rtl838x_switch_priv *priv) @@ -1752,6 +1761,24 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port, return 0; } +static bool rtl83xx_mac_is_unsnoop(const unsigned char *addr) +{ + /* + * RFC4541, section 2.1.2.2 + section 3: + * Unsnoopable address ranges must always be flooded. + * + * mapped MAC for 224.0.0.x -> 01:00:5e:00:00:xx + * mapped MAC for ff02::1 -> 33:33:00:00:00:01 + */ + if (ether_addr_equal_masked(addr, ipv4_ll_mcast_addr_base, + ipv4_ll_mcast_addr_mask) || + ether_addr_equal_masked(addr, ipv6_all_hosts_mcast_addr_base, + ipv6_all_hosts_mcast_addr_mask)) + return true; + + return false; +} + static int rtl83xx_port_mdb_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb, const struct dsa_db db) @@ -1774,6 +1801,13 @@ static int rtl83xx_port_mdb_add(struct dsa_switch *ds, int port, return -EINVAL; } + if (rtl83xx_mac_is_unsnoop(mdb->addr)) { + dev_dbg(priv->dev, + "%s: %pM might belong to an unsnoopable IP. ignore\n", + __func__, mdb->addr); + return -EADDRNOTAVAIL; + } + mutex_lock(&priv->reg_mutex); idx = rtl83xx_find_l2_hash_entry(priv, seed, false, &e); @@ -1847,6 +1881,13 @@ int rtl83xx_port_mdb_del(struct dsa_switch *ds, int port, return 0; } + if (rtl83xx_mac_is_unsnoop(mdb->addr)) { + dev_dbg(priv->dev, + "%s: %pM might belong to an unsnoopable IP. ignore\n", + __func__, mdb->addr); + return 0; + } + mutex_lock(&priv->reg_mutex); idx = rtl83xx_find_l2_hash_entry(priv, seed, true, &e); -- 2.47.2