lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <70e163a6ce3f75f44876b30bf28b26b132b4f27a.1532954671.git.mkubecek@suse.cz>
Date:   Mon, 30 Jul 2018 14:56:24 +0200 (CEST)
From:   Michal Kubecek <mkubecek@...e.cz>
To:     netdev@...r.kernel.org
Cc:     linux-kernel@...r.kernel.org,
        "John W. Linville" <linville@...driver.com>
Subject: [RFC PATCH ethtool v2 08/23] netlink: add helpers for command line
 parsing

Support for easier parsing of subcommand parameters and their values from
command line. Existing parser helpers in ethtool.c are closely tied to
ioctl() interface and using them would be often inconvenient.

Signed-off-by: Michal Kubecek <mkubecek@...e.cz>
---
 Makefile.am       |   1 +
 netlink/netlink.h |   4 +
 netlink/parser.c  | 527 ++++++++++++++++++++++++++++++++++++++++++++++
 netlink/parser.h  |  45 ++++
 4 files changed, 577 insertions(+)
 create mode 100644 netlink/parser.c
 create mode 100644 netlink/parser.h

diff --git a/Makefile.am b/Makefile.am
index 629f04e1202b..7cc4adc53c59 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,6 +23,7 @@ if ETHTOOL_ENABLE_NETLINK
 ethtool_SOURCES += \
 		  netlink/netlink.c netlink/netlink.h netlink/extapi.h \
 		  netlink/strset.c netlink/strset.h netlink/monitor.c \
+		  netlink/parser.c netlink/parser.h \
 		  netlink/drvinfo.c netlink/settings.c \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h
diff --git a/netlink/netlink.h b/netlink/netlink.h
index a572a165718e..32c2f9f33ba1 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -30,6 +30,10 @@ struct nl_context {
 	void *msg;
 	const char *devname;
 	bool is_dump;
+	const char *cmd;
+	const char *param;
+	char **argp;
+	int argc;
 	int exit_code;
 	bool is_monitor;
 	uint8_t filter_cmd;
diff --git a/netlink/parser.c b/netlink/parser.c
new file mode 100644
index 000000000000..589004d1f9f9
--- /dev/null
+++ b/netlink/parser.c
@@ -0,0 +1,527 @@
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+static void parser_err_unknown_param(struct nl_context *nlctx)
+{
+	fprintf(stderr, "ethtool (%s): unknown parameter '%s'\n", nlctx->cmd,
+		nlctx->param);
+}
+
+static void parser_err_dup_param(struct nl_context *nlctx)
+{
+	fprintf(stderr, "ethtool (%s): duplicate parameter '%s'\n", nlctx->cmd,
+		nlctx->param);
+}
+
+static void parser_err_min_argc(struct nl_context *nlctx, unsigned int min_argc)
+{
+	if (min_argc == 1)
+		fprintf(stderr, "ethtool (%s): no value for parameter '%s'\n",
+			nlctx->cmd, nlctx->param);
+	else
+		fprintf(stderr,
+			"ethtool (%s): parameter '%s' requires %u words\n",
+			nlctx->cmd, nlctx->param, min_argc);
+}
+
+static void parser_err_invalid_value(struct nl_context *nlctx, const char *val)
+{
+	fprintf(stderr, "ethtool (%s): invalid value '%s' for parameter '%s'\n",
+		nlctx->cmd, val, nlctx->param);
+}
+
+static void parser_err_invalid_flag(struct nl_context *nlctx, const char *flag)
+{
+	fprintf(stderr, "ethtool (%s): flag '%s' for parameter '%s' is not "
+			"followed by 'on' or 'off'\n",
+		nlctx->cmd, flag, nlctx->param);
+}
+
+static int __parse_u32(const char *arg, u32 *result, u32 min, u32 max, int base)
+{
+	unsigned long long val;
+	char *endptr;
+
+	if (!arg || !arg[0])
+		return -EINVAL;
+	val = strtoul(arg, &endptr, base);
+	if (*endptr || val < min || val > max)
+		return -EINVAL;
+
+	*result = (u32)val;
+	return 0;
+}
+
+static int parse_u32d(const char *arg, u32 *result)
+{
+	return __parse_u32(arg, result, 0, 0xffffffff, 10);
+}
+
+static int parse_x32(const char *arg, u32 *result)
+{
+	return __parse_u32(arg, result, 0, 0xffffffff, 16);
+}
+
+static int parse_u32(const char *arg, u32 *result)
+{
+	if (!arg)
+		return -EINVAL;
+	if ((arg[0] == '0') && (arg[1] == 'x' || arg[1] == 'X'))
+		return parse_x32(arg + 2, result);
+	else
+		return parse_u32d(arg, result);
+}
+
+static int parse_u8(const char *arg, u8 *result)
+{
+	u32 val;
+	int ret = parse_u32(arg, &val);
+
+	if (ret < 0)
+		return ret;
+	if (val > UINT8_MAX)
+		return -EINVAL;
+
+	*result = (u8)val;
+	return 0;
+}
+
+static int lookup_u8(const char *arg, u8 *result,
+		     const struct lookup_entry_u8 *tbl)
+{
+	if (!arg)
+		return -EINVAL;
+	while (tbl->arg) {
+		if (!strcmp(tbl->arg, arg)) {
+			*result = tbl->val;
+			return 0;
+		}
+		tbl++;
+	}
+
+	return -EINVAL;
+}
+
+int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
+			const void *data)
+{
+	const char *arg = *nlctx->argp;
+	u32 val;
+	int ret;
+
+	nlctx->argp++;
+	nlctx->argc--;
+	ret = parse_u32(arg, &val);
+	if (ret < 0) {
+		parser_err_invalid_value(nlctx, arg);
+		return ret;
+	}
+
+	return ethnla_put_u32(nlctx, type, val) ? -EMSGSIZE : 0;
+}
+
+int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
+		       const void *data)
+{
+	const char *arg = *nlctx->argp;
+	u8 val;
+	int ret;
+
+	nlctx->argp++;
+	nlctx->argc--;
+	ret = parse_u8(arg, &val);
+	if (ret < 0) {
+		parser_err_invalid_value(nlctx, arg);
+		return ret;
+	}
+
+	return ethnla_put_u8(nlctx, type, val) ? -EMSGSIZE : 0;
+}
+
+int nl_parse_bool(struct nl_context *nlctx, uint16_t type, const void *data)
+{
+	const char *arg = *nlctx->argp;
+	int ret;
+
+	nlctx->argp++;
+	nlctx->argc--;
+	if (!strcmp(arg, "on"))
+		ret = ethnla_put_u8(nlctx, type, 1);
+	else if (!strcmp(arg, "off"))
+		ret = ethnla_put_u8(nlctx, type, 0);
+	else {
+		parser_err_invalid_value(nlctx, arg);
+		return -EINVAL;
+	}
+
+	return ret ? -EMSGSIZE : 0;
+}
+
+int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type,
+		       const void *data)
+{
+	const char *arg = *nlctx->argp;
+	u8 val;
+	int ret;
+
+	nlctx->argp++;
+	nlctx->argc--;
+	ret = lookup_u8(arg, &val, data);
+	if (ret < 0) {
+		parser_err_invalid_value(nlctx, arg);
+		return ret;
+	}
+
+	return ethnla_put_u8(nlctx, type, val) ? -EMSGSIZE : 0;
+}
+
+int nl_parse_bitfield32(struct nl_context *nlctx, uint16_t type,
+			const void *data)
+{
+	const char *arg = *nlctx->argp;
+	const struct flag_info *flags = data;
+	u32 value, selector;
+	int ret;
+
+	ret = parse_u32(arg, &value);
+	if (isdigit(arg[0])) {
+		char *mask = strchr(arg, '/');
+
+		if (mask) {
+			/* numeric value / mask */
+			*mask = '\0';
+			mask++;
+			ret = parse_u32(mask, &selector);
+			if (ret < 0) {
+				parser_err_invalid_value(nlctx, mask);
+				return ret;
+			}
+		} else {
+			/* numeric value */
+			selector = ~(u32)0;
+		}
+		ret = parse_u32(arg, &value);
+		if (ret < 0) {
+			parser_err_invalid_value(nlctx, arg);
+			return ret;
+		}
+		nlctx->argp++;
+		nlctx->argc--;
+	} else {
+		/* flag on/off... [--] */
+		value = 0;
+		selector = 0;
+		while (nlctx->argc > 0) {
+			const struct flag_info *flag = flags;
+
+			if (!strcmp(*nlctx->argp, "--")) {
+				nlctx->argp++;
+				nlctx->argc--;
+				break;
+			}
+			if (nlctx->argc < 2 ||
+			    (strcmp(nlctx->argp[1], "on") &&
+			     strcmp(nlctx->argp[1], "off"))) {
+				parser_err_invalid_flag(nlctx, *nlctx->argp);
+				return -EINVAL;
+			}
+			while (flag->name && strcmp(*nlctx->argp, flag->name))
+				flag++;
+			if (!flag->name) {
+				parser_err_invalid_value(nlctx, *nlctx->argp);
+				return -EINVAL;
+			}
+			selector |= flag->value;
+			if (!strcmp(nlctx->argp[1], "on"))
+				value |= flag->value;
+
+			nlctx->argp += 2;
+			nlctx->argc -= 2;
+		}
+	}
+
+	return ethnla_put_bitfield32(nlctx, type, value, selector);
+}
+
+static bool is_hex(char c)
+{
+	if (isdigit(c))
+		return true;
+	else
+		return (c >= 'a' && c <= 'f');
+}
+
+/* Return true if a bitset argument should be parsed as numeric, i.e.
+ * (a) it starts with '0x'
+ * (b) it consists only of hex digits and at most one slash which can be
+ *     optionally followed by "0x"
+ */
+static bool is_numeric_bitset(const char *arg)
+{
+	const char *p = arg;
+	bool has_slash = false;
+
+	if (arg[0] == '0' && arg[1] == 'x')
+		return true;
+	while (*p) {
+		if (*p == '/') {
+			if (has_slash)
+				return false;
+			has_slash = true;
+			p++;
+			if (p[0] == '0' && p[1] == 'x')
+				p += 2;
+			continue;
+		}
+		if (!is_hex(*p))
+			return false;
+		p++;
+	}
+	return true;
+}
+
+/* number of significant bits */
+static unsigned int nsb(u32 x)
+{
+	unsigned int ret = 0;
+
+	if (!(x & 0xffff0000U)) {
+		x >>= 16;
+		ret += 16;
+	}
+	if (!(x & 0xff00U)) {
+		x >>= 8;
+		ret += 8;
+	}
+	if (!(x & 0xf0U)) {
+		x >>= 4;
+		ret += 4;
+	}
+	if (!(x & 0xcU)) {
+		x >>= 2;
+		ret += 2;
+	}
+	if (!(x & 0x2U)) {
+		x >>= 1;
+		ret += 1;
+	}
+
+	return ret + x;
+}
+
+/* Parse hex string (without leading "0x") into a bitmap consisting of 32-bit
+ * words. Caller must make sure arg is at least len characters long and dst has
+ * place for at least (len + 7) / 8 32-bit words.
+ * Returns number of significant bits in the bitmap on success and negative
+ * value on error.
+ */
+static int parse_hex_string(const char *arg, unsigned int len, u32 *dst)
+{
+	const char *p = arg;
+	unsigned int nwords = (len + 7) / 8;
+	unsigned int nbits = 0;
+	char buff[9];
+
+	memset(dst, '\0', nwords * sizeof(u32));
+	while (len > 0) {
+		unsigned int chunk = (len % 8) ?: 8;
+		unsigned long val;
+		char *endp;
+
+		memcpy(buff, p, chunk);
+		buff[chunk] = '\0';
+		val = strtoul(buff, &endp, 16);
+		if (*endp)
+			return -EINVAL;
+		*dst++ = (u32)val;
+		if (nbits)
+			nbits += 8 * chunk;
+		else
+			nbits = nsb(val);
+
+		p += chunk;
+		len -= chunk;
+	}
+	return nbits;
+}
+
+static int nl_parse_bitset_compact(struct nl_context *nlctx, uint16_t type)
+{
+	const char *arg = *nlctx->argp;
+	unsigned int nwords, len1, len2;
+	bool has_mask = false;
+	struct nlattr *nest;
+	const char *maskptr;
+	u32 *value = NULL;
+	u32 *mask = NULL;
+	int nbits;
+	int ret;
+
+	if (arg[0] == '0' && arg[1] == 'x')
+		arg += 2;
+
+	maskptr = strchr(arg, '/');
+	len1 = maskptr ? ( maskptr - arg) : strlen(arg);
+	nwords = (len1 + 7) / 8;
+	nbits = 0;
+
+	value = malloc(nwords * sizeof(u32));
+	if (!value)
+		return -ENOMEM;
+	ret = -ENOMEM;
+	mask = malloc(nwords * sizeof(u32));
+	if (!mask)
+		goto free_value;
+
+	if (maskptr) {
+		has_mask = true;
+		maskptr++;
+		if (maskptr[0] == '0' && maskptr[1] == 'x')
+			maskptr += 2;
+		len2 = strlen(maskptr);
+		if (len2 > len1)
+			nwords = (len2 + 7) / 8;
+		mask = malloc(nwords * sizeof(u32));
+		if (!mask)
+			return -ENOMEM;
+		ret = parse_hex_string(maskptr, strlen(maskptr),
+					  mask);
+		if (ret < 0)
+			goto free_mask;
+		nbits = ret;
+	}
+
+	ret = parse_hex_string(arg, len1, value);
+	if (ret < 0)
+		goto free_value;
+	nbits = nbits ?: ret;
+	nwords = (nbits + 31) / 32;
+
+	ret = -EMSGSIZE;
+	if (!(nest = ethnla_nest_start(nlctx, type)) ||
+	    ethnla_put_u32(nlctx, ETHA_BITSET_SIZE, nbits) ||
+	    ethnla_put(nlctx, ETHA_BITSET_VALUE, nwords * sizeof(u32), value) ||
+	    (has_mask &&
+	     ethnla_put(nlctx, ETHA_BITSET_MASK, nwords * sizeof(u32), mask)))
+		goto free_mask;
+	mnl_attr_nest_end(nlctx->nlhdr, nest);
+	ret = 0;
+
+free_mask:
+	free(mask);
+free_value:
+	free(value);
+	nlctx->argp++;
+	nlctx->argc--;
+	return ret;
+}
+
+static int nl_parse_bitset_list(struct nl_context *nlctx, uint16_t type,
+				const void *data)
+{
+	struct nlmsghdr *nlhdr = nlctx->nlhdr;
+	struct nlattr *bitset_attr;
+	struct nlattr *bits_attr;
+	struct nlattr *bit_attr;
+	int ret;
+
+	ret = -EMSGSIZE;
+	bitset_attr = ethnla_nest_start(nlctx, type);
+	if (!bitset_attr)
+		return ret;
+	bits_attr = ethnla_nest_start(nlctx, ETHA_BITSET_BITS);
+	if (!bits_attr)
+		goto err;
+
+	while (nlctx->argc > 0) {
+		if (!strcmp(*nlctx->argp, "--")) {
+			nlctx->argp++;
+			nlctx->argc--;
+			break;
+		}
+		ret = -EINVAL;
+		if (nlctx->argc < 2 ||
+		    (strcmp(nlctx->argp[1], "on") &&
+		     strcmp(nlctx->argp[1], "off"))) {
+			parser_err_invalid_flag(nlctx, *nlctx->argp);
+			goto err;
+		}
+
+		ret = -EMSGSIZE;
+		bit_attr = ethnla_nest_start(nlctx, ETHA_BITS_BIT);
+		if (!bit_attr)
+			goto err;
+		if (ethnla_put_strz(nlctx, ETHA_BIT_NAME, nlctx->argp[0]))
+			goto err;
+		if (ethnla_put_flag(nlctx, ETHA_BIT_VALUE,
+				    !strcmp(nlctx->argp[1], "on")))
+			goto err;
+		mnl_attr_nest_end(nlhdr, bit_attr);
+
+		nlctx->argp += 2;
+		nlctx->argc -= 2;
+	}
+
+	mnl_attr_nest_end(nlhdr, bits_attr);
+	mnl_attr_nest_end(nlhdr, bitset_attr);
+	return 0;
+err:
+	mnl_attr_nest_cancel(nlhdr, bitset_attr);
+	return ret;
+}
+
+int nl_parse_bitset(struct nl_context *nlctx, uint16_t type, const void *data)
+{
+	if (is_numeric_bitset(*nlctx->argp))
+		return nl_parse_bitset_compact(nlctx, type);
+	else
+		return nl_parse_bitset_list(nlctx, type, data);
+}
+
+int nl_parser(struct cmd_context *ctx, const struct param_parser *params,
+	      unsigned int max_type)
+{
+	struct nl_context *nlctx = ctx->nlctx;
+	bool attr_set[max_type];
+	int ret;
+
+	memset(attr_set, '\0', max_type * sizeof(attr_set[0]));
+
+	while (nlctx->argc > 0) {
+		const struct param_parser *parser = params;
+
+		nlctx->param = *nlctx->argp;
+		while (parser->arg) {
+			if (!strcmp(nlctx->param, parser->arg))
+				break;
+			parser++;
+		}
+		if (!parser->arg) {
+			parser_err_unknown_param(nlctx);
+			return -EINVAL;
+		}
+		nlctx->argp++;
+		nlctx->argc--;
+		if (attr_set[parser - params]) {
+			parser_err_dup_param(nlctx);
+			return -EINVAL;
+		}
+		if (nlctx->argc < parser->min_argc) {
+			parser_err_min_argc(nlctx, parser->min_argc);
+			return -EINVAL;
+		}
+
+		ret = parser->handler(nlctx, parser->type, parser->handler_data);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
diff --git a/netlink/parser.h b/netlink/parser.h
new file mode 100644
index 000000000000..7308da25f093
--- /dev/null
+++ b/netlink/parser.h
@@ -0,0 +1,45 @@
+#ifndef ETHTOOL_NETLINK_PARSER_H__
+#define ETHTOOL_NETLINK_PARSER_H__
+
+#include "netlink.h"
+
+struct lookup_entry_u32 {
+	const char	*arg;
+	u32		val;
+};
+
+struct lookup_entry_u16 {
+	const char	*arg;
+	u16		val;
+};
+
+struct lookup_entry_u8{
+	const char	*arg;
+	u8		val;
+};
+
+typedef int (*param_parser_cb_t)(struct nl_context *, uint16_t, const void *);
+
+struct param_parser {
+	const char		*arg;
+	uint16_t		type;
+	param_parser_cb_t	handler;
+	const void		*handler_data;
+	unsigned int		min_argc;
+};
+
+int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
+			const void *data);
+int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
+		       const void *data);
+int nl_parse_bool(struct nl_context *nlctx, uint16_t type, const void *data);
+int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type,
+		       const void *data);
+int nl_parse_bitfield32(struct nl_context *nlctx, uint16_t type,
+			const void *data);
+int nl_parse_bitset(struct nl_context *nlctx, uint16_t type, const void *data);
+
+int nl_parser(struct cmd_context *ctx, const struct param_parser *params,
+	      unsigned int max_type);
+
+#endif /* ETHTOOL_NETLINK_PARSER_H__ */
-- 
2.18.0

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