]> git.itanic.dy.fi Git - linux-stable/commitdiff
bridge: mdb: Add MDB bulk deletion support
authorIdo Schimmel <idosch@nvidia.com>
Sun, 17 Dec 2023 08:32:40 +0000 (10:32 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 20 Dec 2023 11:27:20 +0000 (11:27 +0000)
Implement MDB bulk deletion support in the bridge driver, allowing MDB
entries to be deleted in bulk according to provided parameters.

Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: Petr Machata <petrm@nvidia.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/bridge/br_device.c
net/bridge/br_mdb.c
net/bridge/br_private.h

index 8f40de3af1540623bb5b590a4a6829d152fd5179..65cee0ad3c1b6d90e644abce0a53de7b4657121a 100644 (file)
@@ -471,6 +471,7 @@ static const struct net_device_ops br_netdev_ops = {
        .ndo_fdb_get             = br_fdb_get,
        .ndo_mdb_add             = br_mdb_add,
        .ndo_mdb_del             = br_mdb_del,
+       .ndo_mdb_del_bulk        = br_mdb_del_bulk,
        .ndo_mdb_dump            = br_mdb_dump,
        .ndo_mdb_get             = br_mdb_get,
        .ndo_bridge_getlink      = br_getlink,
index 8cc526067bc28fa7cc015b669318ee8505eff5e8..bc37e47ad8299ea50736a1016a5a43783579800c 100644 (file)
@@ -1412,6 +1412,139 @@ int br_mdb_del(struct net_device *dev, struct nlattr *tb[],
        return err;
 }
 
+struct br_mdb_flush_desc {
+       u32 port_ifindex;
+       u16 vid;
+       u8 rt_protocol;
+       u8 state;
+       u8 state_mask;
+};
+
+static const struct nla_policy br_mdbe_attrs_del_bulk_pol[MDBE_ATTR_MAX + 1] = {
+       [MDBE_ATTR_RTPROT] = NLA_POLICY_MIN(NLA_U8, RTPROT_STATIC),
+       [MDBE_ATTR_STATE_MASK] = NLA_POLICY_MASK(NLA_U8, MDB_PERMANENT),
+};
+
+static int br_mdb_flush_desc_init(struct br_mdb_flush_desc *desc,
+                                 struct nlattr *tb[],
+                                 struct netlink_ext_ack *extack)
+{
+       struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]);
+       struct nlattr *mdbe_attrs[MDBE_ATTR_MAX + 1];
+       int err;
+
+       desc->port_ifindex = entry->ifindex;
+       desc->vid = entry->vid;
+       desc->state = entry->state;
+
+       if (!tb[MDBA_SET_ENTRY_ATTRS])
+               return 0;
+
+       err = nla_parse_nested(mdbe_attrs, MDBE_ATTR_MAX,
+                              tb[MDBA_SET_ENTRY_ATTRS],
+                              br_mdbe_attrs_del_bulk_pol, extack);
+       if (err)
+               return err;
+
+       if (mdbe_attrs[MDBE_ATTR_STATE_MASK])
+               desc->state_mask = nla_get_u8(mdbe_attrs[MDBE_ATTR_STATE_MASK]);
+
+       if (mdbe_attrs[MDBE_ATTR_RTPROT])
+               desc->rt_protocol = nla_get_u8(mdbe_attrs[MDBE_ATTR_RTPROT]);
+
+       return 0;
+}
+
+static void br_mdb_flush_host(struct net_bridge *br,
+                             struct net_bridge_mdb_entry *mp,
+                             const struct br_mdb_flush_desc *desc)
+{
+       u8 state;
+
+       if (desc->port_ifindex && desc->port_ifindex != br->dev->ifindex)
+               return;
+
+       if (desc->rt_protocol)
+               return;
+
+       state = br_group_is_l2(&mp->addr) ? MDB_PERMANENT : 0;
+       if (desc->state_mask && (state & desc->state_mask) != desc->state)
+               return;
+
+       br_multicast_host_leave(mp, true);
+       if (!mp->ports && netif_running(br->dev))
+               mod_timer(&mp->timer, jiffies);
+}
+
+static void br_mdb_flush_pgs(struct net_bridge *br,
+                            struct net_bridge_mdb_entry *mp,
+                            const struct br_mdb_flush_desc *desc)
+{
+       struct net_bridge_port_group __rcu **pp;
+       struct net_bridge_port_group *p;
+
+       for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;) {
+               u8 state;
+
+               if (desc->port_ifindex &&
+                   desc->port_ifindex != p->key.port->dev->ifindex) {
+                       pp = &p->next;
+                       continue;
+               }
+
+               if (desc->rt_protocol && desc->rt_protocol != p->rt_protocol) {
+                       pp = &p->next;
+                       continue;
+               }
+
+               state = p->flags & MDB_PG_FLAGS_PERMANENT ? MDB_PERMANENT : 0;
+               if (desc->state_mask &&
+                   (state & desc->state_mask) != desc->state) {
+                       pp = &p->next;
+                       continue;
+               }
+
+               br_multicast_del_pg(mp, p, pp);
+       }
+}
+
+static void br_mdb_flush(struct net_bridge *br,
+                        const struct br_mdb_flush_desc *desc)
+{
+       struct net_bridge_mdb_entry *mp;
+
+       spin_lock_bh(&br->multicast_lock);
+
+       /* Safe variant is not needed because entries are removed from the list
+        * upon group timer expiration or bridge deletion.
+        */
+       hlist_for_each_entry(mp, &br->mdb_list, mdb_node) {
+               if (desc->vid && desc->vid != mp->addr.vid)
+                       continue;
+
+               br_mdb_flush_host(br, mp, desc);
+               br_mdb_flush_pgs(br, mp, desc);
+       }
+
+       spin_unlock_bh(&br->multicast_lock);
+}
+
+int br_mdb_del_bulk(struct net_device *dev, struct nlattr *tb[],
+                   struct netlink_ext_ack *extack)
+{
+       struct net_bridge *br = netdev_priv(dev);
+       struct br_mdb_flush_desc desc = {};
+       int err;
+
+       err = br_mdb_flush_desc_init(&desc, tb, extack);
+       if (err)
+               return err;
+
+       br_mdb_flush(br, &desc);
+
+       return 0;
+}
+
 static const struct nla_policy br_mdbe_attrs_get_pol[MDBE_ATTR_MAX + 1] = {
        [MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY,
                                              sizeof(struct in_addr),
index 051ea81864ac38600683f65abebe6f8a44559225..b0a92c344722be6bf195d571bf1a26daf759dfca 100644 (file)
@@ -1022,6 +1022,8 @@ int br_mdb_add(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags,
               struct netlink_ext_ack *extack);
 int br_mdb_del(struct net_device *dev, struct nlattr *tb[],
               struct netlink_ext_ack *extack);
+int br_mdb_del_bulk(struct net_device *dev, struct nlattr *tb[],
+                   struct netlink_ext_ack *extack);
 int br_mdb_dump(struct net_device *dev, struct sk_buff *skb,
                struct netlink_callback *cb);
 int br_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid, u32 seq,
@@ -1430,6 +1432,12 @@ static inline int br_mdb_del(struct net_device *dev, struct nlattr *tb[],
        return -EOPNOTSUPP;
 }
 
+static inline int br_mdb_del_bulk(struct net_device *dev, struct nlattr *tb[],
+                                 struct netlink_ext_ack *extack)
+{
+       return -EOPNOTSUPP;
+}
+
 static inline int br_mdb_dump(struct net_device *dev, struct sk_buff *skb,
                              struct netlink_callback *cb)
 {