]> www.infradead.org Git - users/hch/misc.git/commitdiff
net: bridge: Introduce UAPI for BR_BOOLOPT_FDB_LOCAL_VLAN_0
authorPetr Machata <petrm@nvidia.com>
Thu, 4 Sep 2025 17:07:23 +0000 (19:07 +0200)
committerJakub Kicinski <kuba@kernel.org>
Fri, 12 Sep 2025 02:02:50 +0000 (19:02 -0700)
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>
include/uapi/linux/if_bridge.h
net/bridge/br.c
net/bridge/br_fdb.c
net/bridge/br_private.h

index 73876c0e2bba633fbf36ad03c125ebc9585bce20..e52f8207ab278d82b62c856f26b2b0b7bc60df65 100644 (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
 };
 
index c683baa3847f179ca9c0a537b0ff20f3dae8f89f..512872a2ef811e8c5b20db379a7fcf2368d6e35b 100644 (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);
index 4a20578517a54cc8ed9cf6d54ae8218b5c74a1a6..58d22e2b85fc3551bd5aec9c20296ddfcecaa040 100644 (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)
index 87da287f19fe4ae9a8cef25f893e9d1132ddb73d..16be5d2504023034aa10163d747c124c20fc5c7e 100644 (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,