From b0e75977c6883283b55fbbdcd64d1c7111b134f2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 22 Sep 2026 18:10:55 +0000 Subject: [PATCH 1/8] vxlan: update default fdb entries when the lower device changes vxlan_changelink() only refreshed the default fdb entries when the remote IP changed, but vxlan_config_apply() also updates default_dst.remote_ifindex when the lower device changes. A changelink that only swaps the lower device therefore left the all zeros mac rdst pointing at the old ifindex: ip link add vxlan0 type vxlan id 10 group 239.1.1.1 dev eth0 ip link set dev vxlan0 type vxlan group 239.1.1.1 dev eth1 vxlan_xmit_one() uses rdst->remote_ifindex as the route oif, so traffic kept leaving eth0. VNI filter entries have the same problem, and are worse: their fdb entries are keyed on the device remote_ifindex even when the vni carries its own group, but vxlan_vnilist_update_group() only visited the vnis without one. vxlan_vni_delete_group() later looks an entry up with the current remote_ifindex, and vxlan_fdb_find_rdst() requires an exact match, so the lookup failed and the entry survived the delete. Re-adding the same vni then appended a second rdst, duplicating transmitted BUM traffic. Likewise, in vxlan_vni_update_group(), oldrip was only set when newrip was NULL, so changing an existing VNI's group appended the new rdst without deleting the old one. Set oldrip to the previous effective remote IP whenever updating an existing VNI (!create). Pass the old and new ifindex down to vxlan_update_default_fdb_entry() so the append targets the new lower device and the delete still matches the entry created for the old one, and refresh every vni rather than only those inheriting the device group. If updating the vni list fails partway through, unwind the already updated fdb entries (still deleting the new rdst even if re-adding the old one fails) so default_dst and the fdb entries do not diverge. Also run vxlan_multicast_leave() and vxlan_multicast_join() when VXLAN_F_VNIFILTER is set so per-VNI multicast memberships are migrated even when the device default remote_ip is not multicast, and make vxlan_multicast_leave_vnigrp() skip the default group and ignore -EADDRNOTAVAIL when multiple VNIs share a multicast group. The new ifindex is the one vxlan_config_apply() will commit, which is the current one when lowerdev is NULL, so that default_dst and the fdb entries can not diverge. Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260922181102.3989489-2-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 39 ++++++++++---- drivers/net/vxlan/vxlan_multicast.c | 13 ++++- drivers/net/vxlan/vxlan_private.h | 6 +++ drivers/net/vxlan/vxlan_vnifilter.c | 79 ++++++++++++++++++++++------- 4 files changed, 106 insertions(+), 31 deletions(-) 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, From d10b7b93c5e781e1fdf159d072a787583229387b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 22 Sep 2026 18:10:56 +0000 Subject: [PATCH 2/8] vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev() RTM_GETTUNNEL dumps currently run under RTNL lock, but vxlan_vnifilter_dump() also acquires rcu_read_lock(). 1) Currently vxlan_vnifilter_dump_dev() traverses vg->vni_list using list_for_each_entry_safe(). Even though RTNL is held today, writers modify vg->vni_list with list_add_rcu() and list_del_rcu(). Switch to list_for_each_entry_rcu() for proper RCU traversal and as preparation for future lockless dump support. 2) During a paginated dump, RTNL is released between dump skbs. If vxlan_vnifilter_dump_dev() returns early because VXLAN_F_VNIFILTER is not set or vg has no VNIs, cb->args[1] was not cleared. This leaked a non-zero VNI offset to subsequent devices, silently skipping their first N VNIs. Furthermore, if devices are added or removed between dump calls, ordinal device indexes can shift. Track the current device ifindex in cb->args[2] and reset cb->args[1] if the device changes. Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260922181102.3989489-3-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_vnifilter.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index 336e8128be48..0a04e8875f7d 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -333,22 +333,34 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev, struct sk_buff *skb, struct netlink_callback *cb) { - struct vxlan_vni_node *tmp, *v, *vbegin = NULL, *vend = NULL; + struct vxlan_vni_node *v, *vbegin = NULL, *vend = NULL; struct vxlan_dev *vxlan = netdev_priv(dev); struct tunnel_msg *new_tmsg, *tmsg; - int idx = 0, s_idx = cb->args[1]; struct vxlan_vni_group *vg; struct nlmsghdr *nlh; + int idx = 0, s_idx; bool dump_stats; int err = 0; - if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) + if (cb->args[2] != dev->ifindex) { + cb->args[1] = 0; + cb->args[2] = dev->ifindex; + } + s_idx = cb->args[1]; + + if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) { + cb->args[1] = 0; + cb->args[2] = 0; return -EINVAL; + } /* RCU needed because of the vni locking rules (rcu || rtnl) */ vg = rcu_dereference(vxlan->vnigrp); - if (!vg || !vg->num_vnis) + if (!vg || !vg->num_vnis) { + cb->args[1] = 0; + cb->args[2] = 0; return 0; + } tmsg = nlmsg_data(cb->nlh); dump_stats = !!(tmsg->flags & TUNNEL_MSG_FLAG_STATS); @@ -362,7 +374,7 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev, new_tmsg->family = PF_BRIDGE; new_tmsg->ifindex = dev->ifindex; - list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { + list_for_each_entry_rcu(v, &vg->vni_list, vlist) { if (idx < s_idx) { idx++; continue; @@ -394,6 +406,7 @@ update_end: } cb->args[1] = err ? idx : 0; + cb->args[2] = err ? dev->ifindex : 0; nlmsg_end(skb, nlh); From 0e75889596aa6edcf884b0079299213213fc9611 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 22 Sep 2026 18:10:57 +0000 Subject: [PATCH 3/8] vxlan: vnifilter: signal interrupted RTM_GETTUNNEL dumps vxlan_vnifilter_dump() walks all vxlan devices of a netns, and for each one walks vg->vni_list. Both cursors are plain ordinals stored in cb->args[0] and cb->args[1], and RTNL is released between dump skbs. A concurrent __vxlan_vni_add_list(), which inserts sorted by VNI, __vxlan_vni_del_list(), or an in-place group update in vxlan_vni_update() (which splits or merges coalesced VNI ranges) shifts the second cursor, duplicating or skipping entries. Unregistering a vxlan device shifts the first one, and the partially dumped device is then skipped altogether by the "if (idx < s_idx)" test, silently losing the rest of its VNIs. Add a per-netns generation counter, bumped whenever the set of vxlan devices in the netns or any vni_list changes, and feed it to nl_dump_check_consistent() so that user space gets NLM_F_DUMP_INTR and can retry, as vxlan_mdb_dump() already does. The counter is keyed on dev_net(vxlan->dev) rather than vxlan->net, because the dump enumerates devices with for_each_netdev_rcu() in the netns the netdevice lives in, which differs from the packet i/o netns when the device was created with a separate link netns. Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260922181102.3989489-4-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 4 ++++ drivers/net/vxlan/vxlan_private.h | 9 ++++++++ drivers/net/vxlan/vxlan_vnifilter.c | 35 ++++++++++++++++++++++++----- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 7af38cdfb98e..1e5e960fce69 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -4773,6 +4773,10 @@ static int vxlan_netdevice_event(struct notifier_block *unused, struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); + if ((event == NETDEV_REGISTER || event == NETDEV_UNREGISTER) && + netif_is_vxlan(dev)) + vxlan_vnifilter_seq_inc(dev_net(dev)); + if (event == NETDEV_UNREGISTER) vxlan_handle_lowerdev_unregister(vn, dev); else if (event == NETDEV_UDP_TUNNEL_PUSH_INFO) diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h index e4ceca925bde..74d713717cd5 100644 --- a/drivers/net/vxlan/vxlan_private.h +++ b/drivers/net/vxlan/vxlan_private.h @@ -22,6 +22,8 @@ struct vxlan_net { /* sock_list is protected by rtnl lock */ struct hlist_head sock_list[PORT_HASH_SIZE]; struct notifier_block nexthop_notifier_block; + /* Generation counter for RTM_GETTUNNEL dumps */ + atomic_t vnifilter_seq; }; struct vxlan_fdb_key { @@ -177,6 +179,13 @@ vxlan_vnifilter_lookup(struct vxlan_dev *vxlan, __be32 vni) vxlan_vni_rht_params); } +static inline void vxlan_vnifilter_seq_inc(const struct net *net) +{ + struct vxlan_net *vn = net_generic(net, vxlan_net_id); + + atomic_inc(&vn->vnifilter_seq); +} + /* vxlan_core.c */ int vxlan_fdb_create(struct vxlan_dev *vxlan, const u8 *mac, union vxlan_addr *ip, diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index 0a04e8875f7d..181a7614be5c 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -410,9 +410,22 @@ update_end: nlmsg_end(skb, nlh); + nl_dump_check_consistent(cb, nlh); + return err; } +static u32 vxlan_vnifilter_base_seq(const struct net *net) +{ + const struct vxlan_net *vn = net_generic(net, vxlan_net_id); + u32 res = atomic_read(&vn->vnifilter_seq); + + /* Must not return 0 (see nl_dump_check_consistent()) */ + if (!res) + res = 0x80000000; + return res; +} + static int vxlan_vnifilter_dump(struct sk_buff *skb, struct netlink_callback *cb) { int idx = 0, err = 0, s_idx = cb->args[0]; @@ -432,6 +445,9 @@ static int vxlan_vnifilter_dump(struct sk_buff *skb, struct netlink_callback *cb } rcu_read_lock(); + + cb->seq = vxlan_vnifilter_base_seq(net); + if (tmsg->ifindex) { dev = dev_get_by_index_rcu(net, tmsg->ifindex); if (!dev) { @@ -571,8 +587,11 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan, if (ret) goto out; - if (group) + if (group) { memcpy(&vninode->remote_ip, group, sizeof(vninode->remote_ip)); + if (!create) + vxlan_vnifilter_seq_inc(dev_net(vxlan->dev)); + } if (vxlan->dev->flags & IFF_UP) { if (vxlan_addr_multicast(&old_remote_ip) && @@ -719,7 +738,8 @@ static int vxlan_vni_update(struct vxlan_dev *vxlan, return 0; } -static void __vxlan_vni_add_list(struct vxlan_vni_group *vg, +static void __vxlan_vni_add_list(struct vxlan_dev *vxlan, + struct vxlan_vni_group *vg, struct vxlan_vni_node *v) { struct list_head *headp, *hpos; @@ -735,13 +755,16 @@ static void __vxlan_vni_add_list(struct vxlan_vni_group *vg, } list_add_rcu(&v->vlist, hpos); vg->num_vnis++; + vxlan_vnifilter_seq_inc(dev_net(vxlan->dev)); } -static void __vxlan_vni_del_list(struct vxlan_vni_group *vg, +static void __vxlan_vni_del_list(struct vxlan_dev *vxlan, + struct vxlan_vni_group *vg, struct vxlan_vni_node *v) { list_del_rcu(&v->vlist); vg->num_vnis--; + vxlan_vnifilter_seq_inc(dev_net(vxlan->dev)); } static struct vxlan_vni_node *vxlan_vni_alloc(struct vxlan_dev *vxlan, @@ -803,7 +826,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan, return err; } - __vxlan_vni_add_list(vg, vninode); + __vxlan_vni_add_list(vxlan, vg, vninode); if (vxlan->dev->flags & IFF_UP) vxlan_vs_add_del_vninode(vxlan, vninode, false); @@ -849,7 +872,7 @@ static int vxlan_vni_del(struct vxlan_dev *vxlan, if (err) goto out; - __vxlan_vni_del_list(vg, vninode); + __vxlan_vni_del_list(vxlan, vg, vninode); vxlan_vnifilter_notify(vxlan, vninode, RTM_DELTUNNEL); @@ -963,7 +986,7 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan) #if IS_ENABLED(CONFIG_IPV6) hlist_del_init_rcu(&v->hlist6.hlist); #endif - __vxlan_vni_del_list(vg, v); + __vxlan_vni_del_list(vxlan, vg, v); vxlan_vnifilter_notify(vxlan, v, RTM_DELTUNNEL); call_rcu(&v->rcu, vxlan_vni_node_rcu_free); } From 3ea463294b1b0fd14c1837019d920f69683aca74 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 22 Sep 2026 18:10:58 +0000 Subject: [PATCH 4/8] vxlan: pass vxlan_config pointer to helper functions In preparation for converting vxlan->cfg to an RCU-protected pointer, refactor internal helper functions in the RX, TX, MDB, and VNIFILTER paths to accept a pointer to struct vxlan_config (or pass flags/ saddr_family where appropriate) rather than directly accessing vxlan->cfg. No functional changes. Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260922181102.3989489-5-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 345 +++++++++++++++------------- drivers/net/vxlan/vxlan_mdb.c | 21 +- drivers/net/vxlan/vxlan_private.h | 8 +- drivers/net/vxlan/vxlan_vnifilter.c | 5 +- 4 files changed, 207 insertions(+), 172 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 1e5e960fce69..95d26d9a22ad 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -377,14 +377,15 @@ static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN]) /* Look up Ethernet address in forwarding table */ static struct vxlan_fdb *vxlan_find_mac_rcu(struct vxlan_dev *vxlan, + const struct vxlan_config *cfg, const u8 *mac, __be32 vni) { struct vxlan_fdb_key key; memset(&key, 0, sizeof(key)); memcpy(key.eth_addr, mac, sizeof(key.eth_addr)); - if (!(vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA)) - key.vni = vxlan->default_dst.remote_vni; + if (!(cfg->flags & VXLAN_F_COLLECT_METADATA)) + key.vni = cfg->vni; else key.vni = vni; @@ -393,11 +394,12 @@ static struct vxlan_fdb *vxlan_find_mac_rcu(struct vxlan_dev *vxlan, } static struct vxlan_fdb *vxlan_find_mac_tx(struct vxlan_dev *vxlan, + const struct vxlan_config *cfg, const u8 *mac, __be32 vni) { struct vxlan_fdb *f; - f = vxlan_find_mac_rcu(vxlan, mac, vni); + f = vxlan_find_mac_rcu(vxlan, cfg, mac, vni); if (f) { unsigned long now = jiffies; @@ -416,7 +418,7 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan, lockdep_assert_held_once(&vxlan->hash_lock); rcu_read_lock(); - f = vxlan_find_mac_rcu(vxlan, mac, vni); + f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, mac, vni); rcu_read_unlock(); return f; @@ -457,7 +459,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni, rcu_read_lock(); - f = vxlan_find_mac_rcu(vxlan, eth_addr, vni); + f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, eth_addr, vni); if (f) rdst = first_remote_rcu(f); if (!rdst) { @@ -1416,7 +1418,7 @@ static int vxlan_fdb_get(struct sk_buff *skb, rcu_read_lock(); - f = vxlan_find_mac_rcu(vxlan, addr, vni); + f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, addr, vni); if (!f) { NL_SET_ERR_MSG(extack, "Fdb entry not found"); err = -ENOENT; @@ -1434,6 +1436,7 @@ errout: * and Tunnel endpoint. */ static enum skb_drop_reason vxlan_snoop(struct net_device *dev, + const struct vxlan_config *cfg, union vxlan_addr *src_ip, const u8 *src_mac, u32 src_ifindex, __be32 vni) @@ -1452,7 +1455,7 @@ static enum skb_drop_reason vxlan_snoop(struct net_device *dev, ifindex = src_ifindex; #endif - f = vxlan_find_mac_rcu(vxlan, src_mac, vni); + f = vxlan_find_mac_rcu(vxlan, cfg, src_mac, vni); if (likely(f)) { struct vxlan_rdst *rdst = first_remote_rcu(f); unsigned long now = jiffies; @@ -1488,9 +1491,9 @@ static enum skb_drop_reason vxlan_snoop(struct net_device *dev, vxlan_fdb_update(vxlan, src_mac, src_ip, NUD_REACHABLE, NLM_F_EXCL|NLM_F_CREATE, - vxlan->cfg.dst_port, + cfg->dst_port, vni, - vxlan->default_dst.remote_vni, + cfg->vni, ifindex, NTF_SELF, 0, true, NULL); spin_unlock(&vxlan->hash_lock); } @@ -1598,6 +1601,7 @@ static void vxlan_parse_gbp_hdr(struct sk_buff *skb, u32 vxflags, } static enum skb_drop_reason vxlan_set_mac(struct vxlan_dev *vxlan, + const struct vxlan_config *cfg, struct vxlan_sock *vs, struct sk_buff *skb, __be32 vni) { @@ -1623,10 +1627,10 @@ static enum skb_drop_reason vxlan_set_mac(struct vxlan_dev *vxlan, #endif } - if (!(vxlan->cfg.flags & VXLAN_F_LEARN)) + if (!(cfg->flags & VXLAN_F_LEARN)) return SKB_NOT_DROPPED_YET; - return vxlan_snoop(skb->dev, &saddr, eth_hdr(skb)->h_source, + return vxlan_snoop(skb->dev, cfg, &saddr, eth_hdr(skb)->h_source, ifindex, vni); } @@ -1657,18 +1661,21 @@ static bool vxlan_ecn_decapsulate(struct vxlan_sock *vs, void *oiph, static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) { struct vxlan_vni_node *vninode = NULL; - const struct vxlanhdr *vh; - struct vxlan_dev *vxlan; - struct vxlan_sock *vs; - struct vxlan_metadata _md; - struct vxlan_metadata *md = &_md; __be16 protocol = htons(ETH_P_TEB); + const struct vxlan_config *cfg; enum skb_drop_reason reason; + const struct vxlanhdr *vh; + struct vxlan_metadata *md; + struct vxlan_metadata _md; + struct vxlan_dev *vxlan; bool raw_proto = false; - void *oiph; + struct vxlan_sock *vs; __be32 vni = 0; + void *oiph; int nh; + md = &_md; + /* Need UDP and VXLAN header to be present */ reason = pskb_may_pull_reason(skb, VXLAN_HLEN); if (reason) @@ -1696,8 +1703,9 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) goto drop; } - if (vh->vx_flags & vxlan->cfg.reserved_bits.vx_flags || - vh->vx_vni & vxlan->cfg.reserved_bits.vx_vni) { + cfg = &vxlan->cfg; + if (vh->vx_flags & cfg->reserved_bits.vx_flags || + vh->vx_vni & cfg->reserved_bits.vx_vni) { /* If the header uses bits besides those enabled by the * netdevice configuration, treat this as a malformed packet. * This behavior diverges from VXLAN RFC (RFC7348) which @@ -1709,12 +1717,12 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) reason = SKB_DROP_REASON_VXLAN_INVALID_HDR; DEV_STATS_INC(vxlan->dev, rx_frame_errors); DEV_STATS_INC(vxlan->dev, rx_errors); - vxlan_vnifilter_count(vxlan, vni, vninode, + vxlan_vnifilter_count(vxlan, cfg, vni, vninode, VXLAN_VNI_STATS_RX_ERRORS, 0); goto drop; } - if (vxlan->cfg.flags & VXLAN_F_GPE) { + if (cfg->flags & VXLAN_F_GPE) { if (!vxlan_parse_gpe_proto(vh, &protocol)) goto drop; raw_proto = true; @@ -1726,8 +1734,8 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) goto drop; } - if (vxlan->cfg.flags & VXLAN_F_REMCSUM_RX) { - reason = vxlan_remcsum(skb, vxlan->cfg.flags); + if (cfg->flags & VXLAN_F_REMCSUM_RX) { + reason = vxlan_remcsum(skb, cfg->flags); if (unlikely(reason)) goto drop; } @@ -1752,14 +1760,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) memset(md, 0, sizeof(*md)); } - if (vxlan->cfg.flags & VXLAN_F_GBP) - vxlan_parse_gbp_hdr(skb, vxlan->cfg.flags, md); + if (cfg->flags & VXLAN_F_GBP) + vxlan_parse_gbp_hdr(skb, cfg->flags, md); /* Note that GBP and GPE can never be active together. This is * ensured in vxlan_dev_configure. */ if (!raw_proto) { - reason = vxlan_set_mac(vxlan, vs, skb, vni); + reason = vxlan_set_mac(vxlan, cfg, vs, skb, vni); if (reason) goto drop; } else { @@ -1780,7 +1788,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) if (reason) { DEV_STATS_INC(vxlan->dev, rx_length_errors); DEV_STATS_INC(vxlan->dev, rx_errors); - vxlan_vnifilter_count(vxlan, vni, vninode, + vxlan_vnifilter_count(vxlan, cfg, vni, vninode, VXLAN_VNI_STATS_RX_ERRORS, 0); goto drop; } @@ -1792,7 +1800,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) reason = SKB_DROP_REASON_IP_TUNNEL_ECN; DEV_STATS_INC(vxlan->dev, rx_frame_errors); DEV_STATS_INC(vxlan->dev, rx_errors); - vxlan_vnifilter_count(vxlan, vni, vninode, + vxlan_vnifilter_count(vxlan, cfg, vni, vninode, VXLAN_VNI_STATS_RX_ERRORS, 0); goto drop; } @@ -1802,14 +1810,15 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) if (unlikely(!(vxlan->dev->flags & IFF_UP))) { rcu_read_unlock(); dev_dstats_rx_dropped(vxlan->dev); - vxlan_vnifilter_count(vxlan, vni, vninode, + vxlan_vnifilter_count(vxlan, cfg, vni, vninode, VXLAN_VNI_STATS_RX_DROPS, 0); reason = SKB_DROP_REASON_DEV_READY; goto drop; } dev_dstats_rx_add(vxlan->dev, skb->len); - vxlan_vnifilter_count(vxlan, vni, vninode, VXLAN_VNI_STATS_RX, skb->len); + vxlan_vnifilter_count(vxlan, cfg, vni, vninode, VXLAN_VNI_STATS_RX, + skb->len); gro_cells_receive(&vxlan->gro_cells, skb); rcu_read_unlock(); @@ -1850,7 +1859,7 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb) return 0; } -static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) +static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags) { struct neigh_table *tbl = arp_table(dev_net(dev)); struct vxlan_dev *vxlan = netdev_priv(dev); @@ -1864,7 +1873,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) if (!pskb_network_may_pull(skb, arp_hdr_len(dev))) { dev_dstats_tx_dropped(dev); - vxlan_vnifilter_count(vxlan, vni, NULL, + vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); goto out; } @@ -1905,7 +1914,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) neigh_ha_snapshot(ha, n, n->dev); rcu_read_lock(); - f = vxlan_find_mac_tx(vxlan, ha, vni); + f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, ha, vni); if (f) rdst = first_remote_rcu(f); if (rdst && vxlan_addr_any(&rdst->remote_ip)) { @@ -1931,11 +1940,11 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) if (netif_rx(reply) == NET_RX_DROP) { dev_dstats_rx_dropped(dev); - vxlan_vnifilter_count(vxlan, vni, NULL, + vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL, VXLAN_VNI_STATS_RX_DROPS, 0); } - } else if (vxlan->cfg.flags & VXLAN_F_L3MISS) { + } else if (flags & VXLAN_F_L3MISS) { union vxlan_addr ipa = { .sin.sin_addr.s_addr = tip, .sin.sin_family = AF_INET, @@ -2045,7 +2054,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request, return reply; } -static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) +static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags) { struct vxlan_dev *vxlan = netdev_priv(dev); const struct in6_addr *daddr; @@ -2079,7 +2088,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) } neigh_ha_snapshot(ha, n, n->dev); - f = vxlan_find_mac_tx(vxlan, ha, vni); + f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, ha, vni); if (f) rdst = first_remote_rcu(f); if (rdst && vxlan_addr_any(&rdst->remote_ip)) { @@ -2098,10 +2107,10 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) if (netif_rx(reply) == NET_RX_DROP) { dev_dstats_rx_dropped(dev); - vxlan_vnifilter_count(vxlan, vni, NULL, + vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL, VXLAN_VNI_STATS_RX_DROPS, 0); } - } else if (vxlan->cfg.flags & VXLAN_F_L3MISS) { + } else if (flags & VXLAN_F_L3MISS) { union vxlan_addr ipa = { .sin6.sin6_addr = msg->target, .sin6.sin6_family = AF_INET6, @@ -2117,9 +2126,9 @@ out: } #endif -static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) +static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb, + const struct vxlan_config *cfg) { - struct vxlan_dev *vxlan = netdev_priv(dev); struct neigh_table *tbl; struct neighbour *n; @@ -2138,7 +2147,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) tbl = arp_table(dev_net(dev)); pip = ip_hdr(skb); n = neigh_lookup(tbl, &pip->daddr, dev); - if (!n && (vxlan->cfg.flags & VXLAN_F_L3MISS)) { + if (!n && (cfg->flags & VXLAN_F_L3MISS)) { union vxlan_addr ipa = { .sin.sin_addr.s_addr = pip->daddr, .sin.sin_family = AF_INET, @@ -2166,7 +2175,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) tbl = nd_table(dev_net(dev)); pip6 = ipv6_hdr(skb); n = neigh_lookup(tbl, &pip6->daddr, dev); - if (!n && (vxlan->cfg.flags & VXLAN_F_L3MISS)) { + if (!n && (cfg->flags & VXLAN_F_L3MISS)) { union vxlan_addr ipa = { .sin6.sin6_addr = pip6->daddr, .sin6.sin6_family = AF_INET6, @@ -2284,20 +2293,21 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst, /* Bypass encapsulation if the destination is local */ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, - struct vxlan_dev *dst_vxlan, __be32 vni, - bool snoop) + struct vxlan_dev *dst_vxlan, + const struct vxlan_config *src_cfg, + __be32 vni, bool snoop) { + const struct vxlan_config *dst_cfg = &dst_vxlan->cfg; union vxlan_addr loopback; - union vxlan_addr *remote_ip = &dst_vxlan->default_dst.remote_ip; unsigned int len = skb->len; - struct net_device *dev; + struct net_device *dev = dst_vxlan->dev; skb->pkt_type = PACKET_HOST; skb->encapsulation = 0; - skb->dev = dst_vxlan->dev; + skb->dev = dev; __skb_pull(skb, skb_network_offset(skb)); - if (remote_ip->sa.sa_family == AF_INET) { + if (dst_vxlan->default_dst.remote_ip.sa.sa_family == AF_INET) { loopback.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); loopback.sa.sa_family = AF_INET; #if IS_ENABLED(CONFIG_IPV6) @@ -2308,26 +2318,25 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, } rcu_read_lock(); - dev = skb->dev; if (unlikely(!(dev->flags & IFF_UP))) { kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY); goto drop; } - if ((dst_vxlan->cfg.flags & VXLAN_F_LEARN) && snoop) - vxlan_snoop(dev, &loopback, eth_hdr(skb)->h_source, 0, vni); + if ((dst_cfg->flags & VXLAN_F_LEARN) && snoop) + vxlan_snoop(dev, dst_cfg, &loopback, eth_hdr(skb)->h_source, 0, vni); dev_dstats_tx_add(src_vxlan->dev, len); - vxlan_vnifilter_count(src_vxlan, vni, NULL, VXLAN_VNI_STATS_TX, len); + vxlan_vnifilter_count(src_vxlan, src_cfg, vni, NULL, VXLAN_VNI_STATS_TX, len); if (__netif_rx(skb) == NET_RX_SUCCESS) { dev_dstats_rx_add(dst_vxlan->dev, len); - vxlan_vnifilter_count(dst_vxlan, vni, NULL, VXLAN_VNI_STATS_RX, + vxlan_vnifilter_count(dst_vxlan, dst_cfg, vni, NULL, VXLAN_VNI_STATS_RX, len); } else { drop: dev_dstats_rx_dropped(dev); - vxlan_vnifilter_count(dst_vxlan, vni, NULL, + vxlan_vnifilter_count(dst_vxlan, dst_cfg, vni, NULL, VXLAN_VNI_STATS_RX_DROPS, 0); } rcu_read_unlock(); @@ -2335,6 +2344,7 @@ drop: static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, struct vxlan_dev *vxlan, + const struct vxlan_config *cfg, int addr_family, __be16 dst_port, int dst_ifindex, __be32 vni, struct dst_entry *dst, @@ -2350,22 +2360,22 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, /* Bypass encapsulation if the destination is local */ if (rt_flags & RTCF_LOCAL && !(rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) && - vxlan->cfg.flags & VXLAN_F_LOCALBYPASS) { + cfg->flags & VXLAN_F_LOCALBYPASS) { struct vxlan_dev *dst_vxlan; dst_release(dst); dst_vxlan = vxlan_find_vni(vxlan->net, dst_ifindex, vni, addr_family, dst_port, - vxlan->cfg.flags); + cfg->flags); if (!dst_vxlan) { DEV_STATS_INC(dev, tx_errors); - vxlan_vnifilter_count(vxlan, vni, NULL, + vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0); kfree_skb_reason(skb, SKB_DROP_REASON_VXLAN_VNI_NOT_FOUND); return -ENOENT; } - vxlan_encap_bypass(skb, vxlan, dst_vxlan, vni, true); + vxlan_encap_bypass(skb, vxlan, dst_vxlan, cfg, vni, true); return 1; } @@ -2373,30 +2383,35 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, } void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, + const struct vxlan_config *cfg, __be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc) { - struct dst_cache *dst_cache; - struct ip_tunnel_info *info; - struct ip_tunnel_key *pkey; - struct ip_tunnel_key key; - struct vxlan_dev *vxlan = netdev_priv(dev); - const struct iphdr *old_iph; - struct vxlan_metadata _md = {}; - struct vxlan_metadata *md = &_md; unsigned int pkt_len = skb->len; + struct vxlan_metadata _md = {}; __be16 src_port = 0, dst_port; struct dst_entry *ndst = NULL; + enum skb_drop_reason reason; + struct dst_cache *dst_cache; + const struct iphdr *old_iph; + struct ip_tunnel_info *info; + struct ip_tunnel_key *pkey; + struct vxlan_metadata *md; + struct ip_tunnel_key key; + struct vxlan_dev *vxlan; + u32 flags = cfg->flags; + bool udp_sum = false; + bool no_eth_encap; int addr_family; + bool use_cache; + __be32 vni = 0; __u8 tos, ttl; int ifindex; int err = 0; - u32 flags = vxlan->cfg.flags; - bool use_cache; - bool udp_sum = false; - bool xnet = !net_eq(vxlan->net, dev_net(vxlan->dev)); - enum skb_drop_reason reason; - bool no_eth_encap; - __be32 vni = 0; + bool xnet; + + vxlan = netdev_priv(dev); + xnet = !net_eq(vxlan->net, dev_net(vxlan->dev)); + md = &_md; no_eth_encap = flags & VXLAN_F_GPE && skb->protocol != htons(ETH_P_TEB); reason = skb_vlan_inet_prepare(skb, no_eth_encap); @@ -2416,23 +2431,23 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (vxlan_addr_any(&rdst->remote_ip)) { if (did_rsc) { /* short-circuited back to local bridge */ - vxlan_encap_bypass(skb, vxlan, vxlan, + vxlan_encap_bypass(skb, vxlan, vxlan, cfg, default_vni, true); return; } goto drop; } - addr_family = vxlan->cfg.saddr.sa.sa_family; - dst_port = rdst->remote_port ? rdst->remote_port : vxlan->cfg.dst_port; + addr_family = cfg->saddr.sa.sa_family; + dst_port = rdst->remote_port ? rdst->remote_port : cfg->dst_port; vni = (rdst->remote_vni) ? : default_vni; ifindex = rdst->remote_ifindex; if (addr_family == AF_INET) { - key.u.ipv4.src = vxlan->cfg.saddr.sin.sin_addr.s_addr; + key.u.ipv4.src = cfg->saddr.sin.sin_addr.s_addr; key.u.ipv4.dst = rdst->remote_ip.sin.sin_addr.s_addr; } else { - key.u.ipv6.src = vxlan->cfg.saddr.sin6.sin6_addr; + key.u.ipv6.src = cfg->saddr.sin6.sin6_addr; key.u.ipv6.dst = rdst->remote_ip.sin6.sin6_addr; } @@ -2441,11 +2456,11 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (flags & VXLAN_F_TTL_INHERIT) { ttl = ip_tunnel_get_ttl(old_iph, skb); } else { - ttl = vxlan->cfg.ttl; + ttl = cfg->ttl; if (!ttl && vxlan_addr_multicast(&rdst->remote_ip)) ttl = 1; } - tos = vxlan->cfg.tos; + tos = cfg->tos; if (tos == 1) tos = ip_tunnel_get_dsfield(old_iph, skb); if (tos && !info) @@ -2456,9 +2471,9 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, else udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX); #if IS_ENABLED(CONFIG_IPV6) - switch (vxlan->cfg.label_policy) { + switch (cfg->label_policy) { case VXLAN_LABEL_FIXED: - key.label = vxlan->cfg.label; + key.label = cfg->label; break; case VXLAN_LABEL_INHERIT: key.label = ip_tunnel_get_flowlabel(old_iph, skb); @@ -2476,7 +2491,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, } pkey = &info->key; addr_family = ip_tunnel_info_af(info); - dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port; + dst_port = info->key.tp_dst ? : cfg->dst_port; vni = tunnel_id_to_key32(info->key.tun_id); ifindex = 0; dst_cache = &info->dst_cache; @@ -2489,8 +2504,8 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, tos = info->key.tos; udp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags); } - src_port = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min, - vxlan->cfg.port_max, true); + src_port = udp_flow_src_port(dev_net(dev), skb, cfg->port_min, + cfg->port_max, true); rcu_read_lock(); if (addr_family == AF_INET) { @@ -2523,15 +2538,15 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (!info) { /* Bypass encapsulation if the destination is local */ - err = encap_bypass_if_local(skb, dev, vxlan, AF_INET, + err = encap_bypass_if_local(skb, dev, vxlan, cfg, AF_INET, dst_port, ifindex, vni, &rt->dst, rt->rt_flags); if (err) goto out_unlock; - if (vxlan->cfg.df == VXLAN_DF_SET) { + if (cfg->df == VXLAN_DF_SET) { df = htons(IP_DF); - } else if (vxlan->cfg.df == VXLAN_DF_INHERIT) { + } else if (cfg->df == VXLAN_DF_INHERIT) { struct ethhdr *eth = eth_hdr(skb); if (ntohs(eth->h_proto) == ETH_P_IPV6 || @@ -2560,7 +2575,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, unclone->key.u.ipv4.src = pkey->u.ipv4.dst; unclone->key.u.ipv4.dst = saddr; } - vxlan_encap_bypass(skb, vxlan, vxlan, vni, false); + vxlan_encap_bypass(skb, vxlan, vxlan, cfg, vni, false); dst_release(ndst); goto out_unlock; } @@ -2610,7 +2625,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (!info) { u32 rt6i_flags = dst_rt6_info(ndst)->rt6i_flags; - err = encap_bypass_if_local(skb, dev, vxlan, AF_INET6, + err = encap_bypass_if_local(skb, dev, vxlan, cfg, AF_INET6, dst_port, ifindex, vni, ndst, rt6i_flags); if (err) @@ -2634,7 +2649,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, unclone->key.u.ipv6.dst = saddr; } - vxlan_encap_bypass(skb, vxlan, vxlan, vni, false); + vxlan_encap_bypass(skb, vxlan, vxlan, cfg, vni, false); dst_release(ndst); goto out_unlock; } @@ -2655,14 +2670,14 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ip6cb_flags); #endif } - vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX, pkt_len); + vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX, pkt_len); out_unlock: rcu_read_unlock(); return; drop: dev_dstats_tx_dropped(dev); - vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); + vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); kfree_skb_reason(skb, reason); return; @@ -2674,11 +2689,12 @@ tx_error: DEV_STATS_INC(dev, tx_carrier_errors); dst_release(ndst); DEV_STATS_INC(dev, tx_errors); - vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0); + vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0); kfree_skb_reason(skb, reason); } static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev, + const struct vxlan_config *cfg, struct vxlan_fdb *f, __be32 vni, bool did_rsc) { struct vxlan_rdst nh_rdst; @@ -2695,7 +2711,7 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev, do_xmit = vxlan_fdb_nh_path_select(nh, hash, &nh_rdst); if (likely(do_xmit)) - vxlan_xmit_one(skb, dev, vni, &nh_rdst, did_rsc); + vxlan_xmit_one(skb, dev, cfg, vni, &nh_rdst, did_rsc); else goto drop; @@ -2703,15 +2719,15 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev, drop: dev_dstats_tx_dropped(dev); - vxlan_vnifilter_count(netdev_priv(dev), vni, NULL, + vxlan_vnifilter_count(netdev_priv(dev), cfg, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); dev_kfree_skb(skb); } static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev, - u32 nhid, __be32 vni) + u32 nhid, __be32 vni, + const struct vxlan_config *cfg) { - struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_rdst nh_rdst; struct nexthop *nh; bool do_xmit; @@ -2729,11 +2745,11 @@ static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev, do_xmit = vxlan_fdb_nh_path_select(nh, hash, &nh_rdst); rcu_read_unlock(); - if (vxlan->cfg.saddr.sa.sa_family != nh_rdst.remote_ip.sa.sa_family) + if (cfg->saddr.sa.sa_family != nh_rdst.remote_ip.sa.sa_family) goto drop; if (likely(do_xmit)) - vxlan_xmit_one(skb, dev, vni, &nh_rdst, false); + vxlan_xmit_one(skb, dev, cfg, vni, &nh_rdst, false); else goto drop; @@ -2741,7 +2757,7 @@ static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev, drop: dev_dstats_tx_dropped(dev); - vxlan_vnifilter_count(netdev_priv(dev), vni, NULL, + vxlan_vnifilter_count(netdev_priv(dev), cfg, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); dev_kfree_skb(skb); return NETDEV_TX_OK; @@ -2758,34 +2774,41 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_rdst *rdst, *fdst = NULL; const struct ip_tunnel_info *info; + const struct vxlan_config *cfg; + __be32 default_vni; struct vxlan_fdb *f; struct ethhdr *eth; __be32 vni = 0; - u32 nhid = 0; bool did_rsc; + u32 nhid = 0; + u32 flags; + + cfg = &vxlan->cfg; + flags = cfg->flags; + default_vni = cfg->vni; info = skb_tunnel_info(skb); skb_reset_mac_header(skb); - if (vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) { + if (flags & VXLAN_F_COLLECT_METADATA) { if (info && info->mode & IP_TUNNEL_INFO_BRIDGE && info->mode & IP_TUNNEL_INFO_TX) { vni = tunnel_id_to_key32(info->key.tun_id); nhid = info->key.nhid; } else { if (info && info->mode & IP_TUNNEL_INFO_TX) - vxlan_xmit_one(skb, dev, vni, NULL, false); + vxlan_xmit_one(skb, dev, cfg, vni, NULL, false); else kfree_skb_reason(skb, SKB_DROP_REASON_TUNNEL_TXINFO); return NETDEV_TX_OK; } } - if (vxlan->cfg.flags & VXLAN_F_PROXY) { + if (flags & VXLAN_F_PROXY) { eth = eth_hdr(skb); if (ntohs(eth->h_proto) == ETH_P_ARP) - return arp_reduce(dev, skb, vni); + return arp_reduce(dev, skb, vni, flags); #if IS_ENABLED(CONFIG_IPV6) else if (ntohs(eth->h_proto) == ETH_P_IPV6 && pskb_network_may_pull(skb, sizeof(struct ipv6hdr) + @@ -2795,23 +2818,23 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) if (m->icmph.icmp6_code == 0 && m->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) - return neigh_reduce(dev, skb, vni); + return neigh_reduce(dev, skb, vni, flags); } #endif } if (nhid) - return vxlan_xmit_nhid(skb, dev, nhid, vni); + return vxlan_xmit_nhid(skb, dev, nhid, vni, cfg); - if (vxlan->cfg.flags & VXLAN_F_MDB) { + if (flags & VXLAN_F_MDB) { struct vxlan_mdb_entry *mdb_entry; rcu_read_lock(); - mdb_entry = vxlan_mdb_entry_skb_get(vxlan, skb, vni); + mdb_entry = vxlan_mdb_entry_skb_get(vxlan, cfg, skb, vni); if (mdb_entry) { netdev_tx_t ret; - ret = vxlan_mdb_xmit(vxlan, mdb_entry, skb); + ret = vxlan_mdb_xmit(vxlan, cfg, mdb_entry, skb); rcu_read_unlock(); return ret; } @@ -2820,27 +2843,27 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) eth = eth_hdr(skb); rcu_read_lock(); - f = vxlan_find_mac_tx(vxlan, eth->h_dest, vni); + f = vxlan_find_mac_tx(vxlan, cfg, eth->h_dest, vni); did_rsc = false; - if (f && (f->flags & NTF_ROUTER) && (vxlan->cfg.flags & VXLAN_F_RSC) && + if (f && (f->flags & NTF_ROUTER) && (flags & VXLAN_F_RSC) && (ntohs(eth->h_proto) == ETH_P_IP || ntohs(eth->h_proto) == ETH_P_IPV6)) { - did_rsc = route_shortcircuit(dev, skb); + did_rsc = route_shortcircuit(dev, skb, cfg); eth = eth_hdr(skb); if (did_rsc) - f = vxlan_find_mac_tx(vxlan, eth->h_dest, vni); + f = vxlan_find_mac_tx(vxlan, cfg, eth->h_dest, vni); } if (f == NULL) { - f = vxlan_find_mac_tx(vxlan, all_zeros_mac, vni); + f = vxlan_find_mac_tx(vxlan, cfg, all_zeros_mac, vni); if (f == NULL) { - if ((vxlan->cfg.flags & VXLAN_F_L2MISS) && + if ((flags & VXLAN_F_L2MISS) && !is_multicast_ether_addr(eth->h_dest)) vxlan_fdb_miss(vxlan, eth->h_dest); dev_dstats_tx_dropped(dev); - vxlan_vnifilter_count(vxlan, vni, NULL, + vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET); goto out; @@ -2848,8 +2871,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) } if (rcu_access_pointer(f->nh)) { - vxlan_xmit_nh(skb, dev, f, - (vni ? : vxlan->default_dst.remote_vni), did_rsc); + vxlan_xmit_nh(skb, dev, cfg, f, + (vni ? : default_vni), did_rsc); } else { list_for_each_entry_rcu(rdst, &f->remotes, list) { struct sk_buff *skb1; @@ -2860,10 +2883,10 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) } skb1 = skb_clone(skb, GFP_ATOMIC); if (skb1) - vxlan_xmit_one(skb1, dev, vni, rdst, did_rsc); + vxlan_xmit_one(skb1, dev, cfg, vni, rdst, did_rsc); } if (fdst) - vxlan_xmit_one(skb, dev, vni, fdst, did_rsc); + vxlan_xmit_one(skb, dev, cfg, vni, fdst, did_rsc); else kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET); } @@ -3736,7 +3759,7 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan) } int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan, - struct vxlan_config *conf, __be32 vni) + const struct vxlan_config *conf, __be32 vni) { struct vxlan_net *vn = net_generic(src_net, vxlan_net_id); struct vxlan_dev *tmp; @@ -4617,10 +4640,10 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) { const struct vxlan_dev *vxlan = netdev_priv(dev); const struct vxlan_rdst *dst = &vxlan->default_dst; - struct ifla_vxlan_port_range ports = { - .low = htons(vxlan->cfg.port_min), - .high = htons(vxlan->cfg.port_max), - }; + struct ifla_vxlan_port_range ports; + const struct vxlan_config *cfg; + + cfg = &vxlan->cfg; if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst->remote_vni))) goto nla_put_failure; @@ -4642,79 +4665,81 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) if (dst->remote_ifindex && nla_put_u32(skb, IFLA_VXLAN_LINK, dst->remote_ifindex)) goto nla_put_failure; - if (!vxlan_addr_any(&vxlan->cfg.saddr)) { - if (vxlan->cfg.saddr.sa.sa_family == AF_INET) { + if (!vxlan_addr_any(&cfg->saddr)) { + if (cfg->saddr.sa.sa_family == AF_INET) { if (nla_put_in_addr(skb, IFLA_VXLAN_LOCAL, - vxlan->cfg.saddr.sin.sin_addr.s_addr)) + cfg->saddr.sin.sin_addr.s_addr)) goto nla_put_failure; #if IS_ENABLED(CONFIG_IPV6) } else { if (nla_put_in6_addr(skb, IFLA_VXLAN_LOCAL6, - &vxlan->cfg.saddr.sin6.sin6_addr)) + &cfg->saddr.sin6.sin6_addr)) goto nla_put_failure; #endif } } - if (nla_put_u8(skb, IFLA_VXLAN_TTL, vxlan->cfg.ttl) || + if (nla_put_u8(skb, IFLA_VXLAN_TTL, cfg->ttl) || nla_put_u8(skb, IFLA_VXLAN_TTL_INHERIT, - !!(vxlan->cfg.flags & VXLAN_F_TTL_INHERIT)) || - nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan->cfg.tos) || - nla_put_u8(skb, IFLA_VXLAN_DF, vxlan->cfg.df) || - nla_put_be32(skb, IFLA_VXLAN_LABEL, vxlan->cfg.label) || - nla_put_u32(skb, IFLA_VXLAN_LABEL_POLICY, vxlan->cfg.label_policy) || + !!(cfg->flags & VXLAN_F_TTL_INHERIT)) || + nla_put_u8(skb, IFLA_VXLAN_TOS, cfg->tos) || + nla_put_u8(skb, IFLA_VXLAN_DF, cfg->df) || + nla_put_be32(skb, IFLA_VXLAN_LABEL, cfg->label) || + nla_put_u32(skb, IFLA_VXLAN_LABEL_POLICY, cfg->label_policy) || nla_put_u8(skb, IFLA_VXLAN_LEARNING, - !!(vxlan->cfg.flags & VXLAN_F_LEARN)) || + !!(cfg->flags & VXLAN_F_LEARN)) || nla_put_u8(skb, IFLA_VXLAN_PROXY, - !!(vxlan->cfg.flags & VXLAN_F_PROXY)) || + !!(cfg->flags & VXLAN_F_PROXY)) || nla_put_u8(skb, IFLA_VXLAN_RSC, - !!(vxlan->cfg.flags & VXLAN_F_RSC)) || + !!(cfg->flags & VXLAN_F_RSC)) || nla_put_u8(skb, IFLA_VXLAN_L2MISS, - !!(vxlan->cfg.flags & VXLAN_F_L2MISS)) || + !!(cfg->flags & VXLAN_F_L2MISS)) || nla_put_u8(skb, IFLA_VXLAN_L3MISS, - !!(vxlan->cfg.flags & VXLAN_F_L3MISS)) || + !!(cfg->flags & VXLAN_F_L3MISS)) || nla_put_u8(skb, IFLA_VXLAN_COLLECT_METADATA, - !!(vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA)) || - nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan->cfg.age_interval) || - nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->cfg.addrmax) || - nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->cfg.dst_port) || + !!(cfg->flags & VXLAN_F_COLLECT_METADATA)) || + nla_put_u32(skb, IFLA_VXLAN_AGEING, cfg->age_interval) || + nla_put_u32(skb, IFLA_VXLAN_LIMIT, cfg->addrmax) || + nla_put_be16(skb, IFLA_VXLAN_PORT, cfg->dst_port) || nla_put_u8(skb, IFLA_VXLAN_UDP_CSUM, - !(vxlan->cfg.flags & VXLAN_F_UDP_ZERO_CSUM_TX)) || + !(cfg->flags & VXLAN_F_UDP_ZERO_CSUM_TX)) || nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, - !!(vxlan->cfg.flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) || + !!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) || nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, - !!(vxlan->cfg.flags & VXLAN_F_UDP_ZERO_CSUM6_RX)) || + !!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM6_RX)) || nla_put_u8(skb, IFLA_VXLAN_REMCSUM_TX, - !!(vxlan->cfg.flags & VXLAN_F_REMCSUM_TX)) || + !!(cfg->flags & VXLAN_F_REMCSUM_TX)) || nla_put_u8(skb, IFLA_VXLAN_REMCSUM_RX, - !!(vxlan->cfg.flags & VXLAN_F_REMCSUM_RX)) || + !!(cfg->flags & VXLAN_F_REMCSUM_RX)) || nla_put_u8(skb, IFLA_VXLAN_LOCALBYPASS, - !!(vxlan->cfg.flags & VXLAN_F_LOCALBYPASS))) + !!(cfg->flags & VXLAN_F_LOCALBYPASS))) goto nla_put_failure; + ports.low = htons(cfg->port_min); + ports.high = htons(cfg->port_max); if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports)) goto nla_put_failure; - if (vxlan->cfg.flags & VXLAN_F_GBP && + if (cfg->flags & VXLAN_F_GBP && nla_put_flag(skb, IFLA_VXLAN_GBP)) goto nla_put_failure; - if (vxlan->cfg.flags & VXLAN_F_GPE && + if (cfg->flags & VXLAN_F_GPE && nla_put_flag(skb, IFLA_VXLAN_GPE)) goto nla_put_failure; - if (vxlan->cfg.flags & VXLAN_F_REMCSUM_NOPARTIAL && + if (cfg->flags & VXLAN_F_REMCSUM_NOPARTIAL && nla_put_flag(skb, IFLA_VXLAN_REMCSUM_NOPARTIAL)) goto nla_put_failure; - if (vxlan->cfg.flags & VXLAN_F_VNIFILTER && + if (cfg->flags & VXLAN_F_VNIFILTER && nla_put_u8(skb, IFLA_VXLAN_VNIFILTER, - !!(vxlan->cfg.flags & VXLAN_F_VNIFILTER))) + !!(cfg->flags & VXLAN_F_VNIFILTER))) goto nla_put_failure; if (nla_put(skb, IFLA_VXLAN_RESERVED_BITS, - sizeof(vxlan->cfg.reserved_bits), - &vxlan->cfg.reserved_bits)) + sizeof(cfg->reserved_bits), + &cfg->reserved_bits)) goto nla_put_failure; return 0; diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index 841f42ffecb9..56ca92832833 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -165,6 +165,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan, const struct vxlan_mdb_entry *mdb_entry, const struct vxlan_mdb_remote *remote) { + const struct vxlan_config *cfg = &vxlan->cfg; struct vxlan_rdst *rd = rtnl_dereference(remote->rd); struct br_mdb_entry e; struct nlattr *nest; @@ -189,7 +190,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan, vxlan_nla_put_addr(skb, MDBA_MDB_EATTR_DST, &rd->remote_ip)) goto nest_err; - if (rd->remote_port && rd->remote_port != vxlan->cfg.dst_port && + if (rd->remote_port && rd->remote_port != cfg->dst_port && nla_put_u16(skb, MDBA_MDB_EATTR_DST_PORT, be16_to_cpu(rd->remote_port))) goto nest_err; @@ -202,7 +203,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan, nla_put_u32(skb, MDBA_MDB_EATTR_IFINDEX, rd->remote_ifindex)) goto nest_err; - if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && + if ((cfg->flags & VXLAN_F_COLLECT_METADATA) && mdb_entry->key.vni && nla_put_u32(skb, MDBA_MDB_EATTR_SRC_VNI, be32_to_cpu(mdb_entry->key.vni))) goto nest_err; @@ -613,6 +614,7 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg, { struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]); struct vxlan_dev *vxlan = netdev_priv(dev); + const struct vxlan_config *vcfg = &vxlan->cfg; memset(cfg, 0, sizeof(*cfg)); cfg->vxlan = vxlan; @@ -622,7 +624,7 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg, cfg->filter_mode = MCAST_EXCLUDE; cfg->rt_protocol = RTPROT_STATIC; cfg->remote_vni = vxlan->default_dst.remote_vni; - cfg->remote_port = vxlan->cfg.dst_port; + cfg->remote_port = vcfg->dst_port; if (entry->ifindex != dev->ifindex) { NL_SET_ERR_MSG_MOD(extack, "Port net device must be the VXLAN net device"); @@ -957,6 +959,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan, const struct vxlan_mdb_entry *mdb_entry, const struct vxlan_mdb_remote *remote) { + const struct vxlan_config *cfg = &vxlan->cfg; const struct vxlan_mdb_entry_key *group = &mdb_entry->key; struct vxlan_rdst *rd = rtnl_dereference(remote->rd); size_t nlmsg_size; @@ -978,7 +981,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan, /* MDBA_MDB_EATTR_DST */ nlmsg_size += nla_total_size(vxlan_addr_size(&rd->remote_ip)); /* MDBA_MDB_EATTR_DST_PORT */ - if (rd->remote_port && rd->remote_port != vxlan->cfg.dst_port) + if (rd->remote_port && rd->remote_port != cfg->dst_port) nlmsg_size += nla_total_size(sizeof(u16)); /* MDBA_MDB_EATTR_VNI */ if (rd->remote_vni != vxlan->default_dst.remote_vni) @@ -987,7 +990,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan, if (rd->remote_ifindex) nlmsg_size += nla_total_size(sizeof(u32)); /* MDBA_MDB_EATTR_SRC_VNI */ - if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && group->vni) + if ((cfg->flags & VXLAN_F_COLLECT_METADATA) && group->vni) nlmsg_size += nla_total_size(sizeof(u32)); return nlmsg_size; @@ -1621,6 +1624,7 @@ free: } struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, + const struct vxlan_config *cfg, struct sk_buff *skb, __be32 src_vni) { @@ -1634,7 +1638,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, /* When not in collect metadata mode, 'src_vni' is zero, but MDB * entries are stored with the VNI of the VXLAN device. */ - if (!(vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA)) + if (!(cfg->flags & VXLAN_F_COLLECT_METADATA)) src_vni = vxlan->default_dst.remote_vni; memset(&group, 0, sizeof(group)); @@ -1700,6 +1704,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, } netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan, + const struct vxlan_config *cfg, const struct vxlan_mdb_entry *mdb_entry, struct sk_buff *skb) { @@ -1721,12 +1726,12 @@ netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan, skb1 = skb_clone(skb, GFP_ATOMIC); if (skb1) - vxlan_xmit_one(skb1, vxlan->dev, src_vni, + vxlan_xmit_one(skb1, vxlan->dev, cfg, src_vni, rcu_dereference(remote->rd), false); } if (fremote) - vxlan_xmit_one(skb, vxlan->dev, src_vni, + vxlan_xmit_one(skb, vxlan->dev, cfg, src_vni, rcu_dereference(fremote->rd), false); else kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET); diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h index 74d713717cd5..a2034ad418f7 100644 --- a/drivers/net/vxlan/vxlan_private.h +++ b/drivers/net/vxlan/vxlan_private.h @@ -204,9 +204,10 @@ int vxlan_fdb_update(struct vxlan_dev *vxlan, __u32 ifindex, __u16 ndm_flags, u32 nhid, bool swdev_notify, struct netlink_ext_ack *extack); void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, + const struct vxlan_config *cfg, __be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc); int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan, - struct vxlan_config *conf, __be32 vni); + const struct vxlan_config *conf, __be32 vni); /* vxlan_vnifilter.c */ int vxlan_vnigroup_init(struct vxlan_dev *vxlan); @@ -214,7 +215,8 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan); int vxlan_vnifilter_init(void); void vxlan_vnifilter_uninit(void); -void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni, +void vxlan_vnifilter_count(struct vxlan_dev *vxlan, + const struct vxlan_config *cfg, __be32 vni, struct vxlan_vni_node *vninode, int type, unsigned int len); @@ -256,9 +258,11 @@ int vxlan_mdb_del_bulk(struct net_device *dev, struct nlattr *tb[], int vxlan_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid, u32 seq, struct netlink_ext_ack *extack); struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, + const struct vxlan_config *cfg, struct sk_buff *skb, __be32 src_vni); netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan, + const struct vxlan_config *cfg, const struct vxlan_mdb_entry *mdb_entry, struct sk_buff *skb); int vxlan_mdb_init(struct vxlan_dev *vxlan); diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index 181a7614be5c..cdda7688f3af 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -171,13 +171,14 @@ static void vxlan_vnifilter_stats_add(struct vxlan_vni_node *vninode, u64_stats_update_end(&pstats->syncp); } -void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni, +void vxlan_vnifilter_count(struct vxlan_dev *vxlan, + const struct vxlan_config *cfg, __be32 vni, struct vxlan_vni_node *vninode, int type, unsigned int len) { struct vxlan_vni_node *vnode; - if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) + if (!cfg || !(cfg->flags & VXLAN_F_VNIFILTER)) return; if (vninode) { From 0259a5f1279f7aa39d1325bc7cc1c897d65521e5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 22 Sep 2026 18:10:59 +0000 Subject: [PATCH 5/8] vxlan: move VXLAN_F_MDB to struct vxlan_dev flags VXLAN_F_MDB is an internal runtime state flag indicating whether any MDB entries are configured on the device, rather than a netlink configuration attribute. In preparation for converting vxlan->cfg to an RCU-protected pointer, move VXLAN_F_MDB from struct vxlan_config to a dedicated 'flags' field in struct vxlan_dev as VXLAN_DEV_F_MDB, using atomic bitops (set_bit(), clear_bit(), test_bit()) to avoid KCSAN data races between the TX path and RTNL operations. This avoids having to dynamically reallocate and publish a new vxlan_config structure via RCU whenever the first MDB entry is added or the last one is removed, and prevents potential memory allocation failures during MDB teardown under memory pressure. mlxsw validates cfg->flags against a deny-by-default mask, so VXLAN_F_MDB used to make mlxsw_sp_nve_vxlan_can_offload() reject a device with MDB entries as carrying an unsupported flag. Add an explicit VXLAN_DEV_F_MDB test there to keep that rejection, with a message naming the actual reason. Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260922181102.3989489-6-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c | 5 +++++ drivers/net/vxlan/vxlan_core.c | 2 +- drivers/net/vxlan/vxlan_mdb.c | 6 +++--- include/net/vxlan.h | 6 +++++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c index 52c2fe3644d4..b78aff31c98f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c @@ -92,6 +92,11 @@ static bool mlxsw_sp_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve, return false; } + if (test_bit(VXLAN_DEV_F_MDB, &vxlan->flags)) { + NL_SET_ERR_MSG_MOD(extack, "VxLAN: MDB entries are not supported"); + return false; + } + switch (cfg->saddr.sa.sa_family) { case AF_INET: if (!mlxsw_sp_nve_vxlan_ipv4_flags_check(cfg, extack)) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 95d26d9a22ad..6503571b9431 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2826,7 +2826,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) if (nhid) return vxlan_xmit_nhid(skb, dev, nhid, vni, cfg); - if (flags & VXLAN_F_MDB) { + if (test_bit(VXLAN_DEV_F_MDB, &vxlan->flags)) { struct vxlan_mdb_entry *mdb_entry; rcu_read_lock(); diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index 56ca92832833..cf606256d092 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -1219,7 +1219,7 @@ vxlan_mdb_entry_get(struct vxlan_dev *vxlan, goto err_free_entry; if (hlist_is_singular_node(&mdb_entry->mdb_node, &vxlan->mdb_list)) - vxlan->cfg.flags |= VXLAN_F_MDB; + set_bit(VXLAN_DEV_F_MDB, &vxlan->flags); return mdb_entry; @@ -1236,7 +1236,7 @@ static void vxlan_mdb_entry_put(struct vxlan_dev *vxlan, return; if (hlist_is_singular_node(&mdb_entry->mdb_node, &vxlan->mdb_list)) - vxlan->cfg.flags &= ~VXLAN_F_MDB; + clear_bit(VXLAN_DEV_F_MDB, &vxlan->flags); rhashtable_remove_fast(&vxlan->mdb_tbl, &mdb_entry->rhnode, vxlan_mdb_rht_params); @@ -1762,7 +1762,7 @@ void vxlan_mdb_fini(struct vxlan_dev *vxlan) struct vxlan_mdb_flush_desc desc = {}; vxlan_mdb_flush(vxlan, &desc); - WARN_ON_ONCE(vxlan->cfg.flags & VXLAN_F_MDB); + WARN_ON_ONCE(test_bit(VXLAN_DEV_F_MDB, &vxlan->flags)); rhashtable_free_and_destroy(&vxlan->mdb_tbl, vxlan_mdb_check_empty, NULL); } diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 7b8207505523..d323f91af236 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -300,6 +300,7 @@ struct vxlan_dev { spinlock_t hash_lock; unsigned int addrcnt; struct gro_cells gro_cells; + unsigned long flags; struct vxlan_config cfg; @@ -313,6 +314,10 @@ struct vxlan_dev { unsigned int mdb_seq; }; +enum vxlan_dev_flags { + VXLAN_DEV_F_MDB, +}; + #define VXLAN_F_LEARN 0x01 #define VXLAN_F_PROXY 0x02 #define VXLAN_F_RSC 0x04 @@ -331,7 +336,6 @@ struct vxlan_dev { #define VXLAN_F_IPV6_LINKLOCAL 0x8000 #define VXLAN_F_TTL_INHERIT 0x10000 #define VXLAN_F_VNIFILTER 0x20000 -#define VXLAN_F_MDB 0x40000 #define VXLAN_F_LOCALBYPASS 0x80000 #define VXLAN_F_MC_ROUTE 0x100000 From 0db0197ef9e1da4eb4718dff5e9568739394ff65 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 22 Sep 2026 18:11:00 +0000 Subject: [PATCH 6/8] vxlan: convert configuration to RCU protection Move 'struct vxlan_config' from an embedded structure inside 'struct vxlan_dev' to a dynamically allocated RCU-protected pointer 'vxlan->cfg'. Updating configuration via vxlan_changelink() or vxlan_dev_configure() allocates a new struct vxlan_config and publishes it with rcu_assign_pointer(), freeing the previous config with kfree_rcu(). Readers are converted to use rcu_dereference() or rtnl_dereference(). In vxlan_xmit(), acquire rcu_read_lock() to protect config access. Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260922181102.3989489-7-edumazet@google.com Signed-off-by: Jakub Kicinski --- .../mellanox/mlxsw/spectrum_nve_vxlan.c | 14 +- .../mellanox/mlxsw/spectrum_switchdev.c | 57 ++-- drivers/net/vxlan/vxlan_core.c | 274 ++++++++++++------ drivers/net/vxlan/vxlan_mdb.c | 10 +- drivers/net/vxlan/vxlan_multicast.c | 12 +- drivers/net/vxlan/vxlan_vnifilter.c | 21 +- include/net/vxlan.h | 3 +- 7 files changed, 263 insertions(+), 128 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c index b78aff31c98f..21e7b499f387 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c @@ -59,8 +59,11 @@ static bool mlxsw_sp_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve, const struct mlxsw_sp_nve_params *params, struct netlink_ext_ack *extack) { - struct vxlan_dev *vxlan = netdev_priv(params->dev); - struct vxlan_config *cfg = &vxlan->cfg; + const struct vxlan_config *cfg; + struct vxlan_dev *vxlan; + + vxlan = netdev_priv(params->dev); + cfg = rtnl_dereference(vxlan->cfg); if (vxlan_addr_multicast(&cfg->remote_ip)) { NL_SET_ERR_MSG_MOD(extack, "VxLAN: Multicast destination IP is not supported"); @@ -153,8 +156,11 @@ static void mlxsw_sp_nve_vxlan_config(const struct mlxsw_sp_nve *nve, const struct mlxsw_sp_nve_params *params, struct mlxsw_sp_nve_config *config) { - struct vxlan_dev *vxlan = netdev_priv(params->dev); - struct vxlan_config *cfg = &vxlan->cfg; + const struct vxlan_config *cfg; + struct vxlan_dev *vxlan; + + vxlan = netdev_priv(params->dev); + cfg = rtnl_dereference(vxlan->cfg); config->type = MLXSW_SP_NVE_TYPE_VXLAN; config->ttl = cfg->ttl; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index fe45e533a4b2..de60b698bea9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -2513,15 +2513,17 @@ mlxsw_sp_bridge_vlan_aware_vxlan_join(struct mlxsw_sp_bridge_device *bridge_devi { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); - struct mlxsw_sp_nve_params params = { - .type = MLXSW_SP_NVE_TYPE_VXLAN, - .vni = vxlan->cfg.vni, - .dev = vxlan_dev, - .ethertype = ethertype, - }; + struct mlxsw_sp_nve_params params; + const struct vxlan_config *cfg; struct mlxsw_sp_fid *fid; int err; + cfg = rtnl_dereference(vxlan->cfg); + params.type = MLXSW_SP_NVE_TYPE_VXLAN; + params.vni = cfg->vni; + params.dev = vxlan_dev; + params.ethertype = ethertype; + /* If the VLAN is 0, we need to find the VLAN that is configured as * PVID and egress untagged on the bridge port of the VxLAN device. * It is possible no such VLAN exists @@ -2704,15 +2706,17 @@ mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device, { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); - struct mlxsw_sp_nve_params params = { - .type = MLXSW_SP_NVE_TYPE_VXLAN, - .vni = vxlan->cfg.vni, - .dev = vxlan_dev, - .ethertype = ETH_P_8021Q, - }; + struct mlxsw_sp_nve_params params; + const struct vxlan_config *cfg; struct mlxsw_sp_fid *fid; int err; + cfg = rtnl_dereference(vxlan->cfg); + params.type = MLXSW_SP_NVE_TYPE_VXLAN; + params.vni = cfg->vni; + params.dev = vxlan_dev; + params.ethertype = ETH_P_8021Q; + fid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex); if (IS_ERR(fid)) { NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1D FID"); @@ -2933,10 +2937,13 @@ static void __mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp, const struct net_device *vxlan_dev) { struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); + const struct vxlan_config *cfg; struct mlxsw_sp_fid *fid; + cfg = rtnl_dereference(vxlan->cfg); + /* If the VxLAN device is down, then the FID does not have a VNI */ - fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg.vni); + fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, cfg->vni); if (!fid) return; @@ -3029,11 +3036,13 @@ static void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev, struct switchdev_notifier_vxlan_fdb_info info; struct vxlan_dev *vxlan = netdev_priv(dev); enum switchdev_notifier_type type; + const struct vxlan_config *cfg; + cfg = rtnl_dereference(vxlan->cfg); type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE : SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE; mlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, &info.remote_ip); - info.remote_port = vxlan->cfg.dst_port; + info.remote_port = cfg->dst_port; info.remote_vni = vni; info.remote_ifindex = 0; ether_addr_copy(info.eth_addr, mac); @@ -3236,8 +3245,10 @@ __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp, if (adding && netif_is_vxlan(dev)) { struct vxlan_dev *vxlan = netdev_priv(dev); + const struct vxlan_config *cfg; - if (!(vxlan->cfg.flags & VXLAN_F_LEARN)) + cfg = rtnl_dereference(vxlan->cfg); + if (!(cfg->flags & VXLAN_F_LEARN)) return -EINVAL; } @@ -3722,9 +3733,11 @@ mlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work * { struct vxlan_dev *vxlan = netdev_priv(switchdev_work->dev); struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info; - struct vxlan_config *cfg = &vxlan->cfg; + const struct vxlan_config *cfg; struct netlink_ext_ack *extack; + cfg = rcu_dereference_rtnl(vxlan->cfg); + extack = switchdev_notifier_info_to_extack(info); vxlan_fdb_info = container_of(info, struct switchdev_notifier_vxlan_fdb_info, @@ -3851,11 +3864,15 @@ mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp, struct netlink_ext_ack *extack) { struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); - __be32 vni = vxlan->cfg.vni; + const struct vxlan_config *cfg; struct mlxsw_sp_fid *fid; u16 old_vid; + __be32 vni; int err; + cfg = rtnl_dereference(vxlan->cfg); + vni = cfg->vni; + /* We cannot have the same VLAN as PVID and egress untagged on multiple * VxLAN devices. Note that we get this notification before the VLAN is * actually added to the bridge's database, so it is not possible for @@ -3935,12 +3952,16 @@ mlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp, const struct net_device *vxlan_dev, u16 vid) { struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); - __be32 vni = vxlan->cfg.vni; + const struct vxlan_config *cfg; struct mlxsw_sp_fid *fid; + __be32 vni; if (!netif_running(vxlan_dev)) return; + cfg = rtnl_dereference(vxlan->cfg); + vni = cfg->vni; + fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni); if (!fid) return; diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 6503571b9431..783337fc4483 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -110,20 +110,23 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, vni = 0; hlist_for_each_entry_rcu(node, vni_head(vs, vni), hlist) { + const struct vxlan_config *cfg; + if (!node->vxlan) continue; + + cfg = rcu_dereference(node->vxlan->cfg); + vnode = NULL; - if (node->vxlan->cfg.flags & VXLAN_F_VNIFILTER) { + if (cfg->flags & VXLAN_F_VNIFILTER) { vnode = vxlan_vnifilter_lookup(node->vxlan, vni); if (!vnode) continue; - } else if (node->vxlan->default_dst.remote_vni != vni) { + } else if (cfg->vni != vni) { continue; } if (IS_ENABLED(CONFIG_IPV6)) { - const struct vxlan_config *cfg = &node->vxlan->cfg; - if ((cfg->flags & VXLAN_F_IPV6_LINKLOCAL) && cfg->remote_ifindex != ifindex) continue; @@ -157,6 +160,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, u32 portid, u32 seq, int type, unsigned int flags, const struct vxlan_rdst *rdst) { + const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg); unsigned long now = jiffies; struct nda_cacheinfo ci; bool send_ip, send_eth; @@ -216,10 +220,10 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, goto nla_put_failure; if (rdst->remote_port && - rdst->remote_port != vxlan->cfg.dst_port && + rdst->remote_port != cfg->dst_port && nla_put_be16(skb, NDA_PORT, rdst->remote_port)) goto nla_put_failure; - if (rdst->remote_vni != vxlan->default_dst.remote_vni && + if (rdst->remote_vni != cfg->vni && nla_put_u32(skb, NDA_VNI, be32_to_cpu(rdst->remote_vni))) goto nla_put_failure; if (rdst->remote_ifindex && @@ -227,7 +231,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, goto nla_put_failure; } - if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && fdb->key.vni && + if ((cfg->flags & VXLAN_F_COLLECT_METADATA) && fdb->key.vni && nla_put_u32(skb, NDA_SRC_VNI, be32_to_cpu(fdb->key.vni))) goto nla_put_failure; @@ -418,7 +422,7 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan, lockdep_assert_held_once(&vxlan->hash_lock); rcu_read_lock(); - f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, mac, vni); + f = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan->cfg), mac, vni); rcu_read_unlock(); return f; @@ -459,7 +463,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni, rcu_read_lock(); - f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, eth_addr, vni); + f = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan->cfg), eth_addr, vni); if (f) rdst = first_remote_rcu(f); if (!rdst) { @@ -865,12 +869,13 @@ int vxlan_fdb_create(struct vxlan_dev *vxlan, u32 nhid, struct vxlan_fdb **fdb, struct netlink_ext_ack *extack) { + const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg); struct vxlan_rdst *rd = NULL; struct vxlan_fdb *f; int rc; - if (vxlan->cfg.addrmax && - vxlan->addrcnt >= vxlan->cfg.addrmax) + if (cfg->addrmax && + vxlan->addrcnt >= cfg->addrmax) return -ENOSPC; netdev_dbg(vxlan->dev, "add %pM -> %pIS\n", mac, ip); @@ -1156,6 +1161,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan, __be32 *vni, u32 *ifindex, u32 *nhid, struct netlink_ext_ack *extack) { + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); struct net *net = dev_net(vxlan->dev); int err; @@ -1172,7 +1178,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan, return err; } } else { - union vxlan_addr *remote = &vxlan->default_dst.remote_ip; + const union vxlan_addr *remote = &cfg->remote_ip; if (remote->sa.sa_family == AF_INET) { ip->sin.sin_addr.s_addr = htonl(INADDR_ANY); @@ -1192,7 +1198,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan, } *port = nla_get_be16(tb[NDA_PORT]); } else { - *port = vxlan->cfg.dst_port; + *port = cfg->dst_port; } if (tb[NDA_VNI]) { @@ -1202,7 +1208,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan, } *vni = cpu_to_be32(nla_get_u32(tb[NDA_VNI])); } else { - *vni = vxlan->default_dst.remote_vni; + *vni = cfg->vni; } if (tb[NDA_SRC_VNI]) { @@ -1212,7 +1218,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan, } *src_vni = cpu_to_be32(nla_get_u32(tb[NDA_SRC_VNI])); } else { - *src_vni = vxlan->default_dst.remote_vni; + *src_vni = cfg->vni; } if (tb[NDA_IFINDEX]) { @@ -1407,18 +1413,21 @@ static int vxlan_fdb_get(struct sk_buff *skb, struct netlink_ext_ack *extack) { struct vxlan_dev *vxlan = netdev_priv(dev); + const struct vxlan_config *cfg; struct vxlan_fdb *f; __be32 vni; int err; + cfg = rcu_dereference_rtnl(vxlan->cfg); + if (tb[NDA_VNI]) vni = cpu_to_be32(nla_get_u32(tb[NDA_VNI])); else - vni = vxlan->default_dst.remote_vni; + vni = cfg->vni; rcu_read_lock(); - f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, addr, vni); + f = vxlan_find_mac_rcu(vxlan, cfg, addr, vni); if (!f) { NL_SET_ERR_MSG(extack, "Fdb entry not found"); err = -ENOENT; @@ -1521,6 +1530,7 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs) static void vxlan_sock_release(struct vxlan_dev *vxlan) { + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock); #if IS_ENABLED(CONFIG_IPV6) struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock); @@ -1530,7 +1540,7 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan) RCU_INIT_POINTER(vxlan->vn4_sock, NULL); - if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) + if (cfg->flags & VXLAN_F_VNIFILTER) vxlan_vs_del_vnigrp(vxlan); else vxlan_vs_del_dev(vxlan); @@ -1703,7 +1713,8 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) goto drop; } - cfg = &vxlan->cfg; + cfg = rcu_dereference(vxlan->cfg); + if (vh->vx_flags & cfg->reserved_bits.vx_flags || vh->vx_vni & cfg->reserved_bits.vx_vni) { /* If the header uses bits besides those enabled by the @@ -1859,7 +1870,8 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb) return 0; } -static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags) +static int arp_reduce(struct net_device *dev, struct sk_buff *skb, + const struct vxlan_config *cfg, __be32 vni) { struct neigh_table *tbl = arp_table(dev_net(dev)); struct vxlan_dev *vxlan = netdev_priv(dev); @@ -1873,7 +1885,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u if (!pskb_network_may_pull(skb, arp_hdr_len(dev))) { dev_dstats_tx_dropped(dev); - vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL, + vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); goto out; } @@ -1914,7 +1926,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u neigh_ha_snapshot(ha, n, n->dev); rcu_read_lock(); - f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, ha, vni); + f = vxlan_find_mac_tx(vxlan, cfg, ha, vni); if (f) rdst = first_remote_rcu(f); if (rdst && vxlan_addr_any(&rdst->remote_ip)) { @@ -1940,11 +1952,11 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u if (netif_rx(reply) == NET_RX_DROP) { dev_dstats_rx_dropped(dev); - vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL, + vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_RX_DROPS, 0); } - } else if (flags & VXLAN_F_L3MISS) { + } else if (cfg->flags & VXLAN_F_L3MISS) { union vxlan_addr ipa = { .sin.sin_addr.s_addr = tip, .sin.sin_family = AF_INET, @@ -2054,7 +2066,8 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request, return reply; } -static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags) +static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, + const struct vxlan_config *cfg, __be32 vni) { struct vxlan_dev *vxlan = netdev_priv(dev); const struct in6_addr *daddr; @@ -2088,7 +2101,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, } neigh_ha_snapshot(ha, n, n->dev); - f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, ha, vni); + f = vxlan_find_mac_tx(vxlan, cfg, ha, vni); if (f) rdst = first_remote_rcu(f); if (rdst && vxlan_addr_any(&rdst->remote_ip)) { @@ -2107,10 +2120,10 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, if (netif_rx(reply) == NET_RX_DROP) { dev_dstats_rx_dropped(dev); - vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL, + vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_RX_DROPS, 0); } - } else if (flags & VXLAN_F_L3MISS) { + } else if (cfg->flags & VXLAN_F_L3MISS) { union vxlan_addr ipa = { .sin6.sin6_addr = msg->target, .sin6.sin6_family = AF_INET6, @@ -2297,7 +2310,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, const struct vxlan_config *src_cfg, __be32 vni, bool snoop) { - const struct vxlan_config *dst_cfg = &dst_vxlan->cfg; + const struct vxlan_config *dst_cfg; union vxlan_addr loopback; unsigned int len = skb->len; struct net_device *dev = dst_vxlan->dev; @@ -2318,6 +2331,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, } rcu_read_lock(); + dst_cfg = rcu_dereference(dst_vxlan->cfg); if (unlikely(!(dev->flags & IFF_UP))) { kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY); goto drop; @@ -2783,7 +2797,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) u32 nhid = 0; u32 flags; - cfg = &vxlan->cfg; + rcu_read_lock(); + cfg = rcu_dereference(vxlan->cfg); flags = cfg->flags; default_vni = cfg->vni; @@ -2801,14 +2816,19 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) vxlan_xmit_one(skb, dev, cfg, vni, NULL, false); else kfree_skb_reason(skb, SKB_DROP_REASON_TUNNEL_TXINFO); + rcu_read_unlock(); return NETDEV_TX_OK; } } if (flags & VXLAN_F_PROXY) { eth = eth_hdr(skb); - if (ntohs(eth->h_proto) == ETH_P_ARP) - return arp_reduce(dev, skb, vni, flags); + if (ntohs(eth->h_proto) == ETH_P_ARP) { + netdev_tx_t res = arp_reduce(dev, skb, cfg, vni); + + rcu_read_unlock(); + return res; + } #if IS_ENABLED(CONFIG_IPV6) else if (ntohs(eth->h_proto) == ETH_P_IPV6 && pskb_network_may_pull(skb, sizeof(struct ipv6hdr) + @@ -2817,32 +2837,36 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) struct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1); if (m->icmph.icmp6_code == 0 && - m->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) - return neigh_reduce(dev, skb, vni, flags); + m->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) { + netdev_tx_t res = neigh_reduce(dev, skb, cfg, vni); + + rcu_read_unlock(); + return res; + } } #endif } - if (nhid) - return vxlan_xmit_nhid(skb, dev, nhid, vni, cfg); + if (nhid) { + netdev_tx_t res = vxlan_xmit_nhid(skb, dev, nhid, vni, cfg); + + rcu_read_unlock(); + return res; + } if (test_bit(VXLAN_DEV_F_MDB, &vxlan->flags)) { struct vxlan_mdb_entry *mdb_entry; - rcu_read_lock(); mdb_entry = vxlan_mdb_entry_skb_get(vxlan, cfg, skb, vni); if (mdb_entry) { - netdev_tx_t ret; + netdev_tx_t ret = vxlan_mdb_xmit(vxlan, cfg, mdb_entry, skb); - ret = vxlan_mdb_xmit(vxlan, cfg, mdb_entry, skb); rcu_read_unlock(); return ret; } - rcu_read_unlock(); } eth = eth_hdr(skb); - rcu_read_lock(); f = vxlan_find_mac_tx(vxlan, cfg, eth->h_dest, vni); did_rsc = false; @@ -2901,12 +2925,15 @@ static void vxlan_cleanup(struct timer_list *t) { struct vxlan_dev *vxlan = timer_container_of(vxlan, t, age_timer); unsigned long next_timer = jiffies + FDB_AGE_INTERVAL; + const struct vxlan_config *cfg; struct vxlan_fdb *f; if (!netif_running(vxlan->dev)) return; rcu_read_lock(); + cfg = rcu_dereference(vxlan->cfg); + hlist_for_each_entry_rcu(f, &vxlan->fdb_list, fdb_node) { unsigned long timeout; @@ -2916,7 +2943,7 @@ static void vxlan_cleanup(struct timer_list *t) if (f->flags & NTF_EXT_LEARNED) continue; - timeout = READ_ONCE(f->updated) + vxlan->cfg.age_interval * HZ; + timeout = READ_ONCE(f->updated) + cfg->age_interval * HZ; if (time_before_eq(timeout, jiffies)) { spin_lock(&vxlan->hash_lock); if (!hlist_unhashed(&f->fdb_node)) { @@ -2960,13 +2987,16 @@ static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan, static int vxlan_init(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); + const struct vxlan_config *cfg; int err; + cfg = rtnl_dereference(vxlan->cfg); + err = rhashtable_init(&vxlan->fdb_hash_tbl, &vxlan_fdb_rht_params); if (err) return err; - if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) { + if (cfg->flags & VXLAN_F_VNIFILTER) { err = vxlan_vnigroup_init(vxlan); if (err) goto err_rhashtable_destroy; @@ -2986,7 +3016,7 @@ static int vxlan_init(struct net_device *dev) err_gro_cells_destroy: gro_cells_destroy(&vxlan->gro_cells); err_vnigroup_uninit: - if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) + if (cfg->flags & VXLAN_F_VNIFILTER) vxlan_vnigroup_uninit(vxlan); err_rhashtable_destroy: rhashtable_destroy(&vxlan->fdb_hash_tbl); @@ -2996,10 +3026,13 @@ err_rhashtable_destroy: static void vxlan_uninit(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); + const struct vxlan_config *cfg; + + cfg = rtnl_dereference(vxlan->cfg); vxlan_mdb_fini(vxlan); - if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) + if (cfg->flags & VXLAN_F_VNIFILTER) vxlan_vnigroup_uninit(vxlan); gro_cells_destroy(&vxlan->gro_cells); @@ -3011,6 +3044,7 @@ static void vxlan_uninit(struct net_device *dev) static int vxlan_open(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); + const struct vxlan_config *cfg; int ret; ret = vxlan_sock_add(vxlan); @@ -3023,7 +3057,8 @@ static int vxlan_open(struct net_device *dev) return ret; } - if (vxlan->cfg.age_interval) + cfg = rtnl_dereference(vxlan->cfg); + if (cfg->age_interval) mod_timer(&vxlan->age_timer, jiffies + FDB_AGE_INTERVAL); return ret; @@ -3045,8 +3080,10 @@ struct vxlan_fdb_flush_desc { static bool vxlan_fdb_is_default_entry(const struct vxlan_fdb *f, const struct vxlan_dev *vxlan) { + const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg); + return is_zero_ether_addr(f->key.eth_addr) && - f->key.vni == vxlan->cfg.vni; + f->key.vni == cfg->vni; } static bool vxlan_fdb_nhid_matches(const struct vxlan_fdb *f, u32 nhid) @@ -3264,14 +3301,18 @@ static int vxlan_change_mtu(struct net_device *dev, int new_mtu) { struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_rdst *dst = &vxlan->default_dst; - struct net_device *lowerdev = __dev_get_by_index(vxlan->net, - dst->remote_ifindex); + const struct vxlan_config *cfg; + struct net_device *lowerdev; + + cfg = rtnl_dereference(vxlan->cfg); + + lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex); /* This check is different than dev->max_mtu, because it looks at * the lowerdev->mtu, rather than the static dev->max_mtu */ if (lowerdev) { - int max_mtu = lowerdev->mtu - vxlan_headroom(vxlan->cfg.flags); + int max_mtu = lowerdev->mtu - vxlan_headroom(cfg->flags); if (new_mtu > max_mtu) return -EINVAL; } @@ -3284,11 +3325,14 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) { struct vxlan_dev *vxlan = netdev_priv(dev); struct ip_tunnel_info *info = skb_tunnel_info(skb); + const struct vxlan_config *cfg; __be16 sport, dport; - sport = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min, - vxlan->cfg.port_max, true); - dport = info->key.tp_dst ? : vxlan->cfg.dst_port; + cfg = rcu_dereference(vxlan->cfg); + + sport = udp_flow_src_port(dev_net(dev), skb, cfg->port_min, + cfg->port_max, true); + dport = info->key.tp_dst ? : cfg->dst_port; if (ip_tunnel_info_af(info) == AF_INET) { struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock); @@ -3398,6 +3442,15 @@ static void vxlan_offload_rx_ports(struct net_device *dev, bool push) } } +static void vxlan_free_dev(struct net_device *dev) +{ + struct vxlan_dev *vxlan = netdev_priv(dev); + struct vxlan_config *cfg = rcu_dereference_protected(vxlan->cfg, 1); + + RCU_INIT_POINTER(vxlan->cfg, NULL); + kfree(cfg); +} + /* Initialize the device structure. */ static void vxlan_setup(struct net_device *dev) { @@ -3406,6 +3459,8 @@ static void vxlan_setup(struct net_device *dev) eth_hw_addr_random(dev); ether_setup(dev); + dev->priv_destructor = vxlan_free_dev; + dev->needs_free_netdev = true; SET_NETDEV_DEVTYPE(dev, &vxlan_type); @@ -3688,21 +3743,22 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6, static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6) { - bool metadata = vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA; + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); + bool metadata = cfg->flags & VXLAN_F_COLLECT_METADATA; struct vxlan_sock *vs = NULL; struct vxlan_dev_node *node; int l3mdev_index = 0; ASSERT_RTNL(); - if (vxlan->cfg.remote_ifindex) + if (cfg->remote_ifindex) l3mdev_index = l3mdev_master_upper_ifindex_by_index( - vxlan->net, vxlan->cfg.remote_ifindex); + vxlan->net, cfg->remote_ifindex); - if (!vxlan->cfg.no_share) { + if (!cfg->no_share) { rcu_read_lock(); vs = vxlan_find_sock(vxlan->net, ipv6 ? AF_INET6 : AF_INET, - vxlan->cfg.dst_port, vxlan->cfg.flags, + cfg->dst_port, cfg->flags, l3mdev_index); if (vs && !refcount_inc_not_zero(&vs->refcnt)) { rcu_read_unlock(); @@ -3712,7 +3768,7 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6) } if (!vs) vs = vxlan_socket_create(vxlan->net, ipv6, - vxlan->cfg.dst_port, vxlan->cfg.flags, + cfg->dst_port, cfg->flags, l3mdev_index); if (IS_ERR(vs)) return PTR_ERR(vs); @@ -3727,7 +3783,7 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6) node = &vxlan->hlist4; } - if (metadata && (vxlan->cfg.flags & VXLAN_F_VNIFILTER)) + if (metadata && (cfg->flags & VXLAN_F_VNIFILTER)) vxlan_vs_add_vnigrp(vxlan, vs, ipv6); else vxlan_vs_add_dev(vs, vxlan, node); @@ -3737,11 +3793,14 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6) static int vxlan_sock_add(struct vxlan_dev *vxlan) { - bool metadata = vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA; - bool ipv6 = vxlan->cfg.flags & VXLAN_F_IPV6 || metadata; - bool ipv4 = !ipv6 || metadata; + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); + bool metadata, ipv6, ipv4; int ret = 0; + metadata = cfg->flags & VXLAN_F_COLLECT_METADATA; + ipv6 = (cfg->flags & VXLAN_F_IPV6) || metadata; + ipv4 = !ipv6 || metadata; + RCU_INIT_POINTER(vxlan->vn4_sock, NULL); #if IS_ENABLED(CONFIG_IPV6) RCU_INIT_POINTER(vxlan->vn6_sock, NULL); @@ -3765,22 +3824,27 @@ int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan, struct vxlan_dev *tmp; list_for_each_entry(tmp, &vn->vxlan_list, next) { + const struct vxlan_config *tmp_cfg; + if (tmp == vxlan) continue; - if (tmp->cfg.flags & VXLAN_F_VNIFILTER) { + + tmp_cfg = rtnl_dereference(tmp->cfg); + + if (tmp_cfg->flags & VXLAN_F_VNIFILTER) { if (!vxlan_vnifilter_lookup(tmp, vni)) continue; - } else if (tmp->cfg.vni != vni) { + } else if (tmp_cfg->vni != vni) { continue; } - if (tmp->cfg.dst_port != conf->dst_port) + if (tmp_cfg->dst_port != conf->dst_port) continue; - if ((tmp->cfg.flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) != + if ((tmp_cfg->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) != (conf->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6))) continue; if ((conf->flags & VXLAN_F_IPV6_LINKLOCAL) && - tmp->cfg.remote_ifindex != conf->remote_ifindex) + tmp_cfg->remote_ifindex != conf->remote_ifindex) continue; return -EEXIST; @@ -3942,7 +4006,7 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf, } static void vxlan_config_apply(struct net_device *dev, - struct vxlan_config *conf, + struct vxlan_config *new_cfg, struct net_device *lowerdev, struct net *src_net, bool changelink) @@ -3950,8 +4014,9 @@ static void vxlan_config_apply(struct net_device *dev, struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_rdst *dst = &vxlan->default_dst; unsigned short needed_headroom = ETH_HLEN; + struct vxlan_config *old_cfg; int max_mtu = ETH_MAX_MTU; - u32 flags = conf->flags; + u32 flags = new_cfg->flags; if (!changelink) { if (flags & VXLAN_F_GPE) @@ -3959,18 +4024,18 @@ static void vxlan_config_apply(struct net_device *dev, else vxlan_ether_setup(dev); - if (conf->mtu) - dev->mtu = conf->mtu; + if (new_cfg->mtu) + dev->mtu = new_cfg->mtu; vxlan->net = src_net; } - dst->remote_vni = conf->vni; + dst->remote_vni = new_cfg->vni; - memcpy(&dst->remote_ip, &conf->remote_ip, sizeof(conf->remote_ip)); + memcpy(&dst->remote_ip, &new_cfg->remote_ip, sizeof(new_cfg->remote_ip)); if (lowerdev) { - dst->remote_ifindex = conf->remote_ifindex; + dst->remote_ifindex = new_cfg->remote_ifindex; netif_inherit_tso_max(dev, lowerdev); @@ -3983,7 +4048,7 @@ static void vxlan_config_apply(struct net_device *dev, if (max_mtu < ETH_MIN_MTU) max_mtu = ETH_MIN_MTU; - if (!changelink && !conf->mtu) + if (!changelink && !new_cfg->mtu) dev->mtu = max_mtu; } @@ -3995,7 +4060,10 @@ static void vxlan_config_apply(struct net_device *dev, needed_headroom += vxlan_headroom(flags); dev->needed_headroom = needed_headroom; - memcpy(&vxlan->cfg, conf, sizeof(*conf)); + old_cfg = rtnl_dereference(vxlan->cfg); + rcu_assign_pointer(vxlan->cfg, new_cfg); + if (old_cfg) + kfree_rcu(old_cfg, rcu); } static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, @@ -4004,13 +4072,18 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, { struct vxlan_dev *vxlan = netdev_priv(dev); struct net_device *lowerdev; + struct vxlan_config *new_cfg; int ret; ret = vxlan_config_validate(src_net, conf, &lowerdev, vxlan, extack); if (ret) return ret; - vxlan_config_apply(dev, conf, lowerdev, src_net, false); + new_cfg = kmemdup(conf, sizeof(*conf), GFP_KERNEL); + if (!new_cfg) + return -ENOMEM; + + vxlan_config_apply(dev, new_cfg, lowerdev, src_net, false); return 0; } @@ -4022,6 +4095,7 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev, struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_dev *vxlan = netdev_priv(dev); struct net_device *remote_dev = NULL; + const struct vxlan_config *cfg; struct vxlan_rdst *dst; int err; @@ -4030,11 +4104,15 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev, if (err) return err; + cfg = rtnl_dereference(vxlan->cfg); + dev->ethtool_ops = &vxlan_ethtool_ops; err = register_netdevice(dev); - if (err) + if (err) { + vxlan_free_dev(dev); return err; + } if (dst->remote_ifindex) { remote_dev = __dev_get_by_index(net, dst->remote_ifindex); @@ -4061,7 +4139,7 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev, &dst->remote_ip, NUD_REACHABLE | NUD_PERMANENT, NLM_F_EXCL | NLM_F_CREATE, - vxlan->cfg.dst_port, + cfg->dst_port, dst->remote_vni, dst->remote_vni, dst->remote_ifindex, @@ -4125,8 +4203,12 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[], memset(conf, 0, sizeof(*conf)); /* if changelink operation, start with old existing cfg */ - if (changelink) - memcpy(conf, &vxlan->cfg, sizeof(*conf)); + if (changelink) { + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); + + if (cfg) + memcpy(conf, cfg, sizeof(*conf)); + } if (data[IFLA_VXLAN_ID]) { __be32 vni = cpu_to_be32(nla_get_u32(data[IFLA_VXLAN_ID])); @@ -4473,9 +4555,11 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], struct netlink_ext_ack *extack) { struct vxlan_dev *vxlan = netdev_priv(dev); + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); bool rem_ip_changed, change_igmp; struct net_device *lowerdev; struct vxlan_config conf; + struct vxlan_config *new_cfg; struct vxlan_rdst *dst; u32 new_ifindex; int err; @@ -4493,13 +4577,19 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], if (err) return err; + new_cfg = kmemdup(&conf, sizeof(conf), GFP_KERNEL); + if (!new_cfg) + return -ENOMEM; + if (dst->remote_dev == lowerdev) lowerdev = NULL; err = netdev_adjacent_change_prepare(dst->remote_dev, lowerdev, dev, extack); - if (err) + if (err) { + kfree(new_cfg); return err; + } /* vxlan_config_apply() only commits remote_ifindex if lowerdev is set */ new_ifindex = lowerdev ? conf.remote_ifindex : dst->remote_ifindex; @@ -4517,7 +4607,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], &conf.remote_ip, NUD_REACHABLE | NUD_PERMANENT, NLM_F_APPEND | NLM_F_CREATE, - vxlan->cfg.dst_port, + cfg->dst_port, conf.vni, conf.vni, new_ifindex, NTF_SELF, 0, true, extack); @@ -4525,13 +4615,14 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], spin_unlock_bh(&vxlan->hash_lock); netdev_adjacent_change_abort(dst->remote_dev, lowerdev, dev); + kfree(new_cfg); return err; } } if (!vxlan_addr_any(&dst->remote_ip)) __vxlan_fdb_delete(vxlan, all_zeros_mac, dst->remote_ip, - vxlan->cfg.dst_port, + cfg->dst_port, dst->remote_vni, dst->remote_vni, dst->remote_ifindex, @@ -4541,7 +4632,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], /* If vni filtering device, also update default fdb entries of * all vnis */ - if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) { + if (cfg->flags & VXLAN_F_VNIFILTER) { err = vxlan_vnilist_update_group(vxlan, &dst->remote_ip, &conf.remote_ip, dst->remote_ifindex, @@ -4555,6 +4646,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], NULL); netdev_adjacent_change_abort(dst->remote_dev, lowerdev, dev); + kfree(new_cfg); return err; } } @@ -4562,20 +4654,20 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], if (change_igmp && (vxlan_addr_multicast(&dst->remote_ip) || - (vxlan->cfg.flags & VXLAN_F_VNIFILTER))) + (cfg->flags & VXLAN_F_VNIFILTER))) err = vxlan_multicast_leave(vxlan); - if (netif_running(dev) && conf.age_interval != vxlan->cfg.age_interval) + if (netif_running(dev) && conf.age_interval != cfg->age_interval) mod_timer(&vxlan->age_timer, jiffies); netdev_adjacent_change_commit(dst->remote_dev, lowerdev, dev); if (lowerdev && lowerdev != dst->remote_dev) dst->remote_dev = lowerdev; - vxlan_config_apply(dev, &conf, lowerdev, vxlan->net, true); + vxlan_config_apply(dev, new_cfg, lowerdev, vxlan->net, true); if (change_igmp && (vxlan_addr_multicast(&dst->remote_ip) || - (vxlan->cfg.flags & VXLAN_F_VNIFILTER))) { + (new_cfg->flags & VXLAN_F_VNIFILTER))) { int join_err = vxlan_multicast_join(vxlan); if (join_err) @@ -4643,7 +4735,7 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) struct ifla_vxlan_port_range ports; const struct vxlan_config *cfg; - cfg = &vxlan->cfg; + cfg = rtnl_dereference(vxlan->cfg); if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst->remote_vni))) goto nla_put_failure; diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index cf606256d092..4ae6369ed4e3 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -165,7 +165,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan, const struct vxlan_mdb_entry *mdb_entry, const struct vxlan_mdb_remote *remote) { - const struct vxlan_config *cfg = &vxlan->cfg; + const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg); struct vxlan_rdst *rd = rtnl_dereference(remote->rd); struct br_mdb_entry e; struct nlattr *nest; @@ -614,7 +614,9 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg, { struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]); struct vxlan_dev *vxlan = netdev_priv(dev); - const struct vxlan_config *vcfg = &vxlan->cfg; + const struct vxlan_config *vcfg; + + vcfg = rtnl_dereference(vxlan->cfg); memset(cfg, 0, sizeof(*cfg)); cfg->vxlan = vxlan; @@ -959,12 +961,12 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan, const struct vxlan_mdb_entry *mdb_entry, const struct vxlan_mdb_remote *remote) { - const struct vxlan_config *cfg = &vxlan->cfg; + const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg); const struct vxlan_mdb_entry_key *group = &mdb_entry->key; struct vxlan_rdst *rd = rtnl_dereference(remote->rd); size_t nlmsg_size; - /* MDBA_MDB_ENTRY_INFO */ + /* MDBA_MDB_ENTRY_INFO */ nlmsg_size = nla_total_size(sizeof(struct br_mdb_entry)) + /* MDBA_MDB_EATTR_TIMER */ nla_total_size(sizeof(u32)); diff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c index b85283605aaf..c717540e95ab 100644 --- a/drivers/net/vxlan/vxlan_multicast.c +++ b/drivers/net/vxlan/vxlan_multicast.c @@ -147,6 +147,8 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev, #endif list_for_each_entry(vxlan, &vn->vxlan_list, next) { + const struct vxlan_config *cfg; + if (!netif_running(vxlan->dev) || vxlan == dev) continue; @@ -158,7 +160,9 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev, rtnl_dereference(vxlan->vn6_sock) != sock6) continue; #endif - if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) { + cfg = rtnl_dereference(vxlan->cfg); + + if (cfg->flags & VXLAN_F_VNIFILTER) { if (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex)) continue; } else { @@ -240,6 +244,7 @@ static int vxlan_multicast_leave_vnigrp(struct vxlan_dev *vxlan) int vxlan_multicast_join(struct vxlan_dev *vxlan) { + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); int ret = 0; if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) { @@ -251,7 +256,7 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan) return ret; } - if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) + if (cfg->flags & VXLAN_F_VNIFILTER) return vxlan_multicast_join_vnigrp(vxlan); return 0; @@ -259,6 +264,7 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan) int vxlan_multicast_leave(struct vxlan_dev *vxlan) { + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); int ret = 0; @@ -272,7 +278,7 @@ int vxlan_multicast_leave(struct vxlan_dev *vxlan) return ret; } - if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) + if (cfg->flags & VXLAN_F_VNIFILTER) return vxlan_multicast_leave_vnigrp(vxlan); return 0; diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index cdda7688f3af..d16d5a6c7dad 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -178,7 +178,7 @@ void vxlan_vnifilter_count(struct vxlan_dev *vxlan, { struct vxlan_vni_node *vnode; - if (!cfg || !(cfg->flags & VXLAN_F_VNIFILTER)) + if (!(cfg->flags & VXLAN_F_VNIFILTER)) return; if (vninode) { @@ -337,6 +337,7 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev, struct vxlan_vni_node *v, *vbegin = NULL, *vend = NULL; struct vxlan_dev *vxlan = netdev_priv(dev); struct tunnel_msg *new_tmsg, *tmsg; + const struct vxlan_config *cfg; struct vxlan_vni_group *vg; struct nlmsghdr *nlh; int idx = 0, s_idx; @@ -349,7 +350,8 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev, } s_idx = cb->args[1]; - if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) { + cfg = rcu_dereference(vxlan->cfg); + if (!(cfg->flags & VXLAN_F_VNIFILTER)) { cb->args[1] = 0; cb->args[2] = 0; return -EINVAL; @@ -506,6 +508,7 @@ int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni, u32 old_ifindex, u32 new_ifindex, struct netlink_ext_ack *extack) { + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); int err = 0; if (old_remote_ip && remote_ip && @@ -519,7 +522,7 @@ int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni, remote_ip, NUD_REACHABLE | NUD_PERMANENT, NLM_F_APPEND | NLM_F_CREATE, - vxlan->cfg.dst_port, + cfg->dst_port, vni, vni, new_ifindex, @@ -533,7 +536,7 @@ int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni, if (old_remote_ip && !vxlan_addr_any(old_remote_ip)) { __vxlan_fdb_delete(vxlan, all_zeros_mac, *old_remote_ip, - vxlan->cfg.dst_port, + cfg->dst_port, vni, vni, old_ifindex, true); @@ -686,6 +689,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan, struct vxlan_vni_node *vninode) { struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); struct vxlan_rdst *dst = &vxlan->default_dst; /* if per vni remote_ip not present, delete the @@ -697,7 +701,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan, __vxlan_fdb_delete(vxlan, all_zeros_mac, (vxlan_addr_any(&vninode->remote_ip) ? dst->remote_ip : vninode->remote_ip), - vxlan->cfg.dst_port, + cfg->dst_port, vninode->vni, vninode->vni, dst->remote_ifindex, true); @@ -801,6 +805,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan, u32 vni, union vxlan_addr *group, struct netlink_ext_ack *extack) { + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); struct vxlan_vni_node *vninode; __be32 v = cpu_to_be32(vni); bool changed = false; @@ -809,7 +814,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan, if (vxlan_vnifilter_lookup(vxlan, v)) return vxlan_vni_update(vxlan, vg, v, group, &changed, extack); - err = vxlan_vni_in_use(vxlan->net, vxlan, &vxlan->cfg, v); + err = vxlan_vni_in_use(vxlan->net, vxlan, cfg, v); if (err) { NL_SET_ERR_MSG(extack, "VNI in use"); return err; @@ -1018,6 +1023,7 @@ static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); + const struct vxlan_config *cfg; struct tunnel_msg *tmsg; struct vxlan_dev *vxlan; struct net_device *dev; @@ -1042,8 +1048,9 @@ static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh, } vxlan = netdev_priv(dev); + cfg = rtnl_dereference(vxlan->cfg); - if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) + if (!(cfg->flags & VXLAN_F_VNIFILTER)) return -EOPNOTSUPP; nlmsg_for_each_attr_type(attr, VXLAN_VNIFILTER_ENTRY, nlh, diff --git a/include/net/vxlan.h b/include/net/vxlan.h index d323f91af236..7ced743ec881 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -229,6 +229,7 @@ struct vxlan_config { bool no_share; enum ifla_vxlan_df df; struct vxlanhdr reserved_bits; + struct rcu_head rcu; }; enum { @@ -302,7 +303,7 @@ struct vxlan_dev { struct gro_cells gro_cells; unsigned long flags; - struct vxlan_config cfg; + struct vxlan_config __rcu *cfg; struct vxlan_vni_group __rcu *vnigrp; From 7e9e6844befcbbed3b576c16e75d5348639774ff Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 22 Sep 2026 18:11:01 +0000 Subject: [PATCH 7/8] vxlan: remove default_dst and use vxlan_config and lowerdev Now that vxlan->cfg is an RCU-protected pointer, storing default destination attributes (remote_ip, remote_vni, remote_ifindex) in vxlan->default_dst is redundant and creates potential data races for lockless readers. Furthermore, several fields of struct vxlan_rdst (remote_port, offloaded, list, rcu, dst_cache) in default_dst were completely unused. The remaining one, remote_dev, only tracked the lower device, a role now taken by vxlan->lowerdev, so drop it from struct vxlan_rdst. Replace vxlan->default_dst with a 'struct net_device *lowerdev' pointer in struct vxlan_dev to track adjacent upper/lower netdev topology under RTNL, and switch all remaining users over to reading configuration attributes from vxlan->cfg. Also update mlx5e_tc_tun_get_remote_ifindex() to read remote_ifindex from vxlan->cfg under rcu_read_lock(). In vxlan_changelink(), pass lowerdev to vxlan_config_apply() to preserve needed_headroom and needed_tailroom. The mtu is still only reconsidered when the lower device changes, so that an unrelated changelink can not shrink it. cfg->remote_ifindex is now committed unconditionally, where default_dst.remote_ifindex was left untouched when IFLA_VXLAN_LINK was cleared, so a changelink dropping the lower device no longer leaves a stale ifindex behind. For the same reason vxlan_changelink() no longer needs to compute the ifindex that will actually be committed. Setting IFLA_VXLAN_LINK to 0 now also tears down the upper/lower adjacency, which netdev_adjacent_change_commit() used to skip for a NULL new device, resets needed_tailroom, and is rejected if per-VNI multicast groups are configured. Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260922181102.3989489-8-edumazet@google.com Signed-off-by: Jakub Kicinski --- .../mellanox/mlx5/core/en/tc_tun_vxlan.c | 11 +- drivers/net/vxlan/vxlan_core.c | 201 ++++++++++-------- drivers/net/vxlan/vxlan_mdb.c | 14 +- drivers/net/vxlan/vxlan_multicast.c | 71 ++++--- drivers/net/vxlan/vxlan_private.h | 15 +- drivers/net/vxlan/vxlan_vnifilter.c | 61 ++++-- include/net/vxlan.h | 3 +- 7 files changed, 213 insertions(+), 163 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c index 7a18a469961d..467fbe43b89e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c @@ -241,9 +241,16 @@ static bool mlx5e_tc_tun_encap_info_equal_vxlan(struct mlx5e_encap_key *a, static int mlx5e_tc_tun_get_remote_ifindex(struct net_device *mirred_dev) { const struct vxlan_dev *vxlan = netdev_priv(mirred_dev); - const struct vxlan_rdst *dst = &vxlan->default_dst; + const struct vxlan_config *cfg; + int ifindex = 0; - return dst->remote_ifindex; + rcu_read_lock(); + cfg = rcu_dereference(vxlan->cfg); + if (cfg) + ifindex = cfg->remote_ifindex; + rcu_read_unlock(); + + return ifindex; } struct mlx5e_tc_tunnel vxlan_tunnel = { diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 783337fc4483..e8027b794e8c 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -804,6 +804,7 @@ static int vxlan_fdb_nh_update(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb, u32 nhid, struct netlink_ext_ack *extack) { struct nexthop *old_nh = rtnl_dereference(fdb->nh); + const struct vxlan_config *cfg; struct nexthop *nh; int err = -EINVAL; @@ -832,7 +833,8 @@ static int vxlan_fdb_nh_update(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb, } /* check nexthop group family */ - switch (vxlan->default_dst.remote_ip.sa.sa_family) { + cfg = rtnl_dereference(vxlan->cfg); + switch (cfg->remote_ip.sa.sa_family) { case AF_INET: if (!nexthop_has_v4(nh)) { err = -EAFNOSUPPORT; @@ -1249,6 +1251,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], const unsigned char *addr, u16 vid, u16 flags, bool *notified, struct netlink_ext_ack *extack) { + const struct vxlan_config *cfg; struct vxlan_dev *vxlan = netdev_priv(dev); /* struct net *net = dev_net(vxlan->dev); */ union vxlan_addr ip; @@ -1276,7 +1279,8 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], return -EINVAL; } - if (vxlan->default_dst.remote_ip.sa.sa_family != ip.sa.sa_family) + cfg = rtnl_dereference(vxlan->cfg); + if (cfg->remote_ip.sa.sa_family != ip.sa.sa_family) return -EAFNOSUPPORT; spin_lock_bh(&vxlan->hash_lock); @@ -2320,7 +2324,14 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, skb->dev = dev; __skb_pull(skb, skb_network_offset(skb)); - if (dst_vxlan->default_dst.remote_ip.sa.sa_family == AF_INET) { + rcu_read_lock(); + dst_cfg = rcu_dereference(dst_vxlan->cfg); + if (unlikely(!(dev->flags & IFF_UP))) { + kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY); + goto drop; + } + + if (dst_cfg->remote_ip.sa.sa_family == AF_INET) { loopback.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); loopback.sa.sa_family = AF_INET; #if IS_ENABLED(CONFIG_IPV6) @@ -2330,13 +2341,6 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, #endif } - rcu_read_lock(); - dst_cfg = rcu_dereference(dst_vxlan->cfg); - if (unlikely(!(dev->flags & IFF_UP))) { - kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY); - goto drop; - } - if ((dst_cfg->flags & VXLAN_F_LEARN) && snoop) vxlan_snoop(dev, dst_cfg, &loopback, eth_hdr(skb)->h_source, 0, vni); @@ -2975,10 +2979,14 @@ static void vxlan_vs_del_dev(struct vxlan_dev *vxlan) static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan, struct vxlan_dev_node *node) { - __be32 vni = vxlan->default_dst.remote_vni; + const struct vxlan_config *cfg; + __be32 vni; ASSERT_RTNL(); + cfg = rtnl_dereference(vxlan->cfg); + vni = cfg->vni; + node->vxlan = vxlan; hlist_add_head_rcu(&node->hlist, vni_head(vs, vni)); } @@ -3300,13 +3308,12 @@ static void vxlan_set_multicast_list(struct net_device *dev) static int vxlan_change_mtu(struct net_device *dev, int new_mtu) { struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_rdst *dst = &vxlan->default_dst; const struct vxlan_config *cfg; struct net_device *lowerdev; cfg = rtnl_dereference(vxlan->cfg); - lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex); + lowerdev = __dev_get_by_index(vxlan->net, cfg->remote_ifindex); /* This check is different than dev->max_mtu, because it looks at * the lowerdev->mtu, rather than the static dev->max_mtu @@ -3635,9 +3642,11 @@ static int vxlan_get_link_ksettings(struct net_device *dev, struct ethtool_link_ksettings *cmd) { struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_rdst *dst = &vxlan->default_dst; - struct net_device *lowerdev = __dev_get_by_index(vxlan->net, - dst->remote_ifindex); + const struct vxlan_config *cfg; + struct net_device *lowerdev; + + cfg = rtnl_dereference(vxlan->cfg); + lowerdev = __dev_get_by_index(vxlan->net, cfg->remote_ifindex); if (!lowerdev) { cmd->base.duplex = DUPLEX_UNKNOWN; @@ -3975,6 +3984,13 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf, return -EINVAL; } + if ((conf->flags & VXLAN_F_VNIFILTER) && old && + vxlan_vnifilter_has_multicast(old)) { + NL_SET_ERR_MSG(extack, + "Local interface required for multicast remote group"); + return -EINVAL; + } + #if IS_ENABLED(CONFIG_IPV6) if (conf->flags & VXLAN_F_IPV6_LINKLOCAL) { NL_SET_ERR_MSG(extack, @@ -4009,10 +4025,9 @@ static void vxlan_config_apply(struct net_device *dev, struct vxlan_config *new_cfg, struct net_device *lowerdev, struct net *src_net, - bool changelink) + bool changelink, bool lowerdev_changed) { struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_rdst *dst = &vxlan->default_dst; unsigned short needed_headroom = ETH_HLEN; struct vxlan_config *old_cfg; int max_mtu = ETH_MAX_MTU; @@ -4025,18 +4040,13 @@ static void vxlan_config_apply(struct net_device *dev, vxlan_ether_setup(dev); if (new_cfg->mtu) - dev->mtu = new_cfg->mtu; + WRITE_ONCE(dev->mtu, new_cfg->mtu); vxlan->net = src_net; } - dst->remote_vni = new_cfg->vni; - - memcpy(&dst->remote_ip, &new_cfg->remote_ip, sizeof(new_cfg->remote_ip)); - + dev->needed_tailroom = 0; if (lowerdev) { - dst->remote_ifindex = new_cfg->remote_ifindex; - netif_inherit_tso_max(dev, lowerdev); needed_headroom = lowerdev->hard_header_len; @@ -4044,16 +4054,17 @@ static void vxlan_config_apply(struct net_device *dev, dev->needed_tailroom = lowerdev->needed_tailroom; - max_mtu = lowerdev->mtu - vxlan_headroom(flags); + max_mtu = READ_ONCE(lowerdev->mtu) - vxlan_headroom(flags); if (max_mtu < ETH_MIN_MTU) max_mtu = ETH_MIN_MTU; if (!changelink && !new_cfg->mtu) - dev->mtu = max_mtu; + WRITE_ONCE(dev->mtu, max_mtu); } - if (dev->mtu > max_mtu) - dev->mtu = max_mtu; + /* A changelink leaving the lower device alone must not shrink the mtu */ + if (lowerdev_changed && READ_ONCE(dev->mtu) > max_mtu) + WRITE_ONCE(dev->mtu, max_mtu); if (flags & VXLAN_F_COLLECT_METADATA) flags |= VXLAN_F_IPV6; @@ -4083,7 +4094,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, if (!new_cfg) return -ENOMEM; - vxlan_config_apply(dev, new_cfg, lowerdev, src_net, false); + vxlan_config_apply(dev, new_cfg, lowerdev, src_net, false, true); return 0; } @@ -4096,10 +4107,8 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev, struct vxlan_dev *vxlan = netdev_priv(dev); struct net_device *remote_dev = NULL; const struct vxlan_config *cfg; - struct vxlan_rdst *dst; int err; - dst = &vxlan->default_dst; err = vxlan_dev_configure(net, dev, conf, extack); if (err) return err; @@ -4114,8 +4123,8 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev, return err; } - if (dst->remote_ifindex) { - remote_dev = __dev_get_by_index(net, dst->remote_ifindex); + if (cfg->remote_ifindex) { + remote_dev = __dev_get_by_index(net, cfg->remote_ifindex); if (!remote_dev) { err = -ENODEV; goto unregister; @@ -4125,7 +4134,7 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev, if (err) goto unregister; - dst->remote_dev = remote_dev; + vxlan->lowerdev = remote_dev; } err = rtnl_configure_link(dev, NULL, 0, NULL); @@ -4133,16 +4142,18 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev, goto unlink; /* create an fdb entry for a valid default destination */ - if (!vxlan_addr_any(&dst->remote_ip)) { + if (!vxlan_addr_any(&cfg->remote_ip)) { + union vxlan_addr rip = cfg->remote_ip; + spin_lock_bh(&vxlan->hash_lock); err = vxlan_fdb_update(vxlan, all_zeros_mac, - &dst->remote_ip, + &rip, NUD_REACHABLE | NUD_PERMANENT, NLM_F_EXCL | NLM_F_CREATE, cfg->dst_port, - dst->remote_vni, - dst->remote_vni, - dst->remote_ifindex, + cfg->vni, + cfg->vni, + cfg->remote_ifindex, NTF_SELF, 0, true, extack); spin_unlock_bh(&vxlan->hash_lock); if (err) @@ -4554,20 +4565,19 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { + bool lowerdev_changed, rem_ip_changed, change_igmp; struct vxlan_dev *vxlan = netdev_priv(dev); - const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); - bool rem_ip_changed, change_igmp; + const struct vxlan_config *cfg; + struct vxlan_config *new_cfg; struct net_device *lowerdev; struct vxlan_config conf; - struct vxlan_config *new_cfg; - struct vxlan_rdst *dst; - u32 new_ifindex; int err; + cfg = rtnl_dereference(vxlan->cfg); + if (!rtnl_dev_link_net_capable(dev, vxlan->net)) return -EPERM; - dst = &vxlan->default_dst; err = vxlan_nl2conf(tb, data, dev, &conf, true, extack); if (err) return err; @@ -4581,26 +4591,23 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], if (!new_cfg) return -ENOMEM; - if (dst->remote_dev == lowerdev) - lowerdev = NULL; - - err = netdev_adjacent_change_prepare(dst->remote_dev, lowerdev, dev, - extack); - if (err) { - kfree(new_cfg); - return err; + lowerdev_changed = vxlan->lowerdev != lowerdev; + if (lowerdev_changed) { + err = netdev_adjacent_change_prepare(vxlan->lowerdev, lowerdev, + dev, extack); + if (err) { + kfree(new_cfg); + 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); + rem_ip_changed = !vxlan_addr_equal(&conf.remote_ip, &cfg->remote_ip); change_igmp = vxlan->dev->flags & IFF_UP && (rem_ip_changed || - dst->remote_ifindex != new_ifindex); + cfg->remote_ifindex != conf.remote_ifindex); /* handle default dst entry */ - if (rem_ip_changed || dst->remote_ifindex != new_ifindex) { + if (rem_ip_changed || cfg->remote_ifindex != conf.remote_ifindex) { spin_lock_bh(&vxlan->hash_lock); if (!vxlan_addr_any(&conf.remote_ip)) { err = vxlan_fdb_update(vxlan, all_zeros_mac, @@ -4609,23 +4616,24 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], NLM_F_APPEND | NLM_F_CREATE, cfg->dst_port, conf.vni, conf.vni, - new_ifindex, + conf.remote_ifindex, NTF_SELF, 0, true, extack); if (err) { spin_unlock_bh(&vxlan->hash_lock); - netdev_adjacent_change_abort(dst->remote_dev, - lowerdev, dev); + if (lowerdev_changed) + netdev_adjacent_change_abort(vxlan->lowerdev, + lowerdev, dev); kfree(new_cfg); return err; } } - if (!vxlan_addr_any(&dst->remote_ip)) + if (!vxlan_addr_any(&cfg->remote_ip)) __vxlan_fdb_delete(vxlan, all_zeros_mac, - dst->remote_ip, + cfg->remote_ip, cfg->dst_port, - dst->remote_vni, - dst->remote_vni, - dst->remote_ifindex, + cfg->vni, + cfg->vni, + cfg->remote_ifindex, true); spin_unlock_bh(&vxlan->hash_lock); @@ -4633,19 +4641,21 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], * all vnis */ if (cfg->flags & VXLAN_F_VNIFILTER) { - err = vxlan_vnilist_update_group(vxlan, &dst->remote_ip, + err = vxlan_vnilist_update_group(vxlan, &cfg->remote_ip, &conf.remote_ip, - dst->remote_ifindex, - new_ifindex, extack); + cfg->remote_ifindex, + conf.remote_ifindex, + extack); if (err) { vxlan_update_default_fdb_entry(vxlan, conf.vni, &conf.remote_ip, - &dst->remote_ip, - new_ifindex, - dst->remote_ifindex, + &cfg->remote_ip, + conf.remote_ifindex, + cfg->remote_ifindex, NULL); - netdev_adjacent_change_abort(dst->remote_dev, - lowerdev, dev); + if (lowerdev_changed) + netdev_adjacent_change_abort(vxlan->lowerdev, + lowerdev, dev); kfree(new_cfg); return err; } @@ -4653,20 +4663,26 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], } if (change_igmp && - (vxlan_addr_multicast(&dst->remote_ip) || + (vxlan_addr_multicast(&cfg->remote_ip) || (cfg->flags & VXLAN_F_VNIFILTER))) err = vxlan_multicast_leave(vxlan); if (netif_running(dev) && conf.age_interval != cfg->age_interval) mod_timer(&vxlan->age_timer, jiffies); - netdev_adjacent_change_commit(dst->remote_dev, lowerdev, dev); - if (lowerdev && lowerdev != dst->remote_dev) - dst->remote_dev = lowerdev; - vxlan_config_apply(dev, new_cfg, lowerdev, vxlan->net, true); + if (lowerdev_changed) { + if (lowerdev) + netdev_adjacent_change_commit(vxlan->lowerdev, lowerdev, + dev); + else + netdev_upper_dev_unlink(vxlan->lowerdev, dev); + vxlan->lowerdev = lowerdev; + } + vxlan_config_apply(dev, new_cfg, lowerdev, vxlan->net, true, + lowerdev_changed); if (change_igmp && - (vxlan_addr_multicast(&dst->remote_ip) || + (vxlan_addr_multicast(&new_cfg->remote_ip) || (new_cfg->flags & VXLAN_F_VNIFILTER))) { int join_err = vxlan_multicast_join(vxlan); @@ -4686,8 +4702,8 @@ static void vxlan_dellink(struct net_device *dev, struct list_head *head) list_del(&vxlan->next); unregister_netdevice_queue(dev, head); - if (vxlan->default_dst.remote_dev) - netdev_upper_dev_unlink(vxlan->default_dst.remote_dev, dev); + if (vxlan->lowerdev) + netdev_upper_dev_unlink(vxlan->lowerdev, dev); } static size_t vxlan_get_size(const struct net_device *dev) @@ -4731,30 +4747,29 @@ static size_t vxlan_get_size(const struct net_device *dev) static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) { const struct vxlan_dev *vxlan = netdev_priv(dev); - const struct vxlan_rdst *dst = &vxlan->default_dst; struct ifla_vxlan_port_range ports; const struct vxlan_config *cfg; cfg = rtnl_dereference(vxlan->cfg); - if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst->remote_vni))) + if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(cfg->vni))) goto nla_put_failure; - if (!vxlan_addr_any(&dst->remote_ip)) { - if (dst->remote_ip.sa.sa_family == AF_INET) { + if (!vxlan_addr_any(&cfg->remote_ip)) { + if (cfg->remote_ip.sa.sa_family == AF_INET) { if (nla_put_in_addr(skb, IFLA_VXLAN_GROUP, - dst->remote_ip.sin.sin_addr.s_addr)) + cfg->remote_ip.sin.sin_addr.s_addr)) goto nla_put_failure; #if IS_ENABLED(CONFIG_IPV6) } else { if (nla_put_in6_addr(skb, IFLA_VXLAN_GROUP6, - &dst->remote_ip.sin6.sin6_addr)) + &cfg->remote_ip.sin6.sin6_addr)) goto nla_put_failure; #endif } } - if (dst->remote_ifindex && nla_put_u32(skb, IFLA_VXLAN_LINK, dst->remote_ifindex)) + if (cfg->remote_ifindex && nla_put_u32(skb, IFLA_VXLAN_LINK, cfg->remote_ifindex)) goto nla_put_failure; if (!vxlan_addr_any(&cfg->saddr)) { @@ -4869,7 +4884,7 @@ static void vxlan_handle_lowerdev_unregister(struct vxlan_net *vn, LIST_HEAD(list_kill); list_for_each_entry_safe(vxlan, next, &vn->vxlan_list, next) { - struct vxlan_rdst *dst = &vxlan->default_dst; + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); /* In case we created vxlan device with carrier * and we loose the carrier due to module unload @@ -4877,7 +4892,7 @@ static void vxlan_handle_lowerdev_unregister(struct vxlan_net *vn, * cases, it's not necessary and remote_ifindex * is 0 here, so no matches. */ - if (dst->remote_ifindex == dev->ifindex) + if (cfg->remote_ifindex == dev->ifindex) vxlan_dellink(vxlan->dev, &list_kill); } diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index 4ae6369ed4e3..c1a055199055 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -195,7 +195,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan, be16_to_cpu(rd->remote_port))) goto nest_err; - if (rd->remote_vni != vxlan->default_dst.remote_vni && + if (rd->remote_vni != cfg->vni && nla_put_u32(skb, MDBA_MDB_EATTR_VNI, be32_to_cpu(rd->remote_vni))) goto nest_err; @@ -620,12 +620,12 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg, memset(cfg, 0, sizeof(*cfg)); cfg->vxlan = vxlan; - cfg->group.vni = vxlan->default_dst.remote_vni; + cfg->group.vni = vcfg->vni; INIT_LIST_HEAD(&cfg->src_list); cfg->nlflags = nlmsg_flags; cfg->filter_mode = MCAST_EXCLUDE; cfg->rt_protocol = RTPROT_STATIC; - cfg->remote_vni = vxlan->default_dst.remote_vni; + cfg->remote_vni = vcfg->vni; cfg->remote_port = vcfg->dst_port; if (entry->ifindex != dev->ifindex) { @@ -986,7 +986,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan, if (rd->remote_port && rd->remote_port != cfg->dst_port) nlmsg_size += nla_total_size(sizeof(u16)); /* MDBA_MDB_EATTR_VNI */ - if (rd->remote_vni != vxlan->default_dst.remote_vni) + if (rd->remote_vni != cfg->vni) nlmsg_size += nla_total_size(sizeof(u32)); /* MDBA_MDB_EATTR_IFINDEX */ if (rd->remote_ifindex) @@ -1488,11 +1488,13 @@ static int vxlan_mdb_get_parse(struct net_device *dev, struct nlattr *tb[], { struct br_mdb_entry *entry = nla_data(tb[MDBA_GET_ENTRY]); struct nlattr *mdbe_attrs[MDBE_ATTR_MAX + 1]; + const struct vxlan_config *cfg; struct vxlan_dev *vxlan = netdev_priv(dev); int err; + cfg = rtnl_dereference(vxlan->cfg); memset(group, 0, sizeof(*group)); - group->vni = vxlan->default_dst.remote_vni; + group->vni = cfg->vni; if (!tb[MDBA_GET_ENTRY_ATTRS]) { vxlan_mdb_group_set(group, entry, NULL); @@ -1641,7 +1643,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, * entries are stored with the VNI of the VXLAN device. */ if (!(cfg->flags & VXLAN_F_COLLECT_METADATA)) - src_vni = vxlan->default_dst.remote_vni; + src_vni = cfg->vni; memset(&group, 0, sizeof(group)); group.vni = src_vni; diff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c index c717540e95ab..a5ef6f66f00d 100644 --- a/drivers/net/vxlan/vxlan_multicast.c +++ b/drivers/net/vxlan/vxlan_multicast.c @@ -14,11 +14,12 @@ /* Update multicast group membership when first VNI on * multicast address is brought up */ -int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip, +int vxlan_igmp_join(struct vxlan_dev *vxlan, const union vxlan_addr *rip, int rifindex) { - union vxlan_addr *ip = (rip ? : &vxlan->default_dst.remote_ip); - int ifindex = (rifindex ? : vxlan->default_dst.remote_ifindex); + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); + const union vxlan_addr *ip = (rip ? : &cfg->remote_ip); + int ifindex = (rifindex ? : cfg->remote_ifindex); int ret = -EINVAL; struct sock *sk; @@ -47,11 +48,12 @@ int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip, return ret; } -int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip, +int vxlan_igmp_leave(struct vxlan_dev *vxlan, const union vxlan_addr *rip, int rifindex) { - union vxlan_addr *ip = (rip ? : &vxlan->default_dst.remote_ip); - int ifindex = (rifindex ? : vxlan->default_dst.remote_ifindex); + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); + const union vxlan_addr *ip = (rip ? : &cfg->remote_ip); + int ifindex = (rifindex ? : cfg->remote_ifindex); int ret = -EINVAL; struct sock *sk; @@ -80,8 +82,8 @@ int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip, return ret; } -static bool vxlan_group_used_match(union vxlan_addr *ip, int ifindex, - union vxlan_addr *rip, int rifindex) +static bool vxlan_group_used_match(const union vxlan_addr *ip, int ifindex, + const union vxlan_addr *rip, int rifindex) { if (!vxlan_addr_multicast(rip)) return false; @@ -96,14 +98,16 @@ static bool vxlan_group_used_match(union vxlan_addr *ip, int ifindex, } static bool vxlan_group_used_by_vnifilter(struct vxlan_dev *vxlan, - union vxlan_addr *ip, int ifindex) + const struct vxlan_config *cfg, + const union vxlan_addr *ip, + int ifindex) { struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); struct vxlan_vni_node *v, *tmp; if (vxlan_group_used_match(ip, ifindex, - &vxlan->default_dst.remote_ip, - vxlan->default_dst.remote_ifindex)) + &cfg->remote_ip, + cfg->remote_ifindex)) return true; list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { @@ -112,7 +116,7 @@ static bool vxlan_group_used_by_vnifilter(struct vxlan_dev *vxlan, if (vxlan_group_used_match(ip, ifindex, &v->remote_ip, - vxlan->default_dst.remote_ifindex)) + cfg->remote_ifindex)) return true; } @@ -121,16 +125,17 @@ static bool vxlan_group_used_by_vnifilter(struct vxlan_dev *vxlan, /* See if multicast group is already in use by other ID */ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev, - __be32 vni, union vxlan_addr *rip, int rifindex) + __be32 vni, const union vxlan_addr *rip, int rifindex) { - union vxlan_addr *ip = (rip ? : &dev->default_dst.remote_ip); - int ifindex = (rifindex ? : dev->default_dst.remote_ifindex); + const struct vxlan_config *dev_cfg = rtnl_dereference(dev->cfg); + const union vxlan_addr *ip = (rip ? : &dev_cfg->remote_ip); + int ifindex = (rifindex ? : dev_cfg->remote_ifindex); struct vxlan_dev *vxlan; struct vxlan_sock *sock4; #if IS_ENABLED(CONFIG_IPV6) struct vxlan_sock *sock6; #endif - unsigned short family = dev->default_dst.remote_ip.sa.sa_family; + unsigned short family = dev_cfg->remote_ip.sa.sa_family; sock4 = rtnl_dereference(dev->vn4_sock); @@ -163,12 +168,12 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev, cfg = rtnl_dereference(vxlan->cfg); if (cfg->flags & VXLAN_F_VNIFILTER) { - if (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex)) + if (!vxlan_group_used_by_vnifilter(vxlan, cfg, ip, ifindex)) continue; } else { if (!vxlan_group_used_match(ip, ifindex, - &vxlan->default_dst.remote_ip, - vxlan->default_dst.remote_ifindex)) + &cfg->remote_ip, + cfg->remote_ifindex)) continue; } @@ -178,7 +183,8 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev, return false; } -static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan) +static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan, + const struct vxlan_config *cfg) { struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); struct vxlan_vni_node *v, *tmp, *vgood = NULL; @@ -189,7 +195,7 @@ static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan) continue; /* skip if address is same as default address */ if (vxlan_addr_equal(&v->remote_ip, - &vxlan->default_dst.remote_ip)) + &cfg->remote_ip)) continue; ret = vxlan_igmp_join(vxlan, &v->remote_ip, 0); if (ret == -EADDRINUSE) @@ -204,7 +210,7 @@ out: if (!vxlan_addr_multicast(&v->remote_ip)) continue; if (vxlan_addr_equal(&v->remote_ip, - &vxlan->default_dst.remote_ip)) + &cfg->remote_ip)) continue; vxlan_igmp_leave(vxlan, &v->remote_ip, 0); if (v == vgood) @@ -215,7 +221,8 @@ out: return ret; } -static int vxlan_multicast_leave_vnigrp(struct vxlan_dev *vxlan) +static int vxlan_multicast_leave_vnigrp(struct vxlan_dev *vxlan, + const struct vxlan_config *cfg) { struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); @@ -227,7 +234,7 @@ static int vxlan_multicast_leave_vnigrp(struct vxlan_dev *vxlan) continue; /* skip if address is same as default address */ if (vxlan_addr_equal(&v->remote_ip, - &vxlan->default_dst.remote_ip)) + &cfg->remote_ip)) continue; if (!vxlan_group_used(vn, vxlan, v->vni, &v->remote_ip, 0)) { @@ -247,9 +254,9 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan) const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); int ret = 0; - if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) { - ret = vxlan_igmp_join(vxlan, &vxlan->default_dst.remote_ip, - vxlan->default_dst.remote_ifindex); + if (vxlan_addr_multicast(&cfg->remote_ip)) { + ret = vxlan_igmp_join(vxlan, &cfg->remote_ip, + cfg->remote_ifindex); if (ret == -EADDRINUSE) ret = 0; if (ret) @@ -257,7 +264,7 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan) } if (cfg->flags & VXLAN_F_VNIFILTER) - return vxlan_multicast_join_vnigrp(vxlan); + return vxlan_multicast_join_vnigrp(vxlan, cfg); return 0; } @@ -268,10 +275,10 @@ int vxlan_multicast_leave(struct vxlan_dev *vxlan) struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); int ret = 0; - if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip) && + if (vxlan_addr_multicast(&cfg->remote_ip) && !vxlan_group_used(vn, vxlan, 0, NULL, 0)) { - ret = vxlan_igmp_leave(vxlan, &vxlan->default_dst.remote_ip, - vxlan->default_dst.remote_ifindex); + ret = vxlan_igmp_leave(vxlan, &cfg->remote_ip, + cfg->remote_ifindex); if (ret == -EADDRNOTAVAIL) ret = 0; if (ret) @@ -279,7 +286,7 @@ int vxlan_multicast_leave(struct vxlan_dev *vxlan) } if (cfg->flags & VXLAN_F_VNIFILTER) - return vxlan_multicast_leave_vnigrp(vxlan); + return vxlan_multicast_leave_vnigrp(vxlan, cfg); return 0; } diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h index a2034ad418f7..f0ee85f83732 100644 --- a/drivers/net/vxlan/vxlan_private.h +++ b/drivers/net/vxlan/vxlan_private.h @@ -224,14 +224,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); +bool vxlan_vnifilter_has_multicast(const 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, + const union vxlan_addr *old_remote_ip, + const 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, + const union vxlan_addr *old_remote_ip, + const union vxlan_addr *new_remote_ip, u32 old_ifindex, u32 new_ifindex, struct netlink_ext_ack *extack); @@ -240,10 +241,10 @@ int vxlan_vnilist_update_group(struct vxlan_dev *vxlan, int vxlan_multicast_join(struct vxlan_dev *vxlan); int vxlan_multicast_leave(struct vxlan_dev *vxlan); bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev, - __be32 vni, union vxlan_addr *rip, int rifindex); -int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip, + __be32 vni, const union vxlan_addr *rip, int rifindex); +int vxlan_igmp_join(struct vxlan_dev *vxlan, const union vxlan_addr *rip, int rifindex); -int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip, +int vxlan_igmp_leave(struct vxlan_dev *vxlan, const union vxlan_addr *rip, int rifindex); /* vxlan_mdb.c */ diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index d16d5a6c7dad..41095096b698 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -502,9 +502,25 @@ static const struct nla_policy vni_filter_policy[VXLAN_VNIFILTER_MAX + 1] = { [VXLAN_VNIFILTER_ENTRY] = { .type = NLA_NESTED }, }; +bool vxlan_vnifilter_has_multicast(const struct vxlan_dev *vxlan) +{ + struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp); + struct vxlan_vni_node *v; + + if (!vg) + return false; + + list_for_each_entry(v, &vg->vni_list, vlist) { + if (vxlan_addr_multicast(&v->remote_ip)) + return true; + } + + return false; +} + int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni, - union vxlan_addr *old_remote_ip, - union vxlan_addr *remote_ip, + const union vxlan_addr *old_remote_ip, + const union vxlan_addr *remote_ip, u32 old_ifindex, u32 new_ifindex, struct netlink_ext_ack *extack) { @@ -518,8 +534,10 @@ int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni, spin_lock_bh(&vxlan->hash_lock); if (remote_ip && !vxlan_addr_any(remote_ip)) { + union vxlan_addr rip = *remote_ip; + err = vxlan_fdb_update(vxlan, all_zeros_mac, - remote_ip, + &rip, NUD_REACHABLE | NUD_PERMANENT, NLM_F_APPEND | NLM_F_CREATE, cfg->dst_port, @@ -553,8 +571,8 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan, struct netlink_ext_ack *extack) { struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); - struct vxlan_rdst *dst = &vxlan->default_dst; - union vxlan_addr *newrip = NULL, *oldrip = NULL; + const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); + const union vxlan_addr *newrip = NULL, *oldrip = NULL; union vxlan_addr old_remote_ip; int ret = 0; @@ -566,15 +584,15 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan, if (group && !vxlan_addr_any(group)) { newrip = group; } else { - if (!vxlan_addr_any(&dst->remote_ip)) - newrip = &dst->remote_ip; + if (!vxlan_addr_any(&cfg->remote_ip)) + newrip = &cfg->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; + else if (!vxlan_addr_any(&cfg->remote_ip)) + oldrip = &cfg->remote_ip; } if (!newrip && !oldrip) @@ -585,8 +603,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, + cfg->remote_ifindex, + cfg->remote_ifindex, extack); if (ret) goto out; @@ -601,7 +619,7 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan, if (vxlan_addr_multicast(&old_remote_ip) && !vxlan_group_used(vn, vxlan, vninode->vni, &old_remote_ip, - vxlan->default_dst.remote_ifindex)) { + cfg->remote_ifindex)) { ret = vxlan_igmp_leave(vxlan, &old_remote_ip, 0); if (ret == -EADDRNOTAVAIL) @@ -627,12 +645,12 @@ out: } int vxlan_vnilist_update_group(struct vxlan_dev *vxlan, - union vxlan_addr *old_remote_ip, - union vxlan_addr *new_remote_ip, + const union vxlan_addr *old_remote_ip, + const union vxlan_addr *new_remote_ip, u32 old_ifindex, u32 new_ifindex, struct netlink_ext_ack *extack) { - union vxlan_addr *oldrip, *newrip; + const union vxlan_addr *oldrip, *newrip; struct list_head *headp, *hpos; struct vxlan_vni_group *vg; struct vxlan_vni_node *vent; @@ -690,20 +708,19 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan, { struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg); - struct vxlan_rdst *dst = &vxlan->default_dst; /* if per vni remote_ip not present, delete the * default dst remote_ip previously added for this vni */ if (!vxlan_addr_any(&vninode->remote_ip) || - !vxlan_addr_any(&dst->remote_ip)) { + !vxlan_addr_any(&cfg->remote_ip)) { spin_lock_bh(&vxlan->hash_lock); __vxlan_fdb_delete(vxlan, all_zeros_mac, (vxlan_addr_any(&vninode->remote_ip) ? - dst->remote_ip : vninode->remote_ip), + cfg->remote_ip : vninode->remote_ip), cfg->dst_port, vninode->vni, vninode->vni, - dst->remote_ifindex, + cfg->remote_ifindex, true); spin_unlock_bh(&vxlan->hash_lock); } @@ -712,7 +729,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan, if (vxlan_addr_multicast(&vninode->remote_ip) && !vxlan_group_used(vn, vxlan, vninode->vni, &vninode->remote_ip, - dst->remote_ifindex)) { + cfg->remote_ifindex)) { vxlan_igmp_leave(vxlan, &vninode->remote_ip, 0); } } @@ -927,6 +944,7 @@ static int vxlan_process_vni_filter(struct vxlan_dev *vxlan, int cmd, struct netlink_ext_ack *extack) { struct nlattr *vattrs[VXLAN_VNIFILTER_ENTRY_MAX + 1]; + const struct vxlan_config *cfg; u32 vni_start = 0, vni_end = 0; union vxlan_addr group; int err; @@ -964,7 +982,8 @@ static int vxlan_process_vni_filter(struct vxlan_dev *vxlan, memset(&group, 0, sizeof(group)); } - if (vxlan_addr_multicast(&group) && !vxlan->default_dst.remote_ifindex) { + cfg = rtnl_dereference(vxlan->cfg); + if (vxlan_addr_multicast(&group) && !cfg->remote_ifindex) { NL_SET_ERR_MSG(extack, "Local interface required for multicast remote group"); diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 7ced743ec881..c3c9f2ccc3d1 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -204,7 +204,6 @@ struct vxlan_rdst { u8 offloaded:1; __be32 remote_vni; u32 remote_ifindex; - struct net_device *remote_dev; struct list_head list; struct rcu_head rcu; struct dst_cache dst_cache; @@ -295,7 +294,7 @@ struct vxlan_dev { #endif struct net_device *dev; struct net *net; /* netns for packet i/o */ - struct vxlan_rdst default_dst; /* default destination */ + struct net_device *lowerdev; struct timer_list age_timer; spinlock_t hash_lock; From 4b74deb12948aa4057da966e34fe23db3c9877af Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 22 Sep 2026 18:11:02 +0000 Subject: [PATCH 8/8] vxlan: no longer rely on RTNL in vxlan_fill_info() Now that vxlan->cfg is RCU-protected, we can update vxlan_fill_info() to run under RCU read lock instead of relying on RTNL. This completes the transition to RTNL-less link info dumping for VXLAN. Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260922181102.3989489-9-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index e8027b794e8c..27b0b6567d52 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -4750,7 +4750,8 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) struct ifla_vxlan_port_range ports; const struct vxlan_config *cfg; - cfg = rtnl_dereference(vxlan->cfg); + rcu_read_lock(); + cfg = rcu_dereference(vxlan->cfg); if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(cfg->vni))) goto nla_put_failure; @@ -4849,9 +4850,11 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) &cfg->reserved_bits)) goto nla_put_failure; + rcu_read_unlock(); return 0; nla_put_failure: + rcu_read_unlock(); return -EMSGSIZE; }