Skip to content

Commit 9271686

Browse files
committed
Merge branch 'br-flush-filtering'
Nikolay Aleksandrov says: ==================== net: bridge: add flush filtering support This patch-set adds support to specify filtering conditions for a bulk delete (flush) operation. This version uses a new nlmsghdr delete flag called NLM_F_BULK in combination with a new ndo_fdb_del_bulk op which is used to signal that the driver supports bulk deletes (that avoids pushing common mac address checks to ndo_fdb_del implementations and also has a different prototype and parsed attribute expectations, more info in patch 03). The new delete flag can be used for any RTM_DEL* type, implementations just need to be careful with older kernels which are doing non-strict attribute parses. A new rtnl flag (RTNL_FLAG_BULK_DEL_SUPPORTED) is used to show that the delete supports NLM_F_BULK. A proper error is returned if bulk delete is not supported. For old kernels I use the fact that mac address attribute (lladdr) is mandatory in the classic fdb del case, but it's not allowed if bulk deleting so older kernels will error out. Patch 01 and 02 are minor rtnetlink cleanups to make the code easier to read. They remove hardcoded values and use names instead. Patch 03 uses BIT() for rtnl flags. Patch 04 adds the new NLM_F_BULK delete request modifier, patch 05 adds the new bulk delete flag and checks for it if the delete requests have NLM_F_BULK set, it also warns if rtnl register is called with a non-delete kind and the bulk delete flag is set. Patch 06 adds the new ndo_fdb_del_bulk call. Patch 07 adds NLM_F_BULK support to rtnl_fdb_del, on such request strict parsing is used only for the supported attributes, and if the ndo is implemented it's called, the NTF_SELF/MASTER rules are the same as for the standard rtnl_fdb_del. Patch 08 implements bridge-specific minimal ndo_fdb_del_bulk call which uses the current br_fdb_flush to delete all entries. Patch 09 adds filtering support to the new bridge flush op which supports target ifindex (port or bridge), vlan id and flags/state mask. Patch 10 adds ndm state and flags mask attributes which will be used for filtering. Patch 11 converts ndm state/flags and their masks to bridge-private flags and fills them in the filter descriptor for matching. Finally patch 12 fills in the target ifindex (after validating it) and vlan id (already validated by rtnl_fdb_flush) for matching. Flush filtering is needed because user-space applications need a quick way to delete only a specific set of entries, e.g. mlag implementations need a way to flush only dynamic entries excluding externally learned ones or only externally learned ones without static entries etc. Also apps usually want to target only a specific vlan or port/vlan combination. The current 2 flush operations (per port and bridge-wide) are not extensible and cannot provide such filtering. I decided against embedding new attrs into the old flush attributes for multiple reasons - proper error handling on unsupported attributes, older kernels silently flushing all, need for a second mechanism to signal that the attribute should be parsed (e.g. using boolopts), special treatment for permanent entries. Examples: $ bridge fdb flush dev bridge vlan 100 static < flush all static entries on vlan 100 > $ bridge fdb flush dev bridge vlan 1 dynamic < flush all dynamic entries on vlan 1 > $ bridge fdb flush dev bridge port ens16 vlan 1 dynamic < flush all dynamic entries on port ens16 and vlan 1 > $ bridge fdb flush dev ens16 vlan 1 dynamic master < as above: flush all dynamic entries on port ens16 and vlan 1 > $ bridge fdb flush dev bridge nooffloaded nopermanent self < flush all non-offloaded and non-permanent entries > $ bridge fdb flush dev bridge static noextern_learn < flush all static entries which are not externally learned > $ bridge fdb flush dev bridge permanent < flush all permanent entries > $ bridge fdb flush dev bridge port bridge permanent < flush all permanent entries pointing to the bridge itself > Example of a flush call with unsupported netlink attribute (NDA_DST): $ bridge fdb flush dev bridge vlan 100 dynamic dst Error: Unsupported attribute. Example of a flush call on an older kernel: $ bridge fdb flush dev bridge dynamic Error: invalid address. Example of calling PF_UNSPEC RTM_DELNEIGH which doesn't support bulk delete with NLM_F_BULK set (ip neigh is changed to add the flag): $ ip n del 192.168.122.5 lladdr 00:11:22:33:44:55 dev ens3 Error: Bulk delete is not supported. Note that all flags have their negated version (static vs nostatic etc) and there are some tricky cases to handle like "static" which in flag terms means fdbs that have NUD_NOARP but *not* NUD_PERMANENT, so the mask matches on both but we need only NUD_NOARP to be set. That's because permanent entries have both set so we can't just match on NUD_NOARP. Also note that this flush operation doesn't treat permanent entries in a special way (fdb_delete vs fdb_delete_local), it will delete them regardless if any port is using them. We can extend the api with a flag to do that if needed in the future. Patch-sets (in order): - Initial bulk del infra and fdb flush filtering (this set) - iproute2 support - selftests v4: Add and check for rtnl del bulk supported flag when using NLM_F_BULK (new patch 05), patches 01 - 03 are also new minor cleanups to remove use of raw values and make code easier to read, don't rename br_fdb_flush in patch 08, set port ifindex as flush target if NDA_IFINDEX is missing and flush was called with port netdev and NTF_MASTER (patch 12). v3: Add NLM_F_BULK delete modifier and ndo_fdb_del_bulk callback, patches 01 - 03 and 06 are new. Patch 04 is changed to implement bulk_del instead of flush, patches 05, 07 and 08 are adjusted to use NDA_ attributes ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents ae10162 + 0dbe886 commit 9271686

