|
|
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 |
|