0001
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
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
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 }