net: bridge: Introduce UAPI for BR_BOOLOPT_FDB_LOCAL_VLAN_0

The previous patches introduced a new option, BR_BOOLOPT_FDB_LOCAL_VLAN_0.
When enabled, it has local FDB entries installed only on VLAN 0, instead of
duplicating them across all VLANs.

In this patch, add the corresponding UAPI toggle, and the code for turning
the feature on and off.

Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: Petr Machata <petrm@nvidia.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Link: https://patch.msgid.link/ea99bfb10f687fa58091e6e1c2f8acc33f47ca45.1757004393.git.petrm@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
pull/1354/merge
Petr Machata 2025-09-04 19:07:23 +02:00 committed by Jakub Kicinski
parent a29aba64e0
commit 21446c06b4
4 changed files with 123 additions and 0 deletions

View File

@ -823,6 +823,8 @@ struct br_mcast_stats {
/* bridge boolean options
* BR_BOOLOPT_NO_LL_LEARN - disable learning from link-local packets
* BR_BOOLOPT_MCAST_VLAN_SNOOPING - control vlan multicast snooping
* BR_BOOLOPT_FDB_LOCAL_VLAN_0 - local FDB entries installed by the bridge
* driver itself should only be added on VLAN 0
*
* IMPORTANT: if adding a new option do not forget to handle
* it in br_boolopt_toggle/get and bridge sysfs
@ -832,6 +834,7 @@ enum br_boolopt_id {
BR_BOOLOPT_MCAST_VLAN_SNOOPING,
BR_BOOLOPT_MST_ENABLE,
BR_BOOLOPT_MDB_OFFLOAD_FAIL_NOTIFICATION,
BR_BOOLOPT_FDB_LOCAL_VLAN_0,
BR_BOOLOPT_MAX
};

View File

@ -259,6 +259,23 @@ static struct notifier_block br_switchdev_blocking_notifier = {
.notifier_call = br_switchdev_blocking_event,
};
static int
br_toggle_fdb_local_vlan_0(struct net_bridge *br, bool on,
struct netlink_ext_ack *extack)
{
int err;
if (br_opt_get(br, BROPT_FDB_LOCAL_VLAN_0) == on)
return 0;
err = br_fdb_toggle_local_vlan_0(br, on, extack);
if (err)
return err;
br_opt_toggle(br, BROPT_FDB_LOCAL_VLAN_0, on);
return 0;
}
/* br_boolopt_toggle - change user-controlled boolean option
*
* @br: bridge device
@ -287,6 +304,9 @@ int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
case BR_BOOLOPT_MDB_OFFLOAD_FAIL_NOTIFICATION:
br_opt_toggle(br, BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION, on);
break;
case BR_BOOLOPT_FDB_LOCAL_VLAN_0:
err = br_toggle_fdb_local_vlan_0(br, on, extack);
break;
default:
/* shouldn't be called with unsupported options */
WARN_ON(1);
@ -307,6 +327,8 @@ int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
return br_opt_get(br, BROPT_MST_ENABLED);
case BR_BOOLOPT_MDB_OFFLOAD_FAIL_NOTIFICATION:
return br_opt_get(br, BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION);
case BR_BOOLOPT_FDB_LOCAL_VLAN_0:
return br_opt_get(br, BROPT_FDB_LOCAL_VLAN_0);
default:
/* shouldn't be called with unsupported options */
WARN_ON(1);

View File

@ -582,6 +582,102 @@ void br_fdb_cleanup(struct work_struct *work)
mod_delayed_work(system_long_wq, &br->gc_work, work_delay);
}
static void br_fdb_delete_locals_per_vlan_port(struct net_bridge *br,
struct net_bridge_port *p)
{
struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *v;
struct net_device *dev;
if (p) {
vg = nbp_vlan_group(p);
dev = p->dev;
} else {
vg = br_vlan_group(br);
dev = br->dev;
}
list_for_each_entry(v, &vg->vlan_list, vlist)
br_fdb_find_delete_local(br, p, dev->dev_addr, v->vid);
}
static void br_fdb_delete_locals_per_vlan(struct net_bridge *br)
{
struct net_bridge_port *p;
ASSERT_RTNL();
list_for_each_entry(p, &br->port_list, list)
br_fdb_delete_locals_per_vlan_port(br, p);
br_fdb_delete_locals_per_vlan_port(br, NULL);
}
static int br_fdb_insert_locals_per_vlan_port(struct net_bridge *br,
struct net_bridge_port *p,
struct netlink_ext_ack *extack)
{
struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *v;
struct net_device *dev;
int err;
if (p) {
vg = nbp_vlan_group(p);
dev = p->dev;
} else {
vg = br_vlan_group(br);
dev = br->dev;
}
list_for_each_entry(v, &vg->vlan_list, vlist) {
if (!br_vlan_should_use(v))
continue;
err = br_fdb_add_local(br, p, dev->dev_addr, v->vid);
if (err)
return err;
}
return 0;
}
static int br_fdb_insert_locals_per_vlan(struct net_bridge *br,
struct netlink_ext_ack *extack)
{
struct net_bridge_port *p;
int err;
ASSERT_RTNL();
list_for_each_entry(p, &br->port_list, list) {
err = br_fdb_insert_locals_per_vlan_port(br, p, extack);
if (err)
goto rollback;
}
err = br_fdb_insert_locals_per_vlan_port(br, NULL, extack);
if (err)
goto rollback;
return 0;
rollback:
NL_SET_ERR_MSG_MOD(extack, "fdb_local_vlan_0 toggle: FDB entry insertion failed");
br_fdb_delete_locals_per_vlan(br);
return err;
}
int br_fdb_toggle_local_vlan_0(struct net_bridge *br, bool on,
struct netlink_ext_ack *extack)
{
if (!on)
return br_fdb_insert_locals_per_vlan(br, extack);
br_fdb_delete_locals_per_vlan(br);
return 0;
}
static bool __fdb_flush_matches(const struct net_bridge *br,
const struct net_bridge_fdb_entry *f,
const struct net_bridge_fdb_flush_desc *desc)

View File

@ -844,6 +844,8 @@ void br_fdb_find_delete_local(struct net_bridge *br,
void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr);
void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr);
void br_fdb_cleanup(struct work_struct *work);
int br_fdb_toggle_local_vlan_0(struct net_bridge *br, bool on,
struct netlink_ext_ack *extack);
void br_fdb_delete_by_port(struct net_bridge *br,
const struct net_bridge_port *p, u16 vid, int do_all);
struct net_bridge_fdb_entry *br_fdb_find_rcu(struct net_bridge *br,