linux/net/ethtool/wol.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2
   3#include "netlink.h"
   4#include "common.h"
   5#include "bitset.h"
   6
   7struct wol_req_info {
   8        struct ethnl_req_info           base;
   9};
  10
  11struct wol_reply_data {
  12        struct ethnl_reply_data         base;
  13        struct ethtool_wolinfo          wol;
  14        bool                            show_sopass;
  15};
  16
  17#define WOL_REPDATA(__reply_base) \
  18        container_of(__reply_base, struct wol_reply_data, base)
  19
  20const struct nla_policy ethnl_wol_get_policy[] = {
  21        [ETHTOOL_A_WOL_HEADER]          =
  22                NLA_POLICY_NESTED(ethnl_header_policy),
  23};
  24
  25static int wol_prepare_data(const struct ethnl_req_info *req_base,
  26                            struct ethnl_reply_data *reply_base,
  27                            struct genl_info *info)
  28{
  29        struct wol_reply_data *data = WOL_REPDATA(reply_base);
  30        struct net_device *dev = reply_base->dev;
  31        int ret;
  32
  33        if (!dev->ethtool_ops->get_wol)
  34                return -EOPNOTSUPP;
  35
  36        ret = ethnl_ops_begin(dev);
  37        if (ret < 0)
  38                return ret;
  39        dev->ethtool_ops->get_wol(dev, &data->wol);
  40        ethnl_ops_complete(dev);
  41        /* do not include password in notifications */
  42        data->show_sopass = info && (data->wol.supported & WAKE_MAGICSECURE);
  43
  44        return 0;
  45}
  46
  47static int wol_reply_size(const struct ethnl_req_info *req_base,
  48                          const struct ethnl_reply_data *reply_base)
  49{
  50        bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
  51        const struct wol_reply_data *data = WOL_REPDATA(reply_base);
  52        int len;
  53
  54        len = ethnl_bitset32_size(&data->wol.wolopts, &data->wol.supported,
  55                                  WOL_MODE_COUNT, wol_mode_names, compact);
  56        if (len < 0)
  57                return len;
  58        if (data->show_sopass)
  59                len += nla_total_size(sizeof(data->wol.sopass));
  60
  61        return len;
  62}
  63
  64static int wol_fill_reply(struct sk_buff *skb,
  65                          const struct ethnl_req_info *req_base,
  66                          const struct ethnl_reply_data *reply_base)
  67{
  68        bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
  69        const struct wol_reply_data *data = WOL_REPDATA(reply_base);
  70        int ret;
  71
  72        ret = ethnl_put_bitset32(skb, ETHTOOL_A_WOL_MODES, &data->wol.wolopts,
  73                                 &data->wol.supported, WOL_MODE_COUNT,
  74                                 wol_mode_names, compact);
  75        if (ret < 0)
  76                return ret;
  77        if (data->show_sopass &&
  78            nla_put(skb, ETHTOOL_A_WOL_SOPASS, sizeof(data->wol.sopass),
  79                    data->wol.sopass))
  80                return -EMSGSIZE;
  81
  82        return 0;
  83}
  84
  85const struct ethnl_request_ops ethnl_wol_request_ops = {
  86        .request_cmd            = ETHTOOL_MSG_WOL_GET,
  87        .reply_cmd              = ETHTOOL_MSG_WOL_GET_REPLY,
  88        .hdr_attr               = ETHTOOL_A_WOL_HEADER,
  89        .req_info_size          = sizeof(struct wol_req_info),
  90        .reply_data_size        = sizeof(struct wol_reply_data),
  91
  92        .prepare_data           = wol_prepare_data,
  93        .reply_size             = wol_reply_size,
  94        .fill_reply             = wol_fill_reply,
  95};
  96
  97/* WOL_SET */
  98
  99const struct nla_policy ethnl_wol_set_policy[] = {
 100        [ETHTOOL_A_WOL_HEADER]          =
 101                NLA_POLICY_NESTED(ethnl_header_policy),
 102        [ETHTOOL_A_WOL_MODES]           = { .type = NLA_NESTED },
 103        [ETHTOOL_A_WOL_SOPASS]          = { .type = NLA_BINARY,
 104                                            .len = SOPASS_MAX },
 105};
 106
 107int ethnl_set_wol(struct sk_buff *skb, struct genl_info *info)
 108{
 109        struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
 110        struct ethnl_req_info req_info = {};
 111        struct nlattr **tb = info->attrs;
 112        struct net_device *dev;
 113        bool mod = false;
 114        int ret;
 115
 116        ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_WOL_HEADER],
 117                                         genl_info_net(info), info->extack,
 118                                         true);
 119        if (ret < 0)
 120                return ret;
 121        dev = req_info.dev;
 122        ret = -EOPNOTSUPP;
 123        if (!dev->ethtool_ops->get_wol || !dev->ethtool_ops->set_wol)
 124                goto out_dev;
 125
 126        rtnl_lock();
 127        ret = ethnl_ops_begin(dev);
 128        if (ret < 0)
 129                goto out_rtnl;
 130
 131        dev->ethtool_ops->get_wol(dev, &wol);
 132        ret = ethnl_update_bitset32(&wol.wolopts, WOL_MODE_COUNT,
 133                                    tb[ETHTOOL_A_WOL_MODES], wol_mode_names,
 134                                    info->extack, &mod);
 135        if (ret < 0)
 136                goto out_ops;
 137        if (wol.wolopts & ~wol.supported) {
 138                NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_WOL_MODES],
 139                                    "cannot enable unsupported WoL mode");
 140                ret = -EINVAL;
 141                goto out_ops;
 142        }
 143        if (tb[ETHTOOL_A_WOL_SOPASS]) {
 144                if (!(wol.supported & WAKE_MAGICSECURE)) {
 145                        NL_SET_ERR_MSG_ATTR(info->extack,
 146                                            tb[ETHTOOL_A_WOL_SOPASS],
 147                                            "magicsecure not supported, cannot set password");
 148                        ret = -EINVAL;
 149                        goto out_ops;
 150                }
 151                ethnl_update_binary(wol.sopass, sizeof(wol.sopass),
 152                                    tb[ETHTOOL_A_WOL_SOPASS], &mod);
 153        }
 154
 155        if (!mod)
 156                goto out_ops;
 157        ret = dev->ethtool_ops->set_wol(dev, &wol);
 158        if (ret)
 159                goto out_ops;
 160        dev->wol_enabled = !!wol.wolopts;
 161        ethtool_notify(dev, ETHTOOL_MSG_WOL_NTF, NULL);
 162
 163out_ops:
 164        ethnl_ops_complete(dev);
 165out_rtnl:
 166        rtnl_unlock();
 167out_dev:
 168        dev_put(dev);
 169        return ret;
 170}
 171