mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-04-29 01:09:33 -04:00
Merge branch 'net-bridge-fdb-activity-tracking'
Nikolay Aleksandrov says:
====================
net: bridge: fdb activity tracking
This set adds extensions needed for EVPN multi-homing proper and
efficient mac sync. User-space (e.g. FRR) needs to be able to track
non-dynamic entry activity on per-fdb basis depending if a tracked fdb is
currently peer active or locally active and needs to be able to add new
peer active fdb (static + track + inactive) without refreshing it to get
real activity tracking. Patch 02 adds a new NDA attribute - NDA_FDB_EXT_ATTRS
to avoid future pollution of NDA attributes by bridge or vxlan. New
bridge/vxlan specific fdb attributes are embedded in NDA_FDB_EXT_ATTRS,
which is used in patch 03 to pass the new NFEA_ACTIVITY_NOTIFY attribute
which controls if an fdb should be tracked and also reflects its current
state when dumping. It is treated as a bitfield, current valid bits are:
1 - mark an entry for activity tracking
2 - mark an entry as inactive to avoid multiple notifications and
reflect state properly
Patch 04 adds the ability to avoid refreshing an entry when changing it
via the NFEA_DONT_REFRESH flag. That allows user-space to mark a static
entry for tracking and keep its real activity unchanged.
The set has been extensively tested with FRR and those changes will
be upstreamed if/after it gets accepted.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -30,6 +30,7 @@ enum {
|
||||
NDA_SRC_VNI,
|
||||
NDA_PROTOCOL, /* Originator of entry */
|
||||
NDA_NH_ID,
|
||||
NDA_FDB_EXT_ATTRS,
|
||||
__NDA_MAX
|
||||
};
|
||||
|
||||
@@ -172,4 +173,27 @@ enum {
|
||||
};
|
||||
#define NDTA_MAX (__NDTA_MAX - 1)
|
||||
|
||||
/* FDB activity notification bits used in NFEA_ACTIVITY_NOTIFY:
|
||||
* - FDB_NOTIFY_BIT - notify on activity/expire for any entry
|
||||
* - FDB_NOTIFY_INACTIVE_BIT - mark as inactive to avoid multiple notifications
|
||||
*/
|
||||
enum {
|
||||
FDB_NOTIFY_BIT = (1 << 0),
|
||||
FDB_NOTIFY_INACTIVE_BIT = (1 << 1)
|
||||
};
|
||||
|
||||
/* embedded into NDA_FDB_EXT_ATTRS:
|
||||
* [NDA_FDB_EXT_ATTRS] = {
|
||||
* [NFEA_ACTIVITY_NOTIFY]
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
enum {
|
||||
NFEA_UNSPEC,
|
||||
NFEA_ACTIVITY_NOTIFY,
|
||||
NFEA_DONT_REFRESH,
|
||||
__NFEA_MAX
|
||||
};
|
||||
#define NFEA_MAX (__NFEA_MAX - 1)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -349,12 +349,21 @@ void br_fdb_cleanup(struct work_struct *work)
|
||||
*/
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
|
||||
unsigned long this_timer;
|
||||
unsigned long this_timer = f->updated + delay;
|
||||
|
||||
if (test_bit(BR_FDB_STATIC, &f->flags) ||
|
||||
test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags))
|
||||
test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags)) {
|
||||
if (test_bit(BR_FDB_NOTIFY, &f->flags)) {
|
||||
if (time_after(this_timer, now))
|
||||
work_delay = min(work_delay,
|
||||
this_timer - now);
|
||||
else if (!test_and_set_bit(BR_FDB_NOTIFY_INACTIVE,
|
||||
&f->flags))
|
||||
fdb_notify(br, f, RTM_NEWNEIGH, false);
|
||||
}
|
||||
continue;
|
||||
this_timer = f->updated + delay;
|
||||
}
|
||||
|
||||
if (time_after(this_timer, now)) {
|
||||
work_delay = min(work_delay, this_timer - now);
|
||||
} else {
|
||||
@@ -556,11 +565,17 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* returns true if the fdb was modified */
|
||||
static bool __fdb_mark_active(struct net_bridge_fdb_entry *fdb)
|
||||
{
|
||||
return !!(test_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags) &&
|
||||
test_and_clear_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags));
|
||||
}
|
||||
|
||||
void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr, u16 vid, unsigned long flags)
|
||||
{
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
bool fdb_modified = false;
|
||||
|
||||
/* some users want to always flood. */
|
||||
if (hold_time(br) == 0)
|
||||
@@ -575,6 +590,12 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
|
||||
source->dev->name, addr, vid);
|
||||
} else {
|
||||
unsigned long now = jiffies;
|
||||
bool fdb_modified = false;
|
||||
|
||||
if (now != fdb->updated) {
|
||||
fdb->updated = now;
|
||||
fdb_modified = __fdb_mark_active(fdb);
|
||||
}
|
||||
|
||||
/* fastpath: update of existing entry */
|
||||
if (unlikely(source != fdb->dst &&
|
||||
@@ -587,8 +608,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
|
||||
clear_bit(BR_FDB_ADDED_BY_EXT_LEARN,
|
||||
&fdb->flags);
|
||||
}
|
||||
if (now != fdb->updated)
|
||||
fdb->updated = now;
|
||||
|
||||
if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags)))
|
||||
set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
|
||||
if (unlikely(fdb_modified)) {
|
||||
@@ -667,6 +687,23 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
|
||||
&fdb->key.vlan_id))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (test_bit(BR_FDB_NOTIFY, &fdb->flags)) {
|
||||
struct nlattr *nest = nla_nest_start(skb, NDA_FDB_EXT_ATTRS);
|
||||
u8 notify_bits = FDB_NOTIFY_BIT;
|
||||
|
||||
if (!nest)
|
||||
goto nla_put_failure;
|
||||
if (test_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags))
|
||||
notify_bits |= FDB_NOTIFY_INACTIVE_BIT;
|
||||
|
||||
if (nla_put_u8(skb, NFEA_ACTIVITY_NOTIFY, notify_bits)) {
|
||||
nla_nest_cancel(skb, nest);
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
nla_nest_end(skb, nest);
|
||||
}
|
||||
|
||||
nlmsg_end(skb, nlh);
|
||||
return 0;
|
||||
|
||||
@@ -681,7 +718,9 @@ static inline size_t fdb_nlmsg_size(void)
|
||||
+ nla_total_size(ETH_ALEN) /* NDA_LLADDR */
|
||||
+ nla_total_size(sizeof(u32)) /* NDA_MASTER */
|
||||
+ nla_total_size(sizeof(u16)) /* NDA_VLAN */
|
||||
+ nla_total_size(sizeof(struct nda_cacheinfo));
|
||||
+ nla_total_size(sizeof(struct nda_cacheinfo))
|
||||
+ nla_total_size(0) /* NDA_FDB_EXT_ATTRS */
|
||||
+ nla_total_size(sizeof(u8)); /* NFEA_ACTIVITY_NOTIFY */
|
||||
}
|
||||
|
||||
static void fdb_notify(struct net_bridge *br,
|
||||
@@ -791,14 +830,41 @@ int br_fdb_get(struct sk_buff *skb,
|
||||
return err;
|
||||
}
|
||||
|
||||
/* returns true if the fdb is modified */
|
||||
static bool fdb_handle_notify(struct net_bridge_fdb_entry *fdb, u8 notify)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
/* allow to mark an entry as inactive, usually done on creation */
|
||||
if ((notify & FDB_NOTIFY_INACTIVE_BIT) &&
|
||||
!test_and_set_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags))
|
||||
modified = true;
|
||||
|
||||
if ((notify & FDB_NOTIFY_BIT) &&
|
||||
!test_and_set_bit(BR_FDB_NOTIFY, &fdb->flags)) {
|
||||
/* enabled activity tracking */
|
||||
modified = true;
|
||||
} else if (!(notify & FDB_NOTIFY_BIT) &&
|
||||
test_and_clear_bit(BR_FDB_NOTIFY, &fdb->flags)) {
|
||||
/* disabled activity tracking, clear notify state */
|
||||
clear_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/* Update (create or replace) forwarding database entry */
|
||||
static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const u8 *addr, u16 state, u16 flags, u16 vid,
|
||||
u8 ndm_flags)
|
||||
const u8 *addr, struct ndmsg *ndm, u16 flags, u16 vid,
|
||||
struct nlattr *nfea_tb[])
|
||||
{
|
||||
bool is_sticky = !!(ndm_flags & NTF_STICKY);
|
||||
bool is_sticky = !!(ndm->ndm_flags & NTF_STICKY);
|
||||
bool refresh = !nfea_tb[NFEA_DONT_REFRESH];
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
u16 state = ndm->ndm_state;
|
||||
bool modified = false;
|
||||
u8 notify = 0;
|
||||
|
||||
/* If the port cannot learn allow only local and static entries */
|
||||
if (source && !(state & NUD_PERMANENT) && !(state & NUD_NOARP) &&
|
||||
@@ -815,6 +881,13 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
|
||||
if (is_sticky && (state & NUD_PERMANENT))
|
||||
return -EINVAL;
|
||||
|
||||
if (nfea_tb[NFEA_ACTIVITY_NOTIFY]) {
|
||||
notify = nla_get_u8(nfea_tb[NFEA_ACTIVITY_NOTIFY]);
|
||||
if ((notify & ~BR_FDB_NOTIFY_SETTABLE_BITS) ||
|
||||
(notify & BR_FDB_NOTIFY_SETTABLE_BITS) == FDB_NOTIFY_INACTIVE_BIT)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fdb = br_fdb_find(br, addr, vid);
|
||||
if (fdb == NULL) {
|
||||
if (!(flags & NLM_F_CREATE))
|
||||
@@ -858,11 +931,15 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (fdb_handle_notify(fdb, notify))
|
||||
modified = true;
|
||||
|
||||
set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
|
||||
|
||||
fdb->used = jiffies;
|
||||
if (modified) {
|
||||
fdb->updated = jiffies;
|
||||
if (refresh)
|
||||
fdb->updated = jiffies;
|
||||
fdb_notify(br, fdb, RTM_NEWNEIGH, true);
|
||||
}
|
||||
|
||||
@@ -871,7 +948,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
|
||||
|
||||
static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
|
||||
struct net_bridge_port *p, const unsigned char *addr,
|
||||
u16 nlh_flags, u16 vid)
|
||||
u16 nlh_flags, u16 vid, struct nlattr *nfea_tb[])
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
@@ -893,20 +970,25 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
|
||||
err = br_fdb_external_learn_add(br, p, addr, vid, true);
|
||||
} else {
|
||||
spin_lock_bh(&br->hash_lock);
|
||||
err = fdb_add_entry(br, p, addr, ndm->ndm_state,
|
||||
nlh_flags, vid, ndm->ndm_flags);
|
||||
err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb);
|
||||
spin_unlock_bh(&br->hash_lock);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct nla_policy br_nda_fdb_pol[NFEA_MAX + 1] = {
|
||||
[NFEA_ACTIVITY_NOTIFY] = { .type = NLA_U8 },
|
||||
[NFEA_DONT_REFRESH] = { .type = NLA_FLAG },
|
||||
};
|
||||
|
||||
/* Add new permanent fdb entry with RTM_NEWNEIGH */
|
||||
int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
|
||||
struct net_device *dev,
|
||||
const unsigned char *addr, u16 vid, u16 nlh_flags,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct nlattr *nfea_tb[NFEA_MAX + 1], *attr;
|
||||
struct net_bridge_vlan_group *vg;
|
||||
struct net_bridge_port *p = NULL;
|
||||
struct net_bridge_vlan *v;
|
||||
@@ -939,6 +1021,16 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
|
||||
vg = nbp_vlan_group(p);
|
||||
}
|
||||
|
||||
if (tb[NDA_FDB_EXT_ATTRS]) {
|
||||
attr = tb[NDA_FDB_EXT_ATTRS];
|
||||
err = nla_parse_nested(nfea_tb, NFEA_MAX, attr,
|
||||
br_nda_fdb_pol, extack);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
memset(nfea_tb, 0, sizeof(struct nlattr *) * (NFEA_MAX + 1));
|
||||
}
|
||||
|
||||
if (vid) {
|
||||
v = br_vlan_find(vg, vid);
|
||||
if (!v || !br_vlan_should_use(v)) {
|
||||
@@ -947,9 +1039,9 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
|
||||
}
|
||||
|
||||
/* VID was specified, so use it. */
|
||||
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, vid);
|
||||
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, vid, nfea_tb);
|
||||
} else {
|
||||
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, 0);
|
||||
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, 0, nfea_tb);
|
||||
if (err || !vg || !vg->num_vlans)
|
||||
goto out;
|
||||
|
||||
@@ -960,7 +1052,8 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
|
||||
list_for_each_entry(v, &vg->vlan_list, vlist) {
|
||||
if (!br_vlan_should_use(v))
|
||||
continue;
|
||||
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, v->vid);
|
||||
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, v->vid,
|
||||
nfea_tb);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ enum {
|
||||
/* Path to usermode spanning tree program */
|
||||
#define BR_STP_PROG "/sbin/bridge-stp"
|
||||
|
||||
#define BR_FDB_NOTIFY_SETTABLE_BITS (FDB_NOTIFY_BIT | FDB_NOTIFY_INACTIVE_BIT)
|
||||
|
||||
typedef struct bridge_id bridge_id;
|
||||
typedef struct mac_addr mac_addr;
|
||||
typedef __u16 port_id;
|
||||
@@ -184,6 +186,8 @@ enum {
|
||||
BR_FDB_ADDED_BY_USER,
|
||||
BR_FDB_ADDED_BY_EXT_LEARN,
|
||||
BR_FDB_OFFLOADED,
|
||||
BR_FDB_NOTIFY,
|
||||
BR_FDB_NOTIFY_INACTIVE
|
||||
};
|
||||
|
||||
struct net_bridge_fdb_key {
|
||||
|
||||
@@ -1783,6 +1783,7 @@ const struct nla_policy nda_policy[NDA_MAX+1] = {
|
||||
[NDA_MASTER] = { .type = NLA_U32 },
|
||||
[NDA_PROTOCOL] = { .type = NLA_U8 },
|
||||
[NDA_NH_ID] = { .type = NLA_U32 },
|
||||
[NDA_FDB_EXT_ATTRS] = { .type = NLA_NESTED },
|
||||
};
|
||||
|
||||
static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
|
||||
Reference in New Issue
Block a user