| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * ip6_flowlabel.c IPv6 flowlabel manager. |
|---|
| 3 | | - * |
|---|
| 4 | | - * This program is free software; you can redistribute it and/or |
|---|
| 5 | | - * modify it under the terms of the GNU General Public License |
|---|
| 6 | | - * as published by the Free Software Foundation; either version |
|---|
| 7 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 8 | 4 | * |
|---|
| 9 | 5 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
|---|
| 10 | 6 | */ |
|---|
| .. | .. |
|---|
| 21 | 17 | #include <linux/slab.h> |
|---|
| 22 | 18 | #include <linux/export.h> |
|---|
| 23 | 19 | #include <linux/pid_namespace.h> |
|---|
| 20 | +#include <linux/jump_label_ratelimit.h> |
|---|
| 24 | 21 | |
|---|
| 25 | 22 | #include <net/net_namespace.h> |
|---|
| 26 | 23 | #include <net/sock.h> |
|---|
| .. | .. |
|---|
| 56 | 53 | /* Big socket sock */ |
|---|
| 57 | 54 | |
|---|
| 58 | 55 | static DEFINE_SPINLOCK(ip6_sk_fl_lock); |
|---|
| 56 | + |
|---|
| 57 | +DEFINE_STATIC_KEY_DEFERRED_FALSE(ipv6_flowlabel_exclusive, HZ); |
|---|
| 58 | +EXPORT_SYMBOL(ipv6_flowlabel_exclusive); |
|---|
| 59 | 59 | |
|---|
| 60 | 60 | #define for_each_fl_rcu(hash, fl) \ |
|---|
| 61 | 61 | for (fl = rcu_dereference_bh(fl_ht[(hash)]); \ |
|---|
| .. | .. |
|---|
| 94 | 94 | return fl; |
|---|
| 95 | 95 | } |
|---|
| 96 | 96 | |
|---|
| 97 | +static bool fl_shared_exclusive(struct ip6_flowlabel *fl) |
|---|
| 98 | +{ |
|---|
| 99 | + return fl->share == IPV6_FL_S_EXCL || |
|---|
| 100 | + fl->share == IPV6_FL_S_PROCESS || |
|---|
| 101 | + fl->share == IPV6_FL_S_USER; |
|---|
| 102 | +} |
|---|
| 103 | + |
|---|
| 97 | 104 | static void fl_free_rcu(struct rcu_head *head) |
|---|
| 98 | 105 | { |
|---|
| 99 | 106 | struct ip6_flowlabel *fl = container_of(head, struct ip6_flowlabel, rcu); |
|---|
| .. | .. |
|---|
| 107 | 114 | |
|---|
| 108 | 115 | static void fl_free(struct ip6_flowlabel *fl) |
|---|
| 109 | 116 | { |
|---|
| 110 | | - if (fl) |
|---|
| 111 | | - call_rcu(&fl->rcu, fl_free_rcu); |
|---|
| 117 | + if (!fl) |
|---|
| 118 | + return; |
|---|
| 119 | + |
|---|
| 120 | + if (fl_shared_exclusive(fl) || fl->opt) |
|---|
| 121 | + static_branch_slow_dec_deferred(&ipv6_flowlabel_exclusive); |
|---|
| 122 | + |
|---|
| 123 | + call_rcu(&fl->rcu, fl_free_rcu); |
|---|
| 112 | 124 | } |
|---|
| 113 | 125 | |
|---|
| 114 | 126 | static void fl_release(struct ip6_flowlabel *fl) |
|---|
| .. | .. |
|---|
| 244 | 256 | |
|---|
| 245 | 257 | /* Socket flowlabel lists */ |
|---|
| 246 | 258 | |
|---|
| 247 | | -struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label) |
|---|
| 259 | +struct ip6_flowlabel *__fl6_sock_lookup(struct sock *sk, __be32 label) |
|---|
| 248 | 260 | { |
|---|
| 249 | 261 | struct ipv6_fl_socklist *sfl; |
|---|
| 250 | 262 | struct ipv6_pinfo *np = inet6_sk(sk); |
|---|
| .. | .. |
|---|
| 264 | 276 | rcu_read_unlock_bh(); |
|---|
| 265 | 277 | return NULL; |
|---|
| 266 | 278 | } |
|---|
| 267 | | -EXPORT_SYMBOL_GPL(fl6_sock_lookup); |
|---|
| 279 | +EXPORT_SYMBOL_GPL(__fl6_sock_lookup); |
|---|
| 268 | 280 | |
|---|
| 269 | 281 | void fl6_free_socklist(struct sock *sk) |
|---|
| 270 | 282 | { |
|---|
| .. | .. |
|---|
| 359 | 371 | |
|---|
| 360 | 372 | static struct ip6_flowlabel * |
|---|
| 361 | 373 | fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq, |
|---|
| 362 | | - char __user *optval, int optlen, int *err_p) |
|---|
| 374 | + sockptr_t optval, int optlen, int *err_p) |
|---|
| 363 | 375 | { |
|---|
| 364 | 376 | struct ip6_flowlabel *fl = NULL; |
|---|
| 365 | 377 | int olen; |
|---|
| .. | .. |
|---|
| 389 | 401 | memset(fl->opt, 0, sizeof(*fl->opt)); |
|---|
| 390 | 402 | fl->opt->tot_len = sizeof(*fl->opt) + olen; |
|---|
| 391 | 403 | err = -EFAULT; |
|---|
| 392 | | - if (copy_from_user(fl->opt+1, optval+CMSG_ALIGN(sizeof(*freq)), olen)) |
|---|
| 404 | + if (copy_from_sockptr_offset(fl->opt + 1, optval, |
|---|
| 405 | + CMSG_ALIGN(sizeof(*freq)), olen)) |
|---|
| 393 | 406 | goto done; |
|---|
| 394 | 407 | |
|---|
| 395 | 408 | msg.msg_controllen = olen; |
|---|
| .. | .. |
|---|
| 437 | 450 | err = -EINVAL; |
|---|
| 438 | 451 | goto done; |
|---|
| 439 | 452 | } |
|---|
| 453 | + if (fl_shared_exclusive(fl) || fl->opt) |
|---|
| 454 | + static_branch_deferred_inc(&ipv6_flowlabel_exclusive); |
|---|
| 440 | 455 | return fl; |
|---|
| 441 | 456 | |
|---|
| 442 | 457 | done: |
|---|
| 443 | | - fl_free(fl); |
|---|
| 458 | + if (fl) { |
|---|
| 459 | + kfree(fl->opt); |
|---|
| 460 | + kfree(fl); |
|---|
| 461 | + } |
|---|
| 444 | 462 | *err_p = err; |
|---|
| 445 | 463 | return NULL; |
|---|
| 446 | 464 | } |
|---|
| .. | .. |
|---|
| 516 | 534 | return -ENOENT; |
|---|
| 517 | 535 | } |
|---|
| 518 | 536 | |
|---|
| 519 | | -int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) |
|---|
| 537 | +#define socklist_dereference(__sflp) \ |
|---|
| 538 | + rcu_dereference_protected(__sflp, lockdep_is_held(&ip6_sk_fl_lock)) |
|---|
| 539 | + |
|---|
| 540 | +static int ipv6_flowlabel_put(struct sock *sk, struct in6_flowlabel_req *freq) |
|---|
| 520 | 541 | { |
|---|
| 521 | | - int uninitialized_var(err); |
|---|
| 522 | | - struct net *net = sock_net(sk); |
|---|
| 523 | 542 | struct ipv6_pinfo *np = inet6_sk(sk); |
|---|
| 524 | | - struct in6_flowlabel_req freq; |
|---|
| 525 | | - struct ipv6_fl_socklist *sfl1 = NULL; |
|---|
| 526 | | - struct ipv6_fl_socklist *sfl; |
|---|
| 527 | 543 | struct ipv6_fl_socklist __rcu **sflp; |
|---|
| 544 | + struct ipv6_fl_socklist *sfl; |
|---|
| 545 | + |
|---|
| 546 | + if (freq->flr_flags & IPV6_FL_F_REFLECT) { |
|---|
| 547 | + if (sk->sk_protocol != IPPROTO_TCP) |
|---|
| 548 | + return -ENOPROTOOPT; |
|---|
| 549 | + if (!np->repflow) |
|---|
| 550 | + return -ESRCH; |
|---|
| 551 | + np->flow_label = 0; |
|---|
| 552 | + np->repflow = 0; |
|---|
| 553 | + return 0; |
|---|
| 554 | + } |
|---|
| 555 | + |
|---|
| 556 | + spin_lock_bh(&ip6_sk_fl_lock); |
|---|
| 557 | + for (sflp = &np->ipv6_fl_list; |
|---|
| 558 | + (sfl = socklist_dereference(*sflp)) != NULL; |
|---|
| 559 | + sflp = &sfl->next) { |
|---|
| 560 | + if (sfl->fl->label == freq->flr_label) |
|---|
| 561 | + goto found; |
|---|
| 562 | + } |
|---|
| 563 | + spin_unlock_bh(&ip6_sk_fl_lock); |
|---|
| 564 | + return -ESRCH; |
|---|
| 565 | +found: |
|---|
| 566 | + if (freq->flr_label == (np->flow_label & IPV6_FLOWLABEL_MASK)) |
|---|
| 567 | + np->flow_label &= ~IPV6_FLOWLABEL_MASK; |
|---|
| 568 | + *sflp = sfl->next; |
|---|
| 569 | + spin_unlock_bh(&ip6_sk_fl_lock); |
|---|
| 570 | + fl_release(sfl->fl); |
|---|
| 571 | + kfree_rcu(sfl, rcu); |
|---|
| 572 | + return 0; |
|---|
| 573 | +} |
|---|
| 574 | + |
|---|
| 575 | +static int ipv6_flowlabel_renew(struct sock *sk, struct in6_flowlabel_req *freq) |
|---|
| 576 | +{ |
|---|
| 577 | + struct ipv6_pinfo *np = inet6_sk(sk); |
|---|
| 578 | + struct net *net = sock_net(sk); |
|---|
| 579 | + struct ipv6_fl_socklist *sfl; |
|---|
| 580 | + int err; |
|---|
| 581 | + |
|---|
| 582 | + rcu_read_lock_bh(); |
|---|
| 583 | + for_each_sk_fl_rcu(np, sfl) { |
|---|
| 584 | + if (sfl->fl->label == freq->flr_label) { |
|---|
| 585 | + err = fl6_renew(sfl->fl, freq->flr_linger, |
|---|
| 586 | + freq->flr_expires); |
|---|
| 587 | + rcu_read_unlock_bh(); |
|---|
| 588 | + return err; |
|---|
| 589 | + } |
|---|
| 590 | + } |
|---|
| 591 | + rcu_read_unlock_bh(); |
|---|
| 592 | + |
|---|
| 593 | + if (freq->flr_share == IPV6_FL_S_NONE && |
|---|
| 594 | + ns_capable(net->user_ns, CAP_NET_ADMIN)) { |
|---|
| 595 | + struct ip6_flowlabel *fl = fl_lookup(net, freq->flr_label); |
|---|
| 596 | + |
|---|
| 597 | + if (fl) { |
|---|
| 598 | + err = fl6_renew(fl, freq->flr_linger, |
|---|
| 599 | + freq->flr_expires); |
|---|
| 600 | + fl_release(fl); |
|---|
| 601 | + return err; |
|---|
| 602 | + } |
|---|
| 603 | + } |
|---|
| 604 | + return -ESRCH; |
|---|
| 605 | +} |
|---|
| 606 | + |
|---|
| 607 | +static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq, |
|---|
| 608 | + sockptr_t optval, int optlen) |
|---|
| 609 | +{ |
|---|
| 610 | + struct ipv6_fl_socklist *sfl, *sfl1 = NULL; |
|---|
| 528 | 611 | struct ip6_flowlabel *fl, *fl1 = NULL; |
|---|
| 612 | + struct ipv6_pinfo *np = inet6_sk(sk); |
|---|
| 613 | + struct net *net = sock_net(sk); |
|---|
| 614 | + int err; |
|---|
| 529 | 615 | |
|---|
| 616 | + if (freq->flr_flags & IPV6_FL_F_REFLECT) { |
|---|
| 617 | + if (net->ipv6.sysctl.flowlabel_consistency) { |
|---|
| 618 | + net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable\n"); |
|---|
| 619 | + return -EPERM; |
|---|
| 620 | + } |
|---|
| 530 | 621 | |
|---|
| 531 | | - if (optlen < sizeof(freq)) |
|---|
| 622 | + if (sk->sk_protocol != IPPROTO_TCP) |
|---|
| 623 | + return -ENOPROTOOPT; |
|---|
| 624 | + np->repflow = 1; |
|---|
| 625 | + return 0; |
|---|
| 626 | + } |
|---|
| 627 | + |
|---|
| 628 | + if (freq->flr_label & ~IPV6_FLOWLABEL_MASK) |
|---|
| 532 | 629 | return -EINVAL; |
|---|
| 630 | + if (net->ipv6.sysctl.flowlabel_state_ranges && |
|---|
| 631 | + (freq->flr_label & IPV6_FLOWLABEL_STATELESS_FLAG)) |
|---|
| 632 | + return -ERANGE; |
|---|
| 533 | 633 | |
|---|
| 534 | | - if (copy_from_user(&freq, optval, sizeof(freq))) |
|---|
| 535 | | - return -EFAULT; |
|---|
| 634 | + fl = fl_create(net, sk, freq, optval, optlen, &err); |
|---|
| 635 | + if (!fl) |
|---|
| 636 | + return err; |
|---|
| 536 | 637 | |
|---|
| 537 | | - switch (freq.flr_action) { |
|---|
| 538 | | - case IPV6_FL_A_PUT: |
|---|
| 539 | | - if (freq.flr_flags & IPV6_FL_F_REFLECT) { |
|---|
| 540 | | - if (sk->sk_protocol != IPPROTO_TCP) |
|---|
| 541 | | - return -ENOPROTOOPT; |
|---|
| 542 | | - if (!np->repflow) |
|---|
| 543 | | - return -ESRCH; |
|---|
| 544 | | - np->flow_label = 0; |
|---|
| 545 | | - np->repflow = 0; |
|---|
| 546 | | - return 0; |
|---|
| 547 | | - } |
|---|
| 548 | | - spin_lock_bh(&ip6_sk_fl_lock); |
|---|
| 549 | | - for (sflp = &np->ipv6_fl_list; |
|---|
| 550 | | - (sfl = rcu_dereference_protected(*sflp, |
|---|
| 551 | | - lockdep_is_held(&ip6_sk_fl_lock))) != NULL; |
|---|
| 552 | | - sflp = &sfl->next) { |
|---|
| 553 | | - if (sfl->fl->label == freq.flr_label) { |
|---|
| 554 | | - if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK)) |
|---|
| 555 | | - np->flow_label &= ~IPV6_FLOWLABEL_MASK; |
|---|
| 556 | | - *sflp = sfl->next; |
|---|
| 557 | | - spin_unlock_bh(&ip6_sk_fl_lock); |
|---|
| 558 | | - fl_release(sfl->fl); |
|---|
| 559 | | - kfree_rcu(sfl, rcu); |
|---|
| 560 | | - return 0; |
|---|
| 561 | | - } |
|---|
| 562 | | - } |
|---|
| 563 | | - spin_unlock_bh(&ip6_sk_fl_lock); |
|---|
| 564 | | - return -ESRCH; |
|---|
| 638 | + sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); |
|---|
| 565 | 639 | |
|---|
| 566 | | - case IPV6_FL_A_RENEW: |
|---|
| 640 | + if (freq->flr_label) { |
|---|
| 641 | + err = -EEXIST; |
|---|
| 567 | 642 | rcu_read_lock_bh(); |
|---|
| 568 | 643 | for_each_sk_fl_rcu(np, sfl) { |
|---|
| 569 | | - if (sfl->fl->label == freq.flr_label) { |
|---|
| 570 | | - err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires); |
|---|
| 571 | | - rcu_read_unlock_bh(); |
|---|
| 572 | | - return err; |
|---|
| 644 | + if (sfl->fl->label == freq->flr_label) { |
|---|
| 645 | + if (freq->flr_flags & IPV6_FL_F_EXCL) { |
|---|
| 646 | + rcu_read_unlock_bh(); |
|---|
| 647 | + goto done; |
|---|
| 648 | + } |
|---|
| 649 | + fl1 = sfl->fl; |
|---|
| 650 | + if (!atomic_inc_not_zero(&fl1->users)) |
|---|
| 651 | + fl1 = NULL; |
|---|
| 652 | + break; |
|---|
| 573 | 653 | } |
|---|
| 574 | 654 | } |
|---|
| 575 | 655 | rcu_read_unlock_bh(); |
|---|
| 576 | 656 | |
|---|
| 577 | | - if (freq.flr_share == IPV6_FL_S_NONE && |
|---|
| 578 | | - ns_capable(net->user_ns, CAP_NET_ADMIN)) { |
|---|
| 579 | | - fl = fl_lookup(net, freq.flr_label); |
|---|
| 580 | | - if (fl) { |
|---|
| 581 | | - err = fl6_renew(fl, freq.flr_linger, freq.flr_expires); |
|---|
| 582 | | - fl_release(fl); |
|---|
| 583 | | - return err; |
|---|
| 584 | | - } |
|---|
| 585 | | - } |
|---|
| 586 | | - return -ESRCH; |
|---|
| 587 | | - |
|---|
| 588 | | - case IPV6_FL_A_GET: |
|---|
| 589 | | - if (freq.flr_flags & IPV6_FL_F_REFLECT) { |
|---|
| 590 | | - struct net *net = sock_net(sk); |
|---|
| 591 | | - if (net->ipv6.sysctl.flowlabel_consistency) { |
|---|
| 592 | | - net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable\n"); |
|---|
| 593 | | - return -EPERM; |
|---|
| 594 | | - } |
|---|
| 595 | | - |
|---|
| 596 | | - if (sk->sk_protocol != IPPROTO_TCP) |
|---|
| 597 | | - return -ENOPROTOOPT; |
|---|
| 598 | | - |
|---|
| 599 | | - np->repflow = 1; |
|---|
| 600 | | - return 0; |
|---|
| 601 | | - } |
|---|
| 602 | | - |
|---|
| 603 | | - if (freq.flr_label & ~IPV6_FLOWLABEL_MASK) |
|---|
| 604 | | - return -EINVAL; |
|---|
| 605 | | - |
|---|
| 606 | | - if (net->ipv6.sysctl.flowlabel_state_ranges && |
|---|
| 607 | | - (freq.flr_label & IPV6_FLOWLABEL_STATELESS_FLAG)) |
|---|
| 608 | | - return -ERANGE; |
|---|
| 609 | | - |
|---|
| 610 | | - fl = fl_create(net, sk, &freq, optval, optlen, &err); |
|---|
| 611 | | - if (!fl) |
|---|
| 612 | | - return err; |
|---|
| 613 | | - sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); |
|---|
| 614 | | - |
|---|
| 615 | | - if (freq.flr_label) { |
|---|
| 616 | | - err = -EEXIST; |
|---|
| 617 | | - rcu_read_lock_bh(); |
|---|
| 618 | | - for_each_sk_fl_rcu(np, sfl) { |
|---|
| 619 | | - if (sfl->fl->label == freq.flr_label) { |
|---|
| 620 | | - if (freq.flr_flags&IPV6_FL_F_EXCL) { |
|---|
| 621 | | - rcu_read_unlock_bh(); |
|---|
| 622 | | - goto done; |
|---|
| 623 | | - } |
|---|
| 624 | | - fl1 = sfl->fl; |
|---|
| 625 | | - if (!atomic_inc_not_zero(&fl1->users)) |
|---|
| 626 | | - fl1 = NULL; |
|---|
| 627 | | - break; |
|---|
| 628 | | - } |
|---|
| 629 | | - } |
|---|
| 630 | | - rcu_read_unlock_bh(); |
|---|
| 631 | | - |
|---|
| 632 | | - if (!fl1) |
|---|
| 633 | | - fl1 = fl_lookup(net, freq.flr_label); |
|---|
| 634 | | - if (fl1) { |
|---|
| 657 | + if (!fl1) |
|---|
| 658 | + fl1 = fl_lookup(net, freq->flr_label); |
|---|
| 659 | + if (fl1) { |
|---|
| 635 | 660 | recheck: |
|---|
| 636 | | - err = -EEXIST; |
|---|
| 637 | | - if (freq.flr_flags&IPV6_FL_F_EXCL) |
|---|
| 638 | | - goto release; |
|---|
| 639 | | - err = -EPERM; |
|---|
| 640 | | - if (fl1->share == IPV6_FL_S_EXCL || |
|---|
| 641 | | - fl1->share != fl->share || |
|---|
| 642 | | - ((fl1->share == IPV6_FL_S_PROCESS) && |
|---|
| 643 | | - (fl1->owner.pid != fl->owner.pid)) || |
|---|
| 644 | | - ((fl1->share == IPV6_FL_S_USER) && |
|---|
| 645 | | - !uid_eq(fl1->owner.uid, fl->owner.uid))) |
|---|
| 646 | | - goto release; |
|---|
| 661 | + err = -EEXIST; |
|---|
| 662 | + if (freq->flr_flags&IPV6_FL_F_EXCL) |
|---|
| 663 | + goto release; |
|---|
| 664 | + err = -EPERM; |
|---|
| 665 | + if (fl1->share == IPV6_FL_S_EXCL || |
|---|
| 666 | + fl1->share != fl->share || |
|---|
| 667 | + ((fl1->share == IPV6_FL_S_PROCESS) && |
|---|
| 668 | + (fl1->owner.pid != fl->owner.pid)) || |
|---|
| 669 | + ((fl1->share == IPV6_FL_S_USER) && |
|---|
| 670 | + !uid_eq(fl1->owner.uid, fl->owner.uid))) |
|---|
| 671 | + goto release; |
|---|
| 647 | 672 | |
|---|
| 648 | | - err = -ENOMEM; |
|---|
| 649 | | - if (!sfl1) |
|---|
| 650 | | - goto release; |
|---|
| 651 | | - if (fl->linger > fl1->linger) |
|---|
| 652 | | - fl1->linger = fl->linger; |
|---|
| 653 | | - if ((long)(fl->expires - fl1->expires) > 0) |
|---|
| 654 | | - fl1->expires = fl->expires; |
|---|
| 655 | | - fl_link(np, sfl1, fl1); |
|---|
| 656 | | - fl_free(fl); |
|---|
| 657 | | - return 0; |
|---|
| 673 | + err = -ENOMEM; |
|---|
| 674 | + if (!sfl1) |
|---|
| 675 | + goto release; |
|---|
| 676 | + if (fl->linger > fl1->linger) |
|---|
| 677 | + fl1->linger = fl->linger; |
|---|
| 678 | + if ((long)(fl->expires - fl1->expires) > 0) |
|---|
| 679 | + fl1->expires = fl->expires; |
|---|
| 680 | + fl_link(np, sfl1, fl1); |
|---|
| 681 | + fl_free(fl); |
|---|
| 682 | + return 0; |
|---|
| 658 | 683 | |
|---|
| 659 | 684 | release: |
|---|
| 660 | | - fl_release(fl1); |
|---|
| 661 | | - goto done; |
|---|
| 662 | | - } |
|---|
| 685 | + fl_release(fl1); |
|---|
| 686 | + goto done; |
|---|
| 663 | 687 | } |
|---|
| 664 | | - err = -ENOENT; |
|---|
| 665 | | - if (!(freq.flr_flags&IPV6_FL_F_CREATE)) |
|---|
| 666 | | - goto done; |
|---|
| 688 | + } |
|---|
| 689 | + err = -ENOENT; |
|---|
| 690 | + if (!(freq->flr_flags & IPV6_FL_F_CREATE)) |
|---|
| 691 | + goto done; |
|---|
| 667 | 692 | |
|---|
| 668 | | - err = -ENOMEM; |
|---|
| 669 | | - if (!sfl1) |
|---|
| 670 | | - goto done; |
|---|
| 693 | + err = -ENOMEM; |
|---|
| 694 | + if (!sfl1) |
|---|
| 695 | + goto done; |
|---|
| 671 | 696 | |
|---|
| 672 | | - err = mem_check(sk); |
|---|
| 673 | | - if (err != 0) |
|---|
| 674 | | - goto done; |
|---|
| 697 | + err = mem_check(sk); |
|---|
| 698 | + if (err != 0) |
|---|
| 699 | + goto done; |
|---|
| 675 | 700 | |
|---|
| 676 | | - fl1 = fl_intern(net, fl, freq.flr_label); |
|---|
| 677 | | - if (fl1) |
|---|
| 678 | | - goto recheck; |
|---|
| 701 | + fl1 = fl_intern(net, fl, freq->flr_label); |
|---|
| 702 | + if (fl1) |
|---|
| 703 | + goto recheck; |
|---|
| 679 | 704 | |
|---|
| 680 | | - if (!freq.flr_label) { |
|---|
| 681 | | - if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label, |
|---|
| 682 | | - &fl->label, sizeof(fl->label))) { |
|---|
| 683 | | - /* Intentionally ignore fault. */ |
|---|
| 684 | | - } |
|---|
| 705 | + if (!freq->flr_label) { |
|---|
| 706 | + size_t offset = offsetof(struct in6_flowlabel_req, flr_label); |
|---|
| 707 | + |
|---|
| 708 | + if (copy_to_sockptr_offset(optval, offset, &fl->label, |
|---|
| 709 | + sizeof(fl->label))) { |
|---|
| 710 | + /* Intentionally ignore fault. */ |
|---|
| 685 | 711 | } |
|---|
| 686 | | - |
|---|
| 687 | | - fl_link(np, sfl1, fl); |
|---|
| 688 | | - return 0; |
|---|
| 689 | | - |
|---|
| 690 | | - default: |
|---|
| 691 | | - return -EINVAL; |
|---|
| 692 | 712 | } |
|---|
| 693 | 713 | |
|---|
| 714 | + fl_link(np, sfl1, fl); |
|---|
| 715 | + return 0; |
|---|
| 694 | 716 | done: |
|---|
| 695 | 717 | fl_free(fl); |
|---|
| 696 | 718 | kfree(sfl1); |
|---|
| 697 | 719 | return err; |
|---|
| 720 | +} |
|---|
| 721 | + |
|---|
| 722 | +int ipv6_flowlabel_opt(struct sock *sk, sockptr_t optval, int optlen) |
|---|
| 723 | +{ |
|---|
| 724 | + struct in6_flowlabel_req freq; |
|---|
| 725 | + |
|---|
| 726 | + if (optlen < sizeof(freq)) |
|---|
| 727 | + return -EINVAL; |
|---|
| 728 | + if (copy_from_sockptr(&freq, optval, sizeof(freq))) |
|---|
| 729 | + return -EFAULT; |
|---|
| 730 | + |
|---|
| 731 | + switch (freq.flr_action) { |
|---|
| 732 | + case IPV6_FL_A_PUT: |
|---|
| 733 | + return ipv6_flowlabel_put(sk, &freq); |
|---|
| 734 | + case IPV6_FL_A_RENEW: |
|---|
| 735 | + return ipv6_flowlabel_renew(sk, &freq); |
|---|
| 736 | + case IPV6_FL_A_GET: |
|---|
| 737 | + return ipv6_flowlabel_get(sk, &freq, optval, optlen); |
|---|
| 738 | + default: |
|---|
| 739 | + return -EINVAL; |
|---|
| 740 | + } |
|---|
| 698 | 741 | } |
|---|
| 699 | 742 | |
|---|
| 700 | 743 | #ifdef CONFIG_PROC_FS |
|---|
| .. | .. |
|---|
| 762 | 805 | { |
|---|
| 763 | 806 | struct ip6fl_iter_state *state = ip6fl_seq_private(seq); |
|---|
| 764 | 807 | |
|---|
| 765 | | - state->pid_ns = proc_pid_ns(file_inode(seq->file)); |
|---|
| 808 | + state->pid_ns = proc_pid_ns(file_inode(seq->file)->i_sb); |
|---|
| 766 | 809 | |
|---|
| 767 | 810 | rcu_read_lock_bh(); |
|---|
| 768 | 811 | return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; |
|---|
| .. | .. |
|---|
| 858 | 901 | |
|---|
| 859 | 902 | void ip6_flowlabel_cleanup(void) |
|---|
| 860 | 903 | { |
|---|
| 904 | + static_key_deferred_flush(&ipv6_flowlabel_exclusive); |
|---|
| 861 | 905 | del_timer(&ip6_fl_gc_timer); |
|---|
| 862 | 906 | unregister_pernet_subsys(&ip6_flowlabel_net_ops); |
|---|
| 863 | 907 | } |
|---|