.. | .. |
---|
107 | 107 | return NULL; |
---|
108 | 108 | } |
---|
109 | 109 | |
---|
110 | | -static const struct genl_ops *genl_get_cmd(u8 cmd, |
---|
111 | | - const struct genl_family *family) |
---|
| 110 | +static int genl_get_cmd_cnt(const struct genl_family *family) |
---|
| 111 | +{ |
---|
| 112 | + return family->n_ops + family->n_small_ops; |
---|
| 113 | +} |
---|
| 114 | + |
---|
| 115 | +static void genl_op_from_full(const struct genl_family *family, |
---|
| 116 | + unsigned int i, struct genl_ops *op) |
---|
| 117 | +{ |
---|
| 118 | + *op = family->ops[i]; |
---|
| 119 | + |
---|
| 120 | + if (!op->maxattr) |
---|
| 121 | + op->maxattr = family->maxattr; |
---|
| 122 | + if (!op->policy) |
---|
| 123 | + op->policy = family->policy; |
---|
| 124 | +} |
---|
| 125 | + |
---|
| 126 | +static int genl_get_cmd_full(u32 cmd, const struct genl_family *family, |
---|
| 127 | + struct genl_ops *op) |
---|
112 | 128 | { |
---|
113 | 129 | int i; |
---|
114 | 130 | |
---|
115 | 131 | for (i = 0; i < family->n_ops; i++) |
---|
116 | | - if (family->ops[i].cmd == cmd) |
---|
117 | | - return &family->ops[i]; |
---|
| 132 | + if (family->ops[i].cmd == cmd) { |
---|
| 133 | + genl_op_from_full(family, i, op); |
---|
| 134 | + return 0; |
---|
| 135 | + } |
---|
118 | 136 | |
---|
119 | | - return NULL; |
---|
| 137 | + return -ENOENT; |
---|
| 138 | +} |
---|
| 139 | + |
---|
| 140 | +static void genl_op_from_small(const struct genl_family *family, |
---|
| 141 | + unsigned int i, struct genl_ops *op) |
---|
| 142 | +{ |
---|
| 143 | + memset(op, 0, sizeof(*op)); |
---|
| 144 | + op->doit = family->small_ops[i].doit; |
---|
| 145 | + op->dumpit = family->small_ops[i].dumpit; |
---|
| 146 | + op->cmd = family->small_ops[i].cmd; |
---|
| 147 | + op->internal_flags = family->small_ops[i].internal_flags; |
---|
| 148 | + op->flags = family->small_ops[i].flags; |
---|
| 149 | + op->validate = family->small_ops[i].validate; |
---|
| 150 | + |
---|
| 151 | + op->maxattr = family->maxattr; |
---|
| 152 | + op->policy = family->policy; |
---|
| 153 | +} |
---|
| 154 | + |
---|
| 155 | +static int genl_get_cmd_small(u32 cmd, const struct genl_family *family, |
---|
| 156 | + struct genl_ops *op) |
---|
| 157 | +{ |
---|
| 158 | + int i; |
---|
| 159 | + |
---|
| 160 | + for (i = 0; i < family->n_small_ops; i++) |
---|
| 161 | + if (family->small_ops[i].cmd == cmd) { |
---|
| 162 | + genl_op_from_small(family, i, op); |
---|
| 163 | + return 0; |
---|
| 164 | + } |
---|
| 165 | + |
---|
| 166 | + return -ENOENT; |
---|
| 167 | +} |
---|
| 168 | + |
---|
| 169 | +static int genl_get_cmd(u32 cmd, const struct genl_family *family, |
---|
| 170 | + struct genl_ops *op) |
---|
| 171 | +{ |
---|
| 172 | + if (!genl_get_cmd_full(cmd, family, op)) |
---|
| 173 | + return 0; |
---|
| 174 | + return genl_get_cmd_small(cmd, family, op); |
---|
| 175 | +} |
---|
| 176 | + |
---|
| 177 | +static void genl_get_cmd_by_index(unsigned int i, |
---|
| 178 | + const struct genl_family *family, |
---|
| 179 | + struct genl_ops *op) |
---|
| 180 | +{ |
---|
| 181 | + if (i < family->n_ops) |
---|
| 182 | + genl_op_from_full(family, i, op); |
---|
| 183 | + else if (i < family->n_ops + family->n_small_ops) |
---|
| 184 | + genl_op_from_small(family, i - family->n_ops, op); |
---|
| 185 | + else |
---|
| 186 | + WARN_ON_ONCE(1); |
---|
120 | 187 | } |
---|
121 | 188 | |
---|
122 | 189 | static int genl_allocate_reserve_groups(int n_groups, int *first_id) |
---|
.. | .. |
---|
222 | 289 | |
---|
223 | 290 | family->mcgrp_offset = first_id; |
---|
224 | 291 | |
---|
225 | | - /* if still initializing, can't and don't need to to realloc bitmaps */ |
---|
| 292 | + /* if still initializing, can't and don't need to realloc bitmaps */ |
---|
226 | 293 | if (!init_net.genl_sock) |
---|
227 | 294 | return 0; |
---|
228 | 295 | |
---|
.. | .. |
---|
286 | 353 | |
---|
287 | 354 | static int genl_validate_ops(const struct genl_family *family) |
---|
288 | 355 | { |
---|
289 | | - const struct genl_ops *ops = family->ops; |
---|
290 | | - unsigned int n_ops = family->n_ops; |
---|
291 | 356 | int i, j; |
---|
292 | 357 | |
---|
293 | | - if (WARN_ON(n_ops && !ops)) |
---|
| 358 | + if (WARN_ON(family->n_ops && !family->ops) || |
---|
| 359 | + WARN_ON(family->n_small_ops && !family->small_ops)) |
---|
294 | 360 | return -EINVAL; |
---|
295 | 361 | |
---|
296 | | - if (!n_ops) |
---|
297 | | - return 0; |
---|
| 362 | + for (i = 0; i < genl_get_cmd_cnt(family); i++) { |
---|
| 363 | + struct genl_ops op; |
---|
298 | 364 | |
---|
299 | | - for (i = 0; i < n_ops; i++) { |
---|
300 | | - if (ops[i].dumpit == NULL && ops[i].doit == NULL) |
---|
| 365 | + genl_get_cmd_by_index(i, family, &op); |
---|
| 366 | + if (op.dumpit == NULL && op.doit == NULL) |
---|
301 | 367 | return -EINVAL; |
---|
302 | | - for (j = i + 1; j < n_ops; j++) |
---|
303 | | - if (ops[i].cmd == ops[j].cmd) |
---|
| 368 | + for (j = i + 1; j < genl_get_cmd_cnt(family); j++) { |
---|
| 369 | + struct genl_ops op2; |
---|
| 370 | + |
---|
| 371 | + genl_get_cmd_by_index(j, family, &op2); |
---|
| 372 | + if (op.cmd == op2.cmd) |
---|
304 | 373 | return -EINVAL; |
---|
| 374 | + } |
---|
305 | 375 | } |
---|
306 | 376 | |
---|
307 | 377 | return 0; |
---|
.. | .. |
---|
351 | 421 | start = end = GENL_ID_VFS_DQUOT; |
---|
352 | 422 | } |
---|
353 | 423 | |
---|
354 | | - if (family->maxattr && !family->parallel_ops) { |
---|
355 | | - family->attrbuf = kmalloc_array(family->maxattr + 1, |
---|
356 | | - sizeof(struct nlattr *), |
---|
357 | | - GFP_KERNEL); |
---|
358 | | - if (family->attrbuf == NULL) { |
---|
359 | | - err = -ENOMEM; |
---|
360 | | - goto errout_locked; |
---|
361 | | - } |
---|
362 | | - } else |
---|
363 | | - family->attrbuf = NULL; |
---|
364 | | - |
---|
365 | | - family->id = idr_alloc(&genl_fam_idr, family, |
---|
366 | | - start, end + 1, GFP_KERNEL); |
---|
| 424 | + family->id = idr_alloc_cyclic(&genl_fam_idr, family, |
---|
| 425 | + start, end + 1, GFP_KERNEL); |
---|
367 | 426 | if (family->id < 0) { |
---|
368 | 427 | err = family->id; |
---|
369 | | - goto errout_free; |
---|
| 428 | + goto errout_locked; |
---|
370 | 429 | } |
---|
371 | 430 | |
---|
372 | 431 | err = genl_validate_assign_mc_groups(family); |
---|
.. | .. |
---|
385 | 444 | |
---|
386 | 445 | errout_remove: |
---|
387 | 446 | idr_remove(&genl_fam_idr, family->id); |
---|
388 | | -errout_free: |
---|
389 | | - kfree(family->attrbuf); |
---|
390 | 447 | errout_locked: |
---|
391 | 448 | genl_unlock_all(); |
---|
392 | 449 | return err; |
---|
.. | .. |
---|
418 | 475 | wait_event(genl_sk_destructing_waitq, |
---|
419 | 476 | atomic_read(&genl_sk_destructing_cnt) == 0); |
---|
420 | 477 | genl_unlock(); |
---|
421 | | - |
---|
422 | | - kfree(family->attrbuf); |
---|
423 | 478 | |
---|
424 | 479 | genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0); |
---|
425 | 480 | |
---|
.. | .. |
---|
458 | 513 | } |
---|
459 | 514 | EXPORT_SYMBOL(genlmsg_put); |
---|
460 | 515 | |
---|
461 | | -static int genl_lock_start(struct netlink_callback *cb) |
---|
| 516 | +static struct genl_dumpit_info *genl_dumpit_info_alloc(void) |
---|
462 | 517 | { |
---|
463 | | - /* our ops are always const - netlink API doesn't propagate that */ |
---|
464 | | - const struct genl_ops *ops = cb->data; |
---|
| 518 | + return kmalloc(sizeof(struct genl_dumpit_info), GFP_KERNEL); |
---|
| 519 | +} |
---|
| 520 | + |
---|
| 521 | +static void genl_dumpit_info_free(const struct genl_dumpit_info *info) |
---|
| 522 | +{ |
---|
| 523 | + kfree(info); |
---|
| 524 | +} |
---|
| 525 | + |
---|
| 526 | +static struct nlattr ** |
---|
| 527 | +genl_family_rcv_msg_attrs_parse(const struct genl_family *family, |
---|
| 528 | + struct nlmsghdr *nlh, |
---|
| 529 | + struct netlink_ext_ack *extack, |
---|
| 530 | + const struct genl_ops *ops, |
---|
| 531 | + int hdrlen, |
---|
| 532 | + enum genl_validate_flags no_strict_flag) |
---|
| 533 | +{ |
---|
| 534 | + enum netlink_validation validate = ops->validate & no_strict_flag ? |
---|
| 535 | + NL_VALIDATE_LIBERAL : |
---|
| 536 | + NL_VALIDATE_STRICT; |
---|
| 537 | + struct nlattr **attrbuf; |
---|
| 538 | + int err; |
---|
| 539 | + |
---|
| 540 | + if (!ops->maxattr) |
---|
| 541 | + return NULL; |
---|
| 542 | + |
---|
| 543 | + attrbuf = kmalloc_array(ops->maxattr + 1, |
---|
| 544 | + sizeof(struct nlattr *), GFP_KERNEL); |
---|
| 545 | + if (!attrbuf) |
---|
| 546 | + return ERR_PTR(-ENOMEM); |
---|
| 547 | + |
---|
| 548 | + err = __nlmsg_parse(nlh, hdrlen, attrbuf, ops->maxattr, ops->policy, |
---|
| 549 | + validate, extack); |
---|
| 550 | + if (err) { |
---|
| 551 | + kfree(attrbuf); |
---|
| 552 | + return ERR_PTR(err); |
---|
| 553 | + } |
---|
| 554 | + return attrbuf; |
---|
| 555 | +} |
---|
| 556 | + |
---|
| 557 | +static void genl_family_rcv_msg_attrs_free(struct nlattr **attrbuf) |
---|
| 558 | +{ |
---|
| 559 | + kfree(attrbuf); |
---|
| 560 | +} |
---|
| 561 | + |
---|
| 562 | +struct genl_start_context { |
---|
| 563 | + const struct genl_family *family; |
---|
| 564 | + struct nlmsghdr *nlh; |
---|
| 565 | + struct netlink_ext_ack *extack; |
---|
| 566 | + const struct genl_ops *ops; |
---|
| 567 | + int hdrlen; |
---|
| 568 | +}; |
---|
| 569 | + |
---|
| 570 | +static int genl_start(struct netlink_callback *cb) |
---|
| 571 | +{ |
---|
| 572 | + struct genl_start_context *ctx = cb->data; |
---|
| 573 | + const struct genl_ops *ops = ctx->ops; |
---|
| 574 | + struct genl_dumpit_info *info; |
---|
| 575 | + struct nlattr **attrs = NULL; |
---|
465 | 576 | int rc = 0; |
---|
466 | 577 | |
---|
| 578 | + if (ops->validate & GENL_DONT_VALIDATE_DUMP) |
---|
| 579 | + goto no_attrs; |
---|
| 580 | + |
---|
| 581 | + if (ctx->nlh->nlmsg_len < nlmsg_msg_size(ctx->hdrlen)) |
---|
| 582 | + return -EINVAL; |
---|
| 583 | + |
---|
| 584 | + attrs = genl_family_rcv_msg_attrs_parse(ctx->family, ctx->nlh, ctx->extack, |
---|
| 585 | + ops, ctx->hdrlen, |
---|
| 586 | + GENL_DONT_VALIDATE_DUMP_STRICT); |
---|
| 587 | + if (IS_ERR(attrs)) |
---|
| 588 | + return PTR_ERR(attrs); |
---|
| 589 | + |
---|
| 590 | +no_attrs: |
---|
| 591 | + info = genl_dumpit_info_alloc(); |
---|
| 592 | + if (!info) { |
---|
| 593 | + genl_family_rcv_msg_attrs_free(attrs); |
---|
| 594 | + return -ENOMEM; |
---|
| 595 | + } |
---|
| 596 | + info->family = ctx->family; |
---|
| 597 | + info->op = *ops; |
---|
| 598 | + info->attrs = attrs; |
---|
| 599 | + |
---|
| 600 | + cb->data = info; |
---|
467 | 601 | if (ops->start) { |
---|
468 | | - genl_lock(); |
---|
| 602 | + if (!ctx->family->parallel_ops) |
---|
| 603 | + genl_lock(); |
---|
469 | 604 | rc = ops->start(cb); |
---|
470 | | - genl_unlock(); |
---|
| 605 | + if (!ctx->family->parallel_ops) |
---|
| 606 | + genl_unlock(); |
---|
| 607 | + } |
---|
| 608 | + |
---|
| 609 | + if (rc) { |
---|
| 610 | + genl_family_rcv_msg_attrs_free(info->attrs); |
---|
| 611 | + genl_dumpit_info_free(info); |
---|
| 612 | + cb->data = NULL; |
---|
471 | 613 | } |
---|
472 | 614 | return rc; |
---|
473 | 615 | } |
---|
474 | 616 | |
---|
475 | 617 | static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb) |
---|
476 | 618 | { |
---|
477 | | - /* our ops are always const - netlink API doesn't propagate that */ |
---|
478 | | - const struct genl_ops *ops = cb->data; |
---|
| 619 | + const struct genl_ops *ops = &genl_dumpit_info(cb)->op; |
---|
479 | 620 | int rc; |
---|
480 | 621 | |
---|
481 | 622 | genl_lock(); |
---|
.. | .. |
---|
486 | 627 | |
---|
487 | 628 | static int genl_lock_done(struct netlink_callback *cb) |
---|
488 | 629 | { |
---|
489 | | - /* our ops are always const - netlink API doesn't propagate that */ |
---|
490 | | - const struct genl_ops *ops = cb->data; |
---|
| 630 | + const struct genl_dumpit_info *info = genl_dumpit_info(cb); |
---|
| 631 | + const struct genl_ops *ops = &info->op; |
---|
491 | 632 | int rc = 0; |
---|
492 | 633 | |
---|
493 | 634 | if (ops->done) { |
---|
.. | .. |
---|
495 | 636 | rc = ops->done(cb); |
---|
496 | 637 | genl_unlock(); |
---|
497 | 638 | } |
---|
| 639 | + genl_family_rcv_msg_attrs_free(info->attrs); |
---|
| 640 | + genl_dumpit_info_free(info); |
---|
498 | 641 | return rc; |
---|
499 | 642 | } |
---|
500 | 643 | |
---|
501 | | -static int genl_family_rcv_msg(const struct genl_family *family, |
---|
502 | | - struct sk_buff *skb, |
---|
503 | | - struct nlmsghdr *nlh, |
---|
504 | | - struct netlink_ext_ack *extack) |
---|
| 644 | +static int genl_parallel_done(struct netlink_callback *cb) |
---|
505 | 645 | { |
---|
506 | | - const struct genl_ops *ops; |
---|
507 | | - struct net *net = sock_net(skb->sk); |
---|
508 | | - struct genl_info info; |
---|
509 | | - struct genlmsghdr *hdr = nlmsg_data(nlh); |
---|
| 646 | + const struct genl_dumpit_info *info = genl_dumpit_info(cb); |
---|
| 647 | + const struct genl_ops *ops = &info->op; |
---|
| 648 | + int rc = 0; |
---|
| 649 | + |
---|
| 650 | + if (ops->done) |
---|
| 651 | + rc = ops->done(cb); |
---|
| 652 | + genl_family_rcv_msg_attrs_free(info->attrs); |
---|
| 653 | + genl_dumpit_info_free(info); |
---|
| 654 | + return rc; |
---|
| 655 | +} |
---|
| 656 | + |
---|
| 657 | +static int genl_family_rcv_msg_dumpit(const struct genl_family *family, |
---|
| 658 | + struct sk_buff *skb, |
---|
| 659 | + struct nlmsghdr *nlh, |
---|
| 660 | + struct netlink_ext_ack *extack, |
---|
| 661 | + const struct genl_ops *ops, |
---|
| 662 | + int hdrlen, struct net *net) |
---|
| 663 | +{ |
---|
| 664 | + struct genl_start_context ctx; |
---|
| 665 | + int err; |
---|
| 666 | + |
---|
| 667 | + if (!ops->dumpit) |
---|
| 668 | + return -EOPNOTSUPP; |
---|
| 669 | + |
---|
| 670 | + ctx.family = family; |
---|
| 671 | + ctx.nlh = nlh; |
---|
| 672 | + ctx.extack = extack; |
---|
| 673 | + ctx.ops = ops; |
---|
| 674 | + ctx.hdrlen = hdrlen; |
---|
| 675 | + |
---|
| 676 | + if (!family->parallel_ops) { |
---|
| 677 | + struct netlink_dump_control c = { |
---|
| 678 | + .module = family->module, |
---|
| 679 | + .data = &ctx, |
---|
| 680 | + .start = genl_start, |
---|
| 681 | + .dump = genl_lock_dumpit, |
---|
| 682 | + .done = genl_lock_done, |
---|
| 683 | + }; |
---|
| 684 | + |
---|
| 685 | + genl_unlock(); |
---|
| 686 | + err = __netlink_dump_start(net->genl_sock, skb, nlh, &c); |
---|
| 687 | + genl_lock(); |
---|
| 688 | + } else { |
---|
| 689 | + struct netlink_dump_control c = { |
---|
| 690 | + .module = family->module, |
---|
| 691 | + .data = &ctx, |
---|
| 692 | + .start = genl_start, |
---|
| 693 | + .dump = ops->dumpit, |
---|
| 694 | + .done = genl_parallel_done, |
---|
| 695 | + }; |
---|
| 696 | + |
---|
| 697 | + err = __netlink_dump_start(net->genl_sock, skb, nlh, &c); |
---|
| 698 | + } |
---|
| 699 | + |
---|
| 700 | + return err; |
---|
| 701 | +} |
---|
| 702 | + |
---|
| 703 | +static int genl_family_rcv_msg_doit(const struct genl_family *family, |
---|
| 704 | + struct sk_buff *skb, |
---|
| 705 | + struct nlmsghdr *nlh, |
---|
| 706 | + struct netlink_ext_ack *extack, |
---|
| 707 | + const struct genl_ops *ops, |
---|
| 708 | + int hdrlen, struct net *net) |
---|
| 709 | +{ |
---|
510 | 710 | struct nlattr **attrbuf; |
---|
511 | | - int hdrlen, err; |
---|
| 711 | + struct genl_info info; |
---|
| 712 | + int err; |
---|
512 | 713 | |
---|
513 | | - /* this family doesn't exist in this netns */ |
---|
514 | | - if (!family->netnsok && !net_eq(net, &init_net)) |
---|
515 | | - return -ENOENT; |
---|
516 | | - |
---|
517 | | - hdrlen = GENL_HDRLEN + family->hdrsize; |
---|
518 | | - if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) |
---|
519 | | - return -EINVAL; |
---|
520 | | - |
---|
521 | | - ops = genl_get_cmd(hdr->cmd, family); |
---|
522 | | - if (ops == NULL) |
---|
| 714 | + if (!ops->doit) |
---|
523 | 715 | return -EOPNOTSUPP; |
---|
524 | 716 | |
---|
525 | | - if ((ops->flags & GENL_ADMIN_PERM) && |
---|
526 | | - !netlink_capable(skb, CAP_NET_ADMIN)) |
---|
527 | | - return -EPERM; |
---|
528 | | - |
---|
529 | | - if ((ops->flags & GENL_UNS_ADMIN_PERM) && |
---|
530 | | - !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) |
---|
531 | | - return -EPERM; |
---|
532 | | - |
---|
533 | | - if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) { |
---|
534 | | - int rc; |
---|
535 | | - |
---|
536 | | - if (ops->dumpit == NULL) |
---|
537 | | - return -EOPNOTSUPP; |
---|
538 | | - |
---|
539 | | - if (!family->parallel_ops) { |
---|
540 | | - struct netlink_dump_control c = { |
---|
541 | | - .module = family->module, |
---|
542 | | - /* we have const, but the netlink API doesn't */ |
---|
543 | | - .data = (void *)ops, |
---|
544 | | - .start = genl_lock_start, |
---|
545 | | - .dump = genl_lock_dumpit, |
---|
546 | | - .done = genl_lock_done, |
---|
547 | | - }; |
---|
548 | | - |
---|
549 | | - genl_unlock(); |
---|
550 | | - rc = __netlink_dump_start(net->genl_sock, skb, nlh, &c); |
---|
551 | | - genl_lock(); |
---|
552 | | - |
---|
553 | | - } else { |
---|
554 | | - struct netlink_dump_control c = { |
---|
555 | | - .module = family->module, |
---|
556 | | - .start = ops->start, |
---|
557 | | - .dump = ops->dumpit, |
---|
558 | | - .done = ops->done, |
---|
559 | | - }; |
---|
560 | | - |
---|
561 | | - rc = __netlink_dump_start(net->genl_sock, skb, nlh, &c); |
---|
562 | | - } |
---|
563 | | - |
---|
564 | | - return rc; |
---|
565 | | - } |
---|
566 | | - |
---|
567 | | - if (ops->doit == NULL) |
---|
568 | | - return -EOPNOTSUPP; |
---|
569 | | - |
---|
570 | | - if (family->maxattr && family->parallel_ops) { |
---|
571 | | - attrbuf = kmalloc_array(family->maxattr + 1, |
---|
572 | | - sizeof(struct nlattr *), |
---|
573 | | - GFP_KERNEL); |
---|
574 | | - if (attrbuf == NULL) |
---|
575 | | - return -ENOMEM; |
---|
576 | | - } else |
---|
577 | | - attrbuf = family->attrbuf; |
---|
578 | | - |
---|
579 | | - if (attrbuf) { |
---|
580 | | - err = nlmsg_parse(nlh, hdrlen, attrbuf, family->maxattr, |
---|
581 | | - ops->policy, extack); |
---|
582 | | - if (err < 0) |
---|
583 | | - goto out; |
---|
584 | | - } |
---|
| 717 | + attrbuf = genl_family_rcv_msg_attrs_parse(family, nlh, extack, |
---|
| 718 | + ops, hdrlen, |
---|
| 719 | + GENL_DONT_VALIDATE_STRICT); |
---|
| 720 | + if (IS_ERR(attrbuf)) |
---|
| 721 | + return PTR_ERR(attrbuf); |
---|
585 | 722 | |
---|
586 | 723 | info.snd_seq = nlh->nlmsg_seq; |
---|
587 | 724 | info.snd_portid = NETLINK_CB(skb).portid; |
---|
.. | .. |
---|
605 | 742 | family->post_doit(ops, skb, &info); |
---|
606 | 743 | |
---|
607 | 744 | out: |
---|
608 | | - if (family->parallel_ops) |
---|
609 | | - kfree(attrbuf); |
---|
| 745 | + genl_family_rcv_msg_attrs_free(attrbuf); |
---|
610 | 746 | |
---|
611 | 747 | return err; |
---|
| 748 | +} |
---|
| 749 | + |
---|
| 750 | +static int genl_family_rcv_msg(const struct genl_family *family, |
---|
| 751 | + struct sk_buff *skb, |
---|
| 752 | + struct nlmsghdr *nlh, |
---|
| 753 | + struct netlink_ext_ack *extack) |
---|
| 754 | +{ |
---|
| 755 | + struct net *net = sock_net(skb->sk); |
---|
| 756 | + struct genlmsghdr *hdr = nlmsg_data(nlh); |
---|
| 757 | + struct genl_ops op; |
---|
| 758 | + int hdrlen; |
---|
| 759 | + |
---|
| 760 | + /* this family doesn't exist in this netns */ |
---|
| 761 | + if (!family->netnsok && !net_eq(net, &init_net)) |
---|
| 762 | + return -ENOENT; |
---|
| 763 | + |
---|
| 764 | + hdrlen = GENL_HDRLEN + family->hdrsize; |
---|
| 765 | + if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) |
---|
| 766 | + return -EINVAL; |
---|
| 767 | + |
---|
| 768 | + if (genl_get_cmd(hdr->cmd, family, &op)) |
---|
| 769 | + return -EOPNOTSUPP; |
---|
| 770 | + |
---|
| 771 | + if ((op.flags & GENL_ADMIN_PERM) && |
---|
| 772 | + !netlink_capable(skb, CAP_NET_ADMIN)) |
---|
| 773 | + return -EPERM; |
---|
| 774 | + |
---|
| 775 | + if ((op.flags & GENL_UNS_ADMIN_PERM) && |
---|
| 776 | + !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) |
---|
| 777 | + return -EPERM; |
---|
| 778 | + |
---|
| 779 | + if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) |
---|
| 780 | + return genl_family_rcv_msg_dumpit(family, skb, nlh, extack, |
---|
| 781 | + &op, hdrlen, net); |
---|
| 782 | + else |
---|
| 783 | + return genl_family_rcv_msg_doit(family, skb, nlh, extack, |
---|
| 784 | + &op, hdrlen, net); |
---|
612 | 785 | } |
---|
613 | 786 | |
---|
614 | 787 | static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, |
---|
.. | .. |
---|
661 | 834 | nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr)) |
---|
662 | 835 | goto nla_put_failure; |
---|
663 | 836 | |
---|
664 | | - if (family->n_ops) { |
---|
| 837 | + if (genl_get_cmd_cnt(family)) { |
---|
665 | 838 | struct nlattr *nla_ops; |
---|
666 | 839 | int i; |
---|
667 | 840 | |
---|
668 | | - nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS); |
---|
| 841 | + nla_ops = nla_nest_start_noflag(skb, CTRL_ATTR_OPS); |
---|
669 | 842 | if (nla_ops == NULL) |
---|
670 | 843 | goto nla_put_failure; |
---|
671 | 844 | |
---|
672 | | - for (i = 0; i < family->n_ops; i++) { |
---|
| 845 | + for (i = 0; i < genl_get_cmd_cnt(family); i++) { |
---|
673 | 846 | struct nlattr *nest; |
---|
674 | | - const struct genl_ops *ops = &family->ops[i]; |
---|
675 | | - u32 op_flags = ops->flags; |
---|
| 847 | + struct genl_ops op; |
---|
| 848 | + u32 op_flags; |
---|
676 | 849 | |
---|
677 | | - if (ops->dumpit) |
---|
| 850 | + genl_get_cmd_by_index(i, family, &op); |
---|
| 851 | + op_flags = op.flags; |
---|
| 852 | + if (op.dumpit) |
---|
678 | 853 | op_flags |= GENL_CMD_CAP_DUMP; |
---|
679 | | - if (ops->doit) |
---|
| 854 | + if (op.doit) |
---|
680 | 855 | op_flags |= GENL_CMD_CAP_DO; |
---|
681 | | - if (ops->policy) |
---|
| 856 | + if (op.policy) |
---|
682 | 857 | op_flags |= GENL_CMD_CAP_HASPOL; |
---|
683 | 858 | |
---|
684 | | - nest = nla_nest_start(skb, i + 1); |
---|
| 859 | + nest = nla_nest_start_noflag(skb, i + 1); |
---|
685 | 860 | if (nest == NULL) |
---|
686 | 861 | goto nla_put_failure; |
---|
687 | 862 | |
---|
688 | | - if (nla_put_u32(skb, CTRL_ATTR_OP_ID, ops->cmd) || |
---|
| 863 | + if (nla_put_u32(skb, CTRL_ATTR_OP_ID, op.cmd) || |
---|
689 | 864 | nla_put_u32(skb, CTRL_ATTR_OP_FLAGS, op_flags)) |
---|
690 | 865 | goto nla_put_failure; |
---|
691 | 866 | |
---|
.. | .. |
---|
699 | 874 | struct nlattr *nla_grps; |
---|
700 | 875 | int i; |
---|
701 | 876 | |
---|
702 | | - nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS); |
---|
| 877 | + nla_grps = nla_nest_start_noflag(skb, CTRL_ATTR_MCAST_GROUPS); |
---|
703 | 878 | if (nla_grps == NULL) |
---|
704 | 879 | goto nla_put_failure; |
---|
705 | 880 | |
---|
.. | .. |
---|
709 | 884 | |
---|
710 | 885 | grp = &family->mcgrps[i]; |
---|
711 | 886 | |
---|
712 | | - nest = nla_nest_start(skb, i + 1); |
---|
| 887 | + nest = nla_nest_start_noflag(skb, i + 1); |
---|
713 | 888 | if (nest == NULL) |
---|
714 | 889 | goto nla_put_failure; |
---|
715 | 890 | |
---|
.. | .. |
---|
749 | 924 | nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id)) |
---|
750 | 925 | goto nla_put_failure; |
---|
751 | 926 | |
---|
752 | | - nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS); |
---|
| 927 | + nla_grps = nla_nest_start_noflag(skb, CTRL_ATTR_MCAST_GROUPS); |
---|
753 | 928 | if (nla_grps == NULL) |
---|
754 | 929 | goto nla_put_failure; |
---|
755 | 930 | |
---|
756 | | - nest = nla_nest_start(skb, 1); |
---|
| 931 | + nest = nla_nest_start_noflag(skb, 1); |
---|
757 | 932 | if (nest == NULL) |
---|
758 | 933 | goto nla_put_failure; |
---|
759 | 934 | |
---|
.. | .. |
---|
841 | 1016 | return skb; |
---|
842 | 1017 | } |
---|
843 | 1018 | |
---|
844 | | -static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { |
---|
| 1019 | +static const struct nla_policy ctrl_policy_family[] = { |
---|
845 | 1020 | [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, |
---|
846 | 1021 | [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, |
---|
847 | 1022 | .len = GENL_NAMSIZ - 1 }, |
---|
.. | .. |
---|
935 | 1110 | return 0; |
---|
936 | 1111 | } |
---|
937 | 1112 | |
---|
| 1113 | +struct ctrl_dump_policy_ctx { |
---|
| 1114 | + struct netlink_policy_dump_state *state; |
---|
| 1115 | + const struct genl_family *rt; |
---|
| 1116 | + unsigned int opidx; |
---|
| 1117 | + u32 op; |
---|
| 1118 | + u16 fam_id; |
---|
| 1119 | + u8 policies:1, |
---|
| 1120 | + single_op:1; |
---|
| 1121 | +}; |
---|
| 1122 | + |
---|
| 1123 | +static const struct nla_policy ctrl_policy_policy[] = { |
---|
| 1124 | + [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, |
---|
| 1125 | + [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, |
---|
| 1126 | + .len = GENL_NAMSIZ - 1 }, |
---|
| 1127 | + [CTRL_ATTR_OP] = { .type = NLA_U32 }, |
---|
| 1128 | +}; |
---|
| 1129 | + |
---|
| 1130 | +static int ctrl_dumppolicy_start(struct netlink_callback *cb) |
---|
| 1131 | +{ |
---|
| 1132 | + const struct genl_dumpit_info *info = genl_dumpit_info(cb); |
---|
| 1133 | + struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; |
---|
| 1134 | + struct nlattr **tb = info->attrs; |
---|
| 1135 | + const struct genl_family *rt; |
---|
| 1136 | + struct genl_ops op; |
---|
| 1137 | + int err, i; |
---|
| 1138 | + |
---|
| 1139 | + BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); |
---|
| 1140 | + |
---|
| 1141 | + if (!tb[CTRL_ATTR_FAMILY_ID] && !tb[CTRL_ATTR_FAMILY_NAME]) |
---|
| 1142 | + return -EINVAL; |
---|
| 1143 | + |
---|
| 1144 | + if (tb[CTRL_ATTR_FAMILY_ID]) { |
---|
| 1145 | + ctx->fam_id = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]); |
---|
| 1146 | + } else { |
---|
| 1147 | + rt = genl_family_find_byname( |
---|
| 1148 | + nla_data(tb[CTRL_ATTR_FAMILY_NAME])); |
---|
| 1149 | + if (!rt) |
---|
| 1150 | + return -ENOENT; |
---|
| 1151 | + ctx->fam_id = rt->id; |
---|
| 1152 | + } |
---|
| 1153 | + |
---|
| 1154 | + rt = genl_family_find_byid(ctx->fam_id); |
---|
| 1155 | + if (!rt) |
---|
| 1156 | + return -ENOENT; |
---|
| 1157 | + |
---|
| 1158 | + ctx->rt = rt; |
---|
| 1159 | + |
---|
| 1160 | + if (tb[CTRL_ATTR_OP]) { |
---|
| 1161 | + ctx->single_op = true; |
---|
| 1162 | + ctx->op = nla_get_u32(tb[CTRL_ATTR_OP]); |
---|
| 1163 | + |
---|
| 1164 | + err = genl_get_cmd(ctx->op, rt, &op); |
---|
| 1165 | + if (err) { |
---|
| 1166 | + NL_SET_BAD_ATTR(cb->extack, tb[CTRL_ATTR_OP]); |
---|
| 1167 | + return err; |
---|
| 1168 | + } |
---|
| 1169 | + |
---|
| 1170 | + if (!op.policy) |
---|
| 1171 | + return -ENODATA; |
---|
| 1172 | + |
---|
| 1173 | + return netlink_policy_dump_add_policy(&ctx->state, op.policy, |
---|
| 1174 | + op.maxattr); |
---|
| 1175 | + } |
---|
| 1176 | + |
---|
| 1177 | + for (i = 0; i < genl_get_cmd_cnt(rt); i++) { |
---|
| 1178 | + genl_get_cmd_by_index(i, rt, &op); |
---|
| 1179 | + |
---|
| 1180 | + if (op.policy) { |
---|
| 1181 | + err = netlink_policy_dump_add_policy(&ctx->state, |
---|
| 1182 | + op.policy, |
---|
| 1183 | + op.maxattr); |
---|
| 1184 | + if (err) |
---|
| 1185 | + goto err_free_state; |
---|
| 1186 | + } |
---|
| 1187 | + } |
---|
| 1188 | + |
---|
| 1189 | + if (!ctx->state) |
---|
| 1190 | + return -ENODATA; |
---|
| 1191 | + return 0; |
---|
| 1192 | + |
---|
| 1193 | +err_free_state: |
---|
| 1194 | + netlink_policy_dump_free(ctx->state); |
---|
| 1195 | + return err; |
---|
| 1196 | +} |
---|
| 1197 | + |
---|
| 1198 | +static void *ctrl_dumppolicy_prep(struct sk_buff *skb, |
---|
| 1199 | + struct netlink_callback *cb) |
---|
| 1200 | +{ |
---|
| 1201 | + struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; |
---|
| 1202 | + void *hdr; |
---|
| 1203 | + |
---|
| 1204 | + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, |
---|
| 1205 | + cb->nlh->nlmsg_seq, &genl_ctrl, |
---|
| 1206 | + NLM_F_MULTI, CTRL_CMD_GETPOLICY); |
---|
| 1207 | + if (!hdr) |
---|
| 1208 | + return NULL; |
---|
| 1209 | + |
---|
| 1210 | + if (nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, ctx->fam_id)) |
---|
| 1211 | + return NULL; |
---|
| 1212 | + |
---|
| 1213 | + return hdr; |
---|
| 1214 | +} |
---|
| 1215 | + |
---|
| 1216 | +static int ctrl_dumppolicy_put_op(struct sk_buff *skb, |
---|
| 1217 | + struct netlink_callback *cb, |
---|
| 1218 | + struct genl_ops *op) |
---|
| 1219 | +{ |
---|
| 1220 | + struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; |
---|
| 1221 | + struct nlattr *nest_pol, *nest_op; |
---|
| 1222 | + void *hdr; |
---|
| 1223 | + int idx; |
---|
| 1224 | + |
---|
| 1225 | + /* skip if we have nothing to show */ |
---|
| 1226 | + if (!op->policy) |
---|
| 1227 | + return 0; |
---|
| 1228 | + if (!op->doit && |
---|
| 1229 | + (!op->dumpit || op->validate & GENL_DONT_VALIDATE_DUMP)) |
---|
| 1230 | + return 0; |
---|
| 1231 | + |
---|
| 1232 | + hdr = ctrl_dumppolicy_prep(skb, cb); |
---|
| 1233 | + if (!hdr) |
---|
| 1234 | + return -ENOBUFS; |
---|
| 1235 | + |
---|
| 1236 | + nest_pol = nla_nest_start(skb, CTRL_ATTR_OP_POLICY); |
---|
| 1237 | + if (!nest_pol) |
---|
| 1238 | + goto err; |
---|
| 1239 | + |
---|
| 1240 | + nest_op = nla_nest_start(skb, op->cmd); |
---|
| 1241 | + if (!nest_op) |
---|
| 1242 | + goto err; |
---|
| 1243 | + |
---|
| 1244 | + /* for now both do/dump are always the same */ |
---|
| 1245 | + idx = netlink_policy_dump_get_policy_idx(ctx->state, |
---|
| 1246 | + op->policy, |
---|
| 1247 | + op->maxattr); |
---|
| 1248 | + |
---|
| 1249 | + if (op->doit && nla_put_u32(skb, CTRL_ATTR_POLICY_DO, idx)) |
---|
| 1250 | + goto err; |
---|
| 1251 | + |
---|
| 1252 | + if (op->dumpit && !(op->validate & GENL_DONT_VALIDATE_DUMP) && |
---|
| 1253 | + nla_put_u32(skb, CTRL_ATTR_POLICY_DUMP, idx)) |
---|
| 1254 | + goto err; |
---|
| 1255 | + |
---|
| 1256 | + nla_nest_end(skb, nest_op); |
---|
| 1257 | + nla_nest_end(skb, nest_pol); |
---|
| 1258 | + genlmsg_end(skb, hdr); |
---|
| 1259 | + |
---|
| 1260 | + return 0; |
---|
| 1261 | +err: |
---|
| 1262 | + genlmsg_cancel(skb, hdr); |
---|
| 1263 | + return -ENOBUFS; |
---|
| 1264 | +} |
---|
| 1265 | + |
---|
| 1266 | +static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) |
---|
| 1267 | +{ |
---|
| 1268 | + struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; |
---|
| 1269 | + void *hdr; |
---|
| 1270 | + |
---|
| 1271 | + if (!ctx->policies) { |
---|
| 1272 | + while (ctx->opidx < genl_get_cmd_cnt(ctx->rt)) { |
---|
| 1273 | + struct genl_ops op; |
---|
| 1274 | + |
---|
| 1275 | + if (ctx->single_op) { |
---|
| 1276 | + int err; |
---|
| 1277 | + |
---|
| 1278 | + err = genl_get_cmd(ctx->op, ctx->rt, &op); |
---|
| 1279 | + if (WARN_ON(err)) |
---|
| 1280 | + return skb->len; |
---|
| 1281 | + |
---|
| 1282 | + /* break out of the loop after this one */ |
---|
| 1283 | + ctx->opidx = genl_get_cmd_cnt(ctx->rt); |
---|
| 1284 | + } else { |
---|
| 1285 | + genl_get_cmd_by_index(ctx->opidx, ctx->rt, &op); |
---|
| 1286 | + } |
---|
| 1287 | + |
---|
| 1288 | + if (ctrl_dumppolicy_put_op(skb, cb, &op)) |
---|
| 1289 | + return skb->len; |
---|
| 1290 | + |
---|
| 1291 | + ctx->opidx++; |
---|
| 1292 | + } |
---|
| 1293 | + |
---|
| 1294 | + /* completed with the per-op policy index list */ |
---|
| 1295 | + ctx->policies = true; |
---|
| 1296 | + } |
---|
| 1297 | + |
---|
| 1298 | + while (netlink_policy_dump_loop(ctx->state)) { |
---|
| 1299 | + struct nlattr *nest; |
---|
| 1300 | + |
---|
| 1301 | + hdr = ctrl_dumppolicy_prep(skb, cb); |
---|
| 1302 | + if (!hdr) |
---|
| 1303 | + goto nla_put_failure; |
---|
| 1304 | + |
---|
| 1305 | + nest = nla_nest_start(skb, CTRL_ATTR_POLICY); |
---|
| 1306 | + if (!nest) |
---|
| 1307 | + goto nla_put_failure; |
---|
| 1308 | + |
---|
| 1309 | + if (netlink_policy_dump_write(skb, ctx->state)) |
---|
| 1310 | + goto nla_put_failure; |
---|
| 1311 | + |
---|
| 1312 | + nla_nest_end(skb, nest); |
---|
| 1313 | + |
---|
| 1314 | + genlmsg_end(skb, hdr); |
---|
| 1315 | + } |
---|
| 1316 | + |
---|
| 1317 | + return skb->len; |
---|
| 1318 | + |
---|
| 1319 | +nla_put_failure: |
---|
| 1320 | + genlmsg_cancel(skb, hdr); |
---|
| 1321 | + return skb->len; |
---|
| 1322 | +} |
---|
| 1323 | + |
---|
| 1324 | +static int ctrl_dumppolicy_done(struct netlink_callback *cb) |
---|
| 1325 | +{ |
---|
| 1326 | + struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; |
---|
| 1327 | + |
---|
| 1328 | + netlink_policy_dump_free(ctx->state); |
---|
| 1329 | + return 0; |
---|
| 1330 | +} |
---|
| 1331 | + |
---|
938 | 1332 | static const struct genl_ops genl_ctrl_ops[] = { |
---|
939 | 1333 | { |
---|
940 | 1334 | .cmd = CTRL_CMD_GETFAMILY, |
---|
| 1335 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
---|
| 1336 | + .policy = ctrl_policy_family, |
---|
| 1337 | + .maxattr = ARRAY_SIZE(ctrl_policy_family) - 1, |
---|
941 | 1338 | .doit = ctrl_getfamily, |
---|
942 | 1339 | .dumpit = ctrl_dumpfamily, |
---|
943 | | - .policy = ctrl_policy, |
---|
| 1340 | + }, |
---|
| 1341 | + { |
---|
| 1342 | + .cmd = CTRL_CMD_GETPOLICY, |
---|
| 1343 | + .policy = ctrl_policy_policy, |
---|
| 1344 | + .maxattr = ARRAY_SIZE(ctrl_policy_policy) - 1, |
---|
| 1345 | + .start = ctrl_dumppolicy_start, |
---|
| 1346 | + .dumpit = ctrl_dumppolicy, |
---|
| 1347 | + .done = ctrl_dumppolicy_done, |
---|
944 | 1348 | }, |
---|
945 | 1349 | }; |
---|
946 | 1350 | |
---|
.. | .. |
---|
957 | 1361 | .id = GENL_ID_CTRL, |
---|
958 | 1362 | .name = "nlctrl", |
---|
959 | 1363 | .version = 0x2, |
---|
960 | | - .maxattr = CTRL_ATTR_MAX, |
---|
961 | 1364 | .netnsok = true, |
---|
962 | 1365 | }; |
---|
963 | | - |
---|
964 | | -static int genl_bind(struct net *net, int group) |
---|
965 | | -{ |
---|
966 | | - struct genl_family *f; |
---|
967 | | - int err = 0; |
---|
968 | | - unsigned int id; |
---|
969 | | - |
---|
970 | | - down_read(&cb_lock); |
---|
971 | | - |
---|
972 | | - idr_for_each_entry(&genl_fam_idr, f, id) { |
---|
973 | | - if (group >= f->mcgrp_offset && |
---|
974 | | - group < f->mcgrp_offset + f->n_mcgrps) { |
---|
975 | | - int fam_grp = group - f->mcgrp_offset; |
---|
976 | | - |
---|
977 | | - if (!f->netnsok && net != &init_net) |
---|
978 | | - err = -ENOENT; |
---|
979 | | - else if (f->mcast_bind) |
---|
980 | | - err = f->mcast_bind(net, fam_grp); |
---|
981 | | - else |
---|
982 | | - err = 0; |
---|
983 | | - break; |
---|
984 | | - } |
---|
985 | | - } |
---|
986 | | - up_read(&cb_lock); |
---|
987 | | - |
---|
988 | | - return err; |
---|
989 | | -} |
---|
990 | | - |
---|
991 | | -static void genl_unbind(struct net *net, int group) |
---|
992 | | -{ |
---|
993 | | - struct genl_family *f; |
---|
994 | | - unsigned int id; |
---|
995 | | - |
---|
996 | | - down_read(&cb_lock); |
---|
997 | | - |
---|
998 | | - idr_for_each_entry(&genl_fam_idr, f, id) { |
---|
999 | | - if (group >= f->mcgrp_offset && |
---|
1000 | | - group < f->mcgrp_offset + f->n_mcgrps) { |
---|
1001 | | - int fam_grp = group - f->mcgrp_offset; |
---|
1002 | | - |
---|
1003 | | - if (f->mcast_unbind) |
---|
1004 | | - f->mcast_unbind(net, fam_grp); |
---|
1005 | | - break; |
---|
1006 | | - } |
---|
1007 | | - } |
---|
1008 | | - up_read(&cb_lock); |
---|
1009 | | -} |
---|
1010 | 1366 | |
---|
1011 | 1367 | static int __net_init genl_pernet_init(struct net *net) |
---|
1012 | 1368 | { |
---|
1013 | 1369 | struct netlink_kernel_cfg cfg = { |
---|
1014 | 1370 | .input = genl_rcv, |
---|
1015 | 1371 | .flags = NL_CFG_F_NONROOT_RECV, |
---|
1016 | | - .bind = genl_bind, |
---|
1017 | | - .unbind = genl_unbind, |
---|
1018 | 1372 | }; |
---|
1019 | 1373 | |
---|
1020 | 1374 | /* we'll bump the group number right afterwards */ |
---|
.. | .. |
---|
1058 | 1412 | panic("GENL: Cannot register controller: %d\n", err); |
---|
1059 | 1413 | } |
---|
1060 | 1414 | |
---|
1061 | | -subsys_initcall(genl_init); |
---|
1062 | | - |
---|
1063 | | -/** |
---|
1064 | | - * genl_family_attrbuf - return family's attrbuf |
---|
1065 | | - * @family: the family |
---|
1066 | | - * |
---|
1067 | | - * Return the family's attrbuf, while validating that it's |
---|
1068 | | - * actually valid to access it. |
---|
1069 | | - * |
---|
1070 | | - * You cannot use this function with a family that has parallel_ops |
---|
1071 | | - * and you can only use it within (pre/post) doit/dumpit callbacks. |
---|
1072 | | - */ |
---|
1073 | | -struct nlattr **genl_family_attrbuf(const struct genl_family *family) |
---|
1074 | | -{ |
---|
1075 | | - if (!WARN_ON(family->parallel_ops)) |
---|
1076 | | - lockdep_assert_held(&genl_mutex); |
---|
1077 | | - |
---|
1078 | | - return family->attrbuf; |
---|
1079 | | -} |
---|
1080 | | -EXPORT_SYMBOL(genl_family_attrbuf); |
---|
| 1415 | +core_initcall(genl_init); |
---|
1081 | 1416 | |
---|
1082 | 1417 | static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group, |
---|
1083 | 1418 | gfp_t flags) |
---|