diff options
Diffstat (limited to 'net/ipv6/udp.c')
-rw-r--r-- | net/ipv6/udp.c | 131 |
1 files changed, 63 insertions, 68 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 7092ff78fd84..4836af8f582d 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -79,7 +79,6 @@ static unsigned int udp6_ehashfn(struct net *net, | |||
79 | int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) | 79 | int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) |
80 | { | 80 | { |
81 | const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2); | 81 | const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2); |
82 | int sk_ipv6only = ipv6_only_sock(sk); | ||
83 | int sk2_ipv6only = inet_v6_ipv6only(sk2); | 82 | int sk2_ipv6only = inet_v6_ipv6only(sk2); |
84 | int addr_type = ipv6_addr_type(&sk->sk_v6_rcv_saddr); | 83 | int addr_type = ipv6_addr_type(&sk->sk_v6_rcv_saddr); |
85 | int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED; | 84 | int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED; |
@@ -95,7 +94,7 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) | |||
95 | return 1; | 94 | return 1; |
96 | 95 | ||
97 | if (addr_type == IPV6_ADDR_ANY && | 96 | if (addr_type == IPV6_ADDR_ANY && |
98 | !(sk_ipv6only && addr_type2 == IPV6_ADDR_MAPPED)) | 97 | !(ipv6_only_sock(sk) && addr_type2 == IPV6_ADDR_MAPPED)) |
99 | return 1; | 98 | return 1; |
100 | 99 | ||
101 | if (sk2_rcv_saddr6 && | 100 | if (sk2_rcv_saddr6 && |
@@ -473,7 +472,7 @@ try_again: | |||
473 | sin6->sin6_addr = ipv6_hdr(skb)->saddr; | 472 | sin6->sin6_addr = ipv6_hdr(skb)->saddr; |
474 | sin6->sin6_scope_id = | 473 | sin6->sin6_scope_id = |
475 | ipv6_iface_scope_id(&sin6->sin6_addr, | 474 | ipv6_iface_scope_id(&sin6->sin6_addr, |
476 | IP6CB(skb)->iif); | 475 | inet6_iif(skb)); |
477 | } | 476 | } |
478 | *addr_len = sizeof(*sin6); | 477 | *addr_len = sizeof(*sin6); |
479 | } | 478 | } |
@@ -534,11 +533,15 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
534 | struct udphdr *uh = (struct udphdr*)(skb->data+offset); | 533 | struct udphdr *uh = (struct udphdr*)(skb->data+offset); |
535 | struct sock *sk; | 534 | struct sock *sk; |
536 | int err; | 535 | int err; |
536 | struct net *net = dev_net(skb->dev); | ||
537 | 537 | ||
538 | sk = __udp6_lib_lookup(dev_net(skb->dev), daddr, uh->dest, | 538 | sk = __udp6_lib_lookup(net, daddr, uh->dest, |
539 | saddr, uh->source, inet6_iif(skb), udptable); | 539 | saddr, uh->source, inet6_iif(skb), udptable); |
540 | if (sk == NULL) | 540 | if (sk == NULL) { |
541 | ICMP6_INC_STATS_BH(net, __in6_dev_get(skb->dev), | ||
542 | ICMP6_MIB_INERRORS); | ||
541 | return; | 543 | return; |
544 | } | ||
542 | 545 | ||
543 | if (type == ICMPV6_PKT_TOOBIG) { | 546 | if (type == ICMPV6_PKT_TOOBIG) { |
544 | if (!ip6_sk_accept_pmtu(sk)) | 547 | if (!ip6_sk_accept_pmtu(sk)) |
@@ -674,7 +677,7 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
674 | goto csum_error; | 677 | goto csum_error; |
675 | } | 678 | } |
676 | 679 | ||
677 | if (sk_rcvqueues_full(sk, skb, sk->sk_rcvbuf)) { | 680 | if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) { |
678 | UDP6_INC_STATS_BH(sock_net(sk), | 681 | UDP6_INC_STATS_BH(sock_net(sk), |
679 | UDP_MIB_RCVBUFERRORS, is_udplite); | 682 | UDP_MIB_RCVBUFERRORS, is_udplite); |
680 | goto drop; | 683 | goto drop; |
@@ -703,43 +706,26 @@ drop: | |||
703 | return -1; | 706 | return -1; |
704 | } | 707 | } |
705 | 708 | ||
706 | static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk, | 709 | static bool __udp_v6_is_mcast_sock(struct net *net, struct sock *sk, |
707 | __be16 loc_port, const struct in6_addr *loc_addr, | 710 | __be16 loc_port, const struct in6_addr *loc_addr, |
708 | __be16 rmt_port, const struct in6_addr *rmt_addr, | 711 | __be16 rmt_port, const struct in6_addr *rmt_addr, |
709 | int dif) | 712 | int dif, unsigned short hnum) |
710 | { | 713 | { |
711 | struct hlist_nulls_node *node; | 714 | struct inet_sock *inet = inet_sk(sk); |
712 | unsigned short num = ntohs(loc_port); | ||
713 | |||
714 | sk_nulls_for_each_from(sk, node) { | ||
715 | struct inet_sock *inet = inet_sk(sk); | ||
716 | |||
717 | if (!net_eq(sock_net(sk), net)) | ||
718 | continue; | ||
719 | |||
720 | if (udp_sk(sk)->udp_port_hash == num && | ||
721 | sk->sk_family == PF_INET6) { | ||
722 | if (inet->inet_dport) { | ||
723 | if (inet->inet_dport != rmt_port) | ||
724 | continue; | ||
725 | } | ||
726 | if (!ipv6_addr_any(&sk->sk_v6_daddr) && | ||
727 | !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) | ||
728 | continue; | ||
729 | |||
730 | if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) | ||
731 | continue; | ||
732 | 715 | ||
733 | if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { | 716 | if (!net_eq(sock_net(sk), net)) |
734 | if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr)) | 717 | return false; |
735 | continue; | 718 | |
736 | } | 719 | if (udp_sk(sk)->udp_port_hash != hnum || |
737 | if (!inet6_mc_check(sk, loc_addr, rmt_addr)) | 720 | sk->sk_family != PF_INET6 || |
738 | continue; | 721 | (inet->inet_dport && inet->inet_dport != rmt_port) || |
739 | return sk; | 722 | (!ipv6_addr_any(&sk->sk_v6_daddr) && |
740 | } | 723 | !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) || |
741 | } | 724 | (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)) |
742 | return NULL; | 725 | return false; |
726 | if (!inet6_mc_check(sk, loc_addr, rmt_addr)) | ||
727 | return false; | ||
728 | return true; | ||
743 | } | 729 | } |
744 | 730 | ||
745 | static void flush_stack(struct sock **stack, unsigned int count, | 731 | static void flush_stack(struct sock **stack, unsigned int count, |
@@ -763,6 +749,7 @@ static void flush_stack(struct sock **stack, unsigned int count, | |||
763 | 749 | ||
764 | if (skb1 && udpv6_queue_rcv_skb(sk, skb1) <= 0) | 750 | if (skb1 && udpv6_queue_rcv_skb(sk, skb1) <= 0) |
765 | skb1 = NULL; | 751 | skb1 = NULL; |
752 | sock_put(sk); | ||
766 | } | 753 | } |
767 | if (unlikely(skb1)) | 754 | if (unlikely(skb1)) |
768 | kfree_skb(skb1); | 755 | kfree_skb(skb1); |
@@ -788,43 +775,51 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, | |||
788 | { | 775 | { |
789 | struct sock *sk, *stack[256 / sizeof(struct sock *)]; | 776 | struct sock *sk, *stack[256 / sizeof(struct sock *)]; |
790 | const struct udphdr *uh = udp_hdr(skb); | 777 | const struct udphdr *uh = udp_hdr(skb); |
791 | struct udp_hslot *hslot = udp_hashslot(udptable, net, ntohs(uh->dest)); | 778 | struct hlist_nulls_node *node; |
792 | int dif; | 779 | unsigned short hnum = ntohs(uh->dest); |
793 | unsigned int i, count = 0; | 780 | struct udp_hslot *hslot = udp_hashslot(udptable, net, hnum); |
781 | int dif = inet6_iif(skb); | ||
782 | unsigned int count = 0, offset = offsetof(typeof(*sk), sk_nulls_node); | ||
783 | unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10); | ||
784 | |||
785 | if (use_hash2) { | ||
786 | hash2_any = udp6_portaddr_hash(net, &in6addr_any, hnum) & | ||
787 | udp_table.mask; | ||
788 | hash2 = udp6_portaddr_hash(net, daddr, hnum) & udp_table.mask; | ||
789 | start_lookup: | ||
790 | hslot = &udp_table.hash2[hash2]; | ||
791 | offset = offsetof(typeof(*sk), __sk_common.skc_portaddr_node); | ||
792 | } | ||
794 | 793 | ||
795 | spin_lock(&hslot->lock); | 794 | spin_lock(&hslot->lock); |
796 | sk = sk_nulls_head(&hslot->head); | 795 | sk_nulls_for_each_entry_offset(sk, node, &hslot->head, offset) { |
797 | dif = inet6_iif(skb); | 796 | if (__udp_v6_is_mcast_sock(net, sk, |
798 | sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif); | 797 | uh->dest, daddr, |
799 | while (sk) { | 798 | uh->source, saddr, |
800 | /* If zero checksum and no_check is not on for | 799 | dif, hnum) && |
801 | * the socket then skip it. | 800 | /* If zero checksum and no_check is not on for |
802 | */ | 801 | * the socket then skip it. |
803 | if (uh->check || udp_sk(sk)->no_check6_rx) | 802 | */ |
803 | (uh->check || udp_sk(sk)->no_check6_rx)) { | ||
804 | if (unlikely(count == ARRAY_SIZE(stack))) { | ||
805 | flush_stack(stack, count, skb, ~0); | ||
806 | count = 0; | ||
807 | } | ||
804 | stack[count++] = sk; | 808 | stack[count++] = sk; |
805 | 809 | sock_hold(sk); | |
806 | sk = udp_v6_mcast_next(net, sk_nulls_next(sk), uh->dest, daddr, | ||
807 | uh->source, saddr, dif); | ||
808 | if (unlikely(count == ARRAY_SIZE(stack))) { | ||
809 | if (!sk) | ||
810 | break; | ||
811 | flush_stack(stack, count, skb, ~0); | ||
812 | count = 0; | ||
813 | } | 810 | } |
814 | } | 811 | } |
815 | /* | ||
816 | * before releasing the lock, we must take reference on sockets | ||
817 | */ | ||
818 | for (i = 0; i < count; i++) | ||
819 | sock_hold(stack[i]); | ||
820 | 812 | ||
821 | spin_unlock(&hslot->lock); | 813 | spin_unlock(&hslot->lock); |
822 | 814 | ||
815 | /* Also lookup *:port if we are using hash2 and haven't done so yet. */ | ||
816 | if (use_hash2 && hash2 != hash2_any) { | ||
817 | hash2 = hash2_any; | ||
818 | goto start_lookup; | ||
819 | } | ||
820 | |||
823 | if (count) { | 821 | if (count) { |
824 | flush_stack(stack, count, skb, count - 1); | 822 | flush_stack(stack, count, skb, count - 1); |
825 | |||
826 | for (i = 0; i < count; i++) | ||
827 | sock_put(stack[i]); | ||
828 | } else { | 823 | } else { |
829 | kfree_skb(skb); | 824 | kfree_skb(skb); |
830 | } | 825 | } |