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