bridge: mcast: Delete empty MDB entry when port group creation fails

When a port group cannot be created for a new group, the MDB entry that
was created for it is left with no ports and is not joined by the host.
Its timer is never armed, so the entry is never deleted, but it is
counted towards mcast_hash_max. When this maximum is reached, the bridge
disables multicast snooping.

Since commit b57e8d870d ("net: bridge: Maintain number of MDB entries
in net_bridge_mcast_port"), port group creation also fails when the port
reached its mcast_max_groups limit. A host behind such a port can
therefore disable snooping by sending reports for new groups, which is
what the limit is meant to prevent. The RTM_NEWMDB path has the same
problem.

Fix by deleting the entry after a failure if it is unused. Delete it
immediately rather than arming its timer, so that it stops counting
towards the maximum right away.

Fixes: b57e8d870d ("net: bridge: Maintain number of MDB entries in net_bridge_mcast_port")
Reported-by: Yunlong Ma <yunlong_ma@buaa.edu.cn>
Closes: https://lore.kernel.org/netdev/2f5b61e2.1e71a.1a11fcb3809.Coremail.yunlong_ma@buaa.edu.cn/
Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@proxmox.com>
Link: https://patch.msgid.link/20261016101407.2030133-2-idosch@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Ido Schimmel 2026-10-16 13:14:06 +03:00 committed by Jakub Kicinski
parent 38dda0ecd4
commit 1d0590f96f
3 changed files with 23 additions and 3 deletions

View File

@ -819,6 +819,7 @@ static int br_mdb_add_group_src_fwd(const struct br_mdb_config *cfg,
struct br_mdb_config sg_cfg;
struct br_ip sg_ip;
u8 flags = 0;
int err;
sg_ip = cfg->group;
sg_ip.src = src_ip->src;
@ -842,7 +843,11 @@ static int br_mdb_add_group_src_fwd(const struct br_mdb_config *cfg,
sg_cfg.filter_mode = MCAST_INCLUDE;
sg_cfg.rt_protocol = cfg->rt_protocol;
sg_cfg.nlflags = cfg->nlflags;
return br_mdb_add_group_sg(&sg_cfg, sgmp, brmctx, flags, extack);
err = br_mdb_add_group_sg(&sg_cfg, sgmp, brmctx, flags, extack);
if (err)
br_multicast_del_group(sgmp);
return err;
}
static int br_mdb_add_group_src(const struct br_mdb_config *cfg,
@ -1047,6 +1052,7 @@ static int br_mdb_add_group(const struct br_mdb_config *cfg,
struct net_bridge_mcast *brmctx;
struct br_ip group = cfg->group;
unsigned char flags = 0;
int err;
brmctx = __br_mdb_choose_context(br, entry, extack);
if (!brmctx)
@ -1073,9 +1079,13 @@ static int br_mdb_add_group(const struct br_mdb_config *cfg,
flags |= MDB_PG_FLAGS_PERMANENT;
if (br_multicast_is_star_g(&group))
return br_mdb_add_group_star_g(cfg, mp, brmctx, flags, extack);
err = br_mdb_add_group_star_g(cfg, mp, brmctx, flags, extack);
else
return br_mdb_add_group_sg(cfg, mp, brmctx, flags, extack);
err = br_mdb_add_group_sg(cfg, mp, brmctx, flags, extack);
if (err)
br_multicast_del_group(mp);
return err;
}
static int __br_mdb_add(const struct br_mdb_config *cfg,

View File

@ -647,6 +647,14 @@ static void br_multicast_del_mdb_entry(struct net_bridge_mdb_entry *mp)
queue_work(system_long_wq, &br->mcast_gc_work);
}
void br_multicast_del_group(struct net_bridge_mdb_entry *mp)
{
if (mp->ports || mp->host_joined)
return;
br_multicast_del_mdb_entry(mp);
}
static void br_multicast_group_expired(struct timer_list *t)
{
struct net_bridge_mdb_entry *mp = timer_container_of(mp, t, timer);
@ -1554,6 +1562,7 @@ __br_multicast_add_group(struct net_bridge_mcast *brmctx,
p = br_multicast_new_port_group(pmctx->port, group, *pp, 0, src,
filter_mode, RTPROT_KERNEL, NULL);
if (unlikely(!p)) {
br_multicast_del_group(mp);
p = ERR_PTR(-ENOMEM);
goto out;
}

View File

@ -998,6 +998,7 @@ struct net_bridge_mdb_entry *
br_mdb_ip_get(struct net_bridge *br, struct br_ip *dst);
struct net_bridge_mdb_entry *
br_multicast_new_group(struct net_bridge *br, struct br_ip *group);
void br_multicast_del_group(struct net_bridge_mdb_entry *mp);
struct net_bridge_port_group *
br_multicast_new_port_group(struct net_bridge_port *port,
const struct br_ip *group,