blob: ac35513abeed2f53f6c206a64d8ffc16e7805cb9 [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/**
Michal Kubecek98130542020-03-12 21:07:38 +010021 * ethnl_parse_header_dev_get() - parse request header
Michal Kubecek041b1c52019-12-27 15:55:23 +010022 * @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 */
Michal Kubecek98130542020-03-12 21:07:38 +010036int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
37 const struct nlattr *header, struct net *net,
38 struct netlink_ext_ack *extack, bool require_dev)
Michal Kubecek041b1c52019-12-27 15:55:23 +010039{
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 Kubecek05243992020-03-12 21:07:48 +0100218 [ETHTOOL_MSG_FEATURES_GET] = &ethnl_features_request_ops,
Michal Kubeceke16c3382020-03-12 21:08:08 +0100219 [ETHTOOL_MSG_PRIVFLAGS_GET] = &ethnl_privflags_request_ops,
Michal Kubeceke4a17172020-03-12 21:08:23 +0100220 [ETHTOOL_MSG_RINGS_GET] = &ethnl_rings_request_ops,
Michal Kubecek0c849792020-03-12 21:08:38 +0100221 [ETHTOOL_MSG_CHANNELS_GET] = &ethnl_channels_request_ops,
Michal Kubecek728480f2019-12-27 15:55:38 +0100222};
223
224static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
225{
226 return (struct ethnl_dump_ctx *)cb->ctx;
227}
228
229/**
230 * ethnl_default_parse() - Parse request message
231 * @req_info: pointer to structure to put data into
232 * @nlhdr: pointer to request message header
233 * @net: request netns
234 * @request_ops: struct request_ops for request type
235 * @extack: netlink extack for error reporting
236 * @require_dev: fail if no device identified in header
237 *
238 * Parse universal request header and call request specific ->parse_request()
239 * callback (if defined) to parse the rest of the message.
240 *
241 * Return: 0 on success or negative error code
242 */
243static int ethnl_default_parse(struct ethnl_req_info *req_info,
244 const struct nlmsghdr *nlhdr, struct net *net,
245 const struct ethnl_request_ops *request_ops,
246 struct netlink_ext_ack *extack, bool require_dev)
247{
248 struct nlattr **tb;
249 int ret;
250
251 tb = kmalloc_array(request_ops->max_attr + 1, sizeof(tb[0]),
252 GFP_KERNEL);
253 if (!tb)
254 return -ENOMEM;
255
256 ret = nlmsg_parse(nlhdr, GENL_HDRLEN, tb, request_ops->max_attr,
257 request_ops->request_policy, extack);
258 if (ret < 0)
259 goto out;
Michal Kubecek98130542020-03-12 21:07:38 +0100260 ret = ethnl_parse_header_dev_get(req_info, tb[request_ops->hdr_attr],
261 net, extack, require_dev);
Michal Kubecek728480f2019-12-27 15:55:38 +0100262 if (ret < 0)
263 goto out;
264
265 if (request_ops->parse_request) {
266 ret = request_ops->parse_request(req_info, tb, extack);
267 if (ret < 0)
268 goto out;
269 }
270
271 ret = 0;
272out:
273 kfree(tb);
274 return ret;
275}
276
277/**
278 * ethnl_init_reply_data() - Initialize reply data for GET request
Michal Kubecekd2c4b442020-01-26 23:11:01 +0100279 * @reply_data: pointer to embedded struct ethnl_reply_data
280 * @ops: instance of struct ethnl_request_ops describing the layout
281 * @dev: network device to initialize the reply for
Michal Kubecek728480f2019-12-27 15:55:38 +0100282 *
283 * Fills the reply data part with zeros and sets the dev member. Must be called
284 * before calling the ->fill_reply() callback (for each iteration when handling
285 * dump requests).
286 */
287static void ethnl_init_reply_data(struct ethnl_reply_data *reply_data,
288 const struct ethnl_request_ops *ops,
289 struct net_device *dev)
290{
291 memset(reply_data, 0, ops->reply_data_size);
292 reply_data->dev = dev;
293}
294
295/* default ->doit() handler for GET type requests */
296static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info)
297{
298 struct ethnl_reply_data *reply_data = NULL;
299 struct ethnl_req_info *req_info = NULL;
300 const u8 cmd = info->genlhdr->cmd;
301 const struct ethnl_request_ops *ops;
302 struct sk_buff *rskb;
303 void *reply_payload;
304 int reply_len;
305 int ret;
306
307 ops = ethnl_default_requests[cmd];
308 if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", cmd))
309 return -EOPNOTSUPP;
310 req_info = kzalloc(ops->req_info_size, GFP_KERNEL);
311 if (!req_info)
312 return -ENOMEM;
313 reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);
314 if (!reply_data) {
315 kfree(req_info);
316 return -ENOMEM;
317 }
318
319 ret = ethnl_default_parse(req_info, info->nlhdr, genl_info_net(info), ops,
320 info->extack, !ops->allow_nodev_do);
321 if (ret < 0)
322 goto err_dev;
323 ethnl_init_reply_data(reply_data, ops, req_info->dev);
324
325 rtnl_lock();
326 ret = ops->prepare_data(req_info, reply_data, info);
327 rtnl_unlock();
328 if (ret < 0)
329 goto err_cleanup;
Dan Carpenterd97772d2020-01-08 08:41:25 +0300330 ret = ops->reply_size(req_info, reply_data);
Michal Kubecek728480f2019-12-27 15:55:38 +0100331 if (ret < 0)
332 goto err_cleanup;
Dan Carpenterd97772d2020-01-08 08:41:25 +0300333 reply_len = ret;
Michal Kubecek728480f2019-12-27 15:55:38 +0100334 ret = -ENOMEM;
335 rskb = ethnl_reply_init(reply_len, req_info->dev, ops->reply_cmd,
336 ops->hdr_attr, info, &reply_payload);
337 if (!rskb)
338 goto err_cleanup;
339 ret = ops->fill_reply(rskb, req_info, reply_data);
340 if (ret < 0)
341 goto err_msg;
342 if (ops->cleanup_data)
343 ops->cleanup_data(reply_data);
344
345 genlmsg_end(rskb, reply_payload);
346 if (req_info->dev)
347 dev_put(req_info->dev);
348 kfree(reply_data);
349 kfree(req_info);
350 return genlmsg_reply(rskb, info);
351
352err_msg:
353 WARN_ONCE(ret == -EMSGSIZE, "calculated message payload length (%d) not sufficient\n", reply_len);
354 nlmsg_free(rskb);
355err_cleanup:
356 if (ops->cleanup_data)
357 ops->cleanup_data(reply_data);
358err_dev:
359 if (req_info->dev)
360 dev_put(req_info->dev);
361 kfree(reply_data);
362 kfree(req_info);
363 return ret;
364}
365
366static int ethnl_default_dump_one(struct sk_buff *skb, struct net_device *dev,
367 const struct ethnl_dump_ctx *ctx)
368{
369 int ret;
370
371 ethnl_init_reply_data(ctx->reply_data, ctx->ops, dev);
372 rtnl_lock();
373 ret = ctx->ops->prepare_data(ctx->req_info, ctx->reply_data, NULL);
374 rtnl_unlock();
375 if (ret < 0)
376 goto out;
377 ret = ethnl_fill_reply_header(skb, dev, ctx->ops->hdr_attr);
378 if (ret < 0)
379 goto out;
380 ret = ctx->ops->fill_reply(skb, ctx->req_info, ctx->reply_data);
381
382out:
383 if (ctx->ops->cleanup_data)
384 ctx->ops->cleanup_data(ctx->reply_data);
385 ctx->reply_data->dev = NULL;
386 return ret;
387}
388
389/* Default ->dumpit() handler for GET requests. Device iteration copied from
390 * rtnl_dump_ifinfo(); we have to be more careful about device hashtable
391 * persistence as we cannot guarantee to hold RTNL lock through the whole
392 * function as rtnetnlink does.
393 */
394static int ethnl_default_dumpit(struct sk_buff *skb,
395 struct netlink_callback *cb)
396{
397 struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb);
398 struct net *net = sock_net(skb->sk);
399 int s_idx = ctx->pos_idx;
400 int h, idx = 0;
401 int ret = 0;
402 void *ehdr;
403
404 rtnl_lock();
405 for (h = ctx->pos_hash; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
406 struct hlist_head *head;
407 struct net_device *dev;
408 unsigned int seq;
409
410 head = &net->dev_index_head[h];
411
412restart_chain:
413 seq = net->dev_base_seq;
414 cb->seq = seq;
415 idx = 0;
416 hlist_for_each_entry(dev, head, index_hlist) {
417 if (idx < s_idx)
418 goto cont;
419 dev_hold(dev);
420 rtnl_unlock();
421
422 ehdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
423 cb->nlh->nlmsg_seq,
424 &ethtool_genl_family, 0,
425 ctx->ops->reply_cmd);
426 if (!ehdr) {
427 dev_put(dev);
428 ret = -EMSGSIZE;
429 goto out;
430 }
431 ret = ethnl_default_dump_one(skb, dev, ctx);
432 dev_put(dev);
433 if (ret < 0) {
434 genlmsg_cancel(skb, ehdr);
435 if (ret == -EOPNOTSUPP)
436 goto lock_and_cont;
437 if (likely(skb->len))
438 ret = skb->len;
439 goto out;
440 }
441 genlmsg_end(skb, ehdr);
442lock_and_cont:
443 rtnl_lock();
444 if (net->dev_base_seq != seq) {
445 s_idx = idx + 1;
446 goto restart_chain;
447 }
448cont:
449 idx++;
450 }
451
452 }
453 rtnl_unlock();
454
455out:
456 ctx->pos_hash = h;
457 ctx->pos_idx = idx;
458 nl_dump_check_consistent(cb, nlmsg_hdr(skb));
459
460 return ret;
461}
462
463/* generic ->start() handler for GET requests */
464static int ethnl_default_start(struct netlink_callback *cb)
465{
466 struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb);
467 struct ethnl_reply_data *reply_data;
468 const struct ethnl_request_ops *ops;
469 struct ethnl_req_info *req_info;
470 struct genlmsghdr *ghdr;
471 int ret;
472
473 BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));
474
475 ghdr = nlmsg_data(cb->nlh);
476 ops = ethnl_default_requests[ghdr->cmd];
477 if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", ghdr->cmd))
478 return -EOPNOTSUPP;
479 req_info = kzalloc(ops->req_info_size, GFP_KERNEL);
480 if (!req_info)
481 return -ENOMEM;
482 reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);
483 if (!reply_data) {
Dan Carpentera6dd0482020-01-08 08:39:48 +0300484 ret = -ENOMEM;
485 goto free_req_info;
Michal Kubecek728480f2019-12-27 15:55:38 +0100486 }
487
488 ret = ethnl_default_parse(req_info, cb->nlh, sock_net(cb->skb->sk), ops,
489 cb->extack, false);
490 if (req_info->dev) {
491 /* We ignore device specification in dump requests but as the
492 * same parser as for non-dump (doit) requests is used, it
493 * would take reference to the device if it finds one
494 */
495 dev_put(req_info->dev);
496 req_info->dev = NULL;
497 }
498 if (ret < 0)
Dan Carpentera6dd0482020-01-08 08:39:48 +0300499 goto free_reply_data;
Michal Kubecek728480f2019-12-27 15:55:38 +0100500
501 ctx->ops = ops;
502 ctx->req_info = req_info;
503 ctx->reply_data = reply_data;
504 ctx->pos_hash = 0;
505 ctx->pos_idx = 0;
506
507 return 0;
Dan Carpentera6dd0482020-01-08 08:39:48 +0300508
509free_reply_data:
510 kfree(reply_data);
511free_req_info:
512 kfree(req_info);
513
514 return ret;
Michal Kubecek728480f2019-12-27 15:55:38 +0100515}
516
517/* default ->done() handler for GET requests */
518static int ethnl_default_done(struct netlink_callback *cb)
519{
520 struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb);
521
522 kfree(ctx->reply_data);
523 kfree(ctx->req_info);
524
525 return 0;
526}
527
Michal Kubecek5cf2a542019-12-27 15:55:58 +0100528static const struct ethnl_request_ops *
529ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = {
Michal Kubecek73286732019-12-27 15:56:03 +0100530 [ETHTOOL_MSG_LINKINFO_NTF] = &ethnl_linkinfo_request_ops,
Michal Kubecek1b1b1842019-12-27 15:56:18 +0100531 [ETHTOOL_MSG_LINKMODES_NTF] = &ethnl_linkmodes_request_ops,
Michal Kubecek0bda7af2020-01-26 23:11:10 +0100532 [ETHTOOL_MSG_DEBUG_NTF] = &ethnl_debug_request_ops,
Michal Kubecek67bffa72020-01-26 23:11:19 +0100533 [ETHTOOL_MSG_WOL_NTF] = &ethnl_wol_request_ops,
Michal Kubecek9c6451e2020-03-12 21:08:03 +0100534 [ETHTOOL_MSG_FEATURES_NTF] = &ethnl_features_request_ops,
Michal Kubecek111dcba2020-03-12 21:08:18 +0100535 [ETHTOOL_MSG_PRIVFLAGS_NTF] = &ethnl_privflags_request_ops,
Michal Kubecekbc9d1c92020-03-12 21:08:33 +0100536 [ETHTOOL_MSG_RINGS_NTF] = &ethnl_rings_request_ops,
Michal Kubecek5cf2a542019-12-27 15:55:58 +0100537};
538
539/* default notification handler */
540static void ethnl_default_notify(struct net_device *dev, unsigned int cmd,
541 const void *data)
542{
543 struct ethnl_reply_data *reply_data;
544 const struct ethnl_request_ops *ops;
545 struct ethnl_req_info *req_info;
546 struct sk_buff *skb;
547 void *reply_payload;
548 int reply_len;
549 int ret;
550
551 if (WARN_ONCE(cmd > ETHTOOL_MSG_KERNEL_MAX ||
552 !ethnl_default_notify_ops[cmd],
553 "unexpected notification type %u\n", cmd))
554 return;
555 ops = ethnl_default_notify_ops[cmd];
556 req_info = kzalloc(ops->req_info_size, GFP_KERNEL);
557 if (!req_info)
558 return;
559 reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL);
560 if (!reply_data) {
561 kfree(req_info);
562 return;
563 }
564
565 req_info->dev = dev;
566 req_info->flags |= ETHTOOL_FLAG_COMPACT_BITSETS;
567
568 ethnl_init_reply_data(reply_data, ops, dev);
569 ret = ops->prepare_data(req_info, reply_data, NULL);
570 if (ret < 0)
571 goto err_cleanup;
Dan Carpenterd97772d2020-01-08 08:41:25 +0300572 ret = ops->reply_size(req_info, reply_data);
Michal Kubecek5cf2a542019-12-27 15:55:58 +0100573 if (ret < 0)
574 goto err_cleanup;
Dan Carpenterd97772d2020-01-08 08:41:25 +0300575 reply_len = ret;
Michal Kubecek5cf2a542019-12-27 15:55:58 +0100576 ret = -ENOMEM;
577 skb = genlmsg_new(reply_len, GFP_KERNEL);
578 if (!skb)
579 goto err_cleanup;
580 reply_payload = ethnl_bcastmsg_put(skb, cmd);
581 if (!reply_payload)
582 goto err_skb;
583 ret = ethnl_fill_reply_header(skb, dev, ops->hdr_attr);
584 if (ret < 0)
585 goto err_msg;
586 ret = ops->fill_reply(skb, req_info, reply_data);
587 if (ret < 0)
588 goto err_msg;
589 if (ops->cleanup_data)
590 ops->cleanup_data(reply_data);
591
592 genlmsg_end(skb, reply_payload);
593 kfree(reply_data);
594 kfree(req_info);
595 ethnl_multicast(skb, dev);
596 return;
597
598err_msg:
599 WARN_ONCE(ret == -EMSGSIZE,
600 "calculated message payload length (%d) not sufficient\n",
601 reply_len);
602err_skb:
603 nlmsg_free(skb);
604err_cleanup:
605 if (ops->cleanup_data)
606 ops->cleanup_data(reply_data);
607 kfree(reply_data);
608 kfree(req_info);
609 return;
610}
611
Michal Kubecek6b08d6c2019-12-27 15:55:33 +0100612/* notifications */
613
614typedef void (*ethnl_notify_handler_t)(struct net_device *dev, unsigned int cmd,
615 const void *data);
616
617static const ethnl_notify_handler_t ethnl_notify_handlers[] = {
Michal Kubecek73286732019-12-27 15:56:03 +0100618 [ETHTOOL_MSG_LINKINFO_NTF] = ethnl_default_notify,
Michal Kubecek1b1b1842019-12-27 15:56:18 +0100619 [ETHTOOL_MSG_LINKMODES_NTF] = ethnl_default_notify,
Michal Kubecek0bda7af2020-01-26 23:11:10 +0100620 [ETHTOOL_MSG_DEBUG_NTF] = ethnl_default_notify,
Michal Kubecek67bffa72020-01-26 23:11:19 +0100621 [ETHTOOL_MSG_WOL_NTF] = ethnl_default_notify,
Michal Kubecek9c6451e2020-03-12 21:08:03 +0100622 [ETHTOOL_MSG_FEATURES_NTF] = ethnl_default_notify,
Michal Kubecek111dcba2020-03-12 21:08:18 +0100623 [ETHTOOL_MSG_PRIVFLAGS_NTF] = ethnl_default_notify,
Michal Kubecekbc9d1c92020-03-12 21:08:33 +0100624 [ETHTOOL_MSG_RINGS_NTF] = ethnl_default_notify,
Michal Kubecek6b08d6c2019-12-27 15:55:33 +0100625};
626
627void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data)
628{
629 if (unlikely(!ethnl_ok))
630 return;
631 ASSERT_RTNL();
632
633 if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) &&
634 ethnl_notify_handlers[cmd]))
635 ethnl_notify_handlers[cmd](dev, cmd, data);
636 else
637 WARN_ONCE(1, "notification %u not implemented (dev=%s)\n",
638 cmd, netdev_name(dev));
639}
640EXPORT_SYMBOL(ethtool_notify);
641
Michal Kubecek9c6451e2020-03-12 21:08:03 +0100642static void ethnl_notify_features(struct netdev_notifier_info *info)
643{
644 struct net_device *dev = netdev_notifier_info_to_dev(info);
645
646 ethtool_notify(dev, ETHTOOL_MSG_FEATURES_NTF, NULL);
647}
648
649static int ethnl_netdev_event(struct notifier_block *this, unsigned long event,
650 void *ptr)
651{
652 switch (event) {
653 case NETDEV_FEAT_CHANGE:
654 ethnl_notify_features(ptr);
655 break;
656 }
657
658 return NOTIFY_DONE;
659}
660
661static struct notifier_block ethnl_netdev_notifier = {
662 .notifier_call = ethnl_netdev_event,
663};
664
Michal Kubecek2b4a8992019-12-27 15:55:18 +0100665/* genetlink setup */
666
667static const struct genl_ops ethtool_genl_ops[] = {
Michal Kubecek71921692019-12-27 15:55:43 +0100668 {
669 .cmd = ETHTOOL_MSG_STRSET_GET,
670 .doit = ethnl_default_doit,
671 .start = ethnl_default_start,
672 .dumpit = ethnl_default_dumpit,
673 .done = ethnl_default_done,
674 },
Michal Kubecek459e0b82019-12-27 15:55:48 +0100675 {
676 .cmd = ETHTOOL_MSG_LINKINFO_GET,
677 .doit = ethnl_default_doit,
678 .start = ethnl_default_start,
679 .dumpit = ethnl_default_dumpit,
680 .done = ethnl_default_done,
681 },
Michal Kubeceka53f3d42019-12-27 15:55:53 +0100682 {
683 .cmd = ETHTOOL_MSG_LINKINFO_SET,
684 .flags = GENL_UNS_ADMIN_PERM,
685 .doit = ethnl_set_linkinfo,
686 },
Michal Kubecekf625aa92019-12-27 15:56:08 +0100687 {
688 .cmd = ETHTOOL_MSG_LINKMODES_GET,
689 .doit = ethnl_default_doit,
690 .start = ethnl_default_start,
691 .dumpit = ethnl_default_dumpit,
692 .done = ethnl_default_done,
693 },
Michal Kubecekbfbcfe22019-12-27 15:56:13 +0100694 {
695 .cmd = ETHTOOL_MSG_LINKMODES_SET,
696 .flags = GENL_UNS_ADMIN_PERM,
697 .doit = ethnl_set_linkmodes,
698 },
Michal Kubecek3d2b8472019-12-27 15:56:23 +0100699 {
700 .cmd = ETHTOOL_MSG_LINKSTATE_GET,
701 .doit = ethnl_default_doit,
702 .start = ethnl_default_start,
703 .dumpit = ethnl_default_dumpit,
704 .done = ethnl_default_done,
705 },
Michal Kubecek6a94b8c2020-01-26 23:11:04 +0100706 {
707 .cmd = ETHTOOL_MSG_DEBUG_GET,
708 .doit = ethnl_default_doit,
709 .start = ethnl_default_start,
710 .dumpit = ethnl_default_dumpit,
711 .done = ethnl_default_done,
712 },
Michal Kubeceke54d04e2020-01-26 23:11:07 +0100713 {
714 .cmd = ETHTOOL_MSG_DEBUG_SET,
715 .flags = GENL_UNS_ADMIN_PERM,
716 .doit = ethnl_set_debug,
717 },
Michal Kubecek51ea22b2020-01-26 23:11:13 +0100718 {
719 .cmd = ETHTOOL_MSG_WOL_GET,
720 .flags = GENL_UNS_ADMIN_PERM,
721 .doit = ethnl_default_doit,
722 .start = ethnl_default_start,
723 .dumpit = ethnl_default_dumpit,
724 .done = ethnl_default_done,
725 },
Michal Kubecek8d425b12020-01-26 23:11:16 +0100726 {
727 .cmd = ETHTOOL_MSG_WOL_SET,
728 .flags = GENL_UNS_ADMIN_PERM,
729 .doit = ethnl_set_wol,
730 },
Michal Kubecek05243992020-03-12 21:07:48 +0100731 {
732 .cmd = ETHTOOL_MSG_FEATURES_GET,
733 .doit = ethnl_default_doit,
734 .start = ethnl_default_start,
735 .dumpit = ethnl_default_dumpit,
736 .done = ethnl_default_done,
737 },
Michal Kubecek0980bfc2020-03-12 21:07:58 +0100738 {
739 .cmd = ETHTOOL_MSG_FEATURES_SET,
740 .flags = GENL_UNS_ADMIN_PERM,
741 .doit = ethnl_set_features,
742 },
Michal Kubeceke16c3382020-03-12 21:08:08 +0100743 {
744 .cmd = ETHTOOL_MSG_PRIVFLAGS_GET,
745 .doit = ethnl_default_doit,
746 .start = ethnl_default_start,
747 .dumpit = ethnl_default_dumpit,
748 .done = ethnl_default_done,
749 },
Michal Kubecekf265d792020-03-12 21:08:13 +0100750 {
751 .cmd = ETHTOOL_MSG_PRIVFLAGS_SET,
752 .flags = GENL_UNS_ADMIN_PERM,
753 .doit = ethnl_set_privflags,
754 },
Michal Kubeceke4a17172020-03-12 21:08:23 +0100755 {
756 .cmd = ETHTOOL_MSG_RINGS_GET,
757 .doit = ethnl_default_doit,
758 .start = ethnl_default_start,
759 .dumpit = ethnl_default_dumpit,
760 .done = ethnl_default_done,
761 },
Michal Kubecek2fc29292020-03-12 21:08:28 +0100762 {
763 .cmd = ETHTOOL_MSG_RINGS_SET,
764 .flags = GENL_UNS_ADMIN_PERM,
765 .doit = ethnl_set_rings,
766 },
Michal Kubecek0c849792020-03-12 21:08:38 +0100767 {
768 .cmd = ETHTOOL_MSG_CHANNELS_GET,
769 .doit = ethnl_default_doit,
770 .start = ethnl_default_start,
771 .dumpit = ethnl_default_dumpit,
772 .done = ethnl_default_done,
773 },
Michal Kubecek2b4a8992019-12-27 15:55:18 +0100774};
775
Michal Kubecek6b08d6c2019-12-27 15:55:33 +0100776static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
777 [ETHNL_MCGRP_MONITOR] = { .name = ETHTOOL_MCGRP_MONITOR_NAME },
778};
779
Michal Kubecek2b4a8992019-12-27 15:55:18 +0100780static struct genl_family ethtool_genl_family = {
781 .name = ETHTOOL_GENL_NAME,
782 .version = ETHTOOL_GENL_VERSION,
783 .netnsok = true,
784 .parallel_ops = true,
785 .ops = ethtool_genl_ops,
786 .n_ops = ARRAY_SIZE(ethtool_genl_ops),
Michal Kubecek6b08d6c2019-12-27 15:55:33 +0100787 .mcgrps = ethtool_nl_mcgrps,
788 .n_mcgrps = ARRAY_SIZE(ethtool_nl_mcgrps),
Michal Kubecek2b4a8992019-12-27 15:55:18 +0100789};
790
791/* module setup */
792
793static int __init ethnl_init(void)
794{
795 int ret;
796
797 ret = genl_register_family(&ethtool_genl_family);
798 if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
799 return ret;
Michal Kubecek6b08d6c2019-12-27 15:55:33 +0100800 ethnl_ok = true;
Michal Kubecek2b4a8992019-12-27 15:55:18 +0100801
Michal Kubecek9c6451e2020-03-12 21:08:03 +0100802 ret = register_netdevice_notifier(&ethnl_netdev_notifier);
803 WARN(ret < 0, "ethtool: net device notifier registration failed");
804 return ret;
Michal Kubecek2b4a8992019-12-27 15:55:18 +0100805}
806
807subsys_initcall(ethnl_init);