Commit 593b03d4 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents b430081b b5f1d9ec
...@@ -30,6 +30,7 @@ enum { ...@@ -30,6 +30,7 @@ enum {
NDA_SRC_VNI, NDA_SRC_VNI,
NDA_PROTOCOL, /* Originator of entry */ NDA_PROTOCOL, /* Originator of entry */
NDA_NH_ID, NDA_NH_ID,
NDA_FDB_EXT_ATTRS,
__NDA_MAX __NDA_MAX
}; };
...@@ -172,4 +173,27 @@ enum { ...@@ -172,4 +173,27 @@ enum {
}; };
#define NDTA_MAX (__NDTA_MAX - 1) #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 #endif
...@@ -349,12 +349,21 @@ void br_fdb_cleanup(struct work_struct *work) ...@@ -349,12 +349,21 @@ void br_fdb_cleanup(struct work_struct *work)
*/ */
rcu_read_lock(); rcu_read_lock();
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { 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) || 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; continue;
this_timer = f->updated + delay; }
if (time_after(this_timer, now)) { if (time_after(this_timer, now)) {
work_delay = min(work_delay, this_timer - now); work_delay = min(work_delay, this_timer - now);
} else { } else {
...@@ -556,11 +565,17 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, ...@@ -556,11 +565,17 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
return ret; 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, void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
const unsigned char *addr, u16 vid, unsigned long flags) const unsigned char *addr, u16 vid, unsigned long flags)
{ {
struct net_bridge_fdb_entry *fdb; struct net_bridge_fdb_entry *fdb;
bool fdb_modified = false;
/* some users want to always flood. */ /* some users want to always flood. */
if (hold_time(br) == 0) if (hold_time(br) == 0)
...@@ -575,6 +590,12 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, ...@@ -575,6 +590,12 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
source->dev->name, addr, vid); source->dev->name, addr, vid);
} else { } else {
unsigned long now = jiffies; 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 */ /* fastpath: update of existing entry */
if (unlikely(source != fdb->dst && if (unlikely(source != fdb->dst &&
...@@ -587,8 +608,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, ...@@ -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, clear_bit(BR_FDB_ADDED_BY_EXT_LEARN,
&fdb->flags); &fdb->flags);
} }
if (now != fdb->updated)
fdb->updated = now;
if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags)))
set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
if (unlikely(fdb_modified)) { if (unlikely(fdb_modified)) {
...@@ -667,6 +687,23 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, ...@@ -667,6 +687,23 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
&fdb->key.vlan_id)) &fdb->key.vlan_id))
goto nla_put_failure; 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); nlmsg_end(skb, nlh);
return 0; return 0;
...@@ -681,7 +718,9 @@ static inline size_t fdb_nlmsg_size(void) ...@@ -681,7 +718,9 @@ static inline size_t fdb_nlmsg_size(void)
+ nla_total_size(ETH_ALEN) /* NDA_LLADDR */ + nla_total_size(ETH_ALEN) /* NDA_LLADDR */
+ nla_total_size(sizeof(u32)) /* NDA_MASTER */ + nla_total_size(sizeof(u32)) /* NDA_MASTER */
+ nla_total_size(sizeof(u16)) /* NDA_VLAN */ + 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, static void fdb_notify(struct net_bridge *br,
...@@ -791,14 +830,41 @@ int br_fdb_get(struct sk_buff *skb, ...@@ -791,14 +830,41 @@ int br_fdb_get(struct sk_buff *skb,
return err; 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 */ /* Update (create or replace) forwarding database entry */
static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
const u8 *addr, u16 state, u16 flags, u16 vid, const u8 *addr, struct ndmsg *ndm, u16 flags, u16 vid,
u8 ndm_flags) 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; struct net_bridge_fdb_entry *fdb;
u16 state = ndm->ndm_state;
bool modified = false; bool modified = false;
u8 notify = 0;
/* If the port cannot learn allow only local and static entries */ /* If the port cannot learn allow only local and static entries */
if (source && !(state & NUD_PERMANENT) && !(state & NUD_NOARP) && 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, ...@@ -815,6 +881,13 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
if (is_sticky && (state & NUD_PERMANENT)) if (is_sticky && (state & NUD_PERMANENT))
return -EINVAL; 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); fdb = br_fdb_find(br, addr, vid);
if (fdb == NULL) { if (fdb == NULL) {
if (!(flags & NLM_F_CREATE)) if (!(flags & NLM_F_CREATE))
...@@ -858,11 +931,15 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, ...@@ -858,11 +931,15 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
modified = true; modified = true;
} }
if (fdb_handle_notify(fdb, notify))
modified = true;
set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
fdb->used = jiffies; fdb->used = jiffies;
if (modified) { if (modified) {
fdb->updated = jiffies; if (refresh)
fdb->updated = jiffies;
fdb_notify(br, fdb, RTM_NEWNEIGH, true); 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, ...@@ -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, static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
struct net_bridge_port *p, const unsigned char *addr, 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; int err = 0;
...@@ -893,20 +970,25 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br, ...@@ -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); err = br_fdb_external_learn_add(br, p, addr, vid, true);
} else { } else {
spin_lock_bh(&br->hash_lock); spin_lock_bh(&br->hash_lock);
err = fdb_add_entry(br, p, addr, ndm->ndm_state, err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb);
nlh_flags, vid, ndm->ndm_flags);
spin_unlock_bh(&br->hash_lock); spin_unlock_bh(&br->hash_lock);
} }
return err; 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 */ /* Add new permanent fdb entry with RTM_NEWNEIGH */
int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, u16 vid, u16 nlh_flags, const unsigned char *addr, u16 vid, u16 nlh_flags,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct nlattr *nfea_tb[NFEA_MAX + 1], *attr;
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
struct net_bridge_port *p = NULL; struct net_bridge_port *p = NULL;
struct net_bridge_vlan *v; struct net_bridge_vlan *v;
...@@ -939,6 +1021,16 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -939,6 +1021,16 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
vg = nbp_vlan_group(p); 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) { if (vid) {
v = br_vlan_find(vg, vid); v = br_vlan_find(vg, vid);
if (!v || !br_vlan_should_use(v)) { if (!v || !br_vlan_should_use(v)) {
...@@ -947,9 +1039,9 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -947,9 +1039,9 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
} }
/* VID was specified, so use it. */ /* 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 { } 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) if (err || !vg || !vg->num_vlans)
goto out; goto out;
...@@ -960,7 +1052,8 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -960,7 +1052,8 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
list_for_each_entry(v, &vg->vlan_list, vlist) { list_for_each_entry(v, &vg->vlan_list, vlist) {
if (!br_vlan_should_use(v)) if (!br_vlan_should_use(v))
continue; 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) if (err)
goto out; goto out;
} }
......
...@@ -48,6 +48,8 @@ enum { ...@@ -48,6 +48,8 @@ enum {
/* Path to usermode spanning tree program */ /* Path to usermode spanning tree program */
#define BR_STP_PROG "/sbin/bridge-stp" #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 bridge_id bridge_id;
typedef struct mac_addr mac_addr; typedef struct mac_addr mac_addr;
typedef __u16 port_id; typedef __u16 port_id;
...@@ -184,6 +186,8 @@ enum { ...@@ -184,6 +186,8 @@ enum {
BR_FDB_ADDED_BY_USER, BR_FDB_ADDED_BY_USER,
BR_FDB_ADDED_BY_EXT_LEARN, BR_FDB_ADDED_BY_EXT_LEARN,
BR_FDB_OFFLOADED, BR_FDB_OFFLOADED,
BR_FDB_NOTIFY,
BR_FDB_NOTIFY_INACTIVE
}; };
struct net_bridge_fdb_key { struct net_bridge_fdb_key {
......
...@@ -1783,6 +1783,7 @@ const struct nla_policy nda_policy[NDA_MAX+1] = { ...@@ -1783,6 +1783,7 @@ const struct nla_policy nda_policy[NDA_MAX+1] = {
[NDA_MASTER] = { .type = NLA_U32 }, [NDA_MASTER] = { .type = NLA_U32 },
[NDA_PROTOCOL] = { .type = NLA_U8 }, [NDA_PROTOCOL] = { .type = NLA_U8 },
[NDA_NH_ID] = { .type = NLA_U32 }, [NDA_NH_ID] = { .type = NLA_U32 },
[NDA_FDB_EXT_ATTRS] = { .type = NLA_NESTED },
}; };
static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment