diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index ebd1a0404828..bc7f28f0182f 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -278,6 +278,7 @@ Info mask bits meaning:
     ETH_SETTINGS_IM_LINKINFO		link settings
     ETH_SETTINGS_IM_LINKMODES		link modes and related
     ETH_SETTINGS_IM_LINKSTATE		link state
+    ETH_SETTINGS_IM_WOL			struct ethtool_wolinfo
 
 Response contents:
 
@@ -296,12 +297,22 @@ Response contents:
         ETHA_LINKMODES_DUPLEX		(u8)		duplex mode
     ETHA_SETTINGS_LINK_STATE	(nested)	link state
         ETHA_LINKSTATE_LINK		(u8)		link on/off/unknown
+    ETHA_SETTINGS_WOL		(nested)	wake on LAN settings
+        ETHA_WOL_MODES			(bitfield32)	wake on LAN modes
+        ETHA_WOL_SOPASS			(binary)	SecureOn(tm) password
 
 Most of the attributes and their values have the same meaning as matching
 members of the corresponding ioctl structures. For ETHA_LINKMODES_OURS,
 value represents advertised modes and mask represents supported modes.
 ETHA_LINKMODES_PEER in the reply is a bit list.
 
+For ETHA_WOL_MODES, selector reports wake on LAN modes supported by the
+device and value enabled modes.
+
+GET_SETTINGS request is allowed for unprivileged user but ETHA_WOL_SOPASS
+is only provided by kernel in response to privileged (netns CAP_NET_ADMIN)
+requests.
+
 GET_SETTINGS requests allow dumps and messages in the same format as response
 to them are broadcasted as notifications on change of these settings using
 netlink or ioctl ethtool interface.
@@ -348,7 +359,7 @@ ETHTOOL_GSET			ETHNL_CMD_GET_SETTINGS
 ETHTOOL_SSET			ETHNL_CMD_SET_SETTINGS
 ETHTOOL_GDRVINFO		ETHNL_CMD_GET_INFO
 ETHTOOL_GREGS			n/a
-ETHTOOL_GWOL			n/a
+ETHTOOL_GWOL			ETHNL_CMD_GET_SETTINGS
 ETHTOOL_SWOL			n/a
 ETHTOOL_GMSGLVL			n/a
 ETHTOOL_SMSGLVL			n/a
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 45c27c7291d0..532ad11ae87c 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -197,6 +197,7 @@ enum {
 	ETHA_SETTINGS_LINK_INFO,		/* nest - ETHA_LINKINFO_* */
 	ETHA_SETTINGS_LINK_MODES,		/* nest - ETHA_LINKMODES_* */
 	ETHA_SETTINGS_LINK_STATE,		/* nest - ETHA_LINKSTATE_* */
+	ETHA_SETTINGS_WOL,			/* nest - ETHA_WOL_* */
 
 	__ETHA_SETTINGS_CNT,
 	ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -205,10 +206,12 @@ enum {
 #define ETH_SETTINGS_IM_LINKINFO		(1U << 0)
 #define ETH_SETTINGS_IM_LINKMODES		(1U << 1)
 #define ETH_SETTINGS_IM_LINKSTATE		(1U << 2)
+#define ETH_SETTINGS_IM_WOL			(1U << 3)
 
 #define ETH_SETTINGS_IM_ALL (ETH_SETTINGS_IM_LINKINFO | \
 			     ETH_SETTINGS_IM_LINKMODES | \
-			     ETH_SETTINGS_IM_LINKSTATE)
+			     ETH_SETTINGS_IM_LINKSTATE | \
+			     ETH_SETTINGS_IM_WOL)
 
 enum {
 	ETHA_LINKINFO_UNSPEC,
@@ -242,6 +245,15 @@ enum {
 	ETHA_LINKSTATE_MAX = (__ETHA_LINKSTATE_CNT - 1)
 };
 
+enum {
+	ETHA_WOL_UNSPEC,
+	ETHA_WOL_MODES,				/* bitfield32 */
+	ETHA_WOL_SOPASS,			/* binary */
+
+	__ETHA_WOL_CNT,
+	ETHA_WOL_MAX = (__ETHA_WOL_CNT - 1)
+};
+
 /* generic netlink info */
 #define ETHTOOL_GENL_NAME "ethtool"
 #define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index dc907d8b6e43..4b2f08da910a 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -213,3 +213,13 @@ int __ethtool_get_link(struct net_device *dev)
 
 	return netif_running(dev) && dev->ethtool_ops->get_link(dev);
 }