File tree

10 files changed

+269
-35
lines changed

10 files changed

+269
-35
lines changed

include/linux/netdevice.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,6 +1260,10 @@ struct netdev_net_notifier {
12601260
* struct net_device *dev,
12611261
* const unsigned char *addr, u16 vid)
12621262
* Deletes the FDB entry from dev coresponding to addr.
1263+
* int (*ndo_fdb_del_bulk)(struct ndmsg *ndm, struct nlattr *tb[],
1264+
* struct net_device *dev,
1265+
* u16 vid,
1266+
* struct netlink_ext_ack *extack);
12631267
* int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb,
12641268
* struct net_device *dev, struct net_device *filter_dev,
12651269
* int *idx)
@@ -1510,6 +1514,11 @@ struct net_device_ops {
15101514
struct net_device *dev,
15111515
const unsigned char *addr,
15121516
u16 vid);
1517+
int (*ndo_fdb_del_bulk)(struct ndmsg *ndm,
1518+
struct nlattr *tb[],
1519+
struct net_device *dev,
1520+
u16 vid,
1521+
struct netlink_ext_ack *extack);
15131522
int (*ndo_fdb_dump)(struct sk_buff *skb,
15141523
struct netlink_callback *cb,
15151524
struct net_device *dev,

include/net/rtnetlink.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,23 @@ typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *,
1010
typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *);
1111

1212
enum rtnl_link_flags {
13-
RTNL_FLAG_DOIT_UNLOCKED = 1,
13+
RTNL_FLAG_DOIT_UNLOCKED = BIT(0),
14+
RTNL_FLAG_BULK_DEL_SUPPORTED = BIT(1),
1415
};
1516

