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  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]
Date:   Mon, 18 Feb 2019 19:23:05 +0100 (CET)
From:   Michal Kubecek <mkubecek@...e.cz>
To:     netdev@...r.kernel.org
Cc:     David Miller <davem@...emloft.net>, Andrew Lunn <andrew@...n.ch>,
        Jakub Kicinski <jakub.kicinski@...ronome.com>,
        Jiri Pirko <jiri@...nulli.us>, linux-kernel@...r.kernel.org
Subject: [RFC PATCH net-next v3 20/21] ethtool: provide private flags in
 GET_SETTINGS request

Add information about device private flags (as provided by
ETHTOOL_GPFLAGS ioctl command) in GET_SETTINGS reply when
ETH_SETTINGS_IM_PRIVFLAGS flag is set in the request.

Signed-off-by: Michal Kubecek <mkubecek@...e.cz>
---
 Documentation/networking/ethtool-netlink.txt |  9 +-
 include/uapi/linux/ethtool_netlink.h         |  4 +-
 net/ethtool/settings.c                       | 98 ++++++++++++++++++++
 3 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 664c922a05eb..290008aaed0a 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -287,6 +287,7 @@ Info mask bits meaning:
     ETH_SETTINGS_IM_MSGLEVEL		msglevel
     ETH_SETTINGS_IM_LINK		link state
     ETH_SETTINGS_IM_FEATURES		features
+    ETH_SETTINGS_IM_PRIVFLAGS		device private flags
 
 Response contents:
 
@@ -312,6 +313,7 @@ Response contents:
         ETHA_FEATURES_WANTED		(bitset)	dev->wanted_features
         ETHA_FEATURES_ACTIVE		(bitset)	dev->features
         ETHA_FEATURES_NOCHANGE		(bitset)	NETIF_F_NEVER_CHANGE
+    ETHA_SETTINGS_PRIV_FLAGS	(bitset)	device private flags
 
 Most of the attributes and their values have the same meaning as matching
 members of the corresponding ioctl structures. For ETHA_SETTINGS_LINK_MODES,
@@ -333,6 +335,11 @@ itself. ETHA_FEATURES_HW uses mask consisting of all features recognized by
 kernel (to provide all names when using verbose bitmap format), remaining
 three use mask equal to value (to save space).
 
+ETHA_SETTINGS_PRIV_FLAGS is a bitset with values of device private flags.
+These flags are defined by driver, their number and names (as well as meaning)
+are device dependent. For compact bitset format, names can be retrieved as
+ETH_SS_PRIV_FLAGS string set.
+
 GET_SETTINGS request is allowed for unprivileged user but ETHA_SETTINGS_SOPASS
 is only provided by kernel in response to privileged (netns CAP_NET_ADMIN)
 requests.
@@ -389,7 +396,7 @@ ETHTOOL_GGSO			ETHNL_CMD_GET_SETTINGS
 ETHTOOL_SGSO			n/a
 ETHTOOL_GFLAGS			ETHNL_CMD_GET_SETTINGS
 ETHTOOL_SFLAGS			n/a
-ETHTOOL_GPFLAGS			n/a
+ETHTOOL_GPFLAGS			ETHNL_CMD_GET_SETTINGS
 ETHTOOL_SPFLAGS			n/a
 ETHTOOL_GRXFH			n/a
 ETHTOOL_SRXFH			n/a
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 154d7e6a59dd..afd61d36bcf8 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -205,6 +205,7 @@ enum {
 	ETHA_SETTINGS_MSGLEVEL,			/* bitfield32 */
 	ETHA_SETTINGS_LINK,			/* u8 */
 	ETHA_SETTINGS_FEATURES,			/* nest - ETHA_FEATURES_* */
+	ETHA_SETTINGS_PRIV_FLAGS,		/* nest - ETHA_BITSET_* */
 
 	__ETHA_SETTINGS_CNT,
 	ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -216,8 +217,9 @@ enum {
 #define ETH_SETTINGS_IM_MSGLEVEL		0x08
 #define ETH_SETTINGS_IM_LINK			0x10
 #define ETH_SETTINGS_IM_FEATURES		0x20
+#define ETH_SETTINGS_IM_PRIVFLAGS		0x40
 
-#define ETH_SETTINGS_IM_ALL			0x3f
+#define ETH_SETTINGS_IM_ALL			0x7f
 
 enum {
 	ETHA_FEATURES_UNSPEC,
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 32ee273de879..7f72f4250306 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -22,6 +22,9 @@ struct settings_data {
 		u32	active[ETHTOOL_DEV_FEATURE_WORDS];
 		u32	nochange[ETHTOOL_DEV_FEATURE_WORDS];
 	} features;
+	char				(*priv_flag_names)[ETH_GSTRING_LEN];
+	u32				priv_flags;
+	unsigned int			n_priv_flags;
 };
 
 static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
@@ -36,6 +39,7 @@ static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
 	[ETHA_SETTINGS_MSGLEVEL]	= { .type = NLA_REJECT },
 	[ETHA_SETTINGS_LINK]		= { .type = NLA_REJECT },
 	[ETHA_SETTINGS_FEATURES]	= { .type = NLA_REJECT },
+	[ETHA_SETTINGS_PRIV_FLAGS]	= { .type = NLA_REJECT },
 };
 
 static int parse_settings(struct common_req_info *req_info,
@@ -114,6 +118,58 @@ static int ethnl_get_features(struct net_device *dev,
 	return 0;
 }
 
+static int get_priv_flags_info(struct net_device *dev, unsigned int *count,
+			       void **names)
+{
+	const struct ethtool_ops *ops = dev->ethtool_ops;
+	int nflags;
+
+	if (!ops->get_priv_flags || !ops->get_sset_count || !ops->get_strings)
+		return -EOPNOTSUPP;
+	nflags = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
+	if (nflags < 0)
+		return nflags;
+
+	if (names) {
+		*names = kcalloc(nflags, ETH_GSTRING_LEN, GFP_KERNEL);
+		if (!*names)
+			return -ENOMEM;
+		ops->get_strings(dev, ETH_SS_PRIV_FLAGS, *names);
+	}
+
+	/* We can easily pass more than 32 private flags to userspace via
+	 * netlink but we cannot get more with ethtool_ops::get_priv_flags().
+	 * Note that we must not adjust nflags before allocating the space
+	 * for flag names as the buffer must be large enough for all flags.
+	 */
+	if (WARN_ONCE(nflags > 32,
+		      "device %s reports more than 32 private flags (%d)\n",
+		      netdev_name(dev), nflags))
+		nflags = 32;
+
+	*count = nflags;
+	return 0;
+}
+
+static int ethnl_get_priv_flags(struct genl_info *info,
+				struct settings_data *data)
+{
+	struct net_device *dev = data->repdata_base.dev;
+	const struct ethtool_ops *ops = dev->ethtool_ops;
+	unsigned int nflags;
+	void *names;
+	int ret;
+
+	ret = get_priv_flags_info(dev, &nflags, &names);
+	if (ret < 0)
+		return ret;
+
+	data->priv_flags = ops->get_priv_flags(dev);
+	data->priv_flag_names = names;
+	data->n_priv_flags = nflags;
+	return 0;
+}
+
 static int prepare_settings(struct common_req_info *req_info,
 			    struct genl_info *info)
 {
@@ -163,6 +219,11 @@ static int prepare_settings(struct common_req_info *req_info,
 		data->link = __ethtool_get_link(dev);
 	if (req_mask & ETH_SETTINGS_IM_FEATURES)
 		ethnl_get_features(dev, data);
+	if (req_mask & ETH_SETTINGS_IM_PRIVFLAGS) {
+		ret = ethnl_get_priv_flags(info, data);
+		if (ret < 0)
+			req_mask &= ~ETH_SETTINGS_IM_PRIVFLAGS;
+	}
 	ethnl_after_ops(dev);
 
 	data->repdata_base.info_mask = req_mask;
@@ -281,6 +342,17 @@ static int settings_size(const struct common_req_info *req_info)
 			return ret;
 		len += ret;
 	}
+	if (info_mask & ETH_SETTINGS_IM_PRIVFLAGS) {
+		const unsigned int flags =
+			(compact ? ETHNL_BITSET_COMPACT : 0) |
+			ETHNL_BITSET_LEGACY_NAMES;
+
+		ret = ethnl_bitset32_size(data->n_priv_flags, &data->priv_flags,
+					  NULL, data->priv_flag_names, flags);
+		if (ret < 0)
+			return ret;
+		len += ret;
+	}
 
 	return len;
 }
@@ -404,6 +476,18 @@ static int fill_features(struct sk_buff *skb, const struct settings_data *data)
 	return 0;
 }
 
+static int fill_priv_flags(struct sk_buff *skb,
+			   const struct settings_data *data)
+{
+	const unsigned int bitset_flags =
+		(data->reqinfo_base.compact ? ETHNL_BITSET_COMPACT : 0) |
+		ETHNL_BITSET_LEGACY_NAMES;
+
+	return ethnl_put_bitset32(skb, ETHA_SETTINGS_PRIV_FLAGS,
+				  data->n_priv_flags, &data->priv_flags, NULL,
+				  data->priv_flag_names, bitset_flags);
+}
+
 static int fill_settings(struct sk_buff *skb,
 			 const struct common_req_info *req_info)
 {
@@ -443,10 +527,23 @@ static int fill_settings(struct sk_buff *skb,
 		if (ret < 0)
 			return ret;
 	}
+	if (info_mask & ETH_SETTINGS_IM_PRIVFLAGS) {
+		ret = fill_priv_flags(skb, data);
+		if (ret < 0)
+			return ret;
+	}
 
 	return 0;
 }
 
+static void settings_cleanup(struct common_req_info *req_info)
+{
+	const struct settings_data *data =
+		container_of(req_info, struct settings_data, reqinfo_base);
+
+	kfree(data->priv_flag_names);
+}
+
 const struct get_request_ops settings_request_ops = {
 	.request_cmd		= ETHNL_CMD_GET_SETTINGS,
 	.reply_cmd		= ETHNL_CMD_SET_SETTINGS,
@@ -458,4 +555,5 @@ const struct get_request_ops settings_request_ops = {
 	.prepare_data		= prepare_settings,
 	.reply_size		= settings_size,
 	.fill_reply		= fill_settings,
+	.cleanup		= settings_cleanup,
 };
-- 
2.20.1

Powered by blists - more mailing lists