vxlan_vni_add() installs the new VNI - hash table insert, group list, socket attach and the RTM_NEWTUNNEL notification - before calling vxlan_vni_update_group(), which in turn calls vxlan_update_default_fdb_entry() to store the group (or the device's default remote) into the all-zeros fdb entry of that VNI. Now that vxlan_fdb_update_existing() rejects appending a remote to a nexthop-backed default entry, the group store can fail with -EOPNOTSUPP after the VNI has already been installed and announced, leaving it half-installed: present in the tables and announced, but with no remote stored and no multicast join. Check whether the default fdb entry for the VNI is backed by a nexthop before installing the VNI, and reject the add up front. Note: the missing unwind in vxlan_vni_add() predates this series; this check avoids giving user space a deterministic way to reach it. Cc: stable@vger.kernel.org Fixes: 1274e1cc4226 ("vxlan: ecmp support for mac fdb entries") Signed-off-by: Henry Martin --- v5: fix a build error on current net/main: vxlan->cfg became an RCU-managed pointer, so reference the function-local cfg (reported by kernel test robot). v4: new patch; the failure path was made reachable by patch 1, so guard it before the VNI is installed (sashiko review of v2). drivers/net/vxlan/vxlan_core.c | 14 ++++++++++++++ drivers/net/vxlan/vxlan_private.h | 2 ++ drivers/net/vxlan/vxlan_vnifilter.c | 14 ++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 3390e341d1e58..13143085065f6 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -422,6 +422,20 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan, return f; } +bool vxlan_fdb_entry_nh_backed(struct vxlan_dev *vxlan, const u8 *mac, + __be32 vni) +{ + struct vxlan_fdb *f; + bool ret; + + spin_lock_bh(&vxlan->hash_lock); + f = vxlan_find_mac(vxlan, mac, vni); + ret = f && rcu_access_pointer(f->nh); + spin_unlock_bh(&vxlan->hash_lock); + + return ret; +} + /* caller should hold vxlan->hash_lock */ static struct vxlan_rdst *vxlan_fdb_find_rdst(struct vxlan_fdb *f, union vxlan_addr *ip, __be16 port, diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h index b1eec22163608..0454689965aeb 100644 --- a/drivers/net/vxlan/vxlan_private.h +++ b/drivers/net/vxlan/vxlan_private.h @@ -11,6 +11,8 @@ extern unsigned int vxlan_net_id; extern const u8 all_zeros_mac[ETH_ALEN + 2]; +bool vxlan_fdb_entry_nh_backed(struct vxlan_dev *vxlan, + const u8 *mac, __be32 vni); extern const struct rhashtable_params vxlan_vni_rht_params; #define PORT_HASH_BITS 8 diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index dd94085e08865..f07ae2a6a59ed 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -737,6 +737,20 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan, return err; } + /* A default fdb entry that is backed by a nexthop group can + * never take a remote; reject before the VNI is installed, + * otherwise a failure in vxlan_vni_update_group() would leave + * the VNI half-installed. + */ + if ((group && !vxlan_addr_any(group)) || + !vxlan_addr_any(&cfg->remote_ip)) { + if (vxlan_fdb_entry_nh_backed(vxlan, all_zeros_mac, v)) { + NL_SET_ERR_MSG(extack, + "Cannot add a remote to a nexthop fdb entry"); + return -EOPNOTSUPP; + } + } + vninode = vxlan_vni_alloc(vxlan, v); if (!vninode) return -ENOMEM; -- 2.43.7