Skip to content

Commit 7497b0a

Browse files
committed
Merge branch 'vxlan-fdb-flushing'
Amit Cohen says: ==================== Extend VXLAN driver to support FDB flushing The merge commit 9271686 ("Merge branch 'br-flush-filtering'") added support for FDB flushing in bridge driver. Extend VXLAN driver to support FDB flushing also. Add support for filtering by fields which are relevant for VXLAN FDBs: * Source VNI * Nexthop ID * 'router' flag * Destination VNI * Destination Port * Destination IP Without this set, flush for VXLAN device fails: $ bridge fdb flush dev vx10 RTNETLINK answers: Operation not supported With this set, such flush works with the relevant arguments, for example: $ bridge fdb flush dev vx10 vni 5000 dst 193.2.2.1 < flush all vx10 entries with VNI 5000 and destination IP 193.2.2.1> Some preparations are required, handle them before adding flushing support in VXLAN driver. See more details in commit messages. Patch set overview: Patch #1 prepares flush policy to be used by VXLAN driver Patches #2-#3 are preparations in VXLAN driver Patch #4 adds an initial support for flushing in VXLAN driver Patches #5-#9 add support for filtering by several attributes Patch #10 adds a test for FDB flush with VXLAN Patch #11 extends the test to check FDB flush with bridge ==================== Acked-by: Nikolay Aleksandrov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
2 parents 0e6bb5b + f826f2a commit 7497b0a

File tree

7 files changed

+1049
-38
lines changed

7 files changed

+1049
-38
lines changed

drivers/net/vxlan/vxlan_core.c

Lines changed: 199 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3022,9 +3022,101 @@ static int vxlan_open(struct net_device *dev)
30223022
return ret;
30233023
}
30243024

3025+
struct vxlan_fdb_flush_desc {
3026+
bool ignore_default_entry;
3027+
unsigned long state;
3028+
unsigned long state_mask;
3029+
unsigned long flags;
3030+
unsigned long flags_mask;
3031+
__be32 src_vni;
3032+
u32 nhid;
3033+
__be32 vni;
3034+
__be16 port;
3035+
union vxlan_addr dst_ip;
3036+
};
3037+
3038+
static bool vxlan_fdb_is_default_entry(const struct vxlan_fdb *f,
3039+
const struct vxlan_dev *vxlan)
3040+
{
3041+
return is_zero_ether_addr(f->eth_addr) && f->vni == vxlan->cfg.vni;
3042+
}
3043+
3044+
static bool vxlan_fdb_nhid_matches(const struct vxlan_fdb *f, u32 nhid)
3045+
{
3046+
struct nexthop *nh = rtnl_dereference(f->nh);
3047+
3048+
return nh && nh->id == nhid;
3049+
}
3050+
3051+
static bool vxlan_fdb_flush_matches(const struct vxlan_fdb *f,
3052+
const struct vxlan_dev *vxlan,
3053+
const struct vxlan_fdb_flush_desc *desc)
3054+
{
3055+
if (desc->state_mask && (f->state & desc->state_mask) != desc->state)
3056+
return false;
3057+
3058+
if (desc->flags_mask && (f->flags & desc->flags_mask) != desc->flags)
3059+
return false;
3060+
3061+
if (desc->ignore_default_entry && vxlan_fdb_is_default_entry(f, vxlan))
3062+
return false;
3063+
3064+
if (desc->src_vni && f->vni != desc->src_vni)
3065+
return false;
3066+
3067+
if (desc->nhid && !vxlan_fdb_nhid_matches(f, desc->nhid))
3068+
return false;
3069+
3070+
return true;
3071+
}
3072+
3073+
static bool
3074+
vxlan_fdb_flush_should_match_remotes(const struct vxlan_fdb_flush_desc *desc)
3075+
{
3076+
return desc->vni || desc->port || desc->dst_ip.sa.sa_family;
3077+
}
3078+
3079+
static bool
3080+
vxlan_fdb_flush_remote_matches(const struct vxlan_fdb_flush_desc *desc,
3081+
const struct vxlan_rdst *rd)
3082+
{
3083+
if (desc->vni && rd->remote_vni != desc->vni)
3084+
return false;
3085+
3086+
if (desc->port && rd->remote_port != desc->port)
3087+
return false;
3088+
3089+
if (desc->dst_ip.sa.sa_family &&
3090+
!vxlan_addr_equal(&rd->remote_ip, &desc->dst_ip))
3091+
return false;
3092+
3093+
return true;
3094+
}
3095+
3096+
static void
3097+
vxlan_fdb_flush_match_remotes(struct vxlan_fdb *f, struct vxlan_dev *vxlan,
3098+
const struct vxlan_fdb_flush_desc *desc,
3099+
bool *p_destroy_fdb)
3100+
{
3101+
bool remotes_flushed = false;
3102+
struct vxlan_rdst *rd, *tmp;
3103+
3104+
list_for_each_entry_safe(rd, tmp, &f->remotes, list) {
3105+
if (!vxlan_fdb_flush_remote_matches(desc, rd))
3106+
continue;
3107+
3108+
vxlan_fdb_dst_destroy(vxlan, f, rd, true);
3109+
remotes_flushed = true;
3110+
}
3111+
3112+
*p_destroy_fdb = remotes_flushed && list_empty(&f->remotes);
3113+
}
3114+
30253115
/* Purge the forwarding table */
3026-
static void vxlan_flush(struct vxlan_dev *vxlan, bool do_all)
3116+
static void vxlan_flush(struct vxlan_dev *vxlan,
3117+
const struct vxlan_fdb_flush_desc *desc)
30273118
{
3119+
bool match_remotes = vxlan_fdb_flush_should_match_remotes(desc);
30283120
unsigned int h;
30293121

30303122
for (h = 0; h < FDB_HASH_SIZE; ++h) {
@@ -3034,28 +3126,122 @@ static void vxlan_flush(struct vxlan_dev *vxlan, bool do_all)
30343126
hlist_for_each_safe(p, n, &vxlan->fdb_head[h]) {
30353127
struct vxlan_fdb *f
30363128
= container_of(p, struct vxlan_fdb, hlist);
3037-
if (!do_all && (f->state & (NUD_PERMANENT | NUD_NOARP)))
3038-
continue;
3039-
/* the all_zeros_mac entry is deleted at vxlan_uninit */
3040-
if (is_zero_ether_addr(f->eth_addr) &&
3041-
f->vni == vxlan->cfg.vni)
3129+
3130+
if (!vxlan_fdb_flush_matches(f, vxlan, desc))
30423131
continue;
3132+
3133+
if (match_remotes) {
3134+
bool destroy_fdb = false;
3135+
3136+
vxlan_fdb_flush_match_remotes(f, vxlan, desc,
3137+
&destroy_fdb);
3138+
3139+
if (!destroy_fdb)
3140+
continue;
3141+
}
3142+
30433143
vxlan_fdb_destroy(vxlan, f, true, true);
30443144
}
30453145
spin_unlock_bh(&vxlan->hash_lock[h]);
30463146
}
30473147
}
30483148