+
+int __ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	if (!dev->ethtool_ops->get_wol)
+		return -EOPNOTSUPP;
+
+	dev->ethtool_ops->get_wol(dev, wol);
+
+	return 0;
+}
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index a5ddd7f5cfce..bbe3e51f7308 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -18,6 +18,7 @@ phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN];
 int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
 int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info);
 int __ethtool_get_link(struct net_device *dev);
+int __ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
 
 bool convert_legacy_settings_to_link_ksettings(
 	struct ethtool_link_ksettings *link_ksettings,
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 58669dafeaf9..bba02a218eea 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -1242,11 +1242,11 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
 static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+	int rc;
 
-	if (!dev->ethtool_ops->get_wol)
-		return -EOPNOTSUPP;
-
-	dev->ethtool_ops->get_wol(dev, &wol);
+	rc = __ethtool_get_wol(dev, &wol);
+	if (rc < 0)
+		return rc;
 
 	if (copy_to_user(useraddr, &wol, sizeof(wol)))
 		return -EFAULT;
@@ -1269,6 +1269,8 @@ static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
 		return ret;
 
 	dev->wol_enabled = !!wol.wolopts;
+	ethtool_notify(dev, NULL, ETHNL_CMD_SET_SETTINGS, ETH_SETTINGS_IM_WOL,
+		       NULL);
 
 	return 0;
 }
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 7a2729f01f79..56a045e5e916 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -6,10 +6,12 @@
 
 struct settings_data {
 	struct common_req_info		reqinfo_base;
+	bool				privileged;
 
 	/* everything below here will be reset for each device in dumps */
 	struct common_reply_data	repdata_base;
 	struct ethtool_link_ksettings	ksettings;
+	struct ethtool_wolinfo		wolinfo;
 	struct ethtool_link_settings	*lsettings;
 	int				link;
 	bool				lpm_empty;
@@ -114,15 +116,20 @@ static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
 	[ETHA_SETTINGS_LINK_INFO]	= { .type = NLA_REJECT },
 	[ETHA_SETTINGS_LINK_MODES]	= { .type = NLA_REJECT },
 	[ETHA_SETTINGS_LINK_STATE]	= { .type = NLA_REJECT },
+	[ETHA_SETTINGS_WOL]		= { .type = NLA_REJECT },
 };
 
 static int parse_settings(struct common_req_info *req_info,
 			  struct sk_buff *skb, struct genl_info *info,
 			  const struct nlmsghdr *nlhdr)
 {
+	struct settings_data *data =
+		container_of(req_info, struct settings_data, reqinfo_base);
 	struct nlattr *tb[ETHA_SETTINGS_MAX + 1];
 	int ret;
 
+	data->privileged = ethnl_is_privileged(skb);
+
 	ret = ethnlmsg_parse(nlhdr, tb, ETHA_SETTINGS_MAX, get_settings_policy,
 			     info);
 	if (ret < 0)
@@ -159,6 +166,16 @@ static int ethnl_get_link_ksettings(struct genl_info *info,
 	return ret;
 }
 
+static int ethnl_get_wol(struct genl_info *info, struct net_device *dev,
+			 struct ethtool_wolinfo *wolinfo)
+{
+	int ret = __ethtool_get_wol(dev, wolinfo);
+
+	if (ret < 0)
+		ETHNL_SET_ERRMSG(info, "failed to retrieve wol info");
+	return ret;
+}
+
 static int prepare_settings(struct common_req_info *req_info,
 			    struct genl_info *info)
 {
@@ -194,6 +211,11 @@ static int prepare_settings(struct common_req_info *req_info,
 	}
 	if (req_mask & ETH_SETTINGS_IM_LINKSTATE)
 		data->link = __ethtool_get_link(dev);
+	if (req_mask & ETH_SETTINGS_IM_WOL) {
+		ret = ethnl_get_wol(info, dev, &data->wolinfo);
+		if (ret < 0)
+			req_mask &= ~ETH_SETTINGS_IM_WOL;
+	}
 	ethnl_after_ops(dev);
 
 	data->repdata_base.info_mask = req_mask;
@@ -249,6 +271,12 @@ static int link_state_size(int link)
 	return nla_total_size(nla_total_size(sizeof(u8)));
 }
 
+static int wol_size(void)
+{
+	return nla_total_size(nla_total_size(sizeof(struct nla_bitfield32)) +
+			      nla_total_size(SOPASS_MAX));
+}
+
 /* To keep things simple, reserve space for some attributes which may not
  * be added to the message (e.g. ETHA_SETTINGS_SOPASS); therefore the length
  * returned may be bigger than the actual length of the message sent
@@ -272,6 +300,8 @@ static int settings_size(const struct common_req_info *req_info)
 	}
 	if (info_mask & ETH_SETTINGS_IM_LINKSTATE)
 		len += link_state_size(data->link);
+	if (info_mask & ETH_SETTINGS_IM_WOL)
+		len += wol_size();
 
 	return len;
 }
@@ -361,6 +391,33 @@ static int fill_link_state(struct sk_buff *skb, u8 link)
 	return -EMSGSIZE;
 }
 
+static int fill_wolinfo(struct sk_buff *skb,
+			const struct ethtool_wolinfo *wolinfo, bool privileged)
+{
+	struct nlattr *nest;
+
+	nest = ethnl_nest_start(skb, ETHA_SETTINGS_WOL);
+	if (!nest)
+		return -EMSGSIZE;
+	if (nla_put_bitfield32(skb, ETHA_WOL_MODES, wolinfo->wolopts,
+			       wolinfo->supported))
+		goto err;
+	/* ioctl() restricts read access to wolinfo but the actual
+	 * reason is to hide sopass from unprivileged users; netlink
+	 * can show wol modes without sopass
+	 */
+	if (privileged &&
+	    nla_put(skb, ETHA_WOL_SOPASS, sizeof(wolinfo->sopass),
+		    wolinfo->sopass))
+		goto err;
+	nla_nest_end(skb, nest);
+	return 0;
+
+err:
+	nla_nest_cancel(skb, nest);
+	return -EMSGSIZE;
+}
+
 static int fill_settings(struct sk_buff *skb,
 			 const struct common_req_info *req_info)
 {
@@ -386,6 +443,11 @@ static int fill_settings(struct sk_buff *skb,
 		if (ret < 0)
 			return ret;
 	}
+	if (info_mask & ETH_SETTINGS_IM_WOL) {
+		ret = fill_wolinfo(skb, &data->wolinfo, data->privileged);
+		if (ret < 0)
+			return ret;
+	}
 
 	return 0;
 }
@@ -431,6 +493,7 @@ static const struct nla_policy set_settings_policy[ETHA_SETTINGS_MAX + 1] = {
 	[ETHA_SETTINGS_LINK_INFO]	= { .type = NLA_NESTED },
 	[ETHA_SETTINGS_LINK_MODES]	= { .type = NLA_NESTED },
 	[ETHA_SETTINGS_LINK_STATE]	= { .type = NLA_REJECT },
+	[ETHA_SETTINGS_WOL]		= { .type = NLA_REJECT },
 };
 
 static int ethnl_set_link_ksettings(struct genl_info *info,
