diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 7f552c98dcc5..7af38cdfb98e 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -4454,6 +4454,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], struct net_device *lowerdev; struct vxlan_config conf; struct vxlan_rdst *dst; + u32 new_ifindex; int err; if (!rtnl_dev_link_net_capable(dev, vxlan->net)) @@ -4477,13 +4478,16 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], if (err) return err; + /* vxlan_config_apply() only commits remote_ifindex if lowerdev is set */ + new_ifindex = lowerdev ? conf.remote_ifindex : dst->remote_ifindex; + rem_ip_changed = !vxlan_addr_equal(&conf.remote_ip, &dst->remote_ip); change_igmp = vxlan->dev->flags & IFF_UP && (rem_ip_changed || - dst->remote_ifindex != conf.remote_ifindex); + dst->remote_ifindex != new_ifindex); /* handle default dst entry */ - if (rem_ip_changed) { + if (rem_ip_changed || dst->remote_ifindex != new_ifindex) { spin_lock_bh(&vxlan->hash_lock); if (!vxlan_addr_any(&conf.remote_ip)) { err = vxlan_fdb_update(vxlan, all_zeros_mac, @@ -4492,7 +4496,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], NLM_F_APPEND | NLM_F_CREATE, vxlan->cfg.dst_port, conf.vni, conf.vni, - conf.remote_ifindex, + new_ifindex, NTF_SELF, 0, true, extack); if (err) { spin_unlock_bh(&vxlan->hash_lock); @@ -4511,13 +4515,21 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], true); spin_unlock_bh(&vxlan->hash_lock); - /* If vni filtering device, also update fdb entries of - * all vnis that were using default remote ip + /* If vni filtering device, also update default fdb entries of + * all vnis */ if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) { err = vxlan_vnilist_update_group(vxlan, &dst->remote_ip, - &conf.remote_ip, extack); + &conf.remote_ip, + dst->remote_ifindex, + new_ifindex, extack); if (err) { + vxlan_update_default_fdb_entry(vxlan, conf.vni, + &conf.remote_ip, + &dst->remote_ip, + new_ifindex, + dst->remote_ifindex, + NULL); netdev_adjacent_change_abort(dst->remote_dev, lowerdev, dev); return err; @@ -4525,7 +4537,9 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], } } - if (change_igmp && vxlan_addr_multicast(&dst->remote_ip)) + if (change_igmp && + (vxlan_addr_multicast(&dst->remote_ip) || + (vxlan->cfg.flags & VXLAN_F_VNIFILTER))) err = vxlan_multicast_leave(vxlan); if (netif_running(dev) && conf.age_interval != vxlan->cfg.age_interval) @@ -4536,9 +4550,14 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], dst->remote_dev = lowerdev; vxlan_config_apply(dev, &conf, lowerdev, vxlan->net, true); - if (!err && change_igmp && - vxlan_addr_multicast(&dst->remote_ip)) - err = vxlan_multicast_join(vxlan); + if (change_igmp && + (vxlan_addr_multicast(&dst->remote_ip) || + (vxlan->cfg.flags & VXLAN_F_VNIFILTER))) { + int join_err = vxlan_multicast_join(vxlan); + + if (join_err) + err = join_err; + } return err; } diff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c index 3b75b48dc726..b85283605aaf 100644 --- a/drivers/net/vxlan/vxlan_multicast.c +++ b/drivers/net/vxlan/vxlan_multicast.c @@ -219,10 +219,17 @@ static int vxlan_multicast_leave_vnigrp(struct vxlan_dev *vxlan) int last_err = 0, ret; list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { - if (vxlan_addr_multicast(&v->remote_ip) && - !vxlan_group_used(vn, vxlan, v->vni, &v->remote_ip, + if (!vxlan_addr_multicast(&v->remote_ip)) + continue; + /* skip if address is same as default address */ + if (vxlan_addr_equal(&v->remote_ip, + &vxlan->default_dst.remote_ip)) + continue; + if (!vxlan_group_used(vn, vxlan, v->vni, &v->remote_ip, 0)) { ret = vxlan_igmp_leave(vxlan, &v->remote_ip, 0); + if (ret == -EADDRNOTAVAIL) + ret = 0; if (ret) last_err = ret; } @@ -259,6 +266,8 @@ int vxlan_multicast_leave(struct vxlan_dev *vxlan) !vxlan_group_used(vn, vxlan, 0, NULL, 0)) { ret = vxlan_igmp_leave(vxlan, &vxlan->default_dst.remote_ip, vxlan->default_dst.remote_ifindex); + if (ret == -EADDRNOTAVAIL) + ret = 0; if (ret) return ret; } diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h index b1eec2216360..e4ceca925bde 100644 --- a/drivers/net/vxlan/vxlan_private.h +++ b/drivers/net/vxlan/vxlan_private.h @@ -213,9 +213,15 @@ void vxlan_vs_add_vnigrp(struct vxlan_dev *vxlan, struct vxlan_sock *vs, bool ipv6); void vxlan_vs_del_vnigrp(struct vxlan_dev *vxlan); +int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni, + union vxlan_addr *old_remote_ip, + union vxlan_addr *remote_ip, + u32 old_ifindex, u32 new_ifindex, + struct netlink_ext_ack *extack); int vxlan_vnilist_update_group(struct vxlan_dev *vxlan, union vxlan_addr *old_remote_ip, union vxlan_addr *new_remote_ip, + u32 old_ifindex, u32 new_ifindex, struct netlink_ext_ack *extack); diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index dd94085e0886..336e8128be48 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -470,14 +470,19 @@ static const struct nla_policy vni_filter_policy[VXLAN_VNIFILTER_MAX + 1] = { [VXLAN_VNIFILTER_ENTRY] = { .type = NLA_NESTED }, }; -static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni, - union vxlan_addr *old_remote_ip, - union vxlan_addr *remote_ip, - struct netlink_ext_ack *extack) +int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni, + union vxlan_addr *old_remote_ip, + union vxlan_addr *remote_ip, + u32 old_ifindex, u32 new_ifindex, + struct netlink_ext_ack *extack) { - struct vxlan_rdst *dst = &vxlan->default_dst; int err = 0; + if (old_remote_ip && remote_ip && + vxlan_addr_equal(old_remote_ip, remote_ip) && + old_ifindex == new_ifindex) + return 0; + spin_lock_bh(&vxlan->hash_lock); if (remote_ip && !vxlan_addr_any(remote_ip)) { err = vxlan_fdb_update(vxlan, all_zeros_mac, @@ -487,9 +492,9 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni, vxlan->cfg.dst_port, vni, vni, - dst->remote_ifindex, + new_ifindex, NTF_SELF, 0, true, extack); - if (err) { + if (err && extack) { spin_unlock_bh(&vxlan->hash_lock); return err; } @@ -500,7 +505,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni, *old_remote_ip, vxlan->cfg.dst_port, vni, vni, - dst->remote_ifindex, + old_ifindex, true); } spin_unlock_bh(&vxlan->hash_lock); @@ -532,11 +537,12 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan, newrip = &dst->remote_ip; } - /* if old rip exists, and no newrip, - * explicitly delete old rip - */ - if (!newrip && !vxlan_addr_any(&old_remote_ip)) - oldrip = &old_remote_ip; + if (!create) { + if (!vxlan_addr_any(&old_remote_ip)) + oldrip = &old_remote_ip; + else if (!vxlan_addr_any(&dst->remote_ip)) + oldrip = &dst->remote_ip; + } if (!newrip && !oldrip) return 0; @@ -546,6 +552,8 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan, ret = vxlan_update_default_fdb_entry(vxlan, vninode->vni, oldrip, newrip, + dst->remote_ifindex, + dst->remote_ifindex, extack); if (ret) goto out; @@ -560,6 +568,8 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan, vxlan->default_dst.remote_ifindex)) { ret = vxlan_igmp_leave(vxlan, &old_remote_ip, 0); + if (ret == -EADDRNOTAVAIL) + ret = 0; if (ret) goto out; } @@ -583,8 +593,10 @@ out: int vxlan_vnilist_update_group(struct vxlan_dev *vxlan, union vxlan_addr *old_remote_ip, union vxlan_addr *new_remote_ip, + u32 old_ifindex, u32 new_ifindex, struct netlink_ext_ack *extack) { + union vxlan_addr *oldrip, *newrip; struct list_head *headp, *hpos; struct vxlan_vni_group *vg; struct vxlan_vni_node *vent; @@ -595,17 +607,46 @@ int vxlan_vnilist_update_group(struct vxlan_dev *vxlan, headp = &vg->vni_list; list_for_each_prev(hpos, headp) { vent = list_entry(hpos, struct vxlan_vni_node, vlist); + if (vxlan_addr_any(&vent->remote_ip)) { - ret = vxlan_update_default_fdb_entry(vxlan, vent->vni, - old_remote_ip, - new_remote_ip, - extack); - if (ret) - return ret; + oldrip = old_remote_ip; + newrip = new_remote_ip; + } else { + /* A vni with its own group keeps it, but its fdb entry + * is still keyed on the device remote_ifindex. + */ + oldrip = &vent->remote_ip; + newrip = &vent->remote_ip; } + + ret = vxlan_update_default_fdb_entry(vxlan, vent->vni, + oldrip, newrip, + old_ifindex, new_ifindex, + extack); + if (ret) + goto err_unwind; } return 0; + +err_unwind: + list_for_each_continue(hpos, headp) { + vent = list_entry(hpos, struct vxlan_vni_node, vlist); + + if (vxlan_addr_any(&vent->remote_ip)) { + oldrip = old_remote_ip; + newrip = new_remote_ip; + } else { + oldrip = &vent->remote_ip; + newrip = &vent->remote_ip; + } + + vxlan_update_default_fdb_entry(vxlan, vent->vni, + newrip, oldrip, + new_ifindex, old_ifindex, + NULL); + } + return ret; } static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,