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