Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 
0003 #include "netlink.h"
0004 #include "common.h"
0005 #include "bitset.h"
0006 
0007 struct wol_req_info {
0008     struct ethnl_req_info       base;
0009 };
0010 
0011 struct wol_reply_data {
0012     struct ethnl_reply_data     base;
0013     struct ethtool_wolinfo      wol;
0014     bool                show_sopass;
0015 };
0016 
0017 #define WOL_REPDATA(__reply_base) \
0018     container_of(__reply_base, struct wol_reply_data, base)
0019 
0020 const struct nla_policy ethnl_wol_get_policy[] = {
0021     [ETHTOOL_A_WOL_HEADER]      =
0022         NLA_POLICY_NESTED(ethnl_header_policy),
0023 };
0024 
0025 static int wol_prepare_data(const struct ethnl_req_info *req_base,
0026                 struct ethnl_reply_data *reply_base,
0027                 struct genl_info *info)
0028 {
0029     struct wol_reply_data *data = WOL_REPDATA(reply_base);
0030     struct net_device *dev = reply_base->dev;
0031     int ret;
0032 
0033     if (!dev->ethtool_ops->get_wol)
0034         return -EOPNOTSUPP;
0035 
0036     ret = ethnl_ops_begin(dev);
0037     if (ret < 0)
0038         return ret;
0039     dev->ethtool_ops->get_wol(dev, &data->wol);
0040     ethnl_ops_complete(dev);
0041     /* do not include password in notifications */
0042     data->show_sopass = info && (data->wol.supported & WAKE_MAGICSECURE);
0043 
0044     return 0;
0045 }
0046 
0047 static int wol_reply_size(const struct ethnl_req_info *req_base,
0048               const struct ethnl_reply_data *reply_base)
0049 {
0050     bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
0051     const struct wol_reply_data *data = WOL_REPDATA(reply_base);
0052     int len;
0053 
0054     len = ethnl_bitset32_size(&data->wol.wolopts, &data->wol.supported,
0055                   WOL_MODE_COUNT, wol_mode_names, compact);
0056     if (len < 0)
0057         return len;
0058     if (data->show_sopass)
0059         len += nla_total_size(sizeof(data->wol.sopass));
0060 
0061     return len;
0062 }
0063 
0064 static int wol_fill_reply(struct sk_buff *skb,
0065               const struct ethnl_req_info *req_base,
0066               const struct ethnl_reply_data *reply_base)
0067 {
0068     bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
0069     const struct wol_reply_data *data = WOL_REPDATA(reply_base);
0070     int ret;
0071 
0072     ret = ethnl_put_bitset32(skb, ETHTOOL_A_WOL_MODES, &data->wol.wolopts,
0073                  &data->wol.supported, WOL_MODE_COUNT,
0074                  wol_mode_names, compact);
0075     if (ret < 0)
0076         return ret;
0077     if (data->show_sopass &&
0078         nla_put(skb, ETHTOOL_A_WOL_SOPASS, sizeof(data->wol.sopass),
0079             data->wol.sopass))
0080         return -EMSGSIZE;
0081 
0082     return 0;
0083 }
0084 
0085 const struct ethnl_request_ops ethnl_wol_request_ops = {
0086     .request_cmd        = ETHTOOL_MSG_WOL_GET,
0087     .reply_cmd      = ETHTOOL_MSG_WOL_GET_REPLY,
0088     .hdr_attr       = ETHTOOL_A_WOL_HEADER,
0089     .req_info_size      = sizeof(struct wol_req_info),
0090     .reply_data_size    = sizeof(struct wol_reply_data),
0091 
0092     .prepare_data       = wol_prepare_data,
0093     .reply_size     = wol_reply_size,
0094     .fill_reply     = wol_fill_reply,
0095 };
0096 
0097 /* WOL_SET */
0098 
0099 const struct nla_policy ethnl_wol_set_policy[] = {
0100     [ETHTOOL_A_WOL_HEADER]      =
0101         NLA_POLICY_NESTED(ethnl_header_policy),
0102     [ETHTOOL_A_WOL_MODES]       = { .type = NLA_NESTED },
0103     [ETHTOOL_A_WOL_SOPASS]      = { .type = NLA_BINARY,
0104                         .len = SOPASS_MAX },
0105 };
0106 
0107 int ethnl_set_wol(struct sk_buff *skb, struct genl_info *info)
0108 {
0109     struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
0110     struct ethnl_req_info req_info = {};
0111     struct nlattr **tb = info->attrs;
0112     struct net_device *dev;
0113     bool mod = false;
0114     int ret;
0115 
0116     ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_WOL_HEADER],
0117                      genl_info_net(info), info->extack,
0118                      true);
0119     if (ret < 0)
0120         return ret;
0121     dev = req_info.dev;
0122     ret = -EOPNOTSUPP;
0123     if (!dev->ethtool_ops->get_wol || !dev->ethtool_ops->set_wol)
0124         goto out_dev;
0125 
0126     rtnl_lock();
0127     ret = ethnl_ops_begin(dev);
0128     if (ret < 0)
0129         goto out_rtnl;
0130 
0131     dev->ethtool_ops->get_wol(dev, &wol);
0132     ret = ethnl_update_bitset32(&wol.wolopts, WOL_MODE_COUNT,
0133                     tb[ETHTOOL_A_WOL_MODES], wol_mode_names,
0134                     info->extack, &mod);
0135     if (ret < 0)
0136         goto out_ops;
0137     if (wol.wolopts & ~wol.supported) {
0138         NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_WOL_MODES],
0139                     "cannot enable unsupported WoL mode");
0140         ret = -EINVAL;
0141         goto out_ops;
0142     }
0143     if (tb[ETHTOOL_A_WOL_SOPASS]) {
0144         if (!(wol.supported & WAKE_MAGICSECURE)) {
0145             NL_SET_ERR_MSG_ATTR(info->extack,
0146                         tb[ETHTOOL_A_WOL_SOPASS],
0147                         "magicsecure not supported, cannot set password");
0148             ret = -EINVAL;
0149             goto out_ops;
0150         }
0151         ethnl_update_binary(wol.sopass, sizeof(wol.sopass),
0152                     tb[ETHTOOL_A_WOL_SOPASS], &mod);
0153     }
0154 
0155     if (!mod)
0156         goto out_ops;
0157     ret = dev->ethtool_ops->set_wol(dev, &wol);
0158     if (ret)
0159         goto out_ops;
0160     dev->wol_enabled = !!wol.wolopts;
0161     ethtool_notify(dev, ETHTOOL_MSG_WOL_NTF, NULL);
0162 
0163 out_ops:
0164     ethnl_ops_complete(dev);
0165 out_rtnl:
0166     rtnl_unlock();
0167 out_dev:
0168     ethnl_parse_header_dev_put(&req_info);
0169     return ret;
0170 }