net: bridge: fdb: add support for flush filtering based on ifindex and vlan
authorNikolay Aleksandrov <razor@blackwall.org>
Wed, 13 Apr 2022 10:52:02 +0000 (13:52 +0300)
committerDavid S. Miller <davem@davemloft.net>
Wed, 13 Apr 2022 11:46:26 +0000 (12:46 +0100)
Add support for fdb flush filtering based on destination ifindex and
vlan id. The ifindex must either match a port's device ifindex or the
bridge's. The vlan support is trivial since it's already validated by
rtnl_fdb_del, we just need to fill it in.

Signed-off-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/bridge/br_fdb.c

index 74d759d09f94de1ef32d02df7a1ec8d0fbda2f9a..1a3d583fbc8e2de3e6220ed5d63ab550a192ae1d 100644 (file)
@@ -622,12 +622,44 @@ static unsigned long __ndm_flags_to_fdb_flags(u8 ndm_flags)
        return flags;
 }
 
+static int __fdb_flush_validate_ifindex(const struct net_bridge *br,
+                                       int ifindex,
+                                       struct netlink_ext_ack *extack)
+{
+       const struct net_device *dev;
+
+       dev = __dev_get_by_index(dev_net(br->dev), ifindex);
+       if (!dev) {
+               NL_SET_ERR_MSG_MOD(extack, "Unknown flush device ifindex");
+               return -ENODEV;
+       }
+       if (!netif_is_bridge_master(dev) && !netif_is_bridge_port(dev)) {
+               NL_SET_ERR_MSG_MOD(extack, "Flush device is not a bridge or bridge port");
+               return -EINVAL;
+       }
+       if (netif_is_bridge_master(dev) && dev != br->dev) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Flush bridge device does not match target bridge device");
+               return -EINVAL;
+       }
+       if (netif_is_bridge_port(dev)) {
+               struct net_bridge_port *p = br_port_get_rtnl(dev);
+
+               if (p->br != br) {
+                       NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[],
                       struct net_device *dev, u16 vid,
                       struct netlink_ext_ack *extack)
 {
        u8 ndm_flags = ndm->ndm_flags & ~FDB_FLUSH_IGNORED_NDM_FLAGS;
-       struct net_bridge_fdb_flush_desc desc = {};
+       struct net_bridge_fdb_flush_desc desc = { .vlan_id = vid };
        struct net_bridge_port *p = NULL;
        struct net_bridge *br;
 
@@ -663,6 +695,17 @@ int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[],
 
                desc.flags_mask |= __ndm_flags_to_fdb_flags(ndm_flags_mask);
        }
+       if (tb[NDA_IFINDEX]) {
+               int err, ifidx = nla_get_s32(tb[NDA_IFINDEX]);
+
+               err = __fdb_flush_validate_ifindex(br, ifidx, extack);
+               if (err)
+                       return err;
+               desc.port_ifindex = ifidx;
+       } else if (p) {
+               /* flush was invoked with port device and NTF_MASTER */
+               desc.port_ifindex = p->dev->ifindex;
+       }
 
        br_debug(br, "flushing port ifindex: %d vlan id: %u flags: 0x%lx flags mask: 0x%lx\n",
                 desc.port_ifindex, desc.vlan_id, desc.flags, desc.flags_mask);