Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : :
3 : : #include <net/sock.h>
4 : : #include <linux/ethtool_netlink.h>
5 : : #include "netlink.h"
6 : :
7 : : static struct genl_family ethtool_genl_family;
8 : :
9 : : static bool ethnl_ok __read_mostly;
10 : : static u32 ethnl_bcast_seq;
11 : :
12 : : static const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_MAX + 1] = {
13 : : [ETHTOOL_A_HEADER_UNSPEC] = { .type = NLA_REJECT },
14 : : [ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 },
15 : : [ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING,
16 : : .len = ALTIFNAMSIZ - 1 },
17 : : [ETHTOOL_A_HEADER_FLAGS] = { .type = NLA_U32 },
18 : : };
19 : :
20 : : /**
21 : : * ethnl_parse_header() - parse request header
22 : : * @req_info: structure to put results into
23 : : * @header: nest attribute with request header
24 : : * @net: request netns
25 : : * @extack: netlink extack for error reporting
26 : : * @require_dev: fail if no device identified in header
27 : : *
28 : : * Parse request header in nested attribute @nest and puts results into
29 : : * the structure pointed to by @req_info. Extack from @info is used for error
30 : : * reporting. If req_info->dev is not null on return, reference to it has
31 : : * been taken. If error is returned, *req_info is null initialized and no
32 : : * reference is held.
33 : : *
34 : : * Return: 0 on success or negative error code
35 : : */
36 : 0 : int ethnl_parse_header(struct ethnl_req_info *req_info,
37 : : const struct nlattr *header, struct net *net,
38 : : struct netlink_ext_ack *extack, bool require_dev)
39 : : {
40 : 0 : struct nlattr *tb[ETHTOOL_A_HEADER_MAX + 1];
41 : 0 : const struct nlattr *devname_attr;
42 : 0 : struct net_device *dev = NULL;
43 : 0 : u32 flags = 0;
44 : 0 : int ret;
45 : :
46 [ # # ]: 0 : if (!header) {
47 [ # # ]: 0 : NL_SET_ERR_MSG(extack, "request header missing");
48 : 0 : return -EINVAL;
49 : : }
50 : 0 : ret = nla_parse_nested(tb, ETHTOOL_A_HEADER_MAX, header,
51 : : ethnl_header_policy, extack);
52 [ # # ]: 0 : if (ret < 0)
53 : : return ret;
54 [ # # ]: 0 : if (tb[ETHTOOL_A_HEADER_FLAGS]) {
55 [ # # ]: 0 : flags = nla_get_u32(tb[ETHTOOL_A_HEADER_FLAGS]);
56 [ # # ]: 0 : if (flags & ~ETHTOOL_FLAG_ALL) {
57 [ # # ]: 0 : NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_HEADER_FLAGS],
58 : : "unrecognized request flags");
59 [ # # ]: 0 : nl_set_extack_cookie_u32(extack, ETHTOOL_FLAG_ALL);
60 : 0 : return -EOPNOTSUPP;
61 : : }
62 : : }
63 : :
64 : 0 : devname_attr = tb[ETHTOOL_A_HEADER_DEV_NAME];
65 [ # # ]: 0 : if (tb[ETHTOOL_A_HEADER_DEV_INDEX]) {
66 : 0 : u32 ifindex = nla_get_u32(tb[ETHTOOL_A_HEADER_DEV_INDEX]);
67 : :
68 : 0 : dev = dev_get_by_index(net, ifindex);
69 [ # # ]: 0 : if (!dev) {
70 [ # # ]: 0 : NL_SET_ERR_MSG_ATTR(extack,
71 : : tb[ETHTOOL_A_HEADER_DEV_INDEX],
72 : : "no device matches ifindex");
73 : 0 : return -ENODEV;
74 : : }
75 : : /* if both ifindex and ifname are passed, they must match */
76 [ # # ]: 0 : if (devname_attr &&
77 [ # # ]: 0 : strncmp(dev->name, nla_data(devname_attr), IFNAMSIZ)) {
78 : 0 : dev_put(dev);
79 [ # # ]: 0 : NL_SET_ERR_MSG_ATTR(extack, header,
80 : : "ifindex and name do not match");
81 : 0 : return -ENODEV;
82 : : }
83 [ # # ]: 0 : } else if (devname_attr) {
84 : 0 : dev = dev_get_by_name(net, nla_data(devname_attr));
85 [ # # ]: 0 : if (!dev) {
86 [ # # ]: 0 : NL_SET_ERR_MSG_ATTR(extack, devname_attr,
87 : : "no device matches name");
88 : 0 : return -ENODEV;
89 : : }
90 [ # # ]: 0 : } else if (require_dev) {
91 [ # # ]: 0 : NL_SET_ERR_MSG_ATTR(extack, header,
92 : : "neither ifindex nor name specified");
93 : 0 : return -EINVAL;
94 : : }
95 : :
96 [ # # # # ]: 0 : if (dev && !netif_device_present(dev)) {
97 : 0 : dev_put(dev);
98 [ # # ]: 0 : NL_SET_ERR_MSG(extack, "device not present");
99 : 0 : return -ENODEV;
100 : : }
101 : :
102 : 0 : req_info->dev = dev;
103 : 0 : req_info->flags = flags;
104 : 0 : return 0;
105 : : }
106 : :
107 : : /**
108 : : * ethnl_fill_reply_header() - Put common header into a reply message
109 : : * @skb: skb with the message
110 : : * @dev: network device to describe in header
111 : : * @attrtype: attribute type to use for the nest
112 : : *
113 : : * Create a nested attribute with attributes describing given network device.
114 : : *
115 : : * Return: 0 on success, error value (-EMSGSIZE only) on error
116 : : */
117 : 0 : int ethnl_fill_reply_header(struct sk_buff *skb, struct net_device *dev,
118 : : u16 attrtype)
119 : : {
120 : 0 : struct nlattr *nest;
121 : :
122 [ # # ]: 0 : if (!dev)
123 : : return 0;
124 : 0 : nest = nla_nest_start(skb, attrtype);
125 [ # # ]: 0 : if (!nest)
126 : : return -EMSGSIZE;
127 : :
128 [ # # # # ]: 0 : if (nla_put_u32(skb, ETHTOOL_A_HEADER_DEV_INDEX, (u32)dev->ifindex) ||
129 : 0 : nla_put_string(skb, ETHTOOL_A_HEADER_DEV_NAME, dev->name))
130 : 0 : goto nla_put_failure;
131 : : /* If more attributes are put into reply header, ethnl_header_size()
132 : : * must be updated to account for them.
133 : : */
134 : :
135 : 0 : nla_nest_end(skb, nest);
136 : 0 : return 0;
137 : :
138 : : nla_put_failure:
139 : 0 : nla_nest_cancel(skb, nest);
140 : 0 : return -EMSGSIZE;
141 : : }
142 : :
143 : : /**
144 : : * ethnl_reply_init() - Create skb for a reply and fill device identification
145 : : * @payload: payload length (without netlink and genetlink header)
146 : : * @dev: device the reply is about (may be null)
147 : : * @cmd: ETHTOOL_MSG_* message type for reply
148 : : * @hdr_attrtype: attribute type for common header
149 : : * @info: genetlink info of the received packet we respond to
150 : : * @ehdrp: place to store payload pointer returned by genlmsg_new()
151 : : *
152 : : * Return: pointer to allocated skb on success, NULL on error
153 : : */
154 : 0 : struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
155 : : u16 hdr_attrtype, struct genl_info *info,
156 : : void **ehdrp)
157 : : {
158 : 0 : struct sk_buff *skb;
159 : :
160 : 0 : skb = genlmsg_new(payload, GFP_KERNEL);
161 [ # # ]: 0 : if (!skb)
162 : 0 : goto err;
163 : 0 : *ehdrp = genlmsg_put_reply(skb, info, ðtool_genl_family, 0, cmd);
164 [ # # ]: 0 : if (!*ehdrp)
165 : 0 : goto err_free;
166 : :
167 [ # # ]: 0 : if (dev) {
168 : 0 : int ret;
169 : :
170 : 0 : ret = ethnl_fill_reply_header(skb, dev, hdr_attrtype);
171 [ # # ]: 0 : if (ret < 0)
172 : 0 : goto err_free;
173 : : }
174 : : return skb;
175 : :
176 : 0 : err_free:
177 : 0 : nlmsg_free(skb);
178 : 0 : err:
179 [ # # ]: 0 : if (info)
180 [ # # ]: 0 : GENL_SET_ERR_MSG(info, "failed to setup reply message");
181 : : return NULL;
182 : : }
183 : :
184 : 0 : static void *ethnl_bcastmsg_put(struct sk_buff *skb, u8 cmd)
185 : : {
186 : 0 : return genlmsg_put(skb, 0, ++ethnl_bcast_seq, ðtool_genl_family, 0,
187 : : cmd);
188 : : }
189 : :
190 : 0 : static int ethnl_multicast(struct sk_buff *skb, struct net_device *dev)
191 : : {
192 : 0 : return genlmsg_multicast_netns(ðtool_genl_family, dev_net(dev), skb,
193 : : 0, ETHNL_MCGRP_MONITOR, GFP_KERNEL);
194 : : }
195 : :
196 : : /* GET request helpers */
197 : :
198 : : /**
199 : : * struct ethnl_dump_ctx - context structure for generic dumpit() callback
200 : : * @ops: request ops of currently processed message type
201 : : * @req_info: parsed request header of processed request
202 : : * @reply_data: data needed to compose the reply
203 : : * @pos_hash: saved iteration position - hashbucket
204 : : * @pos_idx: saved iteration position - index
205 : : *
206 : : * These parameters are kept in struct netlink_callback as context preserved
207 : : * between iterations. They are initialized by ethnl_default_start() and used
208 : : * in ethnl_default_dumpit() and ethnl_default_done().
209 : : */
210 : : struct ethnl_dump_ctx {
211 : : const struct ethnl_request_ops *ops;
212 : : struct ethnl_req_info *req_info;
213 : : struct ethnl_reply_data *reply_data;
214 : : int pos_hash;
215 : : int pos_idx;
216 : : };
217 : :
218 : : static const struct ethnl_request_ops *
219 : : ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
220 : : [ETHTOOL_MSG_STRSET_GET] = ðnl_strset_request_ops,
221 : : [ETHTOOL_MSG_LINKINFO_GET] = ðnl_linkinfo_request_ops,
222 : : [ETHTOOL_MSG_LINKMODES_GET] = ðnl_linkmodes_request_ops,
223 : : [ETHTOOL_MSG_LINKSTATE_GET] = ðnl_linkstate_request_ops,
224 : : [ETHTOOL_MSG_DEBUG_GET] = ðnl_debug_request_ops,
225 : : [ETHTOOL_MSG_WOL_GET] = ðnl_wol_request_ops,
226 : : };
227 : :
228 : 0 : static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
229 : : {
230 : 0 : return (struct ethnl_dump_ctx *)cb->ctx;
231 : : }
232 : :
233 : : /**
234 : : * ethnl_default_parse() - Parse request message
235 : : * @req_info: pointer to structure to put data into
236 : : * @nlhdr: pointer to request message header
237 : : * @net: request netns
238 : : * @request_ops: struct request_ops for request type
239 : : * @extack: netlink extack for error reporting
240 : : * @require_dev: fail if no device identified in header
241 : : *
242 : : * Parse universal request header and call request specific ->parse_request()
243 : : * callback (if defined) to parse the rest of the message.
244 : : *
245 : : * Return: 0 on success or negative error code
246 : : */
247 : 0 : static int ethnl_default_parse(struct ethnl_req_info *req_info,
248 : : const struct nlmsghdr *nlhdr, struct net *net,
249 : : const struct ethnl_request_ops *request_ops,
250 : : struct netlink_ext_ack *extack, bool require_dev)
251 : : {
252 : 0 : struct nlattr **tb;
253 : 0 : int ret;
254 : :
255 : 0 : tb = kmalloc_array(request_ops->max_attr + 1, sizeof(tb[0]),
256 : : GFP_KERNEL);
257 [ # # ]: 0 : if (!tb)
258 : : return -ENOMEM;
259 : :
260 : 0 : ret = nlmsg_parse(nlhdr, GENL_HDRLEN, tb, request_ops->max_attr,
261 : : request_ops->request_policy, extack);
262 [ # # ]: 0 : if (ret < 0)
263 : 0 : goto out;
264 : 0 : ret = ethnl_parse_header(req_info, tb[request_ops->hdr_attr], net,
265 : : extack, require_dev);
266 [ # # ]: 0 : if (ret < 0)
267 : 0 : goto out;
268 : :
269 [ # # ]: 0 : if (request_ops->parse_request) {
270 : 0 : ret = request_ops->parse_request(req_info, tb, extack);
271 : 0 : if (ret < 0)
272 : : goto out;
273 : : }
274 : :
275 : : ret = 0;
276 : 0 : out:
277 : 0 : kfree(tb);
278 : 0 : return ret;
279 : : }
280 : :
281 : : /**
282 : : * ethnl_init_reply_data() - Initialize reply data for GET request
283 : : * @reply_data: pointer to embedded struct ethnl_reply_data
284 : : * @ops: instance of struct ethnl_request_ops describing the layout
285 : : * @dev: network device to initialize the reply for
286 : : *
287 : : * Fills the reply data part with zeros and sets the dev member. Must be called
288 : : * before calling the ->fill_reply() callback (for each iteration when handling
289 : : * dump requests).
290 : : */
291 : 0 : static void ethnl_init_reply_data(struct ethnl_reply_data *reply_data,
292 : : const struct ethnl_request_ops *ops,
293 : : struct net_device *dev)
294 : : {
295 : 0 : memset(reply_data, 0, ops->reply_data_size);
296 : 0 : reply_data->dev = dev;
297 : : }
298 : :
299 : : /* default ->doit() handler for GET type requests */
300 : 0 : static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info)
301 : : {
302 : 0 : struct ethnl_reply_data *reply_data = NULL;
303 : 0 : struct ethnl_req_info *req_info = NULL;
304 : 0 : const u8 cmd = info->genlhdr->cmd;
305 : 0 : const struct ethnl_request_ops *ops;
306 : 0 : struct sk_buff *rskb;
307 : 0 : void *reply_payload;
308 : 0 : int reply_len;
309 : 0 : int ret;
310 : :
311 : 0 : ops = ethnl_default_requests[cmd];
312 [ # # # # : 0 : if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", cmd))
# # ]
313 : : return -EOPNOTSUPP;
314 : 0 : req_info = kzalloc(ops->req_info_size, GFP_KERNEL);
315 [ # # ]: 0 : if (!req_info)
316 : : return -ENOMEM;
317 [ # # ]: 0 : reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);
318 [ # # ]: 0 : if (!reply_data) {
319 : 0 : kfree(req_info);
320 : 0 : return -ENOMEM;
321 : : }
322 : :
323 : 0 : ret = ethnl_default_parse(req_info, info->nlhdr, genl_info_net(info), ops,
324 : 0 : info->extack, !ops->allow_nodev_do);
325 [ # # ]: 0 : if (ret < 0)
326 : 0 : goto err_dev;
327 : 0 : ethnl_init_reply_data(reply_data, ops, req_info->dev);
328 : :
329 : 0 : rtnl_lock();
330 : 0 : ret = ops->prepare_data(req_info, reply_data, info);
331 : 0 : rtnl_unlock();
332 [ # # ]: 0 : if (ret < 0)
333 : 0 : goto err_cleanup;
334 : 0 : ret = ops->reply_size(req_info, reply_data);
335 [ # # ]: 0 : if (ret < 0)
336 : 0 : goto err_cleanup;
337 : 0 : reply_len = ret;
338 : 0 : ret = -ENOMEM;
339 : 0 : rskb = ethnl_reply_init(reply_len, req_info->dev, ops->reply_cmd,
340 : 0 : ops->hdr_attr, info, &reply_payload);
341 [ # # ]: 0 : if (!rskb)
342 : 0 : goto err_cleanup;
343 : 0 : ret = ops->fill_reply(rskb, req_info, reply_data);
344 [ # # ]: 0 : if (ret < 0)
345 : 0 : goto err_msg;
346 [ # # ]: 0 : if (ops->cleanup_data)
347 : 0 : ops->cleanup_data(reply_data);
348 : :
349 [ # # ]: 0 : genlmsg_end(rskb, reply_payload);
350 [ # # ]: 0 : if (req_info->dev)
351 : 0 : dev_put(req_info->dev);
352 : 0 : kfree(reply_data);
353 : 0 : kfree(req_info);
354 : 0 : return genlmsg_reply(rskb, info);
355 : :
356 : : err_msg:
357 [ # # # # ]: 0 : WARN_ONCE(ret == -EMSGSIZE, "calculated message payload length (%d) not sufficient\n", reply_len);
358 : 0 : nlmsg_free(rskb);
359 : 0 : err_cleanup:
360 [ # # ]: 0 : if (ops->cleanup_data)
361 : 0 : ops->cleanup_data(reply_data);
362 : 0 : err_dev:
363 [ # # ]: 0 : if (req_info->dev)
364 : 0 : dev_put(req_info->dev);
365 : 0 : kfree(reply_data);
366 : 0 : kfree(req_info);
367 : 0 : return ret;
368 : : }
369 : :
370 : 0 : static int ethnl_default_dump_one(struct sk_buff *skb, struct net_device *dev,
371 : : const struct ethnl_dump_ctx *ctx)
372 : : {
373 : 0 : int ret;
374 : :
375 : 0 : ethnl_init_reply_data(ctx->reply_data, ctx->ops, dev);
376 : 0 : rtnl_lock();
377 : 0 : ret = ctx->ops->prepare_data(ctx->req_info, ctx->reply_data, NULL);
378 : 0 : rtnl_unlock();
379 [ # # ]: 0 : if (ret < 0)
380 : 0 : goto out;
381 : 0 : ret = ethnl_fill_reply_header(skb, dev, ctx->ops->hdr_attr);
382 [ # # ]: 0 : if (ret < 0)
383 : 0 : goto out;
384 : 0 : ret = ctx->ops->fill_reply(skb, ctx->req_info, ctx->reply_data);
385 : :
386 : 0 : out:
387 [ # # ]: 0 : if (ctx->ops->cleanup_data)
388 : 0 : ctx->ops->cleanup_data(ctx->reply_data);
389 : 0 : ctx->reply_data->dev = NULL;
390 : 0 : return ret;
391 : : }
392 : :
393 : : /* Default ->dumpit() handler for GET requests. Device iteration copied from
394 : : * rtnl_dump_ifinfo(); we have to be more careful about device hashtable
395 : : * persistence as we cannot guarantee to hold RTNL lock through the whole
396 : : * function as rtnetnlink does.
397 : : */
398 : 0 : static int ethnl_default_dumpit(struct sk_buff *skb,
399 : : struct netlink_callback *cb)
400 : : {
401 : 0 : struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb);
402 : 0 : struct net *net = sock_net(skb->sk);
403 : 0 : int s_idx = ctx->pos_idx;
404 : 0 : int h, idx = 0;
405 : 0 : int ret = 0;
406 : 0 : void *ehdr;
407 : :
408 : 0 : rtnl_lock();
409 [ # # ]: 0 : for (h = ctx->pos_hash; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
410 : 0 : struct hlist_head *head;
411 : 0 : struct net_device *dev;
412 : 0 : unsigned int seq;
413 : :
414 : 0 : head = &net->dev_index_head[h];
415 : :
416 : 0 : restart_chain:
417 : 0 : seq = net->dev_base_seq;
418 : 0 : cb->seq = seq;
419 : 0 : idx = 0;
420 [ # # # # ]: 0 : hlist_for_each_entry(dev, head, index_hlist) {
421 [ # # ]: 0 : if (idx < s_idx)
422 : 0 : goto cont;
423 : 0 : dev_hold(dev);
424 : 0 : rtnl_unlock();
425 : :
426 : 0 : ehdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
427 : 0 : cb->nlh->nlmsg_seq,
428 : : ðtool_genl_family, 0,
429 : 0 : ctx->ops->reply_cmd);
430 [ # # ]: 0 : if (!ehdr) {
431 : 0 : dev_put(dev);
432 : 0 : ret = -EMSGSIZE;
433 : 0 : goto out;
434 : : }
435 : 0 : ret = ethnl_default_dump_one(skb, dev, ctx);
436 : 0 : dev_put(dev);
437 [ # # ]: 0 : if (ret < 0) {
438 : 0 : genlmsg_cancel(skb, ehdr);
439 [ # # ]: 0 : if (ret == -EOPNOTSUPP)
440 : 0 : goto lock_and_cont;
441 [ # # ]: 0 : if (likely(skb->len))
442 : 0 : ret = skb->len;
443 : 0 : goto out;
444 : : }
445 : 0 : genlmsg_end(skb, ehdr);
446 : 0 : lock_and_cont:
447 : 0 : rtnl_lock();
448 [ # # ]: 0 : if (net->dev_base_seq != seq) {
449 : 0 : s_idx = idx + 1;
450 : 0 : goto restart_chain;
451 : : }
452 : 0 : cont:
453 [ # # ]: 0 : idx++;
454 : : }
455 : :
456 : : }
457 : 0 : rtnl_unlock();
458 : :
459 : 0 : out:
460 : 0 : ctx->pos_hash = h;
461 : 0 : ctx->pos_idx = idx;
462 [ # # ]: 0 : nl_dump_check_consistent(cb, nlmsg_hdr(skb));
463 : :
464 : 0 : return ret;
465 : : }
466 : :
467 : : /* generic ->start() handler for GET requests */
468 : 0 : static int ethnl_default_start(struct netlink_callback *cb)
469 : : {
470 : 0 : struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb);
471 : 0 : struct ethnl_reply_data *reply_data;
472 : 0 : const struct ethnl_request_ops *ops;
473 : 0 : struct ethnl_req_info *req_info;
474 : 0 : struct genlmsghdr *ghdr;
475 : 0 : int ret;
476 : :
477 : 0 : BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));
478 : :
479 [ # # ]: 0 : ghdr = nlmsg_data(cb->nlh);
480 : 0 : ops = ethnl_default_requests[ghdr->cmd];
481 [ # # # # : 0 : if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", ghdr->cmd))
# # ]
482 : : return -EOPNOTSUPP;
483 : 0 : req_info = kzalloc(ops->req_info_size, GFP_KERNEL);
484 [ # # ]: 0 : if (!req_info)
485 : : return -ENOMEM;
486 [ # # ]: 0 : reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);
487 [ # # ]: 0 : if (!reply_data) {
488 : 0 : ret = -ENOMEM;
489 : 0 : goto free_req_info;
490 : : }
491 : :
492 : 0 : ret = ethnl_default_parse(req_info, cb->nlh, sock_net(cb->skb->sk), ops,
493 : : cb->extack, false);
494 [ # # ]: 0 : if (req_info->dev) {
495 : : /* We ignore device specification in dump requests but as the
496 : : * same parser as for non-dump (doit) requests is used, it
497 : : * would take reference to the device if it finds one
498 : : */
499 : 0 : dev_put(req_info->dev);
500 : 0 : req_info->dev = NULL;
501 : : }
502 [ # # ]: 0 : if (ret < 0)
503 : 0 : goto free_reply_data;
504 : :
505 : 0 : ctx->ops = ops;
506 : 0 : ctx->req_info = req_info;
507 : 0 : ctx->reply_data = reply_data;
508 : 0 : ctx->pos_hash = 0;
509 : 0 : ctx->pos_idx = 0;
510 : :
511 : 0 : return 0;
512 : :
513 : : free_reply_data:
514 : 0 : kfree(reply_data);
515 : 0 : free_req_info:
516 : 0 : kfree(req_info);
517 : :
518 : 0 : return ret;
519 : : }
520 : :
521 : : /* default ->done() handler for GET requests */
522 : 0 : static int ethnl_default_done(struct netlink_callback *cb)
523 : : {
524 : 0 : struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb);
525 : :
526 : 0 : kfree(ctx->reply_data);
527 : 0 : kfree(ctx->req_info);
528 : :
529 : 0 : return 0;
530 : : }
531 : :
532 : : static const struct ethnl_request_ops *
533 : : ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = {
534 : : [ETHTOOL_MSG_LINKINFO_NTF] = ðnl_linkinfo_request_ops,
535 : : [ETHTOOL_MSG_LINKMODES_NTF] = ðnl_linkmodes_request_ops,
536 : : [ETHTOOL_MSG_DEBUG_NTF] = ðnl_debug_request_ops,
537 : : [ETHTOOL_MSG_WOL_NTF] = ðnl_wol_request_ops,
538 : : };
539 : :
540 : : /* default notification handler */
541 : 0 : static void ethnl_default_notify(struct net_device *dev, unsigned int cmd,
542 : : const void *data)
543 : : {
544 : 0 : struct ethnl_reply_data *reply_data;
545 : 0 : const struct ethnl_request_ops *ops;
546 : 0 : struct ethnl_req_info *req_info;
547 : 0 : struct sk_buff *skb;
548 : 0 : void *reply_payload;
549 : 0 : int reply_len;
550 : 0 : int ret;
551 : :
552 [ # # # # : 0 : if (WARN_ONCE(cmd > ETHTOOL_MSG_KERNEL_MAX ||
# # # # #
# ]
553 : : !ethnl_default_notify_ops[cmd],
554 : : "unexpected notification type %u\n", cmd))
555 : : return;
556 : 0 : ops = ethnl_default_notify_ops[cmd];
557 : 0 : req_info = kzalloc(ops->req_info_size, GFP_KERNEL);
558 [ # # ]: 0 : if (!req_info)
559 : : return;
560 [ # # ]: 0 : reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);
561 [ # # ]: 0 : if (!reply_data) {
562 : 0 : kfree(req_info);
563 : 0 : return;
564 : : }
565 : :
566 : 0 : req_info->dev = dev;
567 : 0 : req_info->flags |= ETHTOOL_FLAG_COMPACT_BITSETS;
568 : :
569 : 0 : ethnl_init_reply_data(reply_data, ops, dev);
570 : 0 : ret = ops->prepare_data(req_info, reply_data, NULL);
571 [ # # ]: 0 : if (ret < 0)
572 : 0 : goto err_cleanup;
573 : 0 : ret = ops->reply_size(req_info, reply_data);
574 [ # # ]: 0 : if (ret < 0)
575 : 0 : goto err_cleanup;
576 : 0 : reply_len = ret;
577 : 0 : ret = -ENOMEM;
578 : 0 : skb = genlmsg_new(reply_len, GFP_KERNEL);
579 [ # # ]: 0 : if (!skb)
580 : 0 : goto err_cleanup;
581 : 0 : reply_payload = ethnl_bcastmsg_put(skb, cmd);
582 [ # # ]: 0 : if (!reply_payload)
583 : 0 : goto err_skb;
584 : 0 : ret = ethnl_fill_reply_header(skb, dev, ops->hdr_attr);
585 [ # # ]: 0 : if (ret < 0)
586 : 0 : goto err_msg;
587 : 0 : ret = ops->fill_reply(skb, req_info, reply_data);
588 [ # # ]: 0 : if (ret < 0)
589 : 0 : goto err_msg;
590 [ # # ]: 0 : if (ops->cleanup_data)
591 : 0 : ops->cleanup_data(reply_data);
592 : :
593 : 0 : genlmsg_end(skb, reply_payload);
594 : 0 : kfree(reply_data);
595 : 0 : kfree(req_info);
596 : 0 : ethnl_multicast(skb, dev);
597 : 0 : return;
598 : :
599 : 0 : err_msg:
600 [ # # # # ]: 0 : WARN_ONCE(ret == -EMSGSIZE,
601 : : "calculated message payload length (%d) not sufficient\n",
602 : : reply_len);
603 : 0 : err_skb:
604 : 0 : nlmsg_free(skb);
605 : 0 : err_cleanup:
606 [ # # ]: 0 : if (ops->cleanup_data)
607 : 0 : ops->cleanup_data(reply_data);
608 : 0 : kfree(reply_data);
609 : 0 : kfree(req_info);
610 : 0 : return;
611 : : }
612 : :
613 : : /* notifications */
614 : :
615 : : typedef void (*ethnl_notify_handler_t)(struct net_device *dev, unsigned int cmd,
616 : : const void *data);
617 : :
618 : : static const ethnl_notify_handler_t ethnl_notify_handlers[] = {
619 : : [ETHTOOL_MSG_LINKINFO_NTF] = ethnl_default_notify,
620 : : [ETHTOOL_MSG_LINKMODES_NTF] = ethnl_default_notify,
621 : : [ETHTOOL_MSG_DEBUG_NTF] = ethnl_default_notify,
622 : : [ETHTOOL_MSG_WOL_NTF] = ethnl_default_notify,
623 : : };
624 : :
625 : 0 : void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data)
626 : : {
627 [ # # ]: 0 : if (unlikely(!ethnl_ok))
628 : : return;
629 [ # # # # ]: 0 : ASSERT_RTNL();
630 : :
631 [ # # # # ]: 0 : if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) &&
632 : : ethnl_notify_handlers[cmd]))
633 : 0 : ethnl_notify_handlers[cmd](dev, cmd, data);
634 : : else
635 [ # # # # ]: 0 : WARN_ONCE(1, "notification %u not implemented (dev=%s)\n",
636 : : cmd, netdev_name(dev));
637 : : }
638 : : EXPORT_SYMBOL(ethtool_notify);
639 : :
640 : : /* genetlink setup */
641 : :
642 : : static const struct genl_ops ethtool_genl_ops[] = {
643 : : {
644 : : .cmd = ETHTOOL_MSG_STRSET_GET,
645 : : .doit = ethnl_default_doit,
646 : : .start = ethnl_default_start,
647 : : .dumpit = ethnl_default_dumpit,
648 : : .done = ethnl_default_done,
649 : : },
650 : : {
651 : : .cmd = ETHTOOL_MSG_LINKINFO_GET,
652 : : .doit = ethnl_default_doit,
653 : : .start = ethnl_default_start,
654 : : .dumpit = ethnl_default_dumpit,
655 : : .done = ethnl_default_done,
656 : : },
657 : : {
658 : : .cmd = ETHTOOL_MSG_LINKINFO_SET,
659 : : .flags = GENL_UNS_ADMIN_PERM,
660 : : .doit = ethnl_set_linkinfo,
661 : : },
662 : : {
663 : : .cmd = ETHTOOL_MSG_LINKMODES_GET,
664 : : .doit = ethnl_default_doit,
665 : : .start = ethnl_default_start,
666 : : .dumpit = ethnl_default_dumpit,
667 : : .done = ethnl_default_done,
668 : : },
669 : : {
670 : : .cmd = ETHTOOL_MSG_LINKMODES_SET,
671 : : .flags = GENL_UNS_ADMIN_PERM,
672 : : .doit = ethnl_set_linkmodes,
673 : : },
674 : : {
675 : : .cmd = ETHTOOL_MSG_LINKSTATE_GET,
676 : : .doit = ethnl_default_doit,
677 : : .start = ethnl_default_start,
678 : : .dumpit = ethnl_default_dumpit,
679 : : .done = ethnl_default_done,
680 : : },
681 : : {
682 : : .cmd = ETHTOOL_MSG_DEBUG_GET,
683 : : .doit = ethnl_default_doit,
684 : : .start = ethnl_default_start,
685 : : .dumpit = ethnl_default_dumpit,
686 : : .done = ethnl_default_done,
687 : : },
688 : : {
689 : : .cmd = ETHTOOL_MSG_DEBUG_SET,
690 : : .flags = GENL_UNS_ADMIN_PERM,
691 : : .doit = ethnl_set_debug,
692 : : },
693 : : {
694 : : .cmd = ETHTOOL_MSG_WOL_GET,
695 : : .flags = GENL_UNS_ADMIN_PERM,
696 : : .doit = ethnl_default_doit,
697 : : .start = ethnl_default_start,
698 : : .dumpit = ethnl_default_dumpit,
699 : : .done = ethnl_default_done,
700 : : },
701 : : {
702 : : .cmd = ETHTOOL_MSG_WOL_SET,
703 : : .flags = GENL_UNS_ADMIN_PERM,
704 : : .doit = ethnl_set_wol,
705 : : },
706 : : };
707 : :
708 : : static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
709 : : [ETHNL_MCGRP_MONITOR] = { .name = ETHTOOL_MCGRP_MONITOR_NAME },
710 : : };
711 : :
712 : : static struct genl_family ethtool_genl_family = {
713 : : .name = ETHTOOL_GENL_NAME,
714 : : .version = ETHTOOL_GENL_VERSION,
715 : : .netnsok = true,
716 : : .parallel_ops = true,
717 : : .ops = ethtool_genl_ops,
718 : : .n_ops = ARRAY_SIZE(ethtool_genl_ops),
719 : : .mcgrps = ethtool_nl_mcgrps,
720 : : .n_mcgrps = ARRAY_SIZE(ethtool_nl_mcgrps),
721 : : };
722 : :
723 : : /* module setup */
724 : :
725 : 13 : static int __init ethnl_init(void)
726 : : {
727 : 13 : int ret;
728 : :
729 : 13 : ret = genl_register_family(ðtool_genl_family);
730 [ - + + - ]: 13 : if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
731 : : return ret;
732 : 13 : ethnl_ok = true;
733 : :
734 : 13 : return 0;
735 : : }
736 : :
737 : : subsys_initcall(ethnl_init);
|