aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/udp.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/udp.c')
-rw-r--r--net/ipv6/udp.c104
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
62DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6) __read_mostly; 62DEFINE_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 64static 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 */
67static 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;
112gotit:
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
136fail:
137 write_unlock_bh(&udp_hash_lock);
138 return 1;
139} 67}
140 68
141static void udp_v6_hash(struct sock *sk) 69static void udp_v6_hash(struct sock *sk)
@@ -345,6 +273,8 @@ out:
345 273
346static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) 274static 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
860do_confirm: 810do_confirm: