diff options
Diffstat (limited to 'net/core/rtnetlink.c')
| -rw-r--r-- | net/core/rtnetlink.c | 246 | 
1 files changed, 140 insertions, 106 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 01ced4a889e0..a466821d1441 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -678,6 +678,12 @@ int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics)  					continue;  				if (nla_put_string(skb, i + 1, name))  					goto nla_put_failure; +			} else if (i == RTAX_FEATURES - 1) { +				u32 user_features = metrics[i] & RTAX_FEATURE_MASK; + +				BUILD_BUG_ON(RTAX_FEATURE_MASK & DST_FEATURE_MASK); +				if (nla_put_u32(skb, i + 1, user_features)) +					goto nla_put_failure;  			} else {  				if (nla_put_u32(skb, i + 1, metrics[i]))  					goto nla_put_failure; @@ -896,7 +902,9 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,  	       + rtnl_link_get_size(dev) /* IFLA_LINKINFO */  	       + rtnl_link_get_af_size(dev) /* IFLA_AF_SPEC */  	       + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_PORT_ID */ -	       + nla_total_size(MAX_PHYS_ITEM_ID_LEN); /* IFLA_PHYS_SWITCH_ID */ +	       + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_SWITCH_ID */ +	       + nla_total_size(1); /* IFLA_PROTO_DOWN */ +  }  static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev) @@ -1082,7 +1090,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,  	    (dev->ifalias &&  	     nla_put_string(skb, IFLA_IFALIAS, dev->ifalias)) ||  	    nla_put_u32(skb, IFLA_CARRIER_CHANGES, -			atomic_read(&dev->carrier_changes))) +			atomic_read(&dev->carrier_changes)) || +	    nla_put_u8(skb, IFLA_PROTO_DOWN, dev->proto_down))  		goto nla_put_failure;  	if (1) { @@ -1319,6 +1328,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {  	[IFLA_CARRIER_CHANGES]	= { .type = NLA_U32 },  /* ignored */  	[IFLA_PHYS_SWITCH_ID]	= { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN },  	[IFLA_LINK_NETNSID]	= { .type = NLA_S32 }, +	[IFLA_PROTO_DOWN]	= { .type = NLA_U8 },  };  static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { @@ -1328,10 +1338,6 @@ static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {  	[IFLA_INFO_SLAVE_DATA]	= { .type = NLA_NESTED },  }; -static const struct nla_policy ifla_vfinfo_policy[IFLA_VF_INFO_MAX+1] = { -	[IFLA_VF_INFO]		= { .type = NLA_NESTED }, -}; -  static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {  	[IFLA_VF_MAC]		= { .len = sizeof(struct ifla_vf_mac) },  	[IFLA_VF_VLAN]		= { .len = sizeof(struct ifla_vf_vlan) }, @@ -1488,96 +1494,98 @@ static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[])  	return 0;  } -static int do_setvfinfo(struct net_device *dev, struct nlattr *attr) +static int do_setvfinfo(struct net_device *dev, struct nlattr **tb)  { -	int rem, err = -EINVAL; -	struct nlattr *vf;  	const struct net_device_ops *ops = dev->netdev_ops; +	int err = -EINVAL; -	nla_for_each_nested(vf, attr, rem) { -		switch (nla_type(vf)) { -		case IFLA_VF_MAC: { -			struct ifla_vf_mac *ivm; -			ivm = nla_data(vf); -			err = -EOPNOTSUPP; -			if (ops->ndo_set_vf_mac) -				err = ops->ndo_set_vf_mac(dev, ivm->vf, -							  ivm->mac); -			break; -		} -		case IFLA_VF_VLAN: { -			struct ifla_vf_vlan *ivv; -			ivv = nla_data(vf); -			err = -EOPNOTSUPP; -			if (ops->ndo_set_vf_vlan) -				err = ops->ndo_set_vf_vlan(dev, ivv->vf, -							   ivv->vlan, -							   ivv->qos); -			break; -		} -		case IFLA_VF_TX_RATE: { -			struct ifla_vf_tx_rate *ivt; -			struct ifla_vf_info ivf; -			ivt = nla_data(vf); -			err = -EOPNOTSUPP; -			if (ops->ndo_get_vf_config) -				err = ops->ndo_get_vf_config(dev, ivt->vf, -							     &ivf); -			if (err) -				break; -			err = -EOPNOTSUPP; -			if (ops->ndo_set_vf_rate) -				err = ops->ndo_set_vf_rate(dev, ivt->vf, -							   ivf.min_tx_rate, -							   ivt->rate); -			break; -		} -		case IFLA_VF_RATE: { -			struct ifla_vf_rate *ivt; -			ivt = nla_data(vf); -			err = -EOPNOTSUPP; -			if (ops->ndo_set_vf_rate) -				err = ops->ndo_set_vf_rate(dev, ivt->vf, -							   ivt->min_tx_rate, -							   ivt->max_tx_rate); -			break; -		} -		case IFLA_VF_SPOOFCHK: { -			struct ifla_vf_spoofchk *ivs; -			ivs = nla_data(vf); -			err = -EOPNOTSUPP; -			if (ops->ndo_set_vf_spoofchk) -				err = ops->ndo_set_vf_spoofchk(dev, ivs->vf, -							       ivs->setting); -			break; -		} -		case IFLA_VF_LINK_STATE: { -			struct ifla_vf_link_state *ivl; -			ivl = nla_data(vf); -			err = -EOPNOTSUPP; -			if (ops->ndo_set_vf_link_state) -				err = ops->ndo_set_vf_link_state(dev, ivl->vf, -								 ivl->link_state); -			break; -		} -		case IFLA_VF_RSS_QUERY_EN: { -			struct ifla_vf_rss_query_en *ivrssq_en; +	if (tb[IFLA_VF_MAC]) { +		struct ifla_vf_mac *ivm = nla_data(tb[IFLA_VF_MAC]); -			ivrssq_en = nla_data(vf); -			err = -EOPNOTSUPP; -			if (ops->ndo_set_vf_rss_query_en) -				err = ops->ndo_set_vf_rss_query_en(dev, -							    ivrssq_en->vf, -							    ivrssq_en->setting); -			break; -		} -		default: -			err = -EINVAL; -			break; -		} -		if (err) -			break; +		err = -EOPNOTSUPP; +		if (ops->ndo_set_vf_mac) +			err = ops->ndo_set_vf_mac(dev, ivm->vf, +						  ivm->mac); +		if (err < 0) +			return err; +	} + +	if (tb[IFLA_VF_VLAN]) { +		struct ifla_vf_vlan *ivv = nla_data(tb[IFLA_VF_VLAN]); + +		err = -EOPNOTSUPP; +		if (ops->ndo_set_vf_vlan) +			err = ops->ndo_set_vf_vlan(dev, ivv->vf, ivv->vlan, +						   ivv->qos); +		if (err < 0) +			return err;  	} + +	if (tb[IFLA_VF_TX_RATE]) { +		struct ifla_vf_tx_rate *ivt = nla_data(tb[IFLA_VF_TX_RATE]); +		struct ifla_vf_info ivf; + +		err = -EOPNOTSUPP; +		if (ops->ndo_get_vf_config) +			err = ops->ndo_get_vf_config(dev, ivt->vf, &ivf); +		if (err < 0) +			return err; + +		err = -EOPNOTSUPP; +		if (ops->ndo_set_vf_rate) +			err = ops->ndo_set_vf_rate(dev, ivt->vf, +						   ivf.min_tx_rate, +						   ivt->rate); +		if (err < 0) +			return err; +	} + +	if (tb[IFLA_VF_RATE]) { +		struct ifla_vf_rate *ivt = nla_data(tb[IFLA_VF_RATE]); + +		err = -EOPNOTSUPP; +		if (ops->ndo_set_vf_rate) +			err = ops->ndo_set_vf_rate(dev, ivt->vf, +						   ivt->min_tx_rate, +						   ivt->max_tx_rate); +		if (err < 0) +			return err; +	} + +	if (tb[IFLA_VF_SPOOFCHK]) { +		struct ifla_vf_spoofchk *ivs = nla_data(tb[IFLA_VF_SPOOFCHK]); + +		err = -EOPNOTSUPP; +		if (ops->ndo_set_vf_spoofchk) +			err = ops->ndo_set_vf_spoofchk(dev, ivs->vf, +						       ivs->setting); +		if (err < 0) +			return err; +	} + +	if (tb[IFLA_VF_LINK_STATE]) { +		struct ifla_vf_link_state *ivl = nla_data(tb[IFLA_VF_LINK_STATE]); + +		err = -EOPNOTSUPP; +		if (ops->ndo_set_vf_link_state) +			err = ops->ndo_set_vf_link_state(dev, ivl->vf, +							 ivl->link_state); +		if (err < 0) +			return err; +	} + +	if (tb[IFLA_VF_RSS_QUERY_EN]) { +		struct ifla_vf_rss_query_en *ivrssq_en; + +		err = -EOPNOTSUPP; +		ivrssq_en = nla_data(tb[IFLA_VF_RSS_QUERY_EN]); +		if (ops->ndo_set_vf_rss_query_en) +			err = ops->ndo_set_vf_rss_query_en(dev, ivrssq_en->vf, +							   ivrssq_en->setting); +		if (err < 0) +			return err; +	} +  	return err;  } @@ -1773,14 +1781,21 @@ static int do_setlink(const struct sk_buff *skb,  	}  	if (tb[IFLA_VFINFO_LIST]) { +		struct nlattr *vfinfo[IFLA_VF_MAX + 1];  		struct nlattr *attr;  		int rem; +  		nla_for_each_nested(attr, tb[IFLA_VFINFO_LIST], rem) { -			if (nla_type(attr) != IFLA_VF_INFO) { +			if (nla_type(attr) != IFLA_VF_INFO || +			    nla_len(attr) < NLA_HDRLEN) {  				err = -EINVAL;  				goto errout;  			} -			err = do_setvfinfo(dev, attr); +			err = nla_parse_nested(vfinfo, IFLA_VF_MAX, attr, +					       ifla_vf_policy); +			if (err < 0) +				goto errout; +			err = do_setvfinfo(dev, vfinfo);  			if (err < 0)  				goto errout;  			status |= DO_SETLINK_NOTIFY; @@ -1799,10 +1814,13 @@ static int do_setlink(const struct sk_buff *skb,  			goto errout;  		nla_for_each_nested(attr, tb[IFLA_VF_PORTS], rem) { -			if (nla_type(attr) != IFLA_VF_PORT) -				continue; -			err = nla_parse_nested(port, IFLA_PORT_MAX, -				attr, ifla_port_policy); +			if (nla_type(attr) != IFLA_VF_PORT || +			    nla_len(attr) < NLA_HDRLEN) { +				err = -EINVAL; +				goto errout; +			} +			err = nla_parse_nested(port, IFLA_PORT_MAX, attr, +					       ifla_port_policy);  			if (err < 0)  				goto errout;  			if (!port[IFLA_PORT_VF]) { @@ -1853,6 +1871,14 @@ static int do_setlink(const struct sk_buff *skb,  	}  	err = 0; +	if (tb[IFLA_PROTO_DOWN]) { +		err = dev_change_proto_down(dev, +					    nla_get_u8(tb[IFLA_PROTO_DOWN])); +		if (err) +			goto errout; +		status |= DO_SETLINK_NOTIFY; +	} +  errout:  	if (status & DO_SETLINK_MODIFIED) {  		if (status & DO_SETLINK_NOTIFY) @@ -1943,16 +1969,30 @@ static int rtnl_group_dellink(const struct net *net, int group)  	return 0;  } +int rtnl_delete_link(struct net_device *dev) +{ +	const struct rtnl_link_ops *ops; +	LIST_HEAD(list_kill); + +	ops = dev->rtnl_link_ops; +	if (!ops || !ops->dellink) +		return -EOPNOTSUPP; + +	ops->dellink(dev, &list_kill); +	unregister_netdevice_many(&list_kill); + +	return 0; +} +EXPORT_SYMBOL_GPL(rtnl_delete_link); +  static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)  {  	struct net *net = sock_net(skb->sk); -	const struct rtnl_link_ops *ops;  	struct net_device *dev;  	struct ifinfomsg *ifm;  	char ifname[IFNAMSIZ];  	struct nlattr *tb[IFLA_MAX+1];  	int err; -	LIST_HEAD(list_kill);  	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy);  	if (err < 0) @@ -1974,13 +2014,7 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)  	if (!dev)  		return -ENODEV; -	ops = dev->rtnl_link_ops; -	if (!ops || !ops->dellink) -		return -EOPNOTSUPP; - -	ops->dellink(dev, &list_kill); -	unregister_netdevice_many(&list_kill); -	return 0; +	return rtnl_delete_link(dev);  }  int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)  |