From edc6b8f160c798cd211893bc5df25aec7cfca838 Mon Sep 17 00:00:00 2001 From: Gennaro Cimmino Date: Wed, 30 Sep 2026 08:29:24 +0200 Subject: [PATCH] realtek: rtl93xx: trap IGMP and MLD to the CPU On RTL838x and RTL839x, SPCL_TRAP_IGMP_CTRL traps IGMP and MLD to the CPU, and the receive path hands trapped frames to the bridge unmarked, so the bridge sees every report and forwards it itself. The RTL93xx setup aims at the same but does neither: - rteth_930x/931x_hw_init() write VLAN_APP_PKT_CTRL = 0x12, which puts 2 in IGMP_ACT and MLD_ACT. The action encoding in dal_longan_trap.c of the Zyxel XMG1915 GPL U-Boot is 0 forward, 1 trap, 2 copy, 3 trap to master, 4 copy to master, so 2 is a copy. - The actions only apply in a VLAN whose GROUP_MASK selects them, and the VLAN writers leave that mask at 0 (RTL930x) or at whatever the entry held (RTL931x). On an RTL9303, VLAN_APP_PKT_CTRL reads back 0x12, every VLAN write the driver makes carries GROUP_MASK 0, and IGMPv3 and MLDv2 reports reach the CPU as normal forwarding (reason 63). Forcing one group mask bit at a time shows bit 2 selecting IGMP and bit 3 MLD. Trap both (0x9), set bits 2 and 3 in every VLAN entry, and count reason 55 (IGMP_MLD) as trapped, so that the reports reach the bridge unmarked, as on RTL83xx, and are forwarded by the CPU rather than flooded by the switch. RTL931x has the same IGMP_ACT and MLD_ACT fields (rtk_mango_regField_list.c in the D-Link DMS-1250 GPL SDK), and its VLAN entry carries the 16-bit GROUP_MASK in multicast_grp_mask; that bits 2 and 3 select IGMP and MLD there as well is taken from RTL930x. Tested on an RTL9303 (Hasivo S1100W-8XGT-SE), main against this change, with a bench-only print of the CPU reason and of the DSA mark. On main: VLAN_APP_PKT_CTRL 0x12, GROUP_MASK 0 in all 21 VLAN writes, 16 IGMPv3 reports with reason 63 and 8 MLDv2 reports with reason 36 (IPv6 hop-by-hop option), all forwarded by the switch and marked. With this change: 0x9, GROUP_MASK 0x0c in all 21 writes, and the 16 IGMP and 8 MLD reports all arrive with reason 55, trapped and unmarked; tcpdump on the user port still sees them. RTL931x was not tested. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Gennaro Cimmino Link: https://github.com/openwrt/openwrt/pull/25660 Signed-off-by: Markus Stockhausen --- .../files-6.18/drivers/net/dsa/rtl83xx/vlan.c | 6 +++++- .../drivers/net/ethernet/realtek/rtl838x_eth.c | 14 +++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/vlan.c b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/vlan.c index 65ff34f23f9..936a4a25fd6 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/vlan.c +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/vlan.c @@ -469,6 +469,9 @@ void rtl930x_vlan_tables_read(u32 vlan, struct rtldsa_vlan_info *info) info->untagged_ports = v >> 3; } +/* Group mask bits for the IGMP and MLD actions of VLAN_APP_PKT_CTRL */ +#define RTLDSA_93XX_VLAN_GROUP_IGMP_MLD (BIT(2) | BIT(3)) + void rtl930x_vlan_set_tagged(u32 vlan, struct rtldsa_vlan_info *info) { u32 v, w; @@ -481,6 +484,7 @@ void rtl930x_vlan_set_tagged(u32 vlan, struct rtldsa_vlan_info *info) w |= info->hash_mc_fid ? BIT(27) : 0; w |= info->hash_uc_fid ? BIT(28) : 0; w |= info->profile_id << 24; + w |= RTLDSA_93XX_VLAN_GROUP_IGMP_MLD << 16; buf[0] = v; buf[1] = w; @@ -688,7 +692,7 @@ void rtl931x_vlan_set_tagged(u32 vlan, struct rtldsa_vlan_info *info) x |= info->hash_mc_fid ? BIT(30) : 0; x |= ((u32)info->if_id & 0x3ff) << 20; x |= (info->profile_id & 0xf) << 16; - x |= info->multicast_grp_mask & 0xffff; + x |= (info->multicast_grp_mask | RTLDSA_93XX_VLAN_GROUP_IGMP_MLD) & 0xffff; if (info->l2_tunnel_list_id >= 0) { y = info->l2_tunnel_list_id << 18; y |= BIT(31); diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/realtek/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/realtek/rtl838x_eth.c index a9f4599d550..78a2b97f7e7 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/realtek/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/realtek/rtl838x_eth.c @@ -231,7 +231,7 @@ static bool rteth_93xx_decode_tag(struct rteth_frag *frag, struct rteth_dsa_tag t->queue = (frag->cpu_tag[2] >> 11) & 0x1f; t->reason = frag->cpu_tag[7] & 0x3f; t->crc_error = frag->cpu_tag[1] & BIT(6); - t->l2_offloaded = (t->reason >= 19 && t->reason <= 28) ? 0 : 1; + t->l2_offloaded = (t->reason >= 19 && t->reason <= 28) || t->reason == 55 ? 0 : 1; if (t->reason != 63) pr_debug("%s: Reason %d, port %d, queue %d\n", __func__, t->reason, t->port, t->queue); @@ -778,16 +778,20 @@ static void rteth_839x_hw_init(struct rteth_ctrl *ctrl) static void rteth_930x_hw_init(struct rteth_ctrl *ctrl) { - /* Trap MLD and IGMP messages to CPU_PORT */ - regmap_write(ctrl->map, RTETH_930X_VLAN_APP_PKT_CTRL, 0x12); + /* Trap IGMP and MLD to the CPU (IGMP_ACT = MLD_ACT = 1) in every VLAN + * whose group mask selects them + */ + regmap_write(ctrl->map, RTETH_930X_VLAN_APP_PKT_CTRL, 0x9); /* Flush learned FDB entries on link down of a port */ regmap_set_bits(ctrl->map, RTETH_930X_L2_CTRL, BIT(7)); } static void rteth_931x_hw_init(struct rteth_ctrl *ctrl) { - /* Trap MLD and IGMP messages to CPU_PORT */ - regmap_write(ctrl->map, RTETH_931X_VLAN_APP_PKT_CTRL, 0x12); + /* Trap IGMP and MLD to the CPU (IGMP_ACT = MLD_ACT = 1) in every VLAN + * whose group mask selects them + */ + regmap_write(ctrl->map, RTETH_931X_VLAN_APP_PKT_CTRL, 0x9); /* Set PCIE_PWR_DOWN */ regmap_set_bits(ctrl->map, RTETH_931X_PS_SOC_CTRL, BIT(1)); }