diff mbox series

[net-next,6/6] ethtool: specify which header flags are supported per command

Message ID 20201005155753.2333882-7-kuba@kernel.org
State New
Headers show
Series ethtool: allow dumping policies to user space | expand

Commit Message

Jakub Kicinski Oct. 5, 2020, 3:57 p.m. UTC
Perform header flags validation through the policy.

Only pause command supports ETHTOOL_FLAG_STATS. Create a separate
policy to be able to express that in policy dumps to user space.

Note that even though the core will validate the header policy,
it cannot record multiple layers of attributes and we have to
re-parse header sub-attrs. When doing so we could skip attribute
validation, or use most permissive policy.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 net/ethtool/netlink.c | 31 +++++++++++++++++++++----------
 net/ethtool/netlink.h |  1 +
 net/ethtool/pause.c   |  2 +-
 3 files changed, 23 insertions(+), 11 deletions(-)

Comments

Johannes Berg Oct. 5, 2020, 6:58 p.m. UTC | #1
On Mon, 2020-10-05 at 08:57 -0700, Jakub Kicinski wrote:
> 
> @@ -47,19 +61,16 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
>  		NL_SET_ERR_MSG(extack, "request header missing");
>  		return -EINVAL;
>  	}
> +	/* Use most permissive header policy here, ops should specify their
> +	 * actual header policy via NLA_POLICY_NESTED(), and the real
> +	 * validation will happen in genetlink code.
> +	 */
>  	ret = nla_parse_nested(tb, ETHTOOL_A_HEADER_MAX, header,
> -			       ethnl_header_policy, extack);
> +			       ethnl_header_policy_stats, extack);

Would it make sense to just remove the validation here? It's already
done, so it just costs extra cycles and can't really fail, and if there
are more diverse policies in the future this might also very quickly get
out of hand?

johannes
Jakub Kicinski Oct. 5, 2020, 7:25 p.m. UTC | #2
On Mon, 05 Oct 2020 20:58:57 +0200 Johannes Berg wrote:
> On Mon, 2020-10-05 at 08:57 -0700, Jakub Kicinski wrote:
> > 
> > @@ -47,19 +61,16 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
> >  		NL_SET_ERR_MSG(extack, "request header missing");
> >  		return -EINVAL;
> >  	}
> > +	/* Use most permissive header policy here, ops should specify their
> > +	 * actual header policy via NLA_POLICY_NESTED(), and the real
> > +	 * validation will happen in genetlink code.
> > +	 */
> >  	ret = nla_parse_nested(tb, ETHTOOL_A_HEADER_MAX, header,
> > -			       ethnl_header_policy, extack);
> > +			       ethnl_header_policy_stats, extack);  
> 
> Would it make sense to just remove the validation here? It's already
> done, so it just costs extra cycles and can't really fail, and if there
> are more diverse policies in the future this might also very quickly get
> out of hand?

I was slightly worried I missed a command and need last line of defence,
but you're right, I'll just pass a NULL for the policy in v2 :)
Johannes Berg Oct. 5, 2020, 7:28 p.m. UTC | #3
On Mon, 2020-10-05 at 12:25 -0700, Jakub Kicinski wrote:
> On Mon, 05 Oct 2020 20:58:57 +0200 Johannes Berg wrote:

> > On Mon, 2020-10-05 at 08:57 -0700, Jakub Kicinski wrote:

> > > @@ -47,19 +61,16 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,

> > >  		NL_SET_ERR_MSG(extack, "request header missing");

> > >  		return -EINVAL;

> > >  	}

> > > +	/* Use most permissive header policy here, ops should specify their

> > > +	 * actual header policy via NLA_POLICY_NESTED(), and the real

> > > +	 * validation will happen in genetlink code.

> > > +	 */

> > >  	ret = nla_parse_nested(tb, ETHTOOL_A_HEADER_MAX, header,

> > > -			       ethnl_header_policy, extack);

> > > +			       ethnl_header_policy_stats, extack);  

> > 

> > Would it make sense to just remove the validation here? It's already

> > done, so it just costs extra cycles and can't really fail, and if there

> > are more diverse policies in the future this might also very quickly get

> > out of hand?

> 

> I was slightly worried I missed a command and need last line of defence,


Ah. I was just about to suggest to put it into the family policy/maxattr
but that won't work of course since this is nested.

But actually what you _could_ put there is a dummy policy (non-NULL
pointer) with a maxattr of 0, and then all attrs will be completely
rejected for a command where the policy was missed.

Not if you missed the NLA_POLICY_NESTED() link, though.

johannes
diff mbox series

Patch

diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index e78ff7ce2a7d..b972789d6b8d 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -9,12 +9,26 @@  static struct genl_family ethtool_genl_family;
 static bool ethnl_ok __read_mostly;
 static u32 ethnl_bcast_seq;
 
