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