diff options
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/inet6_hashtables.c | 93 |
1 files changed, 2 insertions, 91 deletions
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index 06b01befc90e..ece6d0ee2da5 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c | |||
@@ -233,97 +233,8 @@ static inline u32 inet6_sk_port_offset(const struct sock *sk) | |||
233 | int inet6_hash_connect(struct inet_timewait_death_row *death_row, | 233 | int inet6_hash_connect(struct inet_timewait_death_row *death_row, |
234 | struct sock *sk) | 234 | struct sock *sk) |
235 | { | 235 | { |
236 | struct inet_hashinfo *hinfo = death_row->hashinfo; | 236 | return __inet_hash_connect(death_row, sk, |
237 | const unsigned short snum = inet_sk(sk)->num; | 237 | __inet6_check_established, __inet6_hash); |
238 | struct inet_bind_hashbucket *head; | ||
239 | struct inet_bind_bucket *tb; | ||
240 | int ret; | ||
241 | |||
242 | if (snum == 0) { | ||
243 | int i, port, low, high, remaining; | ||
244 | static u32 hint; | ||
245 | const u32 offset = hint + inet6_sk_port_offset(sk); | ||
246 | struct hlist_node *node; | ||
247 | struct inet_timewait_sock *tw = NULL; | ||
248 | |||
249 | inet_get_local_port_range(&low, &high); | ||
250 | remaining = (high - low) + 1; | ||
251 | |||
252 | local_bh_disable(); | ||
253 | for (i = 1; i <= remaining; i++) { | ||
254 | port = low + (i + offset) % remaining; | ||
255 | head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)]; | ||
256 | spin_lock(&head->lock); | ||
257 | |||
258 | /* Does not bother with rcv_saddr checks, | ||
259 | * because the established check is already | ||
260 | * unique enough. | ||
261 | */ | ||
262 | inet_bind_bucket_for_each(tb, node, &head->chain) { | ||
263 | if (tb->port == port) { | ||
264 | BUG_TRAP(!hlist_empty(&tb->owners)); | ||
265 | if (tb->fastreuse >= 0) | ||
266 | goto next_port; | ||
267 | if (!__inet6_check_established(death_row, | ||
268 | sk, port, | ||
269 | &tw)) | ||
270 | goto ok; | ||
271 | goto next_port; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep, | ||
276 | head, port); | ||
277 | if (!tb) { | ||
278 | spin_unlock(&head->lock); | ||
279 | break; | ||
280 | } | ||
281 | tb->fastreuse = -1; | ||
282 | goto ok; | ||
283 | |||
284 | next_port: | ||
285 | spin_unlock(&head->lock); | ||
286 | } | ||
287 | local_bh_enable(); | ||
288 | |||
289 | return -EADDRNOTAVAIL; | ||
290 | |||
291 | ok: | ||
292 | hint += i; | ||
293 | |||
294 | /* Head lock still held and bh's disabled */ | ||
295 | inet_bind_hash(sk, tb, port); | ||
296 | if (sk_unhashed(sk)) { | ||
297 | inet_sk(sk)->sport = htons(port); | ||
298 | __inet6_hash(hinfo, sk); | ||
299 | } | ||
300 | spin_unlock(&head->lock); | ||
301 | |||
302 | if (tw) { | ||
303 | inet_twsk_deschedule(tw, death_row); | ||
304 | inet_twsk_put(tw); | ||
305 | } | ||
306 | |||
307 | ret = 0; | ||
308 | goto out; | ||
309 | } | ||
310 | |||
311 | head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)]; | ||
312 | tb = inet_csk(sk)->icsk_bind_hash; | ||
313 | spin_lock_bh(&head->lock); | ||
314 | |||
315 | if (sk_head(&tb->owners) == sk && sk->sk_bind_node.next == NULL) { | ||
316 | __inet6_hash(hinfo, sk); | ||
317 | spin_unlock_bh(&head->lock); | ||
318 | return 0; | ||
319 | } else { | ||
320 | spin_unlock(&head->lock); | ||
321 | /* No definite answer... Walk to established hash table */ | ||
322 | ret = __inet6_check_established(death_row, sk, snum, NULL); | ||
323 | out: | ||
324 | local_bh_enable(); | ||
325 | return ret; | ||
326 | } | ||
327 | } | 238 | } |
328 | 239 | ||
329 | EXPORT_SYMBOL_GPL(inet6_hash_connect); | 240 | EXPORT_SYMBOL_GPL(inet6_hash_connect); |