| .. | .. |
|---|
| 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) |
|---|