wol.c 4.1 KB

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