ethtool: set wake-on-lan settings with WOL_SET request
authorMichal Kubecek <mkubecek@suse.cz>
Sun, 26 Jan 2020 22:11:16 +0000 (23:11 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 27 Jan 2020 10:31:36 +0000 (11:31 +0100)
Implement WOL_SET netlink request to set wake-on-lan settings. This is
equivalent to ETHTOOL_SWOL ioctl request.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/networking/ethtool-netlink.rst
include/uapi/linux/ethtool_netlink.h
net/ethtool/netlink.c
net/ethtool/netlink.h
net/ethtool/wol.c

index 5fd85e3..f16f74b 100644 (file)
@@ -188,6 +188,7 @@ Userspace to kernel:
   ``ETHTOOL_MSG_DEBUG_GET``             get debugging settings
   ``ETHTOOL_MSG_DEBUG_SET``             set debugging settings
   ``ETHTOOL_MSG_WOL_GET``               get wake-on-lan settings
+  ``ETHTOOL_MSG_WOL_SET``               set wake-on-lan settings
   ===================================== ================================
 
 Kernel to userspace:
@@ -502,6 +503,23 @@ device, value of modes which are enabled. ``ETHTOOL_A_WOL_SOPASS`` is only
 included in reply if ``WAKE_MAGICSECURE`` mode is supported.
 
 
+WOL_SET
+=======
+
+Set or update wake-on-lan settings.
+
+Request contents:
+
+  ====================================  ======  ==========================
+  ``ETHTOOL_A_WOL_HEADER``              nested  request header
+  ``ETHTOOL_A_WOL_MODES``               bitset  enabled WoL modes
+  ``ETHTOOL_A_WOL_SOPASS``              binary  SecureOn(tm) password
+  ====================================  ======  ==========================
+
+``ETHTOOL_A_WOL_SOPASS`` is only allowed for devices supporting
+``WAKE_MAGICSECURE`` mode.
+
+
 Request translation
 ===================
 
@@ -519,7 +537,7 @@ have their netlink replacement yet.
   ``ETHTOOL_GDRVINFO``                n/a
   ``ETHTOOL_GREGS``                   n/a
   ``ETHTOOL_GWOL``                    ``ETHTOOL_MSG_WOL_GET``
-  ``ETHTOOL_SWOL``                    n/a
+  ``ETHTOOL_SWOL``                    ``ETHTOOL_MSG_WOL_SET``
   ``ETHTOOL_GMSGLVL``                 ``ETHTOOL_MSG_DEBUG_GET``
   ``ETHTOOL_SMSGLVL``                 ``ETHTOOL_MSG_DEBUG_SET``
   ``ETHTOOL_NWAY_RST``                n/a
index dcc5c32..59de356 100644 (file)
@@ -23,6 +23,7 @@ enum {
        ETHTOOL_MSG_DEBUG_GET,
        ETHTOOL_MSG_DEBUG_SET,
        ETHTOOL_MSG_WOL_GET,
+       ETHTOOL_MSG_WOL_SET,
 
        /* add new constants above here */
        __ETHTOOL_MSG_USER_CNT,
index eeb6d85..2c375f9 100644 (file)
@@ -688,6 +688,11 @@ static const struct genl_ops ethtool_genl_ops[] = {
                .dumpit = ethnl_default_dumpit,
                .done   = ethnl_default_done,
        },
+       {
+               .cmd    = ETHTOOL_MSG_WOL_SET,
+               .flags  = GENL_UNS_ADMIN_PERM,
+               .doit   = ethnl_set_wol,
+       },
 };
 
 static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
index 9fcd6f8..60efd87 100644 (file)
@@ -340,5 +340,6 @@ extern const struct ethnl_request_ops ethnl_wol_request_ops;
 int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info);
 int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info);
 int ethnl_set_debug(struct sk_buff *skb, struct genl_info *info);
+int ethnl_set_wol(struct sk_buff *skb, struct genl_info *info);
 
 #endif /* _NET_ETHTOOL_NETLINK_H */
index 7c9a1ef..a272437 100644 (file)
@@ -97,3 +97,79 @@ const struct ethnl_request_ops ethnl_wol_request_ops = {
        .reply_size             = wol_reply_size,
        .fill_reply             = wol_fill_reply,
 };
+
+/* WOL_SET */
+
+static const struct nla_policy
+wol_set_policy[ETHTOOL_A_WOL_MAX + 1] = {
+       [ETHTOOL_A_WOL_UNSPEC]          = { .type = NLA_REJECT },
+       [ETHTOOL_A_WOL_HEADER]          = { .type = NLA_NESTED },
+       [ETHTOOL_A_WOL_MODES]           = { .type = NLA_NESTED },
+       [ETHTOOL_A_WOL_SOPASS]          = { .type = NLA_BINARY,
+                                           .len = SOPASS_MAX },
+};
+
+int ethnl_set_wol(struct sk_buff *skb, struct genl_info *info)
+{
+       struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+       struct nlattr *tb[ETHTOOL_A_WOL_MAX + 1];
+       struct ethnl_req_info req_info = {};
+       struct net_device *dev;
+       bool mod = false;
+       int ret;
+
+       ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, ETHTOOL_A_WOL_MAX,
+                         wol_set_policy, info->extack);
+       if (ret < 0)
+               return ret;
+       ret = ethnl_parse_header(&req_info, tb[ETHTOOL_A_WOL_HEADER],
+                                genl_info_net(info), info->extack, true);
+       if (ret < 0)
+               return ret;
+       dev = req_info.dev;
+       if (!dev->ethtool_ops->get_wol || !dev->ethtool_ops->set_wol)
+               return -EOPNOTSUPP;
+
+       rtnl_lock();
+       ret = ethnl_ops_begin(dev);
+       if (ret < 0)
+               goto out_rtnl;
+
+       dev->ethtool_ops->get_wol(dev, &wol);
+       ret = ethnl_update_bitset32(&wol.wolopts, WOL_MODE_COUNT,
+                                   tb[ETHTOOL_A_WOL_MODES], wol_mode_names,
+                                   info->extack, &mod);
+       if (ret < 0)
+               goto out_ops;
+       if (wol.wolopts & ~wol.supported) {
+               NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_WOL_MODES],
+                                   "cannot enable unsupported WoL mode");
+               ret = -EINVAL;
+               goto out_ops;
+       }
+       if (tb[ETHTOOL_A_WOL_SOPASS]) {
+               if (!(wol.supported & WAKE_MAGICSECURE)) {
+                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                           tb[ETHTOOL_A_WOL_SOPASS],
+                                           "magicsecure not supported, cannot set password");
+                       ret = -EINVAL;
+                       goto out_ops;
+               }
+               ethnl_update_binary(wol.sopass, sizeof(wol.sopass),
+                                   tb[ETHTOOL_A_WOL_SOPASS], &mod);
+       }
+
+       if (!mod)
+               goto out_ops;
+       ret = dev->ethtool_ops->set_wol(dev, &wol);
+       if (ret)
+               goto out_ops;
+       dev->wol_enabled = !!wol.wolopts;
+
+out_ops:
+       ethnl_ops_complete(dev);
+out_rtnl:
+       rtnl_unlock();
+       dev_put(dev);
+       return ret;
+}