Blame SOURCES/0032-netlink-use-policy-dumping-to-check-if-stats-flag-is.patch

7e9df8
From 5966f5dae0dd173fdc7fe34af5f400e36fe782c7 Mon Sep 17 00:00:00 2001
7e9df8
From: Jakub Kicinski <kuba@kernel.org>
7e9df8
Date: Sun, 18 Oct 2020 14:31:50 -0700
7e9df8
Subject: [PATCH 32/37] netlink: use policy dumping to check if stats flag is
7e9df8
 supported
7e9df8
7e9df8
Older kernels don't support statistics, to avoid retries
7e9df8
make use of netlink policy dumps to figure out which
7e9df8
flags kernel actually supports.
7e9df8
7e9df8
v3:
7e9df8
 - s/ctx/policy_ctx/
7e9df8
 - save the flags in nl_context to be able to reuse them,
7e9df8
   and not have to return errors and values from the policy
7e9df8
   get function
7e9df8
7e9df8
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
7e9df8
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
7e9df8
(cherry picked from commit 5c90128a47d7c96cc4dd2c4ad26a0fed1ab60940)
7e9df8
---
7e9df8
 netlink/msgbuff.h |   6 ++
7e9df8
 netlink/netlink.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++
7e9df8
 netlink/netlink.h |   4 ++
7e9df8
 3 files changed, 164 insertions(+)
7e9df8
7e9df8
diff --git a/netlink/msgbuff.h b/netlink/msgbuff.h
7e9df8
index 24b99c5a28d7..7d6731fc24a3 100644
7e9df8
--- a/netlink/msgbuff.h
7e9df8
+++ b/netlink/msgbuff.h
7e9df8
@@ -81,6 +81,12 @@ static inline bool ethnla_put_u32(struct nl_msg_buff *msgbuff, uint16_t type,
7e9df8
 	return ethnla_put(msgbuff, type, sizeof(uint32_t), &data);
7e9df8
 }
7e9df8
 
7e9df8
+static inline bool ethnla_put_u16(struct nl_msg_buff *msgbuff, uint16_t type,
7e9df8
+				  uint16_t data)
7e9df8
+{
7e9df8
+	return ethnla_put(msgbuff, type, sizeof(uint16_t), &data);
7e9df8
+}
7e9df8
+
7e9df8
 static inline bool ethnla_put_u8(struct nl_msg_buff *msgbuff, uint16_t type,
7e9df8
 				 uint8_t data)
7e9df8
 {
7e9df8
diff --git a/netlink/netlink.c b/netlink/netlink.c
7e9df8
index 5677274c2fce..ffe06339f099 100644
7e9df8
--- a/netlink/netlink.c
7e9df8
+++ b/netlink/netlink.c
7e9df8
@@ -135,6 +135,160 @@ bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd,
7e9df8
 	return !(nlctx->ops_info[cmd].op_flags & cap);
7e9df8
 }
7e9df8
 
