18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci#include "netlink.h" 48c2ecf20Sopenharmony_ci#include "common.h" 58c2ecf20Sopenharmony_ci#include "bitset.h" 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_cistruct wol_req_info { 88c2ecf20Sopenharmony_ci struct ethnl_req_info base; 98c2ecf20Sopenharmony_ci}; 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistruct wol_reply_data { 128c2ecf20Sopenharmony_ci struct ethnl_reply_data base; 138c2ecf20Sopenharmony_ci struct ethtool_wolinfo wol; 148c2ecf20Sopenharmony_ci bool show_sopass; 158c2ecf20Sopenharmony_ci}; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define WOL_REPDATA(__reply_base) \ 188c2ecf20Sopenharmony_ci container_of(__reply_base, struct wol_reply_data, base) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciconst struct nla_policy ethnl_wol_get_policy[] = { 218c2ecf20Sopenharmony_ci [ETHTOOL_A_WOL_HEADER] = 228c2ecf20Sopenharmony_ci NLA_POLICY_NESTED(ethnl_header_policy), 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int wol_prepare_data(const struct ethnl_req_info *req_base, 268c2ecf20Sopenharmony_ci struct ethnl_reply_data *reply_base, 278c2ecf20Sopenharmony_ci struct genl_info *info) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct wol_reply_data *data = WOL_REPDATA(reply_base); 308c2ecf20Sopenharmony_ci struct net_device *dev = reply_base->dev; 318c2ecf20Sopenharmony_ci int ret; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (!dev->ethtool_ops->get_wol) 348c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci ret = ethnl_ops_begin(dev); 378c2ecf20Sopenharmony_ci if (ret < 0) 388c2ecf20Sopenharmony_ci return ret; 398c2ecf20Sopenharmony_ci dev->ethtool_ops->get_wol(dev, &data->wol); 408c2ecf20Sopenharmony_ci ethnl_ops_complete(dev); 418c2ecf20Sopenharmony_ci /* do not include password in notifications */ 428c2ecf20Sopenharmony_ci data->show_sopass = info && (data->wol.supported & WAKE_MAGICSECURE); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int wol_reply_size(const struct ethnl_req_info *req_base, 488c2ecf20Sopenharmony_ci const struct ethnl_reply_data *reply_base) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 518c2ecf20Sopenharmony_ci const struct wol_reply_data *data = WOL_REPDATA(reply_base); 528c2ecf20Sopenharmony_ci int len; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci len = ethnl_bitset32_size(&data->wol.wolopts, &data->wol.supported, 558c2ecf20Sopenharmony_ci WOL_MODE_COUNT, wol_mode_names, compact); 568c2ecf20Sopenharmony_ci if (len < 0) 578c2ecf20Sopenharmony_ci return len; 588c2ecf20Sopenharmony_ci if (data->show_sopass) 598c2ecf20Sopenharmony_ci len += nla_total_size(sizeof(data->wol.sopass)); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return len; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int wol_fill_reply(struct sk_buff *skb, 658c2ecf20Sopenharmony_ci const struct ethnl_req_info *req_base, 668c2ecf20Sopenharmony_ci const struct ethnl_reply_data *reply_base) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 698c2ecf20Sopenharmony_ci const struct wol_reply_data *data = WOL_REPDATA(reply_base); 708c2ecf20Sopenharmony_ci int ret; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci ret = ethnl_put_bitset32(skb, ETHTOOL_A_WOL_MODES, &data->wol.wolopts, 738c2ecf20Sopenharmony_ci &data->wol.supported, WOL_MODE_COUNT, 748c2ecf20Sopenharmony_ci wol_mode_names, compact); 758c2ecf20Sopenharmony_ci if (ret < 0) 768c2ecf20Sopenharmony_ci return ret; 778c2ecf20Sopenharmony_ci if (data->show_sopass && 788c2ecf20Sopenharmony_ci nla_put(skb, ETHTOOL_A_WOL_SOPASS, sizeof(data->wol.sopass), 798c2ecf20Sopenharmony_ci data->wol.sopass)) 808c2ecf20Sopenharmony_ci return -EMSGSIZE; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ciconst struct ethnl_request_ops ethnl_wol_request_ops = { 868c2ecf20Sopenharmony_ci .request_cmd = ETHTOOL_MSG_WOL_GET, 878c2ecf20Sopenharmony_ci .reply_cmd = ETHTOOL_MSG_WOL_GET_REPLY, 888c2ecf20Sopenharmony_ci .hdr_attr = ETHTOOL_A_WOL_HEADER, 898c2ecf20Sopenharmony_ci .req_info_size = sizeof(struct wol_req_info), 908c2ecf20Sopenharmony_ci .reply_data_size = sizeof(struct wol_reply_data), 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci .prepare_data = wol_prepare_data, 938c2ecf20Sopenharmony_ci .reply_size = wol_reply_size, 948c2ecf20Sopenharmony_ci .fill_reply = wol_fill_reply, 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* WOL_SET */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ciconst struct nla_policy ethnl_wol_set_policy[] = { 1008c2ecf20Sopenharmony_ci [ETHTOOL_A_WOL_HEADER] = 1018c2ecf20Sopenharmony_ci NLA_POLICY_NESTED(ethnl_header_policy), 1028c2ecf20Sopenharmony_ci [ETHTOOL_A_WOL_MODES] = { .type = NLA_NESTED }, 1038c2ecf20Sopenharmony_ci [ETHTOOL_A_WOL_SOPASS] = { .type = NLA_BINARY, 1048c2ecf20Sopenharmony_ci .len = SOPASS_MAX }, 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ciint ethnl_set_wol(struct sk_buff *skb, struct genl_info *info) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; 1108c2ecf20Sopenharmony_ci struct ethnl_req_info req_info = {}; 1118c2ecf20Sopenharmony_ci struct nlattr **tb = info->attrs; 1128c2ecf20Sopenharmony_ci struct net_device *dev; 1138c2ecf20Sopenharmony_ci bool mod = false; 1148c2ecf20Sopenharmony_ci int ret; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_WOL_HEADER], 1178c2ecf20Sopenharmony_ci genl_info_net(info), info->extack, 1188c2ecf20Sopenharmony_ci true); 1198c2ecf20Sopenharmony_ci if (ret < 0) 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci dev = req_info.dev; 1228c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 1238c2ecf20Sopenharmony_ci if (!dev->ethtool_ops->get_wol || !dev->ethtool_ops->set_wol) 1248c2ecf20Sopenharmony_ci goto out_dev; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci rtnl_lock(); 1278c2ecf20Sopenharmony_ci ret = ethnl_ops_begin(dev); 1288c2ecf20Sopenharmony_ci if (ret < 0) 1298c2ecf20Sopenharmony_ci goto out_rtnl; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci dev->ethtool_ops->get_wol(dev, &wol); 1328c2ecf20Sopenharmony_ci ret = ethnl_update_bitset32(&wol.wolopts, WOL_MODE_COUNT, 1338c2ecf20Sopenharmony_ci tb[ETHTOOL_A_WOL_MODES], wol_mode_names, 1348c2ecf20Sopenharmony_ci info->extack, &mod); 1358c2ecf20Sopenharmony_ci if (ret < 0) 1368c2ecf20Sopenharmony_ci goto out_ops; 1378c2ecf20Sopenharmony_ci if (wol.wolopts & ~wol.supported) { 1388c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_WOL_MODES], 1398c2ecf20Sopenharmony_ci "cannot enable unsupported WoL mode"); 1408c2ecf20Sopenharmony_ci ret = -EINVAL; 1418c2ecf20Sopenharmony_ci goto out_ops; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci if (tb[ETHTOOL_A_WOL_SOPASS]) { 1448c2ecf20Sopenharmony_ci if (!(wol.supported & WAKE_MAGICSECURE)) { 1458c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 1468c2ecf20Sopenharmony_ci tb[ETHTOOL_A_WOL_SOPASS], 1478c2ecf20Sopenharmony_ci "magicsecure not supported, cannot set password"); 1488c2ecf20Sopenharmony_ci ret = -EINVAL; 1498c2ecf20Sopenharmony_ci goto out_ops; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci ethnl_update_binary(wol.sopass, sizeof(wol.sopass), 1528c2ecf20Sopenharmony_ci tb[ETHTOOL_A_WOL_SOPASS], &mod); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (!mod) 1568c2ecf20Sopenharmony_ci goto out_ops; 1578c2ecf20Sopenharmony_ci ret = dev->ethtool_ops->set_wol(dev, &wol); 1588c2ecf20Sopenharmony_ci if (ret) 1598c2ecf20Sopenharmony_ci goto out_ops; 1608c2ecf20Sopenharmony_ci dev->wol_enabled = !!wol.wolopts; 1618c2ecf20Sopenharmony_ci ethtool_notify(dev, ETHTOOL_MSG_WOL_NTF, NULL); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciout_ops: 1648c2ecf20Sopenharmony_ci ethnl_ops_complete(dev); 1658c2ecf20Sopenharmony_ciout_rtnl: 1668c2ecf20Sopenharmony_ci rtnl_unlock(); 1678c2ecf20Sopenharmony_ciout_dev: 1688c2ecf20Sopenharmony_ci dev_put(dev); 1698c2ecf20Sopenharmony_ci return ret; 1708c2ecf20Sopenharmony_ci} 171