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. Fixes: f9c4bb0b245c ("vxlan: vni filtering support on collect metadata device") Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima --- 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 dd94085e088656d27b62420a5c8c95c609510a4c..0a18c32902da08dc2732e2aea10d58b8753fe57f 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 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev, } cb->args[1] = err ? idx : 0; + cb->args[2] = err ? dev->ifindex : 0; nlmsg_end(skb, nlh); -- 2.55.0.1007.g17ff1f9808-goog