3149+
static const struct nla_policy vxlan_del_bulk_policy[NDA_MAX + 1] = {
3150+
[NDA_SRC_VNI] = { .type = NLA_U32 },
3151+
[NDA_NH_ID] = { .type = NLA_U32 },
3152+
[NDA_VNI] = { .type = NLA_U32 },
3153+
[NDA_PORT] = { .type = NLA_U16 },
3154+
[NDA_DST] = NLA_POLICY_RANGE(NLA_BINARY, sizeof(struct in_addr),
3155+
sizeof(struct in6_addr)),
3156+
[NDA_NDM_STATE_MASK] = { .type = NLA_U16 },
3157+
[NDA_NDM_FLAGS_MASK] = { .type = NLA_U8 },
3158+
};
3159+
3160+
#define VXLAN_FDB_FLUSH_IGNORED_NDM_FLAGS (NTF_MASTER | NTF_SELF)
3161+
#define VXLAN_FDB_FLUSH_ALLOWED_NDM_STATES (NUD_PERMANENT | NUD_NOARP)
3162+
#define VXLAN_FDB_FLUSH_ALLOWED_NDM_FLAGS (NTF_EXT_LEARNED | NTF_OFFLOADED | \
3163+
NTF_ROUTER)
3164+
3165+
static int vxlan_fdb_delete_bulk(struct nlmsghdr *nlh, struct net_device *dev,
3166+
struct netlink_ext_ack *extack)
3167+
{
3168+
struct vxlan_dev *vxlan = netdev_priv(dev);
3169+
struct vxlan_fdb_flush_desc desc = {};
3170+
struct ndmsg *ndm = nlmsg_data(nlh);
3171+
struct nlattr *tb[NDA_MAX + 1];
3172+
u8 ndm_flags;
3173+
int err;
3174+
3175+
ndm_flags = ndm->ndm_flags & ~VXLAN_FDB_FLUSH_IGNORED_NDM_FLAGS;
3176+
3177+
err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, vxlan_del_bulk_policy,
3178+
extack);
3179+
if (err)
3180+
return err;
3181+
3182+
if (ndm_flags & ~VXLAN_FDB_FLUSH_ALLOWED_NDM_FLAGS) {
3183+
NL_SET_ERR_MSG(extack, "Unsupported fdb flush ndm flag bits set");
3184+
return -EINVAL;
3185+
}
3186+
if (ndm->ndm_state & ~VXLAN_FDB_FLUSH_ALLOWED_NDM_STATES) {
3187+
NL_SET_ERR_MSG(extack, "Unsupported fdb flush ndm state bits set");
3188+
return -EINVAL;
3189+
}
3190+
3191+
desc.state = ndm->ndm_state;
3192+
desc.flags = ndm_flags;
3193+
3194+
if (tb[NDA_NDM_STATE_MASK])
3195+
desc.state_mask = nla_get_u16(tb[NDA_NDM_STATE_MASK]);
3196+
3197+
if (tb[NDA_NDM_FLAGS_MASK])
3198+
desc.flags_mask = nla_get_u8(tb[NDA_NDM_FLAGS_MASK]);
3199+
3200+
if (tb[NDA_SRC_VNI])
3201+
desc.src_vni = cpu_to_be32(nla_get_u32(tb[NDA_SRC_VNI]));
3202+
3203+
if (tb[NDA_NH_ID])
3204+
desc.nhid = nla_get_u32(tb[NDA_NH_ID]);
3205+
3206+
if (tb[NDA_VNI])
3207+
desc.vni = cpu_to_be32(nla_get_u32(tb[NDA_VNI]));
3208+
3209+
if (tb[NDA_PORT])
3210+
desc.port = nla_get_be16(tb[NDA_PORT]);
3211+
3212+
if (tb[NDA_DST]) {
3213+
union vxlan_addr ip;
3214+
3215+
err = vxlan_nla_get_addr(&ip, tb[NDA_DST]);
3216+
if (err) {
3217+
NL_SET_ERR_MSG_ATTR(extack, tb[NDA_DST],
3218+
"Unsupported address family");
3219+
return err;
3220+
}
3221+
desc.dst_ip = ip;
3222+
}
3223+
3224+
vxlan_flush(vxlan, &desc);
3225+
3226+
return 0;
3227+
}
3228+
30493229
/* Cleanup timer and forwarding table on shutdown */
30503230
static int vxlan_stop(struct net_device *dev)
30513231
{
30523232
struct vxlan_dev *vxlan = netdev_priv(dev);
3233+
struct vxlan_fdb_flush_desc desc = {
3234+
/* Default entry is deleted at vxlan_uninit. */
3235+
.ignore_default_entry = true,
3236+
.state = 0,
3237+
.state_mask = NUD_PERMANENT | NUD_NOARP,
3238+
};
30533239

30543240
vxlan_multicast_leave(vxlan);
30553241

30563242
del_timer_sync(&vxlan->age_timer);
30573243

3058-
vxlan_flush(vxlan, false);
3244+
vxlan_flush(vxlan, &desc);
30593245
vxlan_sock_release(vxlan);
30603246

30613247
return 0;
@@ -3142,6 +3328,7 @@ static const struct net_device_ops vxlan_netdev_ether_ops = {
31423328
.ndo_set_mac_address = eth_mac_addr,
31433329
.ndo_fdb_add = vxlan_fdb_add,
31443330
.ndo_fdb_del = vxlan_fdb_delete,
3331+
.ndo_fdb_del_bulk = vxlan_fdb_delete_bulk,
31453332
.ndo_fdb_dump = vxlan_fdb_dump,
31463333
.ndo_fdb_get = vxlan_fdb_get,
31473334
.ndo_mdb_add = vxlan_mdb_add,
@@ -4294,8 +4481,12 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
42944481
static void vxlan_dellink(struct net_device *dev, struct list_head *head)
42954482
{
42964483
struct vxlan_dev *vxlan = netdev_priv(dev);
4484+
struct vxlan_fdb_flush_desc desc = {
4485+
/* Default entry is deleted at vxlan_uninit. */
4486+
.ignore_default_entry = true,
4487+
};
42974488

4298-
vxlan_flush(vxlan, true);
4489+
vxlan_flush(vxlan, &desc);
42994490

43004491
list_del(&vxlan->next);
43014492
unregister_netdevice_queue(dev, head);

include/linux/netdevice.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,9 +1287,7 @@ struct netdev_net_notifier {
12871287
* struct net_device *dev,
12881288
* const unsigned char *addr, u16 vid)
12891289
* Deletes the FDB entry from dev coresponding to addr.
1290-
* int (*ndo_fdb_del_bulk)(struct ndmsg *ndm, struct nlattr *tb[],
1291-
* struct net_device *dev,
1292-
* u16 vid,
1290+
* int (*ndo_fdb_del_bulk)(struct nlmsghdr *nlh, struct net_device *dev,
12931291
* struct netlink_ext_ack *extack);
12941292
* int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb,
12951293
* struct net_device *dev, struct net_device *filter_dev,
@@ -1564,10 +1562,8 @@ struct net_device_ops {
15641562
struct net_device *dev,
15651563
const unsigned char *addr,
15661564
u16 vid, struct netlink_ext_ack *extack);
1567-
int (*ndo_fdb_del_bulk)(struct ndmsg *ndm,
1568-
struct nlattr *tb[],
1565+
int (*ndo_fdb_del_bulk)(struct nlmsghdr *nlh,
15691566
struct net_device *dev,
1570-
u16 vid,
15711567
struct netlink_ext_ack *extack);
15721568
int (*ndo_fdb_dump)(struct sk_buff *skb,
15731569
struct netlink_callback *cb,

net/bridge/br_fdb.c

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -661,14 +661,30 @@ static int __fdb_flush_validate_ifindex(const struct net_bridge *br,
661661
return 0;
662662
}
663663

664-
int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[],
665-
struct net_device *dev, u16 vid,
664+
static const struct nla_policy br_fdb_del_bulk_policy[NDA_MAX + 1] = {
665+
[NDA_VLAN] = NLA_POLICY_RANGE(NLA_U16, 1, VLAN_N_VID - 2),
666+
[NDA_IFINDEX] = NLA_POLICY_MIN(NLA_S32, 1),
667+
[NDA_NDM_STATE_MASK] = { .type = NLA_U16 },
668+
[NDA_NDM_FLAGS_MASK] = { .type = NLA_U8 },
669+
};
670+
671+
int br_fdb_delete_bulk(struct nlmsghdr *nlh, struct net_device *dev,
666672
struct netlink_ext_ack *extack)
667673
{
668-
u8 ndm_flags = ndm->ndm_flags & ~FDB_FLUSH_IGNORED_NDM_FLAGS;
669-
struct net_bridge_fdb_flush_desc desc = { .vlan_id = vid };
674+
struct net_bridge_fdb_flush_desc desc = {};
675+
struct ndmsg *ndm = nlmsg_data(nlh);
670676
struct net_bridge_port *p = NULL;
677+
struct nlattr *tb[NDA_MAX + 1];
671678
struct net_bridge *br;
679+
u8 ndm_flags;
680+
int err;
681+
682+
ndm_flags = ndm->ndm_flags & ~FDB_FLUSH_IGNORED_NDM_FLAGS;
683+
684+
err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX,
685+
br_fdb_del_bulk_policy, extack);
686+
if (err)
687+
return err;
672688

673689
if (netif_is_bridge_master(dev)) {
674690
br = netdev_priv(dev);
@@ -681,6 +697,9 @@ int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[],
681697
br = p->br;
682698
}
683699

700+
if (tb[NDA_VLAN])
701+
desc.vlan_id = nla_get_u16(tb[NDA_VLAN]);
702+
684703
if (ndm_flags & ~FDB_FLUSH_ALLOWED_NDM_FLAGS) {
685704
NL_SET_ERR_MSG(extack, "Unsupported fdb flush ndm flag bits set");
686705
return -EINVAL;
@@ -703,7 +722,7 @@ int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[],
703722
desc.flags_mask |= __ndm_flags_to_fdb_flags(ndm_flags_mask);
704723
}
705724
if (tb[NDA_IFINDEX]) {
706-
int err, ifidx = nla_get_s32(tb[NDA_IFINDEX]);
725+
int ifidx = nla_get_s32(tb[NDA_IFINDEX]);
707726

708727
err = __fdb_flush_validate_ifindex(br, ifidx, extack);
709728
if (err)

net/bridge/br_private.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -847,8 +847,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
847847
int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
848848
struct net_device *dev, const unsigned char *addr, u16 vid,
849849
struct netlink_ext_ack *extack);
850-
int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[],
851-
struct net_device *dev, u16 vid,
850+
int br_fdb_delete_bulk(struct nlmsghdr *nlh, struct net_device *dev,
852851
struct netlink_ext_ack *extack);
853852
int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
854853
const unsigned char *addr, u16 vid, u16 nlh_flags,

0 commit comments

Comments
 (0)