diff options
Diffstat (limited to 'net/core/rtnetlink.c')
| -rw-r--r-- | net/core/rtnetlink.c | 80 | 
1 files changed, 59 insertions, 21 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 65aebd45002..f965dce6f20 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -60,7 +60,6 @@ struct rtnl_link {  };  static DEFINE_MUTEX(rtnl_mutex); -static u16 min_ifinfo_dump_size;  void rtnl_lock(void)  { @@ -724,10 +723,11 @@ static void copy_rtnl_link_stats64(void *v, const struct rtnl_link_stats64 *b)  }  /* All VF info */ -static inline int rtnl_vfinfo_size(const struct net_device *dev) +static inline int rtnl_vfinfo_size(const struct net_device *dev, +				   u32 ext_filter_mask)  { -	if (dev->dev.parent && dev_is_pci(dev->dev.parent)) { - +	if (dev->dev.parent && dev_is_pci(dev->dev.parent) && +	    (ext_filter_mask & RTEXT_FILTER_VF)) {  		int num_vfs = dev_num_vf(dev->dev.parent);  		size_t size = nla_total_size(sizeof(struct nlattr));  		size += nla_total_size(num_vfs * sizeof(struct nlattr)); @@ -766,7 +766,8 @@ static size_t rtnl_port_size(const struct net_device *dev)  		return port_self_size;  } -static noinline size_t if_nlmsg_size(const struct net_device *dev) +static noinline size_t if_nlmsg_size(const struct net_device *dev, +				     u32 ext_filter_mask)  {  	return NLMSG_ALIGN(sizeof(struct ifinfomsg))  	       + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ @@ -784,8 +785,9 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev)  	       + nla_total_size(4) /* IFLA_MASTER */  	       + nla_total_size(1) /* IFLA_OPERSTATE */  	       + nla_total_size(1) /* IFLA_LINKMODE */ -	       + nla_total_size(4) /* IFLA_NUM_VF */ -	       + rtnl_vfinfo_size(dev) /* IFLA_VFINFO_LIST */ +	       + nla_total_size(ext_filter_mask +			        & RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */ +	       + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */  	       + rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */  	       + rtnl_link_get_size(dev) /* IFLA_LINKINFO */  	       + rtnl_link_get_af_size(dev); /* IFLA_AF_SPEC */ @@ -868,7 +870,7 @@ static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev)  static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,  			    int type, u32 pid, u32 seq, u32 change, -			    unsigned int flags) +			    unsigned int flags, u32 ext_filter_mask)  {  	struct ifinfomsg *ifm;  	struct nlmsghdr *nlh; @@ -941,10 +943,11 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,  		goto nla_put_failure;  	copy_rtnl_link_stats64(nla_data(attr), stats); -	if (dev->dev.parent) +	if (dev->dev.parent && (ext_filter_mask & RTEXT_FILTER_VF))  		NLA_PUT_U32(skb, IFLA_NUM_VF, dev_num_vf(dev->dev.parent)); -	if (dev->netdev_ops->ndo_get_vf_config && dev->dev.parent) { +	if (dev->netdev_ops->ndo_get_vf_config && dev->dev.parent +	    && (ext_filter_mask & RTEXT_FILTER_VF)) {  		int i;  		struct nlattr *vfinfo, *vf; @@ -1048,6 +1051,8 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)  	struct net_device *dev;  	struct hlist_head *head;  	struct hlist_node *node; +	struct nlattr *tb[IFLA_MAX+1]; +	u32 ext_filter_mask = 0;  	s_h = cb->args[0];  	s_idx = cb->args[1]; @@ -1055,6 +1060,13 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)  	rcu_read_lock();  	cb->seq = net->dev_base_seq; +	if (nlmsg_parse(cb->nlh, sizeof(struct rtgenmsg), tb, IFLA_MAX, +			ifla_policy) >= 0) { + +		if (tb[IFLA_EXT_MASK]) +			ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); +	} +  	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {  		idx = 0;  		head = &net->dev_index_head[h]; @@ -1064,7 +1076,8 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)  			if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,  					     NETLINK_CB(cb->skb).pid,  					     cb->nlh->nlmsg_seq, 0, -					     NLM_F_MULTI) <= 0) +					     NLM_F_MULTI, +					     ext_filter_mask) <= 0)  				goto out;  			nl_dump_check_consistent(cb, nlmsg_hdr(skb)); @@ -1100,6 +1113,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {  	[IFLA_VF_PORTS]		= { .type = NLA_NESTED },  	[IFLA_PORT_SELF]	= { .type = NLA_NESTED },  	[IFLA_AF_SPEC]		= { .type = NLA_NESTED }, +	[IFLA_EXT_MASK]		= { .type = NLA_U32 },  };  EXPORT_SYMBOL(ifla_policy); @@ -1509,8 +1523,6 @@ errout:  	if (send_addr_notify)  		call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); -	min_ifinfo_dump_size = max_t(u16, if_nlmsg_size(dev), -				     min_ifinfo_dump_size);  	return err;  } @@ -1842,6 +1854,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)  	struct net_device *dev = NULL;  	struct sk_buff *nskb;  	int err; +	u32 ext_filter_mask = 0;  	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy);  	if (err < 0) @@ -1850,6 +1863,9 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)  	if (tb[IFLA_IFNAME])  		nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); +	if (tb[IFLA_EXT_MASK]) +		ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); +  	ifm = nlmsg_data(nlh);  	if (ifm->ifi_index > 0)  		dev = __dev_get_by_index(net, ifm->ifi_index); @@ -1861,12 +1877,12 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)  	if (dev == NULL)  		return -ENODEV; -	nskb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL); +	nskb = nlmsg_new(if_nlmsg_size(dev, ext_filter_mask), GFP_KERNEL);  	if (nskb == NULL)  		return -ENOBUFS;  	err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).pid, -			       nlh->nlmsg_seq, 0, 0); +			       nlh->nlmsg_seq, 0, 0, ext_filter_mask);  	if (err < 0) {  		/* -EMSGSIZE implies BUG in if_nlmsg_size */  		WARN_ON(err == -EMSGSIZE); @@ -1877,8 +1893,32 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)  	return err;  } -static u16 rtnl_calcit(struct sk_buff *skb) +static u16 rtnl_calcit(struct sk_buff *skb, struct nlmsghdr *nlh)  { +	struct net *net = sock_net(skb->sk); +	struct net_device *dev; +	struct nlattr *tb[IFLA_MAX+1]; +	u32 ext_filter_mask = 0; +	u16 min_ifinfo_dump_size = 0; + +	if (nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, IFLA_MAX, +			ifla_policy) >= 0) { +		if (tb[IFLA_EXT_MASK]) +			ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); +	} + +	if (!ext_filter_mask) +		return NLMSG_GOODSIZE; +	/* +	 * traverse the list of net devices and compute the minimum +	 * buffer size based upon the filter mask. +	 */ +	list_for_each_entry(dev, &net->dev_base_head, dev_list) { +		min_ifinfo_dump_size = max_t(u16, min_ifinfo_dump_size, +					     if_nlmsg_size(dev, +						           ext_filter_mask)); +	} +  	return min_ifinfo_dump_size;  } @@ -1913,13 +1953,11 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change)  	int err = -ENOBUFS;  	size_t if_info_size; -	skb = nlmsg_new((if_info_size = if_nlmsg_size(dev)), GFP_KERNEL); +	skb = nlmsg_new((if_info_size = if_nlmsg_size(dev, 0)), GFP_KERNEL);  	if (skb == NULL)  		goto errout; -	min_ifinfo_dump_size = max_t(u16, if_info_size, min_ifinfo_dump_size); - -	err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0); +	err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0);  	if (err < 0) {  		/* -EMSGSIZE implies BUG in if_nlmsg_size() */  		WARN_ON(err == -EMSGSIZE); @@ -1977,7 +2015,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  			return -EOPNOTSUPP;  		calcit = rtnl_get_calcit(family, type);  		if (calcit) -			min_dump_alloc = calcit(skb); +			min_dump_alloc = calcit(skb, nlh);  		__rtnl_unlock();  		rtnl = net->rtnl;  |