17+
enum rtnl_kinds {
18+
RTNL_KIND_NEW,
19+
RTNL_KIND_DEL,
20+
RTNL_KIND_GET,
21+
RTNL_KIND_SET
22+
};
23+
#define RTNL_KIND_MASK 0x3
24+
25+
static inline enum rtnl_kinds rtnl_msgtype_kind(int msgtype)
26+
{
27+
return msgtype & RTNL_KIND_MASK;
28+
}
29+
1630
void rtnl_register(int protocol, int msgtype,
1731
rtnl_doit_func, rtnl_dumpit_func, unsigned int flags);
1832
int rtnl_register_module(struct module *owner, int protocol, int msgtype,

include/uapi/linux/neighbour.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ enum {
3232
NDA_NH_ID,
3333
NDA_FDB_EXT_ATTRS,
3434
NDA_FLAGS_EXT,
35+
NDA_NDM_STATE_MASK,
36+
NDA_NDM_FLAGS_MASK,
3537
__NDA_MAX
3638
};
3739

include/uapi/linux/netlink.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ struct nlmsghdr {
7272

7373
/* Modifiers to DELETE request */
7474
#define NLM_F_NONREC 0x100 /* Do not delete recursively */
75+
#define NLM_F_BULK 0x200 /* Delete multiple objects */
7576

7677
/* Flags for ACK message */
7778
#define NLM_F_CAPPED 0x100 /* request was capped */

net/bridge/br_device.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ static const struct net_device_ops br_netdev_ops = {
465465
.ndo_fix_features = br_fix_features,
466466
.ndo_fdb_add = br_fdb_add,
467467
.ndo_fdb_del = br_fdb_delete,
468+
.ndo_fdb_del_bulk = br_fdb_delete_bulk,
468469
.ndo_fdb_dump = br_fdb_dump,
469470
.ndo_fdb_get = br_fdb_get,
470471
.ndo_bridge_getlink = br_getlink,

net/bridge/br_fdb.c

Lines changed: 150 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -558,18 +558,161 @@ void br_fdb_cleanup(struct work_struct *work)
558558
mod_delayed_work(system_long_wq, &br->gc_work, work_delay);
559559
}
560560

561-
/* Completely flush all dynamic entries in forwarding database.*/
562-
void br_fdb_flush(struct net_bridge *br)
561+
static bool __fdb_flush_matches(const struct net_bridge *br,
562+
const struct net_bridge_fdb_entry *f,
563+
const struct net_bridge_fdb_flush_desc *desc)
564+
{
565+
const struct net_bridge_port *dst = READ_ONCE(f->dst);
566+
int port_ifidx = dst ? dst->dev->ifindex : br->dev->ifindex;
567+
568+
if (desc->vlan_id && desc->vlan_id != f->key.vlan_id)
569+
return false;
570+
if (desc->port_ifindex && desc->port_ifindex != port_ifidx)
571+
return false;
572+
if (desc->flags_mask && (f->flags & desc->flags_mask) != desc->flags)
573+
return false;
574+
575+
return true;
576+
}
577+
578+
/* Flush forwarding database entries matching the description */
579+
void br_fdb_flush(struct net_bridge *br,
580+
const struct net_bridge_fdb_flush_desc *desc)
563581
{
564582
struct net_bridge_fdb_entry *f;
565-
struct hlist_node *tmp;
566583

567-
spin_lock_bh(&br->hash_lock);
568-
hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) {
569-
if (!test_bit(BR_FDB_STATIC, &f->flags))
584+
rcu_read_lock();
585+
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
586+
if (!__fdb_flush_matches(br, f, desc))
587+
continue;
588+
589+
spin_lock_bh(&br->hash_lock);
590+
if (!hlist_unhashed(&f->fdb_node))
570591
fdb_delete(br, f, true);
592+
spin_unlock_bh(&br->hash_lock);
571593
}
572-
spin_unlock_bh(&br->hash_lock);
594+
rcu_read_unlock();
595+
}
596+
597+
static unsigned long __ndm_state_to_fdb_flags(u16 ndm_state)
598+
{
599+
unsigned long flags = 0;
600+
601+
if (ndm_state & NUD_PERMANENT)
602+
__set_bit(BR_FDB_LOCAL, &flags);
603+
if (ndm_state & NUD_NOARP)
604+
__set_bit(BR_FDB_STATIC, &flags);
605+
606+
return flags;
607+
}
608+
609+
static unsigned long __ndm_flags_to_fdb_flags(u8 ndm_flags)
610+
{
611+
unsigned long flags = 0;
612+
613+
if (ndm_flags & NTF_USE)
614+
__set_bit(BR_FDB_ADDED_BY_USER, &flags);
615+
if (ndm_flags & NTF_EXT_LEARNED)
616+
__set_bit(BR_FDB_ADDED_BY_EXT_LEARN, &flags);
617+
if (ndm_flags & NTF_OFFLOADED)
618+
__set_bit(BR_FDB_OFFLOADED, &flags);
619+
if (ndm_flags & NTF_STICKY)
620+
__set_bit(BR_FDB_STICKY, &flags);
621+
622+
return flags;
623+
}
624+
625+
static int __fdb_flush_validate_ifindex(const struct net_bridge *br,
626+
int ifindex,
627+
struct netlink_ext_ack *extack)
628+
{
629+
const struct net_device *dev;
630+
631+
dev = __dev_get_by_index(dev_net(br->dev), ifindex);
632+
if (!dev) {
633+
NL_SET_ERR_MSG_MOD(extack, "Unknown flush device ifindex");
634+
return -ENODEV;
635+
}
636+
if (!netif_is_bridge_master(dev) && !netif_is_bridge_port(dev)) {
637+
NL_SET_ERR_MSG_MOD(extack, "Flush device is not a bridge or bridge port");
638+
return -EINVAL;
639+
}
640+
if (netif_is_bridge_master(dev) && dev != br->dev) {
641+
NL_SET_ERR_MSG_MOD(extack,
642+
"Flush bridge device does not match target bridge device");
643+
return -EINVAL;
644+
}
645+
if (netif_is_bridge_port(dev)) {
646+
struct net_bridge_port *p = br_port_get_rtnl(dev);
647+
648+
if (p->br != br) {
649+
NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device");
650+
return -EINVAL;
651+
}
652+
}
653+
654+
return 0;
655+
}
656+
657+
int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[],
658+
struct net_device *dev, u16 vid,
659+
struct netlink_ext_ack *extack)
660+
{
661+
u8 ndm_flags = ndm->ndm_flags & ~FDB_FLUSH_IGNORED_NDM_FLAGS;
662+
struct net_bridge_fdb_flush_desc desc = { .vlan_id = vid };
663+
struct net_bridge_port *p = NULL;
664+
struct net_bridge *br;
665+
666+
if (netif_is_bridge_master(dev)) {
667+
br = netdev_priv(dev);
668+
} else {
669+
p = br_port_get_rtnl(dev);
670+
if (!p) {
671+
NL_SET_ERR_MSG_MOD(extack, "Device is not a bridge port");
672+
return -EINVAL;
673+
}
674+
br = p->br;
675+
}
676+
677+
if (ndm_flags & ~FDB_FLUSH_ALLOWED_NDM_FLAGS) {
678+
NL_SET_ERR_MSG(extack, "Unsupported fdb flush ndm flag bits set");
679+
return -EINVAL;
680+
}
681+
if (ndm->ndm_state & ~FDB_FLUSH_ALLOWED_NDM_STATES) {
682+
NL_SET_ERR_MSG(extack, "Unsupported fdb flush ndm state bits set");
683+
return -EINVAL;
684+
}
685+
686+
desc.flags |= __ndm_state_to_fdb_flags(ndm->ndm_state);
687+
desc.flags |= __ndm_flags_to_fdb_flags(ndm_flags);
688+
if (tb[NDA_NDM_STATE_MASK]) {
689+
u16 ndm_state_mask = nla_get_u16(tb[NDA_NDM_STATE_MASK]);
690+
691+
desc.flags_mask |= __ndm_state_to_fdb_flags(ndm_state_mask);
692+
}
693+
if (tb[NDA_NDM_FLAGS_MASK]) {
694+
u8 ndm_flags_mask = nla_get_u8(tb[NDA_NDM_FLAGS_MASK]);
695+
696+
desc.flags_mask |= __ndm_flags_to_fdb_flags(ndm_flags_mask);
697+
}
698+
if (tb[NDA_IFINDEX]) {
699+
int err, ifidx = nla_get_s32(tb[NDA_IFINDEX]);
700+
701+
err = __fdb_flush_validate_ifindex(br, ifidx, extack);
702+
if (err)
703+
return err;
704+
desc.port_ifindex = ifidx;
705+
} else if (p) {
706+
/* flush was invoked with port device and NTF_MASTER */
707+
desc.port_ifindex = p->dev->ifindex;
708+
}
709+
710+
br_debug(br, "flushing port ifindex: %d vlan id: %u flags: 0x%lx flags mask: 0x%lx\n",
711+
desc.port_ifindex, desc.vlan_id, desc.flags, desc.flags_mask);
712+
713+
br_fdb_flush(br, &desc);
714+
715+
return 0;
573716
}
574717