+#define ETHTOOL_FLAGS_BASIC (ETHTOOL_FLAG_COMPACT_BITSETS |	\
+			     ETHTOOL_FLAG_OMIT_REPLY)
+#define ETHTOOL_FLAGS_STATS (ETHTOOL_FLAGS_BASIC | ETHTOOL_FLAG_STATS)
+
 const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_MAX + 1] = {
 	[ETHTOOL_A_HEADER_UNSPEC]	= { .type = NLA_REJECT },
 	[ETHTOOL_A_HEADER_DEV_INDEX]	= { .type = NLA_U32 },
 	[ETHTOOL_A_HEADER_DEV_NAME]	= { .type = NLA_NUL_STRING,
 					    .len = ALTIFNAMSIZ - 1 },
-	[ETHTOOL_A_HEADER_FLAGS]	= { .type = NLA_U32 },
+	[ETHTOOL_A_HEADER_FLAGS]	= NLA_POLICY_MASK(NLA_U32,
+							  ETHTOOL_FLAGS_BASIC),
+};
+
+const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_MAX + 1] = {
+	[ETHTOOL_A_HEADER_UNSPEC]	= { .type = NLA_REJECT },
+	[ETHTOOL_A_HEADER_DEV_INDEX]	= { .type = NLA_U32 },
+	[ETHTOOL_A_HEADER_DEV_NAME]	= { .type = NLA_NUL_STRING,
+					    .len = ALTIFNAMSIZ - 1 },
+	[ETHTOOL_A_HEADER_FLAGS]	= NLA_POLICY_MASK(NLA_U32,
+							  ETHTOOL_FLAGS_STATS),
 };
 
 /**
@@ -47,19 +61,16 @@  int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
 		NL_SET_ERR_MSG(extack, "request header missing");
 		return -EINVAL;
 	}
+	/* Use most permissive header policy here, ops should specify their
+	 * actual header policy via NLA_POLICY_NESTED(), and the real
+	 * validation will happen in genetlink code.
+	 */
 	ret = nla_parse_nested(tb, ETHTOOL_A_HEADER_MAX, header,
-			       ethnl_header_policy, extack);
+			       ethnl_header_policy_stats, extack);
 	if (ret < 0)
 		return ret;
-	if (tb[ETHTOOL_A_HEADER_FLAGS]) {
+	if (tb[ETHTOOL_A_HEADER_FLAGS])
 		flags = nla_get_u32(tb[ETHTOOL_A_HEADER_FLAGS]);
-		if (flags & ~ETHTOOL_FLAG_ALL) {
-			NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_HEADER_FLAGS],
-					    "unrecognized request flags");
-			nl_set_extack_cookie_u32(extack, ETHTOOL_FLAG_ALL);
-			return -EOPNOTSUPP;
-		}
-	}
 
 	devname_attr = tb[ETHTOOL_A_HEADER_DEV_NAME];
 	if (tb[ETHTOOL_A_HEADER_DEV_INDEX]) {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 4fcd2e8b259b..f04d94e5fa77 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -348,6 +348,7 @@  extern const struct ethnl_request_ops ethnl_eee_request_ops;
 extern const struct ethnl_request_ops ethnl_tsinfo_request_ops;
 
 extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_MAX + 1];
+extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_MAX + 1];
 extern const struct nla_policy ethnl_strset_get_policy[ETHTOOL_A_STRSET_MAX + 1];
 extern const struct nla_policy ethnl_linkinfo_get_policy[ETHTOOL_A_LINKINFO_MAX + 1];
 extern const struct nla_policy ethnl_linkinfo_set_policy[ETHTOOL_A_LINKINFO_MAX + 1];
diff --git a/net/ethtool/pause.c b/net/ethtool/pause.c
index a7fbe0e4dca6..a5c2abddc992 100644
--- a/net/ethtool/pause.c
+++ b/net/ethtool/pause.c
@@ -19,7 +19,7 @@  struct pause_reply_data {
 const struct nla_policy ethnl_pause_get_policy[ETHTOOL_A_PAUSE_MAX + 1] = {
 	[ETHTOOL_A_PAUSE_UNSPEC]		= { .type = NLA_REJECT },
 	[ETHTOOL_A_PAUSE_HEADER]		=
-		NLA_POLICY_NESTED(ethnl_header_policy),
+		NLA_POLICY_NESTED(ethnl_header_policy_stats),
 	[ETHTOOL_A_PAUSE_AUTONEG]		= { .type = NLA_REJECT },
 	[ETHTOOL_A_PAUSE_RX]			= { .type = NLA_REJECT },
 	[ETHTOOL_A_PAUSE_TX]			= { .type = NLA_REJECT },