aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/udp.h18
-rw-r--r--net/ipv4/udp.c96
-rw-r--r--net/ipv6/udp.c76
3 files changed, 64 insertions, 126 deletions
diff --git a/include/net/udp.h b/include/net/udp.h
index 766fba1369ce..c490a0f662ac 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -30,25 +30,9 @@
30 30
31#define UDP_HTABLE_SIZE 128 31#define UDP_HTABLE_SIZE 128
32 32
33/* udp.c: This needs to be shared by v4 and v6 because the lookup
34 * and hashing code needs to work with different AF's yet
35 * the port space is shared.
36 */
37extern struct hlist_head udp_hash[UDP_HTABLE_SIZE]; 33extern struct hlist_head udp_hash[UDP_HTABLE_SIZE];
38extern rwlock_t udp_hash_lock; 34extern rwlock_t udp_hash_lock;
39 35
40extern int udp_port_rover;
41
42static inline int udp_lport_inuse(u16 num)
43{
44 struct sock *sk;
45 struct hlist_node *node;
46
47 sk_for_each(sk, node, &udp_hash[num & (UDP_HTABLE_SIZE - 1)])
48 if (inet_sk(sk)->num == num)
49 return 1;
50 return 0;
51}
52 36
53/* Note: this must match 'valbool' in sock_setsockopt */ 37/* Note: this must match 'valbool' in sock_setsockopt */
54#define UDP_CSUM_NOXMIT 1 38#define UDP_CSUM_NOXMIT 1
@@ -63,6 +47,8 @@ extern struct proto udp_prot;
63 47
64struct sk_buff; 48struct sk_buff;
65 49
50extern int udp_get_port(struct sock *sk, unsigned short snum,
51 int (*saddr_cmp)(struct sock *, struct sock *));
66extern void udp_err(struct sk_buff *, u32); 52extern void udp_err(struct sk_buff *, u32);
67 53
68extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, 54extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk,
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 514c1e9ae810..7552b50bcd84 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -118,14 +118,34 @@ DEFINE_SNMP_STAT(struct udp_mib, udp_statistics) __read_mostly;
118struct hlist_head udp_hash[UDP_HTABLE_SIZE]; 118struct hlist_head udp_hash[UDP_HTABLE_SIZE];
119DEFINE_RWLOCK(udp_hash_lock); 119DEFINE_RWLOCK(udp_hash_lock);
120 120
121/* Shared by v4/v6 udp. */ 121/* Shared by v4/v6 udp_get_port */
122int udp_port_rover; 122int udp_port_rover;
123 123
124static int udp_v4_get_port(struct sock *sk, unsigned short snum) 124static inline int udp_lport_inuse(u16 num)
125{ 125{
126 struct sock *sk;
126 struct hlist_node *node; 127 struct hlist_node *node;
128
129 sk_for_each(sk, node, &udp_hash[num & (UDP_HTABLE_SIZE - 1)])
130 if (inet_sk(sk)->num == num)
131 return 1;
132 return 0;
133}
134
135/**
136 * udp_get_port - common port lookup for IPv4 and IPv6
137 *
138 * @sk: socket struct in question
139 * @snum: port number to look up
140 * @saddr_comp: AF-dependent comparison of bound local IP addresses
141 */
142int udp_get_port(struct sock *sk, unsigned short snum,
143 int (*saddr_cmp)(struct sock *sk1, struct sock *sk2))
144{
145 struct hlist_node *node;
146 struct hlist_head *head;
127 struct sock *sk2; 147 struct sock *sk2;
128 struct inet_sock *inet = inet_sk(sk); 148 int error = 1;
129 149
130 write_lock_bh(&udp_hash_lock); 150 write_lock_bh(&udp_hash_lock);
131 if (snum == 0) { 151 if (snum == 0) {
@@ -137,11 +157,10 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum)
137 best_size_so_far = 32767; 157 best_size_so_far = 32767;
138 best = result = udp_port_rover; 158 best = result = udp_port_rover;
139 for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { 159 for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
140 struct hlist_head *list;
141 int size; 160 int size;
142 161
143 list = &udp_hash[result & (UDP_HTABLE_SIZE - 1)]; 162 head = &udp_hash[result & (UDP_HTABLE_SIZE - 1)];
144 if (hlist_empty(list)) { 163 if (hlist_empty(head)) {
145 if (result > sysctl_local_port_range[1]) 164 if (result > sysctl_local_port_range[1])
146 result = sysctl_local_port_range[0] + 165 result = sysctl_local_port_range[0] +
147 ((result - sysctl_local_port_range[0]) & 166 ((result - sysctl_local_port_range[0]) &
@@ -149,12 +168,11 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum)
149 goto gotit; 168 goto gotit;
150 } 169 }
151 size = 0; 170 size = 0;
152 sk_for_each(sk2, node, list) 171 sk_for_each(sk2, node, head)
153 if (++size >= best_size_so_far) 172 if (++size < best_size_so_far) {
154 goto next; 173 best_size_so_far = size;
155 best_size_so_far = size; 174 best = result;
156 best = result; 175 }
157 next:;
158 } 176 }
159 result = best; 177 result = best;
160 for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) { 178 for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) {
@@ -170,38 +188,44 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum)
170gotit: 188gotit:
171 udp_port_rover = snum = result; 189 udp_port_rover = snum = result;
172 } else { 190 } else {
173 sk_for_each(sk2, node, 191 head = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
174 &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]) { 192
175 struct inet_sock *inet2 = inet_sk(sk2); 193 sk_for_each(sk2, node, head)
176 194 if (inet_sk(sk2)->num == snum &&
177 if (inet2->num == snum && 195 sk2 != sk &&
178 sk2 != sk && 196 (!sk2->sk_reuse || !sk->sk_reuse) &&
179 !ipv6_only_sock(sk2) && 197 (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if
180 (!sk2->sk_bound_dev_if || 198 || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
181 !sk->sk_bound_dev_if || 199 (*saddr_cmp)(sk, sk2) )
182 sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
183 (!inet2->rcv_saddr ||
184 !inet->rcv_saddr ||
185 inet2->rcv_saddr == inet->rcv_saddr) &&
186 (!sk2->sk_reuse || !sk->sk_reuse))
187 goto fail; 200 goto fail;
188 }
189 } 201 }
190 inet->num = snum; 202 inet_sk(sk)->num = snum;
191 if (sk_unhashed(sk)) { 203 if (sk_unhashed(sk)) {
192 struct hlist_head *h = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; 204 head = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
193 205 sk_add_node(sk, head);
194 sk_add_node(sk, h);
195 sock_prot_inc_use(sk->sk_prot); 206 sock_prot_inc_use(sk->sk_prot);
196 } 207 }
197 write_unlock_bh(&udp_hash_lock); 208 error = 0;
198 return 0;
199
200fail: 209fail:
201 write_unlock_bh(&udp_hash_lock); 210 write_unlock_bh(&udp_hash_lock);
202 return 1; 211 return error;
212}
213
214static inline int ipv4_rcv_saddr_equal(struct sock *sk1, struct sock *sk2)
215{
216 struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2);
217
218 return ( !ipv6_only_sock(sk2) &&
219 (!inet1->rcv_saddr || !inet2->rcv_saddr ||
220 inet1->rcv_saddr == inet2->rcv_saddr ));
221}
222
223static inline int udp_v4_get_port(struct sock *sk, unsigned short snum)
224{
225 return udp_get_port(sk, snum, ipv4_rcv_saddr_equal);
203} 226}
204 227
228
205static void udp_v4_hash(struct sock *sk) 229static void udp_v4_hash(struct sock *sk)
206{ 230{
207 BUG(); 231 BUG();
@@ -1596,7 +1620,7 @@ EXPORT_SYMBOL(udp_disconnect);
1596EXPORT_SYMBOL(udp_hash); 1620EXPORT_SYMBOL(udp_hash);
1597EXPORT_SYMBOL(udp_hash_lock); 1621EXPORT_SYMBOL(udp_hash_lock);
1598EXPORT_SYMBOL(udp_ioctl); 1622EXPORT_SYMBOL(udp_ioctl);
1599EXPORT_SYMBOL(udp_port_rover); 1623EXPORT_SYMBOL(udp_get_port);
1600EXPORT_SYMBOL(udp_prot); 1624EXPORT_SYMBOL(udp_prot);
1601EXPORT_SYMBOL(udp_sendmsg); 1625EXPORT_SYMBOL(udp_sendmsg);
1602EXPORT_SYMBOL(udp_poll); 1626EXPORT_SYMBOL(udp_poll);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index b9cc55ccb000..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)