575718
/* Flush all entries referring to a specific port.

net/bridge/br_netlink.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,8 +1326,13 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
13261326
br_recalculate_fwd_mask(br);
13271327
}
13281328

1329-
if (data[IFLA_BR_FDB_FLUSH])
1330-
br_fdb_flush(br);
1329+
if (data[IFLA_BR_FDB_FLUSH]) {
1330+
struct net_bridge_fdb_flush_desc desc = {
1331+
.flags_mask = BR_FDB_STATIC
1332+
};
1333+
1334+
br_fdb_flush(br, &desc);
1335+
}
13311336

13321337
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
13331338
if (data[IFLA_BR_MCAST_ROUTER]) {

net/bridge/br_private.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,13 @@ struct net_bridge_fdb_entry {
274274
struct rcu_head rcu;
275275
};
276276

277+
struct net_bridge_fdb_flush_desc {
278+
unsigned long flags;
279+
unsigned long flags_mask;
280+
int port_ifindex;
281+
u16 vlan_id;
282+
};
283+
277284
#define MDB_PG_FLAGS_PERMANENT BIT(0)
278285
#define MDB_PG_FLAGS_OFFLOAD BIT(1)
279286
#define MDB_PG_FLAGS_FAST_LEAVE BIT(2)
@@ -755,11 +762,17 @@ static inline void br_netpoll_disable(struct net_bridge_port *p)
755762
#endif
756763

757764
/* br_fdb.c */
765+
#define FDB_FLUSH_IGNORED_NDM_FLAGS (NTF_MASTER | NTF_SELF)
766+
#define FDB_FLUSH_ALLOWED_NDM_STATES (NUD_PERMANENT | NUD_NOARP)
767+
#define FDB_FLUSH_ALLOWED_NDM_FLAGS (NTF_USE | NTF_EXT_LEARNED | \
768+
NTF_STICKY | NTF_OFFLOADED)
769+
758770
int br_fdb_init(void);
759771
void br_fdb_fini(void);
760772
int br_fdb_hash_init(struct net_bridge *br);
761773
void br_fdb_hash_fini(struct net_bridge *br);
762-
void br_fdb_flush(struct net_bridge *br);
774+
void br_fdb_flush(struct net_bridge *br,
775+
const struct net_bridge_fdb_flush_desc *desc);
763776
void br_fdb_find_delete_local(struct net_bridge *br,
764777
const struct net_bridge_port *p,
765778
const unsigned char *addr, u16 vid);
@@ -781,6 +794,9 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
781794

782795
int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
783796
struct net_device *dev, const unsigned char *addr, u16 vid);
797+
int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[],
798+
struct net_device *dev, u16 vid,
799+
struct netlink_ext_ack *extack);
784800
int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
785801
const unsigned char *addr, u16 vid, u16 nlh_flags,
786802
struct netlink_ext_ack *extack);

net/bridge/br_sysfs_br.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,11 @@ static DEVICE_ATTR_RW(group_addr);
344344
static int set_flush(struct net_bridge *br, unsigned long val,
345345
struct netlink_ext_ack *extack)
346346
{
347-
br_fdb_flush(br);
347+
struct net_bridge_fdb_flush_desc desc = {
348+
.flags_mask = BR_FDB_STATIC
349+
};
350+
351+
br_fdb_flush(br, &desc);
348352
return 0;
349353
}
350354

0 commit comments

Comments
 (0)