7e9df8
+struct ethtool_op_policy_query_ctx {
7e9df8
+	struct nl_context *nlctx;
7e9df8
+	unsigned int op;
7e9df8
+	unsigned int op_hdr_attr;
7e9df8
+
7e9df8
+	bool op_policy_found;
7e9df8
+	bool hdr_policy_found;
7e9df8
+	unsigned int op_policy_idx;
7e9df8
+	unsigned int hdr_policy_idx;
7e9df8
+	uint64_t flag_mask;
7e9df8
+};
7e9df8
+
7e9df8
+static int family_policy_find_op(struct ethtool_op_policy_query_ctx *policy_ctx,
7e9df8
+				 const struct nlattr *op_policy)
7e9df8
+{
7e9df8
+	const struct nlattr *attr;
7e9df8
+	unsigned int type;
7e9df8
+	int ret;
7e9df8
+
7e9df8
+	type = policy_ctx->nlctx->is_dump ?
7e9df8
+		CTRL_ATTR_POLICY_DUMP : CTRL_ATTR_POLICY_DO;
7e9df8
+
7e9df8
+	mnl_attr_for_each_nested(attr, op_policy) {
7e9df8
+		const struct nlattr *tb[CTRL_ATTR_POLICY_DUMP_MAX + 1] = {};
7e9df8
+		DECLARE_ATTR_TB_INFO(tb);
7e9df8
+
7e9df8
+		if (mnl_attr_get_type(attr) != policy_ctx->op)
7e9df8
+			continue;
7e9df8
+
7e9df8
+		ret = mnl_attr_parse_nested(attr, attr_cb, &tb_info);
7e9df8
+		if (ret < 0)
7e9df8
+			return ret;
7e9df8
+
7e9df8
+		if (!tb[type])
7e9df8
+			continue;
7e9df8
+
7e9df8
+		policy_ctx->op_policy_found = true;
7e9df8
+		policy_ctx->op_policy_idx = mnl_attr_get_u32(tb[type]);
7e9df8
+		break;
7e9df8
+	}
7e9df8
+
7e9df8
+	return 0;
7e9df8
+}
7e9df8
+
7e9df8
+static int family_policy_cb(const struct nlmsghdr *nlhdr, void *data)
7e9df8
+{
7e9df8
+	const struct nlattr *tba[NL_POLICY_TYPE_ATTR_MAX + 1] = {};
7e9df8
+	DECLARE_ATTR_TB_INFO(tba);
7e9df8
+	const struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
7e9df8
+	DECLARE_ATTR_TB_INFO(tb);
7e9df8
+	struct ethtool_op_policy_query_ctx *policy_ctx = data;
7e9df8
+	const struct nlattr *policy_attr, *attr_attr, *attr;
7e9df8
+	unsigned int attr_idx, policy_idx;
7e9df8
+	int ret;
7e9df8
+
7e9df8
+	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
7e9df8
+	if (ret < 0)
7e9df8
+		return MNL_CB_ERROR;
7e9df8
+
7e9df8
+	if (!policy_ctx->op_policy_found) {
7e9df8
+		if (!tb[CTRL_ATTR_OP_POLICY]) {
7e9df8
+			fprintf(stderr, "Error: op policy map not present\n");
7e9df8
+			return MNL_CB_ERROR;
7e9df8
+		}
7e9df8
+		ret = family_policy_find_op(policy_ctx, tb[CTRL_ATTR_OP_POLICY]);
7e9df8
+		return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK;
7e9df8
+	}
7e9df8
+
7e9df8
+	if (!tb[CTRL_ATTR_POLICY])
7e9df8
+		return MNL_CB_OK;
7e9df8
+
7e9df8
+	policy_attr = mnl_attr_get_payload(tb[CTRL_ATTR_POLICY]);
7e9df8
+	policy_idx = mnl_attr_get_type(policy_attr);
7e9df8
+	attr_attr = mnl_attr_get_payload(policy_attr);
7e9df8
+	attr_idx = mnl_attr_get_type(attr_attr);
7e9df8
+
7e9df8
+	ret = mnl_attr_parse_nested(attr_attr, attr_cb, &tba_info);
7e9df8
+	if (ret < 0)
7e9df8
+		return MNL_CB_ERROR;
7e9df8
+
7e9df8
+	if (policy_idx == policy_ctx->op_policy_idx &&
7e9df8
+	    attr_idx == policy_ctx->op_hdr_attr) {
7e9df8
+		attr = tba[NL_POLICY_TYPE_ATTR_POLICY_IDX];
7e9df8
+		if (!attr) {
7e9df8
+			fprintf(stderr,	"Error: no policy index in what was expected to be ethtool header attribute\n");
7e9df8
+			return MNL_CB_ERROR;
7e9df8
+		}
7e9df8
+		policy_ctx->hdr_policy_found = true;
7e9df8
+		policy_ctx->hdr_policy_idx = mnl_attr_get_u32(attr);
7e9df8
+	}
7e9df8
+
7e9df8
+	if (policy_ctx->hdr_policy_found &&
7e9df8
+	    policy_ctx->hdr_policy_idx == policy_idx &&
7e9df8
+	    attr_idx == ETHTOOL_A_HEADER_FLAGS) {
7e9df8
+		attr = tba[NL_POLICY_TYPE_ATTR_MASK];
7e9df8
+		if (!attr) {
7e9df8
+			fprintf(stderr,	"Error: validation mask not reported for ethtool header flags\n");
7e9df8
+			return MNL_CB_ERROR;
7e9df8
+		}
7e9df8
+
7e9df8
+		policy_ctx->flag_mask = mnl_attr_get_u64(attr);
7e9df8
+	}
7e9df8
+
7e9df8
+	return MNL_CB_OK;
7e9df8
+}
7e9df8
+
7e9df8
+static int read_flags_policy(struct nl_context *nlctx, struct nl_socket *nlsk,
7e9df8
+			     unsigned int nlcmd, unsigned int hdrattr)
7e9df8
+{
7e9df8
+	struct ethtool_op_policy_query_ctx policy_ctx;
7e9df8
+	struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
7e9df8
+	int ret;
7e9df8
+
7e9df8
+	if (nlctx->ops_info[nlcmd].hdr_policy_loaded)
7e9df8
+		return 0;
7e9df8
+
7e9df8
+	memset(&policy_ctx, 0, sizeof(policy_ctx));
7e9df8
+	policy_ctx.nlctx = nlctx;
7e9df8
+	policy_ctx.op = nlcmd;
7e9df8
+	policy_ctx.op_hdr_attr = hdrattr;
7e9df8
+
7e9df8
+	ret = __msg_init(msgbuff, GENL_ID_CTRL, CTRL_CMD_GETPOLICY,
7e9df8
+			 NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP, 1);
7e9df8
+	if (ret < 0)
7e9df8
+		return ret;
7e9df8
+	ret = -EMSGSIZE;
7e9df8
+	if (ethnla_put_u16(msgbuff, CTRL_ATTR_FAMILY_ID, nlctx->ethnl_fam))
7e9df8
+		return ret;
7e9df8
+	if (ethnla_put_u32(msgbuff, CTRL_ATTR_OP, nlcmd))
7e9df8
+		return ret;
7e9df8
+
7e9df8
+	nlsock_sendmsg(nlsk, NULL);
7e9df8
+	nlsock_process_reply(nlsk, family_policy_cb, &policy_ctx);
7e9df8
+
7e9df8
+	nlctx->ops_info[nlcmd].hdr_policy_loaded = 1;
7e9df8
+	nlctx->ops_info[nlcmd].hdr_flags = policy_ctx.flag_mask;
7e9df8
+	return 0;
7e9df8
+}
7e9df8
+
7e9df8
+u32 get_stats_flag(struct nl_context *nlctx, unsigned int nlcmd,
7e9df8
+		   unsigned int hdrattr)
7e9df8
+{
7e9df8
+	if (!nlctx->ctx->show_stats)
7e9df8
+		return 0;
7e9df8
+	if (nlcmd > ETHTOOL_MSG_USER_MAX ||
7e9df8
+	    !(nlctx->ops_info[nlcmd].op_flags & GENL_CMD_CAP_HASPOL))
7e9df8
+		return 0;
7e9df8
+
7e9df8
+	if (read_flags_policy(nlctx, nlctx->ethnl_socket, nlcmd, hdrattr) < 0)
7e9df8
+		return 0;
7e9df8
+
7e9df8
+	return nlctx->ops_info[nlcmd].hdr_flags & ETHTOOL_FLAG_STATS;
7e9df8
+}
7e9df8
+
7e9df8
 /* initialization */
