diff options
Diffstat (limited to 'net/ipv6/udp.c')
-rw-r--r-- | net/ipv6/udp.c | 104 |
1 files changed, 27 insertions, 77 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 3d54f246411e..9662561701d1 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -61,81 +61,9 @@ | |||
61 | 61 | ||
62 | DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6) __read_mostly; | 62 | DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6) __read_mostly; |
63 | 63 | ||
64 | /* Grrr, addr_type already calculated by caller, but I don't want | 64 | static inline int udp_v6_get_port(struct sock *sk, unsigned short snum) |
65 | * to add some silly "cookie" argument to this method just for that. | ||
66 | */ | ||
67 | static int udp_v6_get_port(struct sock *sk, unsigned short snum) | ||
68 | { | 65 | { |
69 | struct sock *sk2; | 66 | return udp_get_port(sk, snum, ipv6_rcv_saddr_equal); |
70 | struct hlist_node *node; | ||
71 | |||
72 | write_lock_bh(&udp_hash_lock); | ||
73 | if (snum == 0) { | ||
74 | int best_size_so_far, best, result, i; | ||
75 | |||
76 | if (udp_port_rover > sysctl_local_port_range[1] || | ||
77 | udp_port_rover < sysctl_local_port_range[0]) | ||
78 | udp_port_rover = sysctl_local_port_range[0]; | ||
79 | best_size_so_far = 32767; | ||
80 | best = result = udp_port_rover; | ||
81 | for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { | ||
82 | int size; | ||
83 | struct hlist_head *list; | ||
84 | |||
85 | list = &udp_hash[result & (UDP_HTABLE_SIZE - 1)]; | ||
86 | if (hlist_empty(list)) { | ||
87 | if (result > sysctl_local_port_range[1]) | ||
88 | result = sysctl_local_port_range[0] + | ||
89 | ((result - sysctl_local_port_range[0]) & | ||
90 | (UDP_HTABLE_SIZE - 1)); | ||
91 | goto gotit; | ||
92 | } | ||
93 | size = 0; | ||
94 | sk_for_each(sk2, node, list) | ||
95 | if (++size >= best_size_so_far) | ||
96 | goto next; | ||
97 | best_size_so_far = size; | ||
98 | best = result; | ||
99 | next:; | ||
100 | } | ||
101 | result = best; | ||
102 | for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) { | ||
103 | if (result > sysctl_local_port_range[1]) | ||
104 | result = sysctl_local_port_range[0] | ||
105 | + ((result - sysctl_local_port_range[0]) & | ||
106 | (UDP_HTABLE_SIZE - 1)); | ||
107 | if (!udp_lport_inuse(result)) | ||
108 | break; | ||
109 | } | ||
110 | if (i >= (1 << 16) / UDP_HTABLE_SIZE) | ||
111 | goto fail; | ||
112 | gotit: | ||
113 | udp_port_rover = snum = result; | ||
114 | } else { | ||
115 | sk_for_each(sk2, node, | ||
116 | &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]) { | ||
117 | if (inet_sk(sk2)->num == snum && | ||
118 | sk2 != sk && | ||
119 | (!sk2->sk_bound_dev_if || | ||
120 | !sk->sk_bound_dev_if || | ||
121 | sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && | ||
122 | (!sk2->sk_reuse || !sk->sk_reuse) && | ||
123 | ipv6_rcv_saddr_equal(sk, sk2)) | ||
124 | goto fail; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | inet_sk(sk)->num = snum; | ||
129 | if (sk_unhashed(sk)) { | ||
130 | sk_add_node(sk, &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]); | ||
131 | sock_prot_inc_use(sk->sk_prot); | ||
132 | } | ||
133 | write_unlock_bh(&udp_hash_lock); | ||
134 | return 0; | ||
135 | |||
136 | fail: | ||
137 | write_unlock_bh(&udp_hash_lock); | ||
138 | return 1; | ||
139 | } | 67 | } |
140 | 68 | ||
141 | static void udp_v6_hash(struct sock *sk) | 69 | static void udp_v6_hash(struct sock *sk) |
@@ -345,6 +273,8 @@ out: | |||
345 | 273 | ||
346 | static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) | 274 | static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) |
347 | { | 275 | { |
276 | int rc; | ||
277 | |||
348 | if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) { | 278 | if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) { |
349 | kfree_skb(skb); | 279 | kfree_skb(skb); |
350 | return -1; | 280 | return -1; |
@@ -356,7 +286,10 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) | |||
356 | return 0; | 286 | return 0; |
357 | } | 287 | } |
358 | 288 | ||
359 | if (sock_queue_rcv_skb(sk,skb)<0) { | 289 | if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) { |
290 | /* Note that an ENOMEM error is charged twice */ | ||
291 | if (rc == -ENOMEM) | ||
292 | UDP6_INC_STATS_BH(UDP_MIB_RCVBUFERRORS); | ||
360 | UDP6_INC_STATS_BH(UDP_MIB_INERRORS); | 293 | UDP6_INC_STATS_BH(UDP_MIB_INERRORS); |
361 | kfree_skb(skb); | 294 | kfree_skb(skb); |
362 | return 0; | 295 | return 0; |
@@ -475,7 +408,7 @@ static int udpv6_rcv(struct sk_buff **pskb) | |||
475 | uh = skb->h.uh; | 408 | uh = skb->h.uh; |
476 | } | 409 | } |
477 | 410 | ||
478 | if (skb->ip_summed == CHECKSUM_HW && | 411 | if (skb->ip_summed == CHECKSUM_COMPLETE && |
479 | !csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) | 412 | !csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) |
480 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 413 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
481 | 414 | ||
@@ -782,6 +715,8 @@ do_udp_sendmsg: | |||
782 | connected = 0; | 715 | connected = 0; |
783 | } | 716 | } |
784 | 717 | ||
718 | security_sk_classify_flow(sk, fl); | ||
719 | |||
785 | err = ip6_sk_dst_lookup(sk, &dst, fl); | 720 | err = ip6_sk_dst_lookup(sk, &dst, fl); |
786 | if (err) | 721 | if (err) |
787 | goto out; | 722 | goto out; |
@@ -840,7 +775,12 @@ do_append_data: | |||
840 | if (connected) { | 775 | if (connected) { |
841 | ip6_dst_store(sk, dst, | 776 | ip6_dst_store(sk, dst, |
842 | ipv6_addr_equal(&fl->fl6_dst, &np->daddr) ? | 777 | ipv6_addr_equal(&fl->fl6_dst, &np->daddr) ? |
843 | &np->daddr : NULL); | 778 | &np->daddr : NULL, |
779 | #ifdef CONFIG_IPV6_SUBTREES | ||
780 | ipv6_addr_equal(&fl->fl6_src, &np->saddr) ? | ||
781 | &np->saddr : | ||
782 | #endif | ||
783 | NULL); | ||
844 | } else { | 784 | } else { |
845 | dst_release(dst); | 785 | dst_release(dst); |
846 | } | 786 | } |
@@ -855,6 +795,16 @@ out: | |||
855 | UDP6_INC_STATS_USER(UDP_MIB_OUTDATAGRAMS); | 795 | UDP6_INC_STATS_USER(UDP_MIB_OUTDATAGRAMS); |
856 | return len; | 796 | return len; |
857 | } | 797 | } |
798 | /* | ||
799 | * ENOBUFS = no kernel mem, SOCK_NOSPACE = no sndbuf space. Reporting | ||
800 | * ENOBUFS might not be good (it's not tunable per se), but otherwise | ||
801 | * we don't have a good statistic (IpOutDiscards but it can be too many | ||
802 | * things). We could add another new stat but at least for now that | ||
803 | * seems like overkill. | ||
804 | */ | ||
805 | if (err == -ENOBUFS || test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) { | ||
806 | UDP6_INC_STATS_USER(UDP_MIB_SNDBUFERRORS); | ||
807 | } | ||
858 | return err; | 808 | return err; |
859 | 809 | ||
860 | do_confirm: | 810 | do_confirm: |