vxlan: Split vxlan_fdb_update() in two

In order to make it easier to implement rollbacks after FDB update
vetoing, separate the FDB update code to two parts: one that deals with
updates of existing FDB entries, and one that creates new entries.

Signed-off-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Petr Machata 2019-01-16 23:06:32 +00:00 committed by David S. Miller
parent c2b200e0ba
commit a76d1ca296
1 changed files with 97 additions and 61 deletions

View File

@ -855,30 +855,21 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f,
call_rcu(&f->rcu, vxlan_fdb_free); call_rcu(&f->rcu, vxlan_fdb_free);
} }
/* Add new entry to forwarding table -- assumes lock held */ static int vxlan_fdb_update_existing(struct vxlan_dev *vxlan,
static int vxlan_fdb_update(struct vxlan_dev *vxlan, union vxlan_addr *ip,
const u8 *mac, union vxlan_addr *ip,
__u16 state, __u16 flags, __u16 state, __u16 flags,
__be16 port, __be32 src_vni, __be32 vni, __be16 port, __be32 vni,
__u32 ifindex, __u16 ndm_flags, __u32 ifindex, __u16 ndm_flags,
struct vxlan_fdb *f,
bool swdev_notify) bool swdev_notify)
{ {
__u16 fdb_flags = (ndm_flags & ~NTF_USE); __u16 fdb_flags = (ndm_flags & ~NTF_USE);
struct vxlan_rdst *rd = NULL; struct vxlan_rdst *rd = NULL;
struct vxlan_fdb *f;
int notify = 0; int notify = 0;
int rc; int rc;
f = __vxlan_find_mac(vxlan, mac, src_vni); /* Do not allow an externally learned entry to take over an entry added
if (f) { * by the user.
if (flags & NLM_F_EXCL) {
netdev_dbg(vxlan->dev,
"lost race to create %pM\n", mac);
return -EEXIST;
}
/* Do not allow an externally learned entry to take over an
* entry added by the user.
*/ */
if (!(fdb_flags & NTF_EXT_LEARNED) || if (!(fdb_flags & NTF_EXT_LEARNED) ||
!(f->flags & NTF_VXLAN_ADDED_BY_USER)) { !(f->flags & NTF_VXLAN_ADDED_BY_USER)) {
@ -898,11 +889,13 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
/* Only change unicasts */ /* Only change unicasts */
if (!(is_multicast_ether_addr(f->eth_addr) || if (!(is_multicast_ether_addr(f->eth_addr) ||
is_zero_ether_addr(f->eth_addr))) { is_zero_ether_addr(f->eth_addr))) {
notify |= vxlan_fdb_replace(f, ip, port, vni, rc = vxlan_fdb_replace(f, ip, port, vni,
ifindex); ifindex);
} else notify |= rc;
} else {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
}
if ((flags & NLM_F_APPEND) && if ((flags & NLM_F_APPEND) &&
(is_multicast_ether_addr(f->eth_addr) || (is_multicast_ether_addr(f->eth_addr) ||
is_zero_ether_addr(f->eth_addr))) { is_zero_ether_addr(f->eth_addr))) {
@ -915,9 +908,27 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
if (ndm_flags & NTF_USE) if (ndm_flags & NTF_USE)
f->used = jiffies; f->used = jiffies;
} else {
if (!(flags & NLM_F_CREATE)) if (notify) {
return -ENOENT; if (rd == NULL)
rd = first_remote_rtnl(f);
vxlan_fdb_notify(vxlan, f, rd, RTM_NEWNEIGH, swdev_notify);
}
return 0;
}
static int vxlan_fdb_update_create(struct vxlan_dev *vxlan,
const u8 *mac, union vxlan_addr *ip,
__u16 state, __u16 flags,
__be16 port, __be32 src_vni, __be32 vni,
__u32 ifindex, __u16 ndm_flags,
bool swdev_notify)
{
__u16 fdb_flags = (ndm_flags & ~NTF_USE);
struct vxlan_fdb *f;
int rc;
/* Disallow replace to add a multicast entry */ /* Disallow replace to add a multicast entry */
if ((flags & NLM_F_REPLACE) && if ((flags & NLM_F_REPLACE) &&
@ -929,18 +940,43 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
vni, ifindex, fdb_flags, &f); vni, ifindex, fdb_flags, &f);
if (rc < 0) if (rc < 0)
return rc; return rc;
notify = 1;
}
if (notify) {
if (rd == NULL)
rd = first_remote_rtnl(f);
vxlan_fdb_notify(vxlan, f, rd, RTM_NEWNEIGH, swdev_notify);
}
vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), RTM_NEWNEIGH,
swdev_notify);
return 0; return 0;
} }
/* Add new entry to forwarding table -- assumes lock held */
static int vxlan_fdb_update(struct vxlan_dev *vxlan,
const u8 *mac, union vxlan_addr *ip,
__u16 state, __u16 flags,
__be16 port, __be32 src_vni, __be32 vni,
__u32 ifindex, __u16 ndm_flags,
bool swdev_notify)
{
struct vxlan_fdb *f;
f = __vxlan_find_mac(vxlan, mac, src_vni);
if (f) {
if (flags & NLM_F_EXCL) {
netdev_dbg(vxlan->dev,
"lost race to create %pM\n", mac);
return -EEXIST;
}
return vxlan_fdb_update_existing(vxlan, ip, state, flags, port,
vni, ifindex, ndm_flags, f,
swdev_notify);
} else {
if (!(flags & NLM_F_CREATE))
return -ENOENT;
return vxlan_fdb_update_create(vxlan, mac, ip, state, flags,
port, src_vni, vni, ifindex,
ndm_flags, swdev_notify);
}
}
static void vxlan_dst_free(struct rcu_head *head) static void vxlan_dst_free(struct rcu_head *head)
{ {
struct vxlan_rdst *rd = container_of(head, struct vxlan_rdst, rcu); struct vxlan_rdst *rd = container_of(head, struct vxlan_rdst, rcu);