From patchwork Tue Apr 28 22:30:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 220374 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0C3EDC83000 for ; Tue, 28 Apr 2020 22:30:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D5AD5206F0 for ; Tue, 28 Apr 2020 22:30:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726470AbgD1WaI (ORCPT ); Tue, 28 Apr 2020 18:30:08 -0400 Received: from mx2.suse.de ([195.135.220.15]:57114 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725934AbgD1WaH (ORCPT ); Tue, 28 Apr 2020 18:30:07 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 4F8EAACD0; Tue, 28 Apr 2020 22:30:05 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id B4F01E128C; Wed, 29 Apr 2020 00:30:05 +0200 (CEST) Message-Id: In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool 1/2] refactor interface between ioctl and netlink code To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn Date: Wed, 29 Apr 2020 00:30:05 +0200 (CEST) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Move netlink related code from main() to separate handlers in netlink code. Improve the logic of fallback to ioctl and improve error messages when fallback is not possible (e.g. wildcard device name or name longer than IFNAMSIZ). Also handle the (theoretical for now) case when ioctl handler is not available. Signed-off-by: Michal Kubecek --- ethtool.c | 51 +++++++++++----------------------------- netlink/extapi.h | 14 +++++++---- netlink/monitor.c | 15 +++++++++--- netlink/netlink.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++- netlink/netlink.h | 1 + 5 files changed, 93 insertions(+), 47 deletions(-) diff --git a/ethtool.c b/ethtool.c index 1b4e08b6e60f..14c0bdc776f6 100644 --- a/ethtool.c +++ b/ethtool.c @@ -5157,7 +5157,7 @@ struct option { const char *opts; bool no_dev; int (*func)(struct cmd_context *); - int (*nlfunc)(struct cmd_context *); + nl_func_t nlfunc; const char *help; const char *xhelp; }; @@ -5731,6 +5731,11 @@ static int ioctl_init(struct cmd_context *ctx, bool no_dev) ctx->fd = -1; return 0; } + if (strlen(ctx->devname) >= IFNAMSIZ) { + fprintf(stderr, "Device name longer than %u characters\n", + IFNAMSIZ - 1); + exit_bad_args(); + } /* Setup our control structures. */ memset(&ctx->ifr, 0, sizeof(ctx->ifr)); @@ -5750,9 +5755,9 @@ static int ioctl_init(struct cmd_context *ctx, bool no_dev) int main(int argc, char **argp) { - int (*nlfunc)(struct cmd_context *) = NULL; int (*func)(struct cmd_context *); struct cmd_context ctx = {}; + nl_func_t nlfunc = NULL; bool no_dev; int ret; int k; @@ -5775,19 +5780,12 @@ int main(int argc, char **argp) argp += 2; argc -= 2; } -#ifdef ETHTOOL_ENABLE_NETLINK if (*argp && !strcmp(*argp, "--monitor")) { - if (netlink_init(&ctx)) { - fprintf(stderr, - "Option --monitor is only available with netlink.\n"); - return 1; - } else { - ctx.argp = ++argp; - ctx.argc = --argc; - return nl_monitor(&ctx); - } + ctx.argp = ++argp; + ctx.argc = --argc; + ret = nl_monitor(&ctx); + return ret ? 1 : 0; } -#endif /* First argument must be either a valid option or a device * name to get settings for (which we don't expect to begin @@ -5812,40 +5810,17 @@ int main(int argc, char **argp) no_dev = false; opt_found: - if (nlfunc) { - if (netlink_init(&ctx)) - nlfunc = NULL; /* fallback to ioctl() */ - } - if (!no_dev) { ctx.devname = *argp++; argc--; - /* netlink supports altnames, we will have to recheck against - * IFNAMSIZ later in case of fallback to ioctl - */ - if (!ctx.devname || strlen(ctx.devname) >= ALTIFNAMSIZ) { - netlink_done(&ctx); + if (!ctx.devname) exit_bad_args(); - } } - ctx.argc = argc; ctx.argp = argp; + netlink_run_handler(&ctx, nlfunc, !func); - if (nlfunc) { - ret = nlfunc(&ctx); - netlink_done(&ctx); - if ((ret != -EOPNOTSUPP) || !func) - return (ret >= 0) ? ret : 1; - } - - if (ctx.devname && strlen(ctx.devname) >= IFNAMSIZ) { - fprintf(stderr, - "ethtool: device names longer than %u characters are only allowed with netlink\n", - IFNAMSIZ - 1); - exit_bad_args(); - } ret = ioctl_init(&ctx, no_dev); if (ret) return ret; diff --git a/netlink/extapi.h b/netlink/extapi.h index d5b3cd92d4ab..9484b4c5ae7e 100644 --- a/netlink/extapi.h +++ b/netlink/extapi.h @@ -10,10 +10,12 @@ struct cmd_context; struct nl_context; +typedef int (*nl_func_t)(struct cmd_context *); + #ifdef ETHTOOL_ENABLE_NETLINK -int netlink_init(struct cmd_context *ctx); -void netlink_done(struct cmd_context *ctx); +void netlink_run_handler(struct cmd_context *ctx, nl_func_t nlfunc, + bool no_fallback); int nl_gset(struct cmd_context *ctx); int nl_sset(struct cmd_context *ctx); @@ -24,13 +26,15 @@ void nl_monitor_usage(void); #else /* ETHTOOL_ENABLE_NETLINK */ -static inline int netlink_init(struct cmd_context *ctx maybe_unused) +static inline void netlink_run_handler(struct cmd_context *ctx, + nl_func_t nlfunc, bool no_fallback) { - return -EOPNOTSUPP; } -static inline void netlink_done(struct cmd_context *ctx maybe_unused) +static inline int nl_monitor(struct cmd_context *ctx) { + fprintf(stderr, "Netlink not supported by ethtool, option --monitor unsupported.\n"); + return -EOPNOTSUPP; } static inline void nl_monitor_usage(void) diff --git a/netlink/monitor.c b/netlink/monitor.c index 5fce6b64c08c..e20db5fc79d4 100644 --- a/netlink/monitor.c +++ b/netlink/monitor.c @@ -167,16 +167,25 @@ static int parse_monitor(struct cmd_context *ctx) int nl_monitor(struct cmd_context *ctx) { - struct nl_context *nlctx = ctx->nlctx; - struct nl_socket *nlsk = nlctx->ethnl_socket; - uint32_t grpid = nlctx->ethnl_mongrp; + struct nl_context *nlctx; + struct nl_socket *nlsk; + uint32_t grpid; bool is_dev; int ret; + ret = netlink_init(ctx); + if (ret < 0) { + fprintf(stderr, "Netlink interface initialization failed, option --monitor not supported.\n"); + return ret; + } + nlctx = ctx->nlctx; + nlsk = nlctx->ethnl_socket; + grpid = nlctx->ethnl_mongrp; if (!grpid) { fprintf(stderr, "multicast group 'monitor' not found\n"); return -EOPNOTSUPP; } + if (parse_monitor(ctx) < 0) return 1; is_dev = ctx->devname && strcmp(ctx->devname, WILDCARD_DEVNAME); diff --git a/netlink/netlink.c b/netlink/netlink.c index 39f406387233..59dbab2dee00 100644 --- a/netlink/netlink.c +++ b/netlink/netlink.c @@ -205,7 +205,7 @@ out_free: return ret; } -void netlink_done(struct cmd_context *ctx) +static void netlink_done(struct cmd_context *ctx) { if (!ctx->nlctx) return; @@ -214,3 +214,60 @@ void netlink_done(struct cmd_context *ctx) ctx->nlctx = NULL; cleanup_all_strings(); } + +/** + * netlink_run_handler() - run netlink handler for subcommand + * @ctx: command context + * @nlfunc: subcommand netlink handler to call + * @no_fallback: there is no ioctl fallback handler + * + * This function returns only if ioctl() handler should be run as fallback. + * Otherwise it exits with appropriate return code. + */ +void netlink_run_handler(struct cmd_context *ctx, nl_func_t nlfunc, + bool no_fallback) +{ + bool wildcard = ctx->devname && !strcmp(ctx->devname, WILDCARD_DEVNAME); + const char *reason; + int ret; + + if (ctx->devname && strlen(ctx->devname) >= ALTIFNAMSIZ) { + fprintf(stderr, "device name '%s' longer than %u characters\n", + ctx->devname, ALTIFNAMSIZ - 1); + exit(1); + } + + if (!nlfunc) { + reason = "ethtool netlink support for subcommand missing"; + goto no_support; + } + if (netlink_init(ctx)) { + reason = "netlink interface initialization failed"; + goto no_support; + } + + ret = nlfunc(ctx); + netlink_done(ctx); + if (ret != -EOPNOTSUPP || no_fallback) + exit(ret >= 0 ? ret : 1); + reason = "kernel netlink support for subcommand missing"; + +no_support: + if (no_fallback) { + fprintf(stderr, "%s, subcommand not supported by ioctl\n", + reason); + exit(1); + } + if (wildcard) { + fprintf(stderr, "%s, wildcard dump not supported\n", reason); + exit(1); + } + if (ctx->devname && strlen(ctx->devname) >= IFNAMSIZ) { + fprintf(stderr, + "%s, device name longer than %u not supported\n", + reason, IFNAMSIZ - 1); + exit(1); + } + + /* fallback to ioctl() */ +} diff --git a/netlink/netlink.h b/netlink/netlink.h index db078d28fabb..41102d270eaf 100644 --- a/netlink/netlink.h +++ b/netlink/netlink.h @@ -49,6 +49,7 @@ struct attr_tb_info { int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data); int attr_cb(const struct nlattr *attr, void *data); +int netlink_init(struct cmd_context *ctx); const char *get_dev_name(const struct nlattr *nest); int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname); From patchwork Tue Apr 28 22:30:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Kubecek X-Patchwork-Id: 220373 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E8234C83004 for ; Tue, 28 Apr 2020 22:30:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C639A206F0 for ; Tue, 28 Apr 2020 22:30:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726560AbgD1WaP (ORCPT ); Tue, 28 Apr 2020 18:30:15 -0400 Received: from mx2.suse.de ([195.135.220.15]:57176 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725934AbgD1WaO (ORCPT ); Tue, 28 Apr 2020 18:30:14 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 5927BADD6; Tue, 28 Apr 2020 22:30:10 +0000 (UTC) Received: by unicorn.suse.cz (Postfix, from userid 1000) id BB72EE128C; Wed, 29 Apr 2020 00:30:10 +0200 (CEST) Message-Id: <8a495e43783170906ce3bf0213bc1dd7eca0e5c8.1588112572.git.mkubecek@suse.cz> In-Reply-To: References: From: Michal Kubecek Subject: [PATCH ethtool 2/2] netlink: use genetlink ops information to decide about fallback To: John Linville , netdev@vger.kernel.org Cc: Andrew Lunn Date: Wed, 29 Apr 2020 00:30:10 +0200 (CEST) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Currently ethtool falls back to ioctl when netlink socket initialization fails or if netlink request fails with -EOPNOTSUPP (and we do not know that request cannot possibly work via ioctl, e.g. because of wildcard device name or device name longer than IFNAMSIZ). This logic has one problem: we cannot distinguish if -EOPNOTSUPP error code means that subcommand as such is not implemented in kernel or that it failed for some other reason (e.g. specific combination of parameters is not supported by driver). If the latter is the case, fallback to ioctl is pointless as the same request would fail via ioctl as well. In some cases we can even get a duplicate error message. Fortunately, genetlink provides information about supported family commands in the form of CTRL_ATTR_OPS nested attribute. Parse this information when available and use it to only fall back to ioctl only if kernel support for netlink request as such is missing so that there is a chance that ioctl request will work. In such case, do not send netlink request at all. Signed-off-by: Michal Kubecek --- netlink/netlink.c | 138 ++++++++++++++++++++++++++++++++++++--------- netlink/netlink.h | 5 ++ netlink/parser.c | 7 +++ netlink/settings.c | 7 +++ 4 files changed, 129 insertions(+), 28 deletions(-) diff --git a/netlink/netlink.c b/netlink/netlink.c index 59dbab2dee00..ccd88448197d 100644 --- a/netlink/netlink.c +++ b/netlink/netlink.c @@ -92,16 +92,88 @@ int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname) return 0; } +/** + * netlink_cmd_check() - check support for netlink command + * @ctx: ethtool command context + * @cmd: netlink command id + * @devname: device name from user + * @allow_wildcard: wildcard dumps supported + * + * Check if command @cmd is known to be unsupported based on ops information + * from genetlink family id request. Set nlctx->ioctl_fallback if ethtool + * should fall back to ioctl, i.e. when we do not know in advance that + * netlink request is supported. Set nlctx->wildcard_unsupported if "*" was + * used as device name but the request does not support wildcards (on either + * side). + * + * Return: true if we know the netlink request is not supported and should + * fail (and possibly fall back) without actually sending it to kernel. + */ +bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd, + bool allow_wildcard) +{ + bool is_dump = !strcmp(ctx->devname, WILDCARD_DEVNAME); + uint32_t cap = is_dump ? GENL_CMD_CAP_DUMP : GENL_CMD_CAP_DO; + struct nl_context *nlctx = ctx->nlctx; + + if (is_dump && !allow_wildcard) { + nlctx->wildcard_unsupported = true; + return true; + } + if (!nlctx->ops_flags) { + nlctx->ioctl_fallback = true; + return false; + } + if (cmd > ETHTOOL_MSG_USER_MAX || !nlctx->ops_flags[cmd]) { + nlctx->ioctl_fallback = true; + return true; + } + + if (is_dump && !(nlctx->ops_flags[cmd] & GENL_CMD_CAP_DUMP)) + nlctx->wildcard_unsupported = true; + + return !(nlctx->ops_flags[cmd] & cap); +} + /* initialization */ -struct fam_info { - const char *fam_name; - const char *grp_name; - uint16_t fam_id; - uint32_t grp_id; -}; +static int genl_read_ops(struct nl_context *nlctx, + const struct nlattr *ops_attr) +{ + struct nlattr *op_attr; + uint32_t *ops_flags; + int ret; + + ops_flags = calloc(__ETHTOOL_MSG_USER_CNT, sizeof(ops_flags[0])); + if (!ops_flags) + return -ENOMEM; + + mnl_attr_for_each_nested(op_attr, ops_attr) { + const struct nlattr *tb[CTRL_ATTR_OP_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + uint32_t op_id; + + ret = mnl_attr_parse_nested(op_attr, attr_cb, &tb_info); + if (ret < 0) + goto err; + + if (!tb[CTRL_ATTR_OP_ID] || !tb[CTRL_ATTR_OP_FLAGS]) + continue; + op_id = mnl_attr_get_u32(tb[CTRL_ATTR_OP_ID]); + if (op_id >= __ETHTOOL_MSG_USER_CNT) + continue; + + ops_flags[op_id] = mnl_attr_get_u32(tb[CTRL_ATTR_OP_FLAGS]); + } + + nlctx->ops_flags = ops_flags; + return 0; +err: + free(ops_flags); + return ret; +} -static void find_mc_group(struct nlattr *nest, struct fam_info *info) +static void find_mc_group(struct nl_context *nlctx, struct nlattr *nest) { const struct nlattr *grp_tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {}; DECLARE_ATTR_TB_INFO(grp_tb); @@ -116,9 +188,9 @@ static void find_mc_group(struct nlattr *nest, struct fam_info *info) !grp_tb[CTRL_ATTR_MCAST_GRP_ID]) continue; if (strcmp(mnl_attr_get_str(grp_tb[CTRL_ATTR_MCAST_GRP_NAME]), - info->grp_name)) + ETHTOOL_MCGRP_MONITOR_NAME)) continue; - info->grp_id = + nlctx->ethnl_mongrp = mnl_attr_get_u32(grp_tb[CTRL_ATTR_MCAST_GRP_ID]); return; } @@ -126,16 +198,21 @@ static void find_mc_group(struct nlattr *nest, struct fam_info *info) static int family_info_cb(const struct nlmsghdr *nlhdr, void *data) { - struct fam_info *info = data; + struct nl_context *nlctx = data; struct nlattr *attr; + int ret; mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) { switch (mnl_attr_get_type(attr)) { case CTRL_ATTR_FAMILY_ID: - info->fam_id = mnl_attr_get_u16(attr); + nlctx->ethnl_fam = mnl_attr_get_u16(attr); break; + case CTRL_ATTR_OPS: + ret = genl_read_ops(nlctx, attr); + if (ret < 0) + return MNL_CB_ERROR; case CTRL_ATTR_MCAST_GROUPS: - find_mc_group(attr, info); + find_mc_group(nlctx, attr); break; } } @@ -144,41 +221,37 @@ static int family_info_cb(const struct nlmsghdr *nlhdr, void *data) } #ifdef TEST_ETHTOOL -static int get_genl_family(struct nl_socket *nlsk, struct fam_info *info) +static int get_genl_family(struct nl_context *nlctx, struct nl_socket *nlsk) { return 0; } #else -static int get_genl_family(struct nl_socket *nlsk, struct fam_info *info) +static int get_genl_family(struct nl_context *nlctx, struct nl_socket *nlsk) { struct nl_msg_buff *msgbuff = &nlsk->msgbuff; int ret; - nlsk->nlctx->suppress_nlerr = 2; + nlctx->suppress_nlerr = 2; ret = __msg_init(msgbuff, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, NLM_F_REQUEST | NLM_F_ACK, 1); if (ret < 0) goto out; ret = -EMSGSIZE; - if (ethnla_put_strz(msgbuff, CTRL_ATTR_FAMILY_NAME, info->fam_name)) + if (ethnla_put_strz(msgbuff, CTRL_ATTR_FAMILY_NAME, ETHTOOL_GENL_NAME)) goto out; nlsock_sendmsg(nlsk, NULL); - nlsock_process_reply(nlsk, family_info_cb, info); - ret = info->fam_id ? 0 : -EADDRNOTAVAIL; + nlsock_process_reply(nlsk, family_info_cb, nlctx); + ret = nlctx->ethnl_fam ? 0 : -EADDRNOTAVAIL; out: - nlsk->nlctx->suppress_nlerr = 0; + nlctx->suppress_nlerr = 0; return ret; } #endif int netlink_init(struct cmd_context *ctx) { - struct fam_info info = { - .fam_name = ETHTOOL_GENL_NAME, - .grp_name = ETHTOOL_MCGRP_MONITOR_NAME, - }; struct nl_context *nlctx; int ret; @@ -189,11 +262,9 @@ int netlink_init(struct cmd_context *ctx) ret = nlsock_init(nlctx, &nlctx->ethnl_socket, NETLINK_GENERIC); if (ret < 0) goto out_free; - ret = get_genl_family(nlctx->ethnl_socket, &info); + ret = get_genl_family(nlctx, nlctx->ethnl_socket); if (ret < 0) goto out_nlsk; - nlctx->ethnl_fam = info.fam_id; - nlctx->ethnl_mongrp = info.grp_id; ctx->nlctx = nlctx; return 0; @@ -201,6 +272,7 @@ int netlink_init(struct cmd_context *ctx) out_nlsk: nlsock_done(nlctx->ethnl_socket); out_free: + free(nlctx->ops_flags); free(nlctx); return ret; } @@ -210,6 +282,7 @@ static void netlink_done(struct cmd_context *ctx) if (!ctx->nlctx) return; + free(ctx->nlctx->ops_flags); free(ctx->nlctx); ctx->nlctx = NULL; cleanup_all_strings(); @@ -228,6 +301,7 @@ void netlink_run_handler(struct cmd_context *ctx, nl_func_t nlfunc, bool no_fallback) { bool wildcard = ctx->devname && !strcmp(ctx->devname, WILDCARD_DEVNAME); + struct nl_context *nlctx; const char *reason; int ret; @@ -245,12 +319,20 @@ void netlink_run_handler(struct cmd_context *ctx, nl_func_t nlfunc, reason = "netlink interface initialization failed"; goto no_support; } + nlctx = ctx->nlctx; ret = nlfunc(ctx); netlink_done(ctx); - if (ret != -EOPNOTSUPP || no_fallback) + if (no_fallback || ret != -EOPNOTSUPP || !nlctx->ioctl_fallback) { + if (nlctx->wildcard_unsupported) + fprintf(stderr, "%s\n", + "subcommand does not support wildcard dump"); exit(ret >= 0 ? ret : 1); - reason = "kernel netlink support for subcommand missing"; + } + if (nlctx->wildcard_unsupported) + reason = "subcommand does not support wildcard dump"; + else + reason = "kernel netlink support for subcommand missing"; no_support: if (no_fallback) { diff --git a/netlink/netlink.h b/netlink/netlink.h index 41102d270eaf..4419be0f751a 100644 --- a/netlink/netlink.h +++ b/netlink/netlink.h @@ -25,6 +25,7 @@ struct nl_context { unsigned int suppress_nlerr; uint16_t ethnl_fam; uint32_t ethnl_mongrp; + uint32_t *ops_flags; struct nl_socket *ethnl_socket; struct nl_socket *ethnl2_socket; struct nl_socket *rtnl_socket; @@ -36,6 +37,8 @@ struct nl_context { const char *param; char **argp; int argc; + bool ioctl_fallback; + bool wildcard_unsupported; }; struct attr_tb_info { @@ -50,6 +53,8 @@ int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data); int attr_cb(const struct nlattr *attr, void *data); int netlink_init(struct cmd_context *ctx); +bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd, + bool allow_wildcard); const char *get_dev_name(const struct nlattr *nest); int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname); diff --git a/netlink/parser.c b/netlink/parser.c index 40eb4a5c0b26..fff23f2425e9 100644 --- a/netlink/parser.c +++ b/netlink/parser.c @@ -1023,6 +1023,13 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params, goto out_free; } + if (group_style == PARSER_GROUP_MSG) { + ret = -EOPNOTSUPP; + for (buff = buffs; buff; buff = buff->next) + if (msgbuff_len(&buff->msgbuff) > buff->orig_len && + netlink_cmd_check(nlctx->ctx, buff->id, false)) + goto out_free; + } for (buff = buffs; buff; buff = buff->next) { struct nl_msg_buff *msgbuff = &buff->msgbuff; diff --git a/netlink/settings.c b/netlink/settings.c index c8a911d718b9..25aadb988cd1 100644 --- a/netlink/settings.c +++ b/netlink/settings.c @@ -726,6 +726,13 @@ int nl_gset(struct cmd_context *ctx) struct nl_socket *nlsk = nlctx->ethnl_socket; int ret; + if (netlink_cmd_check(ctx, ETHTOOL_MSG_LINKMODES_GET, true) || + netlink_cmd_check(ctx, ETHTOOL_MSG_LINKINFO_GET, true) || + netlink_cmd_check(ctx, ETHTOOL_MSG_WOL_GET, true) || + netlink_cmd_check(ctx, ETHTOOL_MSG_DEBUG_GET, true) || + netlink_cmd_check(ctx, ETHTOOL_MSG_LINKSTATE_GET, true)) + return -EOPNOTSUPP; + nlctx->suppress_nlerr = 1; ret = gset_request(nlsk, ETHTOOL_MSG_LINKMODES_GET,