net: ndo_fdb_dump should report -EMSGSIZE to rtnl_fdb_dump.

When the send skbuff reaches the end, nlmsg_put and friends returns
-EMSGSIZE but it is silently thrown away in ndo_fdb_dump. It is called
within a for_each_netdev loop and the first fdb entry of a following
netdev could fit in the remaining skbuff.  This breaks the mechanism
of cb->args[0] and idx to keep track of the entries that are already
dumped, which results missing entries in bridge fdb show command.

Signed-off-by: Minoura Makoto <minoura@valinux.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
MINOURA Makoto / 箕浦 真 2016-02-25 14:20:48 +09:00 committed by David S. Miller
parent 4c0b6eaf37
commit 472681d57a
4 changed files with 23 additions and 7 deletions

View File

@ -931,8 +931,10 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
cb->nlh->nlmsg_seq, cb->nlh->nlmsg_seq,
RTM_NEWNEIGH, RTM_NEWNEIGH,
NLM_F_MULTI, rd); NLM_F_MULTI, rd);
if (err < 0) if (err < 0) {
cb->args[1] = err;
goto out; goto out;
}
skip: skip:
++idx; ++idx;
} }

View File

@ -723,6 +723,8 @@ int br_fdb_dump(struct sk_buff *skb,
struct net_bridge_fdb_entry *f; struct net_bridge_fdb_entry *f;
hlist_for_each_entry_rcu(f, &br->hash[i], hlist) { hlist_for_each_entry_rcu(f, &br->hash[i], hlist) {
int err;
if (idx < cb->args[0]) if (idx < cb->args[0])
goto skip; goto skip;
@ -741,12 +743,15 @@ int br_fdb_dump(struct sk_buff *skb,
if (!filter_dev && f->dst) if (!filter_dev && f->dst)
goto skip; goto skip;
if (fdb_fill_info(skb, br, f, err = fdb_fill_info(skb, br, f,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, cb->nlh->nlmsg_seq,
RTM_NEWNEIGH, RTM_NEWNEIGH,
NLM_F_MULTI) < 0) NLM_F_MULTI);
if (err < 0) {
cb->args[1] = err;
break; break;
}
skip: skip:
++idx; ++idx;
} }

View File

@ -2911,6 +2911,7 @@ int ndo_dflt_fdb_dump(struct sk_buff *skb,
nlmsg_populate_fdb(skb, cb, dev, &idx, &dev->mc); nlmsg_populate_fdb(skb, cb, dev, &idx, &dev->mc);
out: out:
netif_addr_unlock_bh(dev); netif_addr_unlock_bh(dev);
cb->args[1] = err;
return idx; return idx;
} }
EXPORT_SYMBOL(ndo_dflt_fdb_dump); EXPORT_SYMBOL(ndo_dflt_fdb_dump);
@ -2944,6 +2945,7 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
ops = br_dev->netdev_ops; ops = br_dev->netdev_ops;
} }
cb->args[1] = 0;
for_each_netdev(net, dev) { for_each_netdev(net, dev) {
if (brport_idx && (dev->ifindex != brport_idx)) if (brport_idx && (dev->ifindex != brport_idx))
continue; continue;
@ -2971,12 +2973,16 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
idx = cops->ndo_fdb_dump(skb, cb, br_dev, dev, idx = cops->ndo_fdb_dump(skb, cb, br_dev, dev,
idx); idx);
} }
if (cb->args[1] == -EMSGSIZE)
break;
if (dev->netdev_ops->ndo_fdb_dump) if (dev->netdev_ops->ndo_fdb_dump)
idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, NULL, idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, NULL,
idx); idx);
else else
idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx); idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx);
if (cb->args[1] == -EMSGSIZE)
break;
cops = NULL; cops = NULL;
} }

View File

@ -1093,8 +1093,11 @@ int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
.cb = cb, .cb = cb,
.idx = idx, .idx = idx,
}; };
int err;
switchdev_port_obj_dump(dev, &dump.fdb.obj, switchdev_port_fdb_dump_cb); err = switchdev_port_obj_dump(dev, &dump.fdb.obj,
switchdev_port_fdb_dump_cb);
cb->args[1] = err;
return dump.idx; return dump.idx;
} }
EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump); EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump);