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: <4dcd60f25efe368ada4e0c035dc1d7612ab59132.1532953989.git.mkubecek@suse.cz>
Date:   Mon, 30 Jul 2018 14:53:27 +0200 (CEST)
From:   Michal Kubecek <mkubecek@...e.cz>
To:     netdev@...r.kernel.org
Cc:     linux-kernel@...r.kernel.org, Jiri Pirko <jiri@...nulli.us>,
        David Miller <davem@...emloft.net>,
        Florian Fainelli <f.fainelli@...il.com>,
        Roopa Prabhu <roopa@...ulusnetworks.com>,
        Jakub Kicinski <kubakici@...pl>,
        "John W. Linville" <linville@...driver.com>
Subject: [RFC PATCH net-next v2 09/17] ethtool: implement GET_DRVINFO message

Requests the same information as ETHTOOL_GDRVINFO command in ioct
interface. This is read-only so that corresponding SET_DRVINFO exists but
is only used in kernel replies.

Signed-off-by: Michal Kubecek <mkubecek@...e.cz>
---
 Documentation/networking/ethtool-netlink.txt |  38 +++++-
 include/uapi/linux/ethtool_netlink.h         |  22 ++++
 net/ethtool/Makefile                         |   4 +-
 net/ethtool/common.c                         |  43 ++++++
 net/ethtool/common.h                         |   3 +
 net/ethtool/drvinfo.c                        | 131 +++++++++++++++++++
 net/ethtool/ioctl.c                          |  42 +-----
 net/ethtool/netlink.c                        |   8 ++
 8 files changed, 252 insertions(+), 39 deletions(-)
 create mode 100644 net/ethtool/drvinfo.c

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 8b43f41a8140..1e3d5ffc97ab 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -121,6 +121,8 @@ List of message types
     ETHNL_CMD_EVENT			notification only
     ETHNL_CMD_GET_STRSET
     ETHNL_CMD_SET_STRSET		response only
+    ETHNL_CMD_GET_DRVINFO
+    ETHNL_CMD_SET_DRVINFO		response only
 
 All constants use ETHNL_CMD_ prefix, usually followed by "GET", "SET" or "ACT"
 to indicate the type.
@@ -156,6 +158,40 @@ and also multiple events of the same type (e.g. two or more newly registered
 devices).
 
 
+GET_DRVINFO
+-----------
+
+GET_DRVINFO request corresponds to ETHTOOL_GDRVINFO ioctl command and provides
+basic driver information.
+
+Request contents:
+
+    ETHA_DRVINFO_DEV		(nested)	device identification
+
+Kernel response contents:
+
+    ETHA_DRVINFO_DEV		(nested)	device identification
+    ETHA_DRVINFO_DRIVER		(string)	driver name
+    ETHA_DRVINFO_VERSION	(string)	driver version
+    ETHA_DRVINFO_FWVERSION	(string)	firmware version
+    ETHA_DRVINFO_BUSINFO	(string)	device bus address
+    ETHA_DRVINFO_EROM_VER	(string)	expansion ROM version
+    ETHA_DRVINFO_N_PRIV_FLAGS	(u32)		number of private flags
+    ETHA_DRVINFO_N_STATS	(u32)		number of device stats
+    ETHA_DRVINFO_TESTINFO_LEN	(u32)		number of test results
+    ETHA_DRVINFO_EEDUMP_LEN	(u32)		EEPROM dump size
+    ETHA_DRVINFO_REGDUMP_LEN	(u32)		register dump size
+
+The meaning of these follows the corresponding fields of ETHTOOL_GDRVINFO
+response.
+
+All information is read only, SET_DRVINFO request is not implemented
+(ETHNL_CMD_SET_DRVINFO messages are sent only by kernel in response to
+GET_DRVINFO requests).
+
+GET_DRVINFO requests allow dumps.
+
+
 Request translation
 -------------------
 
@@ -167,7 +203,7 @@ ioctl command			netlink command
 ---------------------------------------------------------------------
 ETHTOOL_GSET			n/a
 ETHTOOL_SSET			n/a
-ETHTOOL_GDRVINFO		n/a
+ETHTOOL_GDRVINFO		ETHNL_CMD_GET_DRVINFO
 ETHTOOL_GREGS			n/a
 ETHTOOL_GWOL			n/a
 ETHTOOL_SWOL			n/a
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 5177c1940c2b..df4de61fac48 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -10,6 +10,8 @@ enum {
 	ETHNL_CMD_EVENT,		/* only for notifications */
 	ETHNL_CMD_GET_STRSET,
 	ETHNL_CMD_SET_STRSET,		/* only for reply */
+	ETHNL_CMD_GET_DRVINFO,
+	ETHNL_CMD_SET_DRVINFO,		/* only for reply */
 
 	__ETHNL_CMD_MAX,
 	ETHNL_CMD_MAX = (__ETHNL_CMD_MAX - 1)
@@ -124,6 +126,26 @@ enum {
 	ETHA_STRSET_MAX = (__ETHA_STRSET_MAX - 1)
 };
 
+/* GET_DRVINFO / SET_DRVINFO */
+
+enum {
+	ETHA_DRVINFO_UNSPEC,
+	ETHA_DRVINFO_DEV,			/* nest - ETHA_DEV_* */
+	ETHA_DRVINFO_DRIVER,			/* string */
+	ETHA_DRVINFO_VERSION,			/* string */
+	ETHA_DRVINFO_FWVERSION,			/* string */
+	ETHA_DRVINFO_BUSINFO,			/* string */
+	ETHA_DRVINFO_EROM_VER,			/* string */
+	ETHA_DRVINFO_N_PRIV_FLAGS,		/* u32 */
+	ETHA_DRVINFO_N_STATS,			/* u32 */
+	ETHA_DRVINFO_TESTINFO_LEN,		/* u32 */
+	ETHA_DRVINFO_EEDUMP_LEN,		/* u32 */
+	ETHA_DRVINFO_REGDUMP_LEN,		/* u32 */
+
+	__ETHA_DRVINFO_MAX,
+	ETHA_DRVINFO_MAX = (__ETHA_DRVINFO_MAX - 1)
+};
+
 /* generic netlink info */
 #define ETHTOOL_GENL_NAME "ethtool"
 #define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index ba260d5b53b2..2e840ae0ba1e 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 
-obj-y				+= ioctl.o
+obj-y				+= ioctl.o common.o
 
 obj-$(CONFIG_ETHTOOL_NETLINK)	+= ethtool_nl.o
 
-ethtool_nl-y	:= netlink.o strset.o
+ethtool_nl-y	:= netlink.o strset.o drvinfo.o
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index 208259c51b73..1dc4a6515996 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 
+#include <linux/rtnetlink.h>
 #include "common.h"
 
 const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = {
@@ -85,3 +86,45 @@ phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
 	[ETHTOOL_PHY_DOWNSHIFT]	= "phy-downshift",
 };
 EXPORT_SYMBOL(phy_tunable_strings);
+
+int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+	const struct ethtool_ops *ops = dev->ethtool_ops;
+
+	memset(info, 0, sizeof(*info));
+	info->cmd = ETHTOOL_GDRVINFO;
+	if (ops->get_drvinfo) {
+		ops->get_drvinfo(dev, info);
+	} else if (dev->dev.parent && dev->dev.parent->driver) {
+		strlcpy(info->bus_info, dev_name(dev->dev.parent),
+			sizeof(info->bus_info));
+		strlcpy(info->driver, dev->dev.parent->driver->name,
+			sizeof(info->driver));
+	} else {
+		return -EOPNOTSUPP;
+	}
+
+	/* this method of obtaining string set info is deprecated;
+	 * Use ETHTOOL_GSSET_INFO instead.
+	 */
+	if (ops->get_sset_count) {
+		int rc;
+
+		rc = ops->get_sset_count(dev, ETH_SS_TEST);
+		if (rc >= 0)
+			info->testinfo_len = rc;
+		rc = ops->get_sset_count(dev, ETH_SS_STATS);
+		if (rc >= 0)
+			info->n_stats = rc;
+		rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
+		if (rc >= 0)
+			info->n_priv_flags = rc;
+	}
+	if (ops->get_regs_len)
+		info->regdump_len = ops->get_regs_len(dev);
+	if (ops->get_eeprom_len)
+		info->eedump_len = ops->get_eeprom_len(dev);
+
+	return 0;
+}
+EXPORT_SYMBOL(__ethtool_get_drvinfo);
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index 45c6492e4aee..0f768c1be527 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -3,6 +3,7 @@
 #ifndef _ETHTOOL_COMMON_H
 #define _ETHTOOL_COMMON_H
 
+#include <linux/netdevice.h>
 #include <linux/ethtool.h>
 
 extern const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN];
@@ -10,4 +11,6 @@ extern const char rss_hash_func_strings[ETH_RSS_HASH_FUNCS_COUNT][ETH_GSTRING_LE
 extern const char tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN];
 extern const char phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN];
 
+int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
+
 #endif /* _ETHTOOL_COMMON_H */
diff --git a/net/ethtool/drvinfo.c b/net/ethtool/drvinfo.c
new file mode 100644
index 000000000000..2bdaf6d7f28c
--- /dev/null
+++ b/net/ethtool/drvinfo.c
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#include "netlink.h"
+#include "common.h"
+
+static const struct nla_policy get_drvinfo_policy[ETHA_DRVINFO_MAX + 1] = {
+	[ETHA_DRVINFO_DEV]		= { .type = NLA_NESTED },
+};
+
+static int prepare_drvinfo(struct ethtool_drvinfo *data, struct genl_info *info,
+			   struct net_device *dev)
+{
+	int ret;
+
+	memset(data, '\0', sizeof(*data));
+	rtnl_lock();
+	ret = __ethtool_get_drvinfo(dev, data);
+	rtnl_unlock();
+	if (ret < 0) {
+		ETHNL_SET_ERRMSG(info, "failed to retrieve driver info");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int drvinfo_size(struct ethtool_drvinfo *drvinfo)
+{
+	int len = 0;
+
+	len += ethnl_str_ifne_size(drvinfo->driver);
+	len += ethnl_str_ifne_size(drvinfo->version);
+	len += ethnl_str_ifne_size(drvinfo->fw_version);
+	len += ethnl_str_ifne_size(drvinfo->bus_info);
+	len += ethnl_str_ifne_size(drvinfo->erom_version);
+	/* n_priv_flags, n_stats, testinfo_len, eedump_len, regdump_len */
+	len += 5 * nla_total_size(sizeof(u32));
+
+	return len;
+}
+
+static int fill_drvinfo(struct sk_buff *skb, struct net_device *dev,
+			struct ethtool_drvinfo *drvinfo)
+{
+	int ret;
+
+	ret = -EMSGSIZE;
+	if (ethnl_put_str_ifne(skb, ETHA_DRVINFO_DRIVER, drvinfo->driver) ||
+	    ethnl_put_str_ifne(skb, ETHA_DRVINFO_VERSION, drvinfo->version) ||
+	    ethnl_put_str_ifne(skb, ETHA_DRVINFO_FWVERSION,
+			       drvinfo->fw_version) ||
+	    ethnl_put_str_ifne(skb, ETHA_DRVINFO_BUSINFO, drvinfo->bus_info) ||
+	    ethnl_put_str_ifne(skb, ETHA_DRVINFO_EROM_VER,
+			       drvinfo->erom_version) ||
+	    nla_put_u32(skb, ETHA_DRVINFO_N_PRIV_FLAGS,
+			drvinfo->n_priv_flags) ||
+	    nla_put_u32(skb, ETHA_DRVINFO_N_STATS, drvinfo->n_stats) ||
+	    nla_put_u32(skb, ETHA_DRVINFO_TESTINFO_LEN,
+			drvinfo->testinfo_len) ||
+	    nla_put_u32(skb, ETHA_DRVINFO_EEDUMP_LEN, drvinfo->eedump_len) ||
+	    nla_put_u32(skb, ETHA_DRVINFO_REGDUMP_LEN, drvinfo->regdump_len))
+		return ret;
+
+	return 0;
+}
+
+int ethnl_get_drvinfo(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr *tb[ETHA_DRVINFO_MAX + 1];
+	struct ethtool_drvinfo drvinfo;
+	struct net_device *dev;
+	struct sk_buff *rskb;
+	int reply_len;
+	void *ehdr;
+	int ret;
+
+	ret = genlmsg_parse(info->nlhdr, &ethtool_genl_family, tb,
+			    ETHA_DRVINFO_MAX, get_drvinfo_policy, info->extack);
+	if (ret < 0)
+		return ret;
+	dev = ethnl_dev_get(info, tb[ETHA_DRVINFO_DEV]);
+	if (IS_ERR(dev))
+		return PTR_ERR(dev);
+
+	ret = prepare_drvinfo(&drvinfo, info, dev);
+	if (ret < 0)
+		goto err_dev;
+	reply_len = drvinfo_size(&drvinfo);
+	rskb = ethnl_reply_init(reply_len, dev, ETHNL_CMD_SET_DRVINFO,
+				ETHA_DRVINFO_DEV, info, &ehdr);
+	if (!rskb)
+		return -ENOMEM;
+	ret = fill_drvinfo(rskb, dev, &drvinfo);
+	if (ret < 0)
+		goto err;
+
+	genlmsg_end(rskb, ehdr);
+	dev_put(dev);
+	return genlmsg_reply(rskb, info);
+
+err:
+	WARN_ONCE(ret == -EMSGSIZE,
+		  "calculated message payload length (%d) not sufficient\n",
+		  reply_len);
+	nlmsg_free(rskb);
+err_dev:
+	dev_put(dev);
+	return ret;
+}
+
+static int drvinfo_dump(struct sk_buff *skb, struct netlink_callback *cb,
+			struct net_device *dev)
+{
+	struct ethtool_drvinfo drvinfo;
+	int ret;
+
+	ret = prepare_drvinfo(&drvinfo, NULL, dev);
+	if (ret < 0)
+		return ret;
+	ret = ethnl_fill_dev(skb, dev, ETHA_DRVINFO_DEV);
+	ret = fill_drvinfo(skb, dev, &drvinfo);
+	return ret;
+}
+
+int ethnl_drvinfo_start(struct netlink_callback *cb)
+{
+	cb->args[0] = (long)drvinfo_dump;
+	cb->args[1] = ETHNL_CMD_SET_DRVINFO;
+
+	return 0;
+}
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index a91b597073f8..7b5831d35bca 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -27,6 +27,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/sched/signal.h>
 #include <linux/net.h>
+#include "common.h"
 
 /*
  * Some useful ethtool_ops methods that're device independent.
@@ -768,45 +769,14 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
 						  void __user *useraddr)
 {
 	struct ethtool_drvinfo info;
-	const struct ethtool_ops *ops = dev->ethtool_ops;
-
-	memset(&info, 0, sizeof(info));
-	info.cmd = ETHTOOL_GDRVINFO;
-	if (ops->get_drvinfo) {
-		ops->get_drvinfo(dev, &info);
-	} else if (dev->dev.parent && dev->dev.parent->driver) {
-		strlcpy(info.bus_info, dev_name(dev->dev.parent),
-			sizeof(info.bus_info));
-		strlcpy(info.driver, dev->dev.parent->driver->name,
-			sizeof(info.driver));
-	} else {
-		return -EOPNOTSUPP;
-	}
-
-	/*
-	 * this method of obtaining string set info is deprecated;
-	 * Use ETHTOOL_GSSET_INFO instead.
-	 */
-	if (ops->get_sset_count) {
-		int rc;
-
-		rc = ops->get_sset_count(dev, ETH_SS_TEST);
-		if (rc >= 0)
-			info.testinfo_len = rc;
-		rc = ops->get_sset_count(dev, ETH_SS_STATS);
-		if (rc >= 0)
-			info.n_stats = rc;
-		rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
-		if (rc >= 0)
-			info.n_priv_flags = rc;
-	}
-	if (ops->get_regs_len)
-		info.regdump_len = ops->get_regs_len(dev);
-	if (ops->get_eeprom_len)
-		info.eedump_len = ops->get_eeprom_len(dev);
+	int rc;
 
+	rc = __ethtool_get_drvinfo(dev, &info);
+	if (rc < 0)
+		return rc;
 	if (copy_to_user(useraddr, &info, sizeof(info)))
 		return -EFAULT;
+
 	return 0;
 }
 
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 237a2cb40be4..305baa02ff70 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -670,8 +670,10 @@ static struct notifier_block ethnl_netdev_notifier = {
 /* genetlink setup */
 
 int ethnl_get_strset(struct sk_buff *skb, struct genl_info *info);
+int ethnl_get_drvinfo(struct sk_buff *skb, struct genl_info *info);
 
 int ethnl_strset_start(struct netlink_callback *cb);
+int ethnl_drvinfo_start(struct netlink_callback *cb);
 
 int ethnl_strset_done(struct netlink_callback *cb);
 
@@ -683,6 +685,12 @@ static const struct genl_ops ethtool_genl_ops[] = {
 		.dumpit	= ethnl_dumpit,
 		.done	= ethnl_strset_done,
 	},
+	{
+		.cmd	= ETHNL_CMD_GET_DRVINFO,
+		.doit	= ethnl_get_drvinfo,
+		.start	= ethnl_drvinfo_start,
+		.dumpit	= ethnl_dumpit,
+	},
 };
 
 static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
-- 
2.18.0

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