diff mbox series

[RFC,2] Validate required parameters in inet6_validate_link_af

Message ID 20190513150513.26872-3-maximmi@mellanox.com
State RFC
Delegated to: David Miller
Headers show
Series [RFC,2] Validate required parameters in inet6_validate_link_af | expand

Commit Message

Maxim Mikityanskiy May 13, 2019, 3:05 p.m. UTC
inet6_set_link_af requires that at least one of IFLA_INET6_TOKEN or
IFLA_INET6_ADDR_GET_MODE is passed. If none of them is passed, it
returns -EINVAL, which may cause do_setlink() to fail in the middle of
processing other commands and give the following warning message:

  A link change request failed with some changes committed already.
  Interface eth0 may have been left with an inconsistent configuration,
  please check.

Check the presence of at least one of them in inet6_validate_link_af to
detect invalid parameters at an early stage, before do_setlink does
anything. Also validate the address generation mode at an early stage.

Signed-off-by: Maxim Mikityanskiy <maximmi@mellanox.com>
---
 net/ipv6/addrconf.c | 61 +++++++++++++++++++++++++++++----------------
 1 file changed, 39 insertions(+), 22 deletions(-)

Comments

Jakub Kicinski May 14, 2019, 11:32 p.m. UTC | #1
On Mon, 13 May 2019 15:05:30 +0000, Maxim Mikityanskiy wrote:
> +	err = -EINVAL;
> +
> +	if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
> +		u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
> +
> +		if (check_addr_gen_mode(mode) < 0)
> +			return -EINVAL;
> +		if (dev && check_stable_privacy(idev, dev_net(dev), mode) < 0)
> +			return -EINVAL;
> +
> +		err = 0;
> +	}
> +
> +	if (tb[IFLA_INET6_TOKEN])
> +		err = 0;
> +
> +	return err;

While at it could you forgo the retval optimization?  Most of the time
it just leads to less readable code for no gain.

The normal way to write this code would be:

	if (!tb[IFLA_INET6_ADDR_GEN_MODE] && !tb[IFLA_INET6_TOKEN])
		return -EINVAL;

	if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
		u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);

		if (check_addr_gen_mode(mode) < 0)
			return -EINVAL;
		if (dev && check_stable_privacy(idev, dev_net(dev), mode) < 0)
			return -EINVAL;
	}

	return 0;
Maxim Mikityanskiy May 15, 2019, 8:14 a.m. UTC | #2
On 2019-05-15 02:32, Jakub Kicinski wrote:
> On Mon, 13 May 2019 15:05:30 +0000, Maxim Mikityanskiy wrote:
>> +	err = -EINVAL;
>> +
>> +	if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
>> +		u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
>> +
>> +		if (check_addr_gen_mode(mode) < 0)
>> +			return -EINVAL;
>> +		if (dev && check_stable_privacy(idev, dev_net(dev), mode) < 0)
>> +			return -EINVAL;
>> +
>> +		err = 0;
>> +	}
>> +
>> +	if (tb[IFLA_INET6_TOKEN])
>> +		err = 0;
>> +
>> +	return err;
> 
> While at it could you forgo the retval optimization?  Most of the time
> it just leads to less readable code for no gain.

OK, I'll make this change in a respin.

> The normal way to write this code would be:
> 
> 	if (!tb[IFLA_INET6_ADDR_GEN_MODE] && !tb[IFLA_INET6_TOKEN])
> 		return -EINVAL;

Yeah, that's how I wrote this check in RFC 1, but here in this patch I 
decided to preserve the pattern that was used in inet6_set_link_af 
before my change, to minimize the changes. I agree it's less readable (I 
didn't like the error handling flow in inet6_set_link_af either), so 
I'll fix it. Thanks for reviewing!

> 	if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
> 		u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
> 
> 		if (check_addr_gen_mode(mode) < 0)
> 			return -EINVAL;
> 		if (dev && check_stable_privacy(idev, dev_net(dev), mode) < 0)
> 			return -EINVAL;
> 	}
> 
> 	return 0;
>
diff mbox series

Patch

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index f96d1de79509..904bd6f5472f 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5661,18 +5661,6 @@  static const struct nla_policy inet6_af_policy[IFLA_INET6_MAX + 1] = {
 	[IFLA_INET6_TOKEN]		= { .len = sizeof(struct in6_addr) },
 };
 
-static int inet6_validate_link_af(const struct net_device *dev,
-				  const struct nlattr *nla)
-{
-	struct nlattr *tb[IFLA_INET6_MAX + 1];
-
-	if (dev && !__in6_dev_get(dev))
-		return -EAFNOSUPPORT;
-
-	return nla_parse_nested_deprecated(tb, IFLA_INET6_MAX, nla,
-					   inet6_af_policy, NULL);
-}
-
 static int check_addr_gen_mode(int mode)
 {
 	if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
@@ -5693,14 +5681,48 @@  static int check_stable_privacy(struct inet6_dev *idev, struct net *net,
 	return 1;
 }
 
+static int inet6_validate_link_af(const struct net_device *dev,
+				  const struct nlattr *nla)
+{
+	struct inet6_dev *idev = NULL;
+	struct nlattr *tb[IFLA_INET6_MAX + 1];
+	int err;
+
+	if (dev) {
+		idev = __in6_dev_get(dev);
+		if (!idev)
+			return -EAFNOSUPPORT;
+	}
+
+	err = nla_parse_nested_deprecated(tb, IFLA_INET6_MAX, nla,
+					  inet6_af_policy, NULL);
+	if (err)
+		return err;
+
+	err = -EINVAL;
+
+	if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
+		u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
+
+		if (check_addr_gen_mode(mode) < 0)
+			return -EINVAL;
+		if (dev && check_stable_privacy(idev, dev_net(dev), mode) < 0)
+			return -EINVAL;
+
+		err = 0;
+	}
+
+	if (tb[IFLA_INET6_TOKEN])
+		err = 0;
+
+	return err;
+}
+
 static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
 {
-	int err = -EINVAL;
 	struct inet6_dev *idev = __in6_dev_get(dev);
 	struct nlattr *tb[IFLA_INET6_MAX + 1];
-
-	if (!idev)
-		return -EAFNOSUPPORT;
+	int err;
 
 	if (nla_parse_nested_deprecated(tb, IFLA_INET6_MAX, nla, NULL, NULL) < 0)
 		BUG();
@@ -5714,15 +5736,10 @@  static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
 	if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
 		u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
 
-		if (check_addr_gen_mode(mode) < 0 ||
-		    check_stable_privacy(idev, dev_net(dev), mode) < 0)
-			return -EINVAL;
-
 		idev->cnf.addr_gen_mode = mode;
-		err = 0;
 	}
 
-	return err;
+	return 0;
 }
 
 static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,