7e9df8
 
7e9df8
 static int genl_read_ops(struct nl_context *nlctx,
7e9df8
diff --git a/netlink/netlink.h b/netlink/netlink.h
7e9df8
index e79143016bd5..c02558540218 100644
7e9df8
--- a/netlink/netlink.h
7e9df8
+++ b/netlink/netlink.h
7e9df8
@@ -27,6 +27,8 @@ enum link_mode_class {
7e9df8
 
7e9df8
 struct nl_op_info {
7e9df8
 	uint32_t		op_flags;
7e9df8
+	uint32_t		hdr_flags;
7e9df8
+	uint8_t			hdr_policy_loaded:1;
7e9df8
 };
7e9df8
 
7e9df8
 struct nl_context {
7e9df8
@@ -70,6 +72,8 @@ bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd,
7e9df8
 		       bool allow_wildcard);
7e9df8
 const char *get_dev_name(const struct nlattr *nest);
7e9df8
 int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname);
7e9df8
+u32 get_stats_flag(struct nl_context *nlctx, unsigned int nlcmd,
7e9df8
+		   unsigned int hdrattr);
7e9df8
 
7e9df8
 int linkmodes_reply_cb(const struct nlmsghdr *nlhdr, void *data);
7e9df8
 int linkinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data);
7e9df8
-- 
7e9df8
2.26.2
7e